luau/Analysis/src/ConstraintGraphBuilder.cpp

2399 lines
87 KiB
C++
Raw Normal View History

2022-06-03 21:32:20 +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-10-21 18:33:43 +01:00
2022-08-11 21:42:54 +01:00
#include "Luau/Ast.h"
2022-08-18 22:04:33 +01:00
#include "Luau/Common.h"
2022-08-11 21:42:54 +01:00
#include "Luau/Constraint.h"
2022-10-21 18:33:43 +01:00
#include "Luau/DcrLogger.h"
2022-09-02 00:00:14 +01:00
#include "Luau/ModuleResolver.h"
2022-07-01 00:29:02 +01:00
#include "Luau/RecursionCounter.h"
2022-10-21 18:33:43 +01:00
#include "Luau/Scope.h"
#include "Luau/TypeUtils.h"
2023-01-03 17:33:19 +00:00
#include "Luau/Type.h"
2022-07-01 00:29:02 +01:00
LUAU_FASTINT(LuauCheckRecursionLimit);
2022-09-08 22:44:50 +01:00
LUAU_FASTFLAG(DebugLuauLogSolverToJson);
2022-09-29 23:11:54 +01:00
LUAU_FASTFLAG(DebugLuauMagicTypes);
2023-01-03 17:33:19 +00:00
LUAU_FASTFLAG(LuauNegatedClassTypes);
2023-01-20 12:02:39 +00:00
LUAU_FASTFLAG(SupportTypeAliasGoToDeclaration);
2022-06-03 21:32:20 +01:00
namespace Luau
{
2022-06-17 01:54:42 +01:00
const AstStat* getFallthrough(const AstStat* node); // TypeInfer.cpp
2022-06-03 21:32:20 +01:00
2022-09-02 00:00:14 +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];
}
2022-09-23 19:32:10 +01:00
static bool matchSetmetatable(const AstExprCall& call)
{
const char* smt = "setmetatable";
if (call.args.size != 2)
return false;
2022-10-13 23:59:53 +01:00
2022-09-23 19:32:10 +01:00
const AstExprGlobal* funcAsGlobal = call.func->as<AstExprGlobal>();
if (!funcAsGlobal || funcAsGlobal->name != smt)
return false;
return true;
}
2022-11-10 22:04:44 +00:00
struct TypeGuard
{
bool isTypeof;
AstExpr* target;
std::string type;
};
static std::optional<TypeGuard> matchTypeGuard(const AstExprBinary* binary)
{
if (binary->op != AstExprBinary::CompareEq && binary->op != AstExprBinary::CompareNe)
return std::nullopt;
AstExpr* left = binary->left;
AstExpr* right = binary->right;
if (right->is<AstExprCall>())
std::swap(left, right);
if (!right->is<AstExprConstantString>())
return std::nullopt;
AstExprCall* call = left->as<AstExprCall>();
AstExprConstantString* string = right->as<AstExprConstantString>();
if (!call || !string)
return std::nullopt;
AstExprGlobal* callee = call->func->as<AstExprGlobal>();
if (!callee)
return std::nullopt;
if (callee->name != "type" && callee->name != "typeof")
return std::nullopt;
if (call->args.size != 1)
return std::nullopt;
return TypeGuard{
/*isTypeof*/ callee->name == "typeof",
/*target*/ call->args.data[0],
/*type*/ std::string(string->value.data, string->value.size),
};
}
2023-02-10 18:50:54 +00:00
static bool matchAssert(const AstExprCall& call)
{
if (call.args.size < 1)
return false;
const AstExprGlobal* funcAsGlobal = call.func->as<AstExprGlobal>();
if (!funcAsGlobal || funcAsGlobal->name != "assert")
return false;
return true;
}
2022-11-10 22:04:44 +00:00
namespace
{
struct Checkpoint
{
size_t offset;
};
Checkpoint checkpoint(const ConstraintGraphBuilder* cgb)
{
return Checkpoint{cgb->constraints.size()};
}
template<typename F>
void forEachConstraint(const Checkpoint& start, const Checkpoint& end, const ConstraintGraphBuilder* cgb, F f)
{
for (size_t i = start.offset; i < end.offset; ++i)
f(cgb->constraints[i]);
}
} // namespace
2022-09-02 00:00:14 +01:00
ConstraintGraphBuilder::ConstraintGraphBuilder(const ModuleName& moduleName, ModulePtr module, TypeArena* arena,
2023-01-03 17:33:19 +00:00
NotNull<ModuleResolver> moduleResolver, NotNull<BuiltinTypes> builtinTypes, NotNull<InternalErrorReporter> ice, const ScopePtr& globalScope,
2022-10-21 18:33:43 +01:00
DcrLogger* logger, NotNull<DataFlowGraph> dfg)
2022-07-01 00:29:02 +01:00
: moduleName(moduleName)
2022-08-11 21:42:54 +01:00
, module(module)
2023-01-03 17:33:19 +00:00
, builtinTypes(builtinTypes)
2022-06-03 21:32:20 +01:00
, arena(arena)
, rootScope(nullptr)
2022-10-21 18:33:43 +01:00
, dfg(dfg)
2022-09-02 00:00:14 +01:00
, moduleResolver(moduleResolver)
2022-07-01 00:29:02 +01:00
, ice(ice)
, globalScope(globalScope)
2022-09-08 22:44:50 +01:00
, logger(logger)
2022-06-03 21:32:20 +01:00
{
2022-09-08 22:44:50 +01:00
if (FFlag::DebugLuauLogSolverToJson)
LUAU_ASSERT(logger);
2022-08-18 22:04:33 +01:00
LUAU_ASSERT(module);
2022-06-03 21:32:20 +01:00
}
2022-07-29 04:41:13 +01:00
TypeId ConstraintGraphBuilder::freshType(const ScopePtr& scope)
2022-06-03 21:32:20 +01:00
{
2023-01-03 17:33:19 +00:00
return arena->addType(FreeType{scope.get()});
2022-06-03 21:32:20 +01:00
}
2022-07-29 04:41:13 +01:00
TypePackId ConstraintGraphBuilder::freshTypePack(const ScopePtr& scope)
2022-06-03 21:32:20 +01:00
{
2022-07-29 04:41:13 +01:00
FreeTypePack f{scope.get()};
2022-06-03 21:32:20 +01:00
return arena->addTypePack(TypePackVar{std::move(f)});
}
2022-08-18 22:04:33 +01:00
ScopePtr ConstraintGraphBuilder::childScope(AstNode* node, const ScopePtr& parent)
2022-06-03 21:32:20 +01:00
{
2022-07-29 04:41:13 +01:00
auto scope = std::make_shared<Scope>(parent);
2022-08-18 22:04:33 +01:00
scopes.emplace_back(node->location, scope);
2022-06-03 21:32:20 +01:00
2022-07-29 04:41:13 +01:00
scope->returnType = parent->returnType;
2022-09-08 22:44:50 +01:00
scope->varargPack = parent->varargPack;
2022-08-18 22:04:33 +01:00
parent->children.push_back(NotNull{scope.get()});
module->astScopes[node] = scope.get();
2022-06-03 21:32:20 +01:00
2022-07-29 04:41:13 +01:00
return scope;
2022-06-03 21:32:20 +01:00
}
2022-10-21 18:33:43 +01:00
NotNull<Constraint> ConstraintGraphBuilder::addConstraint(const ScopePtr& scope, const Location& location, ConstraintV cv)
2022-06-03 21:32:20 +01:00
{
2022-11-10 22:04:44 +00:00
return NotNull{constraints.emplace_back(new Constraint{NotNull{scope.get()}, location, std::move(cv)}).get()};
2022-06-03 21:32:20 +01:00
}
2022-10-21 18:33:43 +01:00
NotNull<Constraint> ConstraintGraphBuilder::addConstraint(const ScopePtr& scope, std::unique_ptr<Constraint> c)
2022-06-03 21:32:20 +01:00
{
2022-11-10 22:04:44 +00:00
return NotNull{constraints.emplace_back(std::move(c)).get()};
2022-06-03 21:32:20 +01:00
}
2022-11-04 17:02:37 +00:00
static void unionRefinements(const std::unordered_map<DefId, TypeId>& lhs, const std::unordered_map<DefId, TypeId>& rhs,
std::unordered_map<DefId, TypeId>& dest, NotNull<TypeArena> arena)
{
for (auto [def, ty] : lhs)
{
auto rhsIt = rhs.find(def);
if (rhsIt == rhs.end())
continue;
std::vector<TypeId> discriminants{{ty, rhsIt->second}};
if (auto destIt = dest.find(def); destIt != dest.end())
discriminants.push_back(destIt->second);
2023-01-03 17:33:19 +00:00
dest[def] = arena->addType(UnionType{std::move(discriminants)});
2022-11-04 17:02:37 +00:00
}
}
2023-02-03 12:34:12 +00:00
static void computeRefinement(const ScopePtr& scope, RefinementId refinement, std::unordered_map<DefId, TypeId>* refis, bool sense,
2022-12-02 10:46:05 +00:00
NotNull<TypeArena> arena, bool eq, std::vector<ConstraintV>* constraints)
2022-11-04 17:02:37 +00:00
{
using RefinementMap = std::unordered_map<DefId, TypeId>;
2023-02-03 12:34:12 +00:00
if (!refinement)
2022-11-04 17:02:37 +00:00
return;
2023-02-10 18:50:54 +00:00
else if (auto variadic = get<Variadic>(refinement))
{
for (RefinementId refi : variadic->refinements)
computeRefinement(scope, refi, refis, sense, arena, eq, constraints);
}
2023-02-03 12:34:12 +00:00
else if (auto negation = get<Negation>(refinement))
return computeRefinement(scope, negation->refinement, refis, !sense, arena, eq, constraints);
else if (auto conjunction = get<Conjunction>(refinement))
2022-11-04 17:02:37 +00:00
{
RefinementMap lhsRefis;
RefinementMap rhsRefis;
computeRefinement(scope, conjunction->lhs, sense ? refis : &lhsRefis, sense, arena, eq, constraints);
computeRefinement(scope, conjunction->rhs, sense ? refis : &rhsRefis, sense, arena, eq, constraints);
if (!sense)
unionRefinements(lhsRefis, rhsRefis, *refis, arena);
}
2023-02-03 12:34:12 +00:00
else if (auto disjunction = get<Disjunction>(refinement))
2022-11-04 17:02:37 +00:00
{
RefinementMap lhsRefis;
RefinementMap rhsRefis;
computeRefinement(scope, disjunction->lhs, sense ? &lhsRefis : refis, sense, arena, eq, constraints);
computeRefinement(scope, disjunction->rhs, sense ? &rhsRefis : refis, sense, arena, eq, constraints);
if (sense)
unionRefinements(lhsRefis, rhsRefis, *refis, arena);
}
2023-02-03 12:34:12 +00:00
else if (auto equivalence = get<Equivalence>(refinement))
2022-11-04 17:02:37 +00:00
{
computeRefinement(scope, equivalence->lhs, refis, sense, arena, true, constraints);
computeRefinement(scope, equivalence->rhs, refis, sense, arena, true, constraints);
}
2023-02-03 12:34:12 +00:00
else if (auto proposition = get<Proposition>(refinement))
2022-11-04 17:02:37 +00:00
{
TypeId discriminantTy = proposition->discriminantTy;
if (!sense && !eq)
2023-01-03 17:33:19 +00:00
discriminantTy = arena->addType(NegationType{proposition->discriminantTy});
2022-12-02 10:46:05 +00:00
else if (eq)
2022-11-04 17:02:37 +00:00
{
2023-01-03 17:33:19 +00:00
discriminantTy = arena->addType(BlockedType{});
2022-12-02 10:46:05 +00:00
constraints->push_back(SingletonOrTopTypeConstraint{discriminantTy, proposition->discriminantTy, !sense});
2022-11-04 17:02:37 +00:00
}
if (auto it = refis->find(proposition->def); it != refis->end())
2023-01-03 17:33:19 +00:00
(*refis)[proposition->def] = arena->addType(IntersectionType{{discriminantTy, it->second}});
2022-11-04 17:02:37 +00:00
else
(*refis)[proposition->def] = discriminantTy;
}
}
2022-12-02 10:46:05 +00:00
static std::pair<DefId, TypeId> computeDiscriminantType(NotNull<TypeArena> arena, const ScopePtr& scope, DefId def, TypeId discriminantTy)
{
LUAU_ASSERT(get<Cell>(def));
while (const Cell* current = get<Cell>(def))
{
if (!current->field)
break;
2023-01-03 17:33:19 +00:00
TableType::Props props{{current->field->propName, Property{discriminantTy}}};
discriminantTy = arena->addType(TableType{std::move(props), std::nullopt, TypeLevel{}, scope.get(), TableState::Sealed});
2022-12-02 10:46:05 +00:00
def = current->field->parent;
current = get<Cell>(def);
}
return {def, discriminantTy};
}
2023-02-03 12:34:12 +00:00
void ConstraintGraphBuilder::applyRefinements(const ScopePtr& scope, Location location, RefinementId refinement)
2022-11-04 17:02:37 +00:00
{
2023-02-03 12:34:12 +00:00
if (!refinement)
2022-11-04 17:02:37 +00:00
return;
std::unordered_map<DefId, TypeId> refinements;
2022-12-02 10:46:05 +00:00
std::vector<ConstraintV> constraints;
2023-02-03 12:34:12 +00:00
computeRefinement(scope, refinement, &refinements, /*sense*/ true, arena, /*eq*/ false, &constraints);
2022-11-04 17:02:37 +00:00
for (auto [def, discriminantTy] : refinements)
{
2022-12-02 10:46:05 +00:00
auto [def2, discriminantTy2] = computeDiscriminantType(arena, scope, def, discriminantTy);
std::optional<TypeId> defTy = scope->lookup(def2);
2022-11-04 17:02:37 +00:00
if (!defTy)
ice->ice("Every DefId must map to a type!");
2023-01-03 17:33:19 +00:00
TypeId resultTy = arena->addType(IntersectionType{{*defTy, discriminantTy2}});
2022-12-02 10:46:05 +00:00
scope->dcrRefinements[def2] = resultTy;
2022-11-04 17:02:37 +00:00
}
for (auto& c : constraints)
addConstraint(scope, location, c);
}
2022-06-03 21:32:20 +01:00
void ConstraintGraphBuilder::visit(AstStatBlock* block)
{
LUAU_ASSERT(scopes.empty());
LUAU_ASSERT(rootScope == nullptr);
2022-08-11 21:42:54 +01:00
ScopePtr scope = std::make_shared<Scope>(globalScope);
2022-07-29 04:41:13 +01:00
rootScope = scope.get();
scopes.emplace_back(block->location, scope);
2022-08-18 22:04:33 +01:00
module->astScopes[block] = NotNull{scope.get()};
2022-07-01 00:29:02 +01:00
2022-07-29 04:41:13 +01:00
rootScope->returnType = freshTypePack(scope);
2022-07-01 00:29:02 +01:00
2022-07-29 04:41:13 +01:00
prepopulateGlobalScope(scope, block);
2022-06-03 21:32:20 +01:00
2022-07-29 04:41:13 +01:00
visitBlockWithoutChildScope(scope, block);
2022-07-01 00:29:02 +01:00
}
2022-07-29 04:41:13 +01:00
void ConstraintGraphBuilder::visitBlockWithoutChildScope(const ScopePtr& scope, AstStatBlock* block)
2022-07-01 00:29:02 +01:00
{
RecursionCounter counter{&recursionCount};
if (recursionCount >= FInt::LuauCheckRecursionLimit)
{
reportCodeTooComplex(block->location);
return;
}
2022-08-04 22:27:28 +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 21:42:54 +01:00
if (scope->privateTypeBindings.count(alias->name.value) != 0)
2022-08-04 22:27:28 +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:04:33 +01:00
defnScope = childScope(alias, scope);
2022-08-04 22:27:28 +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 21:42:54 +01:00
defnScope->privateTypeBindings[name] = TypeFun{gen.ty};
2022-08-04 22:27:28 +01:00
}
for (const auto& [name, genPack] : createGenericPacks(defnScope, alias->genericPacks))
{
initialFun.typePackParams.push_back(genPack);
2022-08-11 21:42:54 +01:00
defnScope->privateTypePackBindings[name] = genPack.tp;
2022-08-04 22:27:28 +01:00
}
2022-08-11 21:42:54 +01:00
scope->privateTypeBindings[alias->name.value] = std::move(initialFun);
2022-08-04 22:27:28 +01:00
astTypeAliasDefiningScopes[alias] = defnScope;
aliasDefinitionLocations[alias->name.value] = alias->location;
}
}
2022-07-01 00:29:02 +01:00
for (AstStat* stat : block->body)
visit(scope, stat);
2022-06-03 21:32:20 +01:00
}
2022-07-29 04:41:13 +01:00
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStat* stat)
2022-06-03 21:32:20 +01:00
{
2022-07-01 00:29:02 +01:00
RecursionLimiter limiter{&recursionCount, FInt::LuauCheckRecursionLimit};
2022-06-03 21:32:20 +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:39:35 +01:00
else if (auto s = stat->as<AstStatFor>())
visit(scope, s);
2022-09-02 00:00:14 +01:00
else if (auto s = stat->as<AstStatForIn>())
visit(scope, s);
2022-08-11 21:42:54 +01:00
else if (auto s = stat->as<AstStatWhile>())
visit(scope, s);
2022-08-18 22:04:33 +01:00
else if (auto s = stat->as<AstStatRepeat>())
visit(scope, s);
2022-06-17 01:54:42 +01:00
else if (auto f = stat->as<AstStatFunction>())
visit(scope, f);
2022-06-03 21:32:20 +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 01:54:42 +01:00
else if (auto a = stat->as<AstStatAssign>())
visit(scope, a);
2022-08-18 22:04:33 +01:00
else if (auto a = stat->as<AstStatCompoundAssign>())
visit(scope, a);
2022-06-17 01:54:42 +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:44:07 +01:00
else if (auto a = stat->as<AstStatTypeAlias>())
visit(scope, a);
2022-08-04 22:27:28 +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-09-29 23:11:54 +01:00
else if (auto s = stat->as<AstStatError>())
visit(scope, s);
2022-06-03 21:32:20 +01:00
else
LUAU_ASSERT(0);
}
2022-07-29 04:41:13 +01:00
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocal* local)
2022-06-03 21:32:20 +01:00
{
std::vector<TypeId> varTypes;
2022-10-21 18:33:43 +01:00
varTypes.reserve(local->vars.size);
2022-06-03 21:32:20 +01:00
2023-01-27 21:28:45 +00:00
// Used to name the first value type, even if it's not placed in varTypes,
// for the purpose of synthetic name attribution.
std::optional<TypeId> firstValueType;
2022-06-03 21:32:20 +01:00
for (AstLocal* local : local->vars)
{
2022-09-23 19:32:10 +01:00
TypeId ty = nullptr;
2022-06-24 02:44:07 +01:00
if (local->annotation)
2023-01-20 12:02:39 +00:00
ty = resolveType(scope, local->annotation, /* inTypeArguments */ false);
2022-06-24 02:44:07 +01:00
2022-06-03 21:32:20 +01:00
varTypes.push_back(ty);
}
2022-06-17 01:54:42 +01:00
for (size_t i = 0; i < local->values.size; ++i)
2022-06-03 21:32:20 +01:00
{
2022-08-25 21:55:08 +01:00
AstExpr* value = local->values.data[i];
2022-09-23 19:32:10 +01:00
const bool hasAnnotation = i < local->vars.size && nullptr != local->vars.data[i]->annotation;
2022-08-25 21:55:08 +01:00
if (value->is<AstExprConstantNil>())
2022-06-17 01:54:42 +01:00
{
2022-11-04 17:02:37 +00: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.
2022-10-21 18:33:43 +01:00
if (!varTypes[i])
varTypes[i] = freshType(scope);
2022-06-17 01:54:42 +01:00
}
2022-11-04 17:02:37 +00:00
// Only function calls and vararg expressions can produce packs. All
// other expressions produce exactly one value.
else if (i != local->values.size - 1 || (!value->is<AstExprCall>() && !value->is<AstExprVarargs>()))
{
std::optional<TypeId> expectedType;
if (hasAnnotation)
expectedType = varTypes.at(i);
TypeId exprType = check(scope, value, expectedType).ty;
if (i < varTypes.size())
{
if (varTypes[i])
addConstraint(scope, local->location, SubtypeConstraint{exprType, varTypes[i]});
else
varTypes[i] = exprType;
}
2023-01-27 21:28:45 +00:00
if (i == 0)
firstValueType = exprType;
2022-11-04 17:02:37 +00:00
}
else
2022-06-17 01:54:42 +01:00
{
2022-09-23 19:32:10 +01:00
std::vector<TypeId> expectedTypes;
if (hasAnnotation)
expectedTypes.insert(begin(expectedTypes), begin(varTypes) + i, end(varTypes));
2022-10-27 23:22:49 +01:00
TypePackId exprPack = checkPack(scope, value, expectedTypes).tp;
2022-06-17 01:54:42 +01:00
if (i < local->vars.size)
{
2023-01-03 17:33:19 +00:00
TypePack packTypes = extendTypePack(*arena, builtinTypes, exprPack, varTypes.size() - i);
2022-10-21 18:33:43 +01:00
// fill out missing values in varTypes with values from exprPack
for (size_t j = i; j < varTypes.size(); ++j)
{
if (!varTypes[j])
{
2022-12-02 10:46:05 +00:00
if (j - i < packTypes.head.size())
varTypes[j] = packTypes.head[j - i];
2022-10-21 18:33:43 +01:00
else
varTypes[j] = freshType(scope);
}
}
2022-06-17 01:54:42 +01:00
std::vector<TypeId> tailValues{varTypes.begin() + i, varTypes.end()};
TypePackId tailPack = arena->addTypePack(std::move(tailValues));
2022-09-02 00:00:14 +01:00
addConstraint(scope, local->location, PackSubtypeConstraint{exprPack, tailPack});
2022-06-17 01:54:42 +01:00
}
}
2022-09-02 00:00:14 +01:00
}
2023-01-27 21:28:45 +00:00
if (local->vars.size == 1 && local->values.size == 1 && firstValueType)
{
AstLocal* var = local->vars.data[0];
AstExpr* value = local->values.data[0];
if (value->is<AstExprTable>())
addConstraint(scope, value->location, NameConstraint{*firstValueType, var->name.value, /*synthetic*/ true});
else if (const AstExprCall* call = value->as<AstExprCall>())
{
if (const AstExprGlobal* global = call->func->as<AstExprGlobal>(); global && global->name == "setmetatable")
{
addConstraint(scope, value->location, NameConstraint{*firstValueType, var->name.value, /*synthetic*/ true});
}
}
}
2022-10-21 18:33:43 +01:00
for (size_t i = 0; i < local->vars.size; ++i)
{
AstLocal* l = local->vars.data[i];
Location location = l->location;
if (!varTypes[i])
varTypes[i] = freshType(scope);
scope->bindings[l] = Binding{varTypes[i], location};
// HACK: In the greedy solver, we say the type state of a variable is the type annotation itself, but
// the actual type state is the corresponding initializer expression (if it exists) or nil otherwise.
if (auto def = dfg->getDef(l))
scope->dcrRefinements[*def] = varTypes[i];
}
2022-09-02 00:00:14 +01:00
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))
2023-01-20 12:02:39 +00:00
{
2023-02-10 18:50:54 +00:00
scope->importedTypeBindings[name] = module->exportedTypeBindings;
2023-01-20 12:02:39 +00:00
if (FFlag::SupportTypeAliasGoToDeclaration)
scope->importedModules[name] = moduleName;
}
2022-09-02 00:00:14 +01:00
}
}
2022-06-03 21:32:20 +01:00
}
}
}
2022-07-29 04:41:13 +01:00
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatFor* for_)
2022-07-14 23:39:35 +01:00
{
2023-02-03 12:34:12 +00:00
if (for_->var->annotation)
resolveType(scope, for_->var->annotation, /* inTypeArguments */ false);
auto inferNumber = [&](AstExpr* expr) {
2022-07-14 23:39:35 +01:00
if (!expr)
return;
2022-08-04 22:27:28 +01:00
2022-10-27 23:22:49 +01:00
TypeId t = check(scope, expr).ty;
2023-01-03 17:33:19 +00:00
addConstraint(scope, expr->location, SubtypeConstraint{t, builtinTypes->numberType});
2022-07-14 23:39:35 +01:00
};
2023-02-03 12:34:12 +00:00
inferNumber(for_->from);
inferNumber(for_->to);
inferNumber(for_->step);
2022-07-14 23:39:35 +01:00
2022-08-18 22:04:33 +01:00
ScopePtr forScope = childScope(for_, scope);
2023-01-03 17:33:19 +00:00
forScope->bindings[for_->var] = Binding{builtinTypes->numberType, for_->var->location};
2022-07-14 23:39:35 +01:00
visit(forScope, for_->body);
}
2022-09-02 00:00:14 +01:00
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatForIn* forIn)
{
ScopePtr loopScope = childScope(forIn, scope);
2022-10-27 23:22:49 +01:00
TypePackId iterator = checkPack(scope, forIn->values).tp;
2022-09-02 00:00:14 +01:00
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);
2022-11-10 22:04:44 +00:00
if (auto def = dfg->getDef(var))
loopScope->dcrRefinements[*def] = ty;
2022-09-02 00:00:14 +01:00
}
// 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 21:42:54 +01:00
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatWhile* while_)
{
check(scope, while_->condition);
2022-08-18 22:04:33 +01:00
ScopePtr whileScope = childScope(while_, scope);
2022-08-11 21:42:54 +01:00
visit(whileScope, while_->body);
}
2022-08-18 22:04:33 +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 04:41:13 +01:00
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocalFunction* function)
2022-06-03 21:32:20 +01:00
{
// Local
// Global
// Dotted path
// Self?
TypeId functionType = nullptr;
auto ty = scope->lookup(function->name);
2022-07-01 00:29:02 +01:00
LUAU_ASSERT(!ty.has_value()); // The parser ensures that every local function has a distinct Symbol for its name.
2023-01-03 17:33:19 +00:00
functionType = arena->addType(BlockedType{});
2022-07-29 04:41:13 +01:00
scope->bindings[function->name] = Binding{functionType, function->name->location};
2022-06-03 21:32:20 +01:00
2022-07-01 00:29:02 +01:00
FunctionSignature sig = checkFunctionSignature(scope, function->func);
2022-07-29 04:41:13 +01:00
sig.bodyScope->bindings[function->name] = Binding{sig.signature, function->func->location};
2022-06-03 21:32:20 +01:00
2022-12-02 10:46:05 +00:00
Checkpoint start = checkpoint(this);
2022-07-01 00:29:02 +01:00
checkFunctionBody(sig.bodyScope, function->func);
2022-12-02 10:46:05 +00:00
Checkpoint end = checkpoint(this);
2022-06-03 21:32:20 +01:00
2022-08-18 22:04:33 +01:00
NotNull<Scope> constraintScope{sig.signatureScope ? sig.signatureScope.get() : sig.bodyScope.get()};
2022-09-02 00:00:14 +01:00
std::unique_ptr<Constraint> c =
std::make_unique<Constraint>(constraintScope, function->name->location, GeneralizationConstraint{functionType, sig.signature});
2022-11-10 22:04:44 +00:00
forEachConstraint(start, end, this, [&c](const ConstraintPtr& constraint) {
c->dependencies.push_back(NotNull{constraint.get()});
});
2022-06-17 01:54:42 +01:00
addConstraint(scope, std::move(c));
}
2022-07-29 04:41:13 +01:00
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatFunction* function)
2022-06-17 01:54:42 +01:00
{
// Name could be AstStatLocal, AstStatGlobal, AstStatIndexName.
// With or without self
2023-01-03 17:33:19 +00:00
TypeId generalizedType = arena->addType(BlockedType{});
2022-06-17 01:54:42 +01:00
2022-07-01 00:29:02 +01:00
FunctionSignature sig = checkFunctionSignature(scope, function->func);
2022-06-03 21:32:20 +01:00
2022-06-17 01:54:42 +01:00
if (AstExprLocal* localName = function->name->as<AstExprLocal>())
2022-06-03 21:32:20 +01:00
{
2022-06-17 01:54:42 +01:00
std::optional<TypeId> existingFunctionTy = scope->lookup(localName->local);
if (existingFunctionTy)
{
2022-12-02 10:46:05 +00:00
addConstraint(scope, function->name->location, SubtypeConstraint{generalizedType, *existingFunctionTy});
Symbol sym{localName->local};
std::optional<DefId> def = dfg->getDef(sym);
LUAU_ASSERT(def);
scope->bindings[sym].typeId = generalizedType;
scope->dcrRefinements[*def] = generalizedType;
2022-06-17 01:54:42 +01:00
}
else
2022-12-02 10:46:05 +00:00
scope->bindings[localName->local] = Binding{generalizedType, localName->location};
2022-07-29 04:41:13 +01:00
sig.bodyScope->bindings[localName->local] = Binding{sig.signature, localName->location};
2022-06-17 01:54:42 +01:00
}
else if (AstExprGlobal* globalName = function->name->as<AstExprGlobal>())
{
std::optional<TypeId> existingFunctionTy = scope->lookup(globalName->name);
2022-12-02 10:46:05 +00:00
if (!existingFunctionTy)
ice->ice("prepopulateGlobalScope did not populate a global name", globalName->location);
generalizedType = *existingFunctionTy;
2022-07-29 04:41:13 +01:00
sig.bodyScope->bindings[globalName->name] = Binding{sig.signature, globalName->location};
2022-06-17 01:54:42 +01:00
}
else if (AstExprIndexName* indexName = function->name->as<AstExprIndexName>())
{
2023-02-10 18:50:54 +00:00
TypeId lvalueType = checkLValue(scope, indexName);
// TODO figure out how to populate the location field of the table Property.
addConstraint(scope, indexName->location, SubtypeConstraint{lvalueType, generalizedType});
2022-07-01 00:29:02 +01:00
}
else if (AstExprError* err = function->name->as<AstExprError>())
{
2023-01-03 17:33:19 +00:00
generalizedType = builtinTypes->errorRecoveryType();
2022-06-03 21:32:20 +01:00
}
2022-12-02 10:46:05 +00:00
if (generalizedType == nullptr)
ice->ice("generalizedType == nullptr", function->location);
2022-06-03 21:32:20 +01:00
2022-12-02 10:46:05 +00:00
Checkpoint start = checkpoint(this);
2022-07-01 00:29:02 +01:00
checkFunctionBody(sig.bodyScope, function->func);
2022-12-02 10:46:05 +00:00
Checkpoint end = checkpoint(this);
2022-07-01 00:29:02 +01:00
2022-08-18 22:04:33 +01:00
NotNull<Scope> constraintScope{sig.signatureScope ? sig.signatureScope.get() : sig.bodyScope.get()};
2022-09-02 00:00:14 +01:00
std::unique_ptr<Constraint> c =
2022-12-02 10:46:05 +00:00
std::make_unique<Constraint>(constraintScope, function->name->location, GeneralizationConstraint{generalizedType, sig.signature});
2022-11-10 22:04:44 +00:00
forEachConstraint(start, end, this, [&c](const ConstraintPtr& constraint) {
c->dependencies.push_back(NotNull{constraint.get()});
});
2022-06-03 21:32:20 +01:00
addConstraint(scope, std::move(c));
}
2022-07-29 04:41:13 +01:00
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatReturn* ret)
2022-06-03 21:32:20 +01:00
{
2022-09-23 19:32:10 +01:00
// At this point, the only way scope->returnType should have anything
// interesting in it is if the function has an explicit return annotation.
// If this is the case, then we can expect that the return expression
// conforms to that.
std::vector<TypeId> expectedTypes;
for (TypeId ty : scope->returnType)
expectedTypes.push_back(ty);
2022-10-27 23:22:49 +01:00
TypePackId exprTypes = checkPack(scope, ret->list, expectedTypes).tp;
2022-09-02 00:00:14 +01:00
addConstraint(scope, ret->location, PackSubtypeConstraint{exprTypes, scope->returnType});
2022-06-03 21:32:20 +01:00
}
2022-07-29 04:41:13 +01:00
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatBlock* block)
2022-06-03 21:32:20 +01:00
{
2022-08-18 22:04:33 +01:00
ScopePtr innerScope = childScope(block, scope);
2022-06-03 21:32:20 +01:00
2022-07-01 00:29:02 +01:00
visitBlockWithoutChildScope(innerScope, block);
2022-06-03 21:32:20 +01:00
}
2022-07-29 04:41:13 +01:00
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatAssign* assign)
2022-06-17 01:54:42 +01:00
{
2022-10-21 18:33:43 +01:00
TypePackId varPackId = checkLValues(scope, assign->vars);
2022-12-02 10:46:05 +00:00
2023-01-03 17:33:19 +00:00
TypePack expectedTypes = extendTypePack(*arena, builtinTypes, varPackId, assign->values.size);
2022-12-02 10:46:05 +00:00
TypePackId valuePack = checkPack(scope, assign->values, expectedTypes.head).tp;
2022-06-17 01:54:42 +01:00
2022-09-02 00:00:14 +01:00
addConstraint(scope, assign->location, PackSubtypeConstraint{valuePack, varPackId});
2022-06-17 01:54:42 +01:00
}
2022-08-18 22:04:33 +01:00
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatCompoundAssign* assign)
{
2023-01-06 16:07:19 +00:00
// We need to tweak the BinaryConstraint that we emit, so we cannot use the
// strategy of falsifying an AST fragment.
TypeId varId = checkLValue(scope, assign->var);
Inference valueInf = check(scope, assign->value);
2022-08-18 22:04:33 +01:00
2023-01-06 16:07:19 +00:00
TypeId resultType = arena->addType(BlockedType{});
addConstraint(scope, assign->location,
2023-02-03 12:34:12 +00:00
BinaryConstraint{assign->op, varId, valueInf.ty, resultType, assign, &module->astOriginalCallTypes, &module->astOverloadResolvedTypes});
2023-01-06 16:07:19 +00:00
addConstraint(scope, assign->location, SubtypeConstraint{resultType, varId});
2022-08-18 22:04:33 +01:00
}
2022-07-29 04:41:13 +01:00
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatIf* ifStatement)
2022-06-17 01:54:42 +01:00
{
2022-10-21 18:33:43 +01:00
ScopePtr condScope = childScope(ifStatement->condition, scope);
2023-02-03 12:34:12 +00:00
auto [_, refinement] = check(condScope, ifStatement->condition, std::nullopt);
2022-06-17 01:54:42 +01:00
2022-08-18 22:04:33 +01:00
ScopePtr thenScope = childScope(ifStatement->thenbody, scope);
2023-02-10 18:50:54 +00:00
applyRefinements(thenScope, ifStatement->condition->location, refinement);
2022-06-17 01:54:42 +01:00
visit(thenScope, ifStatement->thenbody);
if (ifStatement->elsebody)
{
2022-08-18 22:04:33 +01:00
ScopePtr elseScope = childScope(ifStatement->elsebody, scope);
2023-02-10 18:50:54 +00:00
applyRefinements(elseScope, ifStatement->elseLocation.value_or(ifStatement->condition->location), refinementArena.negation(refinement));
2022-06-17 01:54:42 +01:00
visit(elseScope, ifStatement->elsebody);
}
}
2022-07-29 04:41:13 +01:00
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatTypeAlias* alias)
2022-06-24 02:44:07 +01:00
{
2022-08-11 21:42:54 +01:00
auto bindingIt = scope->privateTypeBindings.find(alias->name.value);
2022-08-04 22:27:28 +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 21:42:54 +01:00
if (bindingIt == scope->privateTypeBindings.end() || defnIt == nullptr)
2022-07-01 00:29:02 +01:00
{
2022-08-04 22:27:28 +01:00
return;
2022-07-01 00:29:02 +01:00
}
2022-06-24 02:44:07 +01:00
2022-08-04 22:27:28 +01:00
ScopePtr resolvingScope = *defnIt;
2023-01-20 12:02:39 +00:00
TypeId ty = resolveType(resolvingScope, alias->type, /* inTypeArguments */ false);
2022-08-04 22:27:28 +01:00
2022-09-02 00:00:14 +01:00
if (alias->exported)
{
Name typeName(alias->name.value);
scope->exportedTypeBindings[typeName] = TypeFun{ty};
}
2023-01-03 17:33:19 +00:00
LUAU_ASSERT(get<FreeType>(bindingIt->second.type));
2022-06-24 02:44:07 +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.
2023-01-03 17:33:19 +00:00
asMutable(bindingIt->second.type)->ty.emplace<BoundType>(ty);
2022-06-24 02:44:07 +01:00
2022-09-02 00:00:14 +01:00
addConstraint(scope, alias->location, NameConstraint{ty, alias->name.value});
2022-06-24 02:44:07 +01:00
}
2022-08-04 22:27:28 +01:00
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatDeclareGlobal* global)
{
LUAU_ASSERT(global->type);
2023-01-20 12:02:39 +00:00
TypeId globalTy = resolveType(scope, global->type, /* inTypeArguments */ false);
2022-08-11 21:42:54 +01:00
Name globalName(global->name.value);
module->declaredGlobals[globalName] = globalTy;
2022-08-04 22:27:28 +01:00
scope->bindings[global->name] = Binding{globalTy, global->location};
}
2022-08-11 21:42:54 +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 22:27:28 +01:00
{
2023-01-03 17:33:19 +00:00
std::optional<TypeId> superTy = FFlag::LuauNegatedClassTypes ? std::make_optional(builtinTypes->classType) : std::nullopt;
2022-08-11 21:42:54 +01:00
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;
2023-01-03 17:33:19 +00:00
if (!get<ClassType>(follow(*superTy)))
2022-08-11 21:42:54 +01:00
{
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);
2023-01-03 17:33:19 +00:00
TypeId classTy = arena->addType(ClassType(className, {}, superTy, std::nullopt, {}, {}, moduleName));
ClassType* ctv = getMutable<ClassType>(classTy);
2022-08-11 21:42:54 +01:00
2023-01-03 17:33:19 +00:00
TypeId metaTy = arena->addType(TableType{TableState::Sealed, scope->level, scope.get()});
TableType* metatable = getMutable<TableType>(metaTy);
2022-08-11 21:42:54 +01:00
ctv->metatable = metaTy;
scope->exportedTypeBindings[className] = TypeFun{{}, classTy};
for (const AstDeclaredClassProp& prop : declaredClass->props)
{
Name propName(prop.name.value);
2023-01-20 12:02:39 +00:00
TypeId propTy = resolveType(scope, prop.ty, /* inTypeArguments */ false);
2022-08-11 21:42:54 +01:00
bool assignToMetatable = isMetamethod(propName);
// Function types always take 'self', but this isn't reflected in the
// parsed annotation. Add it here.
if (prop.isMethod)
{
2023-01-03 17:33:19 +00:00
if (FunctionType* ftv = getMutable<FunctionType>(propTy))
2022-08-11 21:42:54 +01:00
{
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.
2023-01-03 17:33:19 +00:00
if (const IntersectionType* itv = get<IntersectionType>(currentTy))
2022-08-11 21:42:54 +01:00
{
std::vector<TypeId> options = itv->parts;
options.push_back(propTy);
2023-01-03 17:33:19 +00:00
TypeId newItv = arena->addType(IntersectionType{std::move(options)});
2022-08-11 21:42:54 +01:00
if (assignToMetatable)
metatable->props[propName] = {newItv};
else
ctv->props[propName] = {newItv};
}
2023-01-03 17:33:19 +00:00
else if (get<FunctionType>(currentTy))
2022-08-11 21:42:54 +01:00
{
2023-01-03 17:33:19 +00:00
TypeId intersection = arena->addType(IntersectionType{{currentTy, propTy}});
2022-08-11 21:42:54 +01:00
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 22:27:28 +01:00
}
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatDeclareFunction* global)
{
2022-08-11 21:42:54 +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 22:44:50 +01:00
{
2022-08-11 21:42:54 +01:00
genericTys.push_back(generic.ty);
2022-09-08 22:44:50 +01:00
scope->privateTypeBindings[name] = TypeFun{generic.ty};
}
2022-08-11 21:42:54 +01:00
std::vector<TypePackId> genericTps;
genericTps.reserve(genericPacks.size());
for (auto& [name, generic] : genericPacks)
2022-09-08 22:44:50 +01:00
{
2022-08-11 21:42:54 +01:00
genericTps.push_back(generic.tp);
2022-09-08 22:44:50 +01:00
scope->privateTypePackBindings[name] = generic.tp;
}
2022-08-11 21:42:54 +01:00
ScopePtr funScope = scope;
if (!generics.empty() || !genericPacks.empty())
2022-08-18 22:04:33 +01:00
funScope = childScope(global, scope);
2022-08-11 21:42:54 +01:00
2023-01-20 12:02:39 +00:00
TypePackId paramPack = resolveTypePack(funScope, global->params, /* inTypeArguments */ false);
TypePackId retPack = resolveTypePack(funScope, global->retTypes, /* inTypeArguments */ false);
2023-01-03 17:33:19 +00:00
TypeId fnType = arena->addType(FunctionType{TypeLevel{}, funScope.get(), std::move(genericTys), std::move(genericTps), paramPack, retPack});
FunctionType* ftv = getMutable<FunctionType>(fnType);
2022-08-11 21:42:54 +01:00
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 22:27:28 +01:00
}
2022-09-29 23:11:54 +01:00
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatError* error)
{
for (AstStat* stat : error->statements)
visit(scope, stat);
for (AstExpr* expr : error->expressions)
check(scope, expr);
}
2022-10-27 23:22:49 +01:00
InferencePack ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstArray<AstExpr*> exprs, const std::vector<TypeId>& expectedTypes)
2022-06-03 21:32:20 +01:00
{
2022-09-02 00:00:14 +01:00
std::vector<TypeId> head;
std::optional<TypePackId> tail;
2022-06-17 01:54:42 +01:00
for (size_t i = 0; i < exprs.size; ++i)
{
AstExpr* expr = exprs.data[i];
if (i < exprs.size - 1)
2022-09-23 19:32:10 +01:00
{
std::optional<TypeId> expectedType;
if (i < expectedTypes.size())
expectedType = expectedTypes[i];
2022-10-27 23:22:49 +01:00
head.push_back(check(scope, expr).ty);
2022-09-23 19:32:10 +01:00
}
2022-06-17 01:54:42 +01:00
else
2022-09-23 19:32:10 +01:00
{
2022-09-29 23:11:54 +01:00
std::vector<TypeId> expectedTailTypes;
if (i < expectedTypes.size())
expectedTailTypes.assign(begin(expectedTypes) + i, end(expectedTypes));
2022-10-27 23:22:49 +01:00
tail = checkPack(scope, expr, expectedTailTypes).tp;
2022-09-23 19:32:10 +01:00
}
2022-06-17 01:54:42 +01:00
}
2022-09-02 00:00:14 +01:00
if (head.empty() && tail)
2022-10-27 23:22:49 +01:00
return InferencePack{*tail};
2022-06-17 01:54:42 +01:00
else
2022-10-27 23:22:49 +01:00
return InferencePack{arena->addTypePack(TypePack{std::move(head), tail})};
2022-06-17 01:54:42 +01:00
}
2022-10-27 23:22:49 +01:00
InferencePack ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExpr* expr, const std::vector<TypeId>& expectedTypes)
2022-06-03 21:32:20 +01:00
{
2022-07-01 00:29:02 +01:00
RecursionCounter counter{&recursionCount};
if (recursionCount >= FInt::LuauCheckRecursionLimit)
{
reportCodeTooComplex(expr->location);
2023-01-03 17:33:19 +00:00
return InferencePack{builtinTypes->errorRecoveryTypePack()};
2022-07-01 00:29:02 +01:00
}
2022-06-03 21:32:20 +01:00
2022-10-27 23:22:49 +01:00
InferencePack result;
2022-06-17 01:54:42 +01:00
if (AstExprCall* call = expr->as<AstExprCall>())
2022-12-02 10:46:05 +00:00
result = checkPack(scope, call, expectedTypes);
2022-07-01 00:29:02 +01:00
else if (AstExprVarargs* varargs = expr->as<AstExprVarargs>())
{
if (scope->varargPack)
2022-10-27 23:22:49 +01:00
result = InferencePack{*scope->varargPack};
2022-07-01 00:29:02 +01:00
else
2023-01-03 17:33:19 +00:00
result = InferencePack{builtinTypes->errorRecoveryTypePack()};
2022-07-01 00:29:02 +01:00
}
2022-06-17 01:54:42 +01:00
else
{
2022-09-23 19:32:10 +01:00
std::optional<TypeId> expectedType;
if (!expectedTypes.empty())
expectedType = expectedTypes[0];
2022-10-27 23:22:49 +01:00
TypeId t = check(scope, expr, expectedType).ty;
result = InferencePack{arena->addTypePack({t})};
2022-06-17 01:54:42 +01:00
}
2022-10-27 23:22:49 +01:00
LUAU_ASSERT(result.tp);
2023-02-03 12:34:12 +00:00
module->astTypePacks[expr] = result.tp;
2022-06-17 01:54:42 +01:00
return result;
2022-06-03 21:32:20 +01:00
}
2022-10-27 23:22:49 +01:00
InferencePack ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExprCall* call, const std::vector<TypeId>& expectedTypes)
2022-06-03 21:32:20 +01:00
{
2022-12-02 10:46:05 +00:00
std::vector<AstExpr*> exprArgs;
2023-02-10 18:50:54 +00:00
std::vector<RefinementId> returnRefinements;
std::vector<std::optional<TypeId>> discriminantTypes;
2022-12-02 10:46:05 +00:00
if (call->self)
{
AstExprIndexName* indexExpr = call->func->as<AstExprIndexName>();
if (!indexExpr)
ice->ice("method call expression has no 'self'");
exprArgs.push_back(indexExpr->expr);
2023-02-10 18:50:54 +00:00
if (auto def = dfg->getDef(indexExpr->expr))
{
TypeId discriminantTy = arena->addType(BlockedType{});
returnRefinements.push_back(refinementArena.proposition(*def, discriminantTy));
discriminantTypes.push_back(discriminantTy);
}
else
discriminantTypes.push_back(std::nullopt);
}
for (AstExpr* arg : call->args)
{
exprArgs.push_back(arg);
if (auto def = dfg->getDef(arg))
{
TypeId discriminantTy = arena->addType(BlockedType{});
returnRefinements.push_back(refinementArena.proposition(*def, discriminantTy));
discriminantTypes.push_back(discriminantTy);
}
else
discriminantTypes.push_back(std::nullopt);
2022-12-02 10:46:05 +00:00
}
Checkpoint startCheckpoint = checkpoint(this);
2022-10-27 23:22:49 +01:00
TypeId fnType = check(scope, call->func).ty;
2022-12-02 10:46:05 +00:00
Checkpoint fnEndCheckpoint = checkpoint(this);
2022-07-01 00:29:02 +01:00
2023-02-10 18:50:54 +00:00
module->astOriginalCallTypes[call->func] = fnType;
2022-12-02 10:46:05 +00:00
TypePackId expectedArgPack = arena->freshTypePack(scope.get());
TypePackId expectedRetPack = arena->freshTypePack(scope.get());
2023-01-03 17:33:19 +00:00
TypeId expectedFunctionType = arena->addType(FunctionType{expectedArgPack, expectedRetPack});
2022-12-02 10:46:05 +00:00
2023-01-03 17:33:19 +00:00
TypeId instantiatedFnType = arena->addType(BlockedType{});
2022-12-02 10:46:05 +00:00
addConstraint(scope, call->location, InstantiationConstraint{instantiatedFnType, fnType});
NotNull<Constraint> extractArgsConstraint = addConstraint(scope, call->location, SubtypeConstraint{instantiatedFnType, expectedFunctionType});
// Fully solve fnType, then extract its argument list as expectedArgPack.
forEachConstraint(startCheckpoint, fnEndCheckpoint, this, [extractArgsConstraint](const ConstraintPtr& constraint) {
extractArgsConstraint->dependencies.emplace_back(constraint.get());
});
const AstExpr* lastArg = exprArgs.size() ? exprArgs[exprArgs.size() - 1] : nullptr;
const bool needTail = lastArg && (lastArg->is<AstExprCall>() || lastArg->is<AstExprVarargs>());
TypePack expectedArgs;
if (!needTail)
2023-01-03 17:33:19 +00:00
expectedArgs = extendTypePack(*arena, builtinTypes, expectedArgPack, exprArgs.size());
2022-12-02 10:46:05 +00:00
else
2023-01-03 17:33:19 +00:00
expectedArgs = extendTypePack(*arena, builtinTypes, expectedArgPack, exprArgs.size() - 1);
2022-10-27 23:22:49 +01:00
2022-12-02 10:46:05 +00:00
std::vector<TypeId> args;
std::optional<TypePackId> argTail;
2023-02-03 12:34:12 +00:00
std::vector<RefinementId> argumentRefinements;
2022-11-18 18:45:14 +00:00
2022-12-02 10:46:05 +00:00
Checkpoint argCheckpoint = checkpoint(this);
2022-11-18 18:45:14 +00:00
2022-12-02 10:46:05 +00:00
for (size_t i = 0; i < exprArgs.size(); ++i)
{
AstExpr* arg = exprArgs[i];
std::optional<TypeId> expectedType;
if (i < expectedArgs.head.size())
expectedType = expectedArgs.head[i];
if (i == 0 && call->self)
{
// The self type has already been computed as a side effect of
// computing fnType. If computing that did not cause us to exceed a
// recursion limit, we can fetch it from astTypes rather than
// recomputing it.
2023-02-03 12:34:12 +00:00
TypeId* selfTy = module->astTypes.find(exprArgs[0]);
2022-12-02 10:46:05 +00:00
if (selfTy)
args.push_back(*selfTy);
else
args.push_back(arena->freshType(scope.get()));
}
else if (i < exprArgs.size() - 1 || !(arg->is<AstExprCall>() || arg->is<AstExprVarargs>()))
2022-12-09 18:07:25 +00:00
{
2023-02-03 12:34:12 +00:00
auto [ty, refinement] = check(scope, arg, expectedType);
2022-12-09 18:07:25 +00:00
args.push_back(ty);
2023-02-03 12:34:12 +00:00
argumentRefinements.push_back(refinement);
2022-12-09 18:07:25 +00:00
}
2022-12-02 10:46:05 +00:00
else
2023-02-10 18:50:54 +00:00
{
auto [tp, refis] = checkPack(scope, arg, {}); // FIXME? not sure about expectedTypes here
argTail = tp;
argumentRefinements.insert(argumentRefinements.end(), refis.begin(), refis.end());
}
2022-11-18 18:45:14 +00:00
}
2022-06-17 01:54:42 +01:00
2022-12-02 10:46:05 +00:00
Checkpoint argEndCheckpoint = checkpoint(this);
// Do not solve argument constraints until after we have extracted the
// expected types from the callable.
forEachConstraint(argCheckpoint, argEndCheckpoint, this, [extractArgsConstraint](const ConstraintPtr& constraint) {
constraint->dependencies.push_back(extractArgsConstraint);
});
2022-10-27 23:22:49 +01:00
if (matchSetmetatable(*call))
2022-09-23 19:32:10 +01:00
{
2022-12-02 10:46:05 +00:00
TypePack argTailPack;
if (argTail && args.size() < 2)
2023-01-03 17:33:19 +00:00
argTailPack = extendTypePack(*arena, builtinTypes, *argTail, 2 - args.size());
2022-12-02 10:46:05 +00:00
LUAU_ASSERT(args.size() + argTailPack.head.size() == 2);
TypeId target = args.size() > 0 ? args[0] : argTailPack.head[0];
TypeId mt = args.size() > 1 ? args[1] : argTailPack.head[args.size() == 0 ? 1 : 0];
2022-10-27 23:22:49 +01:00
AstExpr* targetExpr = call->args.data[0];
2023-01-03 17:33:19 +00:00
MetatableType mtv{target, mt};
2022-10-27 23:22:49 +01:00
TypeId resultTy = arena->addType(mtv);
if (AstExprLocal* targetLocal = targetExpr->as<AstExprLocal>())
2023-01-27 21:28:45 +00:00
{
2022-10-27 23:22:49 +01:00
scope->bindings[targetLocal->local].typeId = resultTy;
2023-01-27 21:28:45 +00:00
auto def = dfg->getDef(targetLocal->local);
if (def)
scope->dcrRefinements[*def] = resultTy; // TODO: typestates: track this as an assignment
}
2023-02-10 18:50:54 +00:00
return InferencePack{arena->addTypePack({resultTy}), {refinementArena.variadic(returnRefinements)}};
2022-09-23 19:32:10 +01:00
}
2022-10-27 23:22:49 +01:00
else
2022-09-23 19:32:10 +01:00
{
2023-02-10 18:50:54 +00:00
if (matchAssert(*call) && !argumentRefinements.empty())
applyRefinements(scope, call->args.data[0]->location, argumentRefinements[0]);
2022-10-27 23:22:49 +01:00
2023-01-03 17:33:19 +00:00
TypeId instantiatedType = arena->addType(BlockedType{});
2022-10-27 23:22:49 +01:00
// TODO: How do expectedTypes play into this? Do they?
TypePackId rets = arena->addTypePack(BlockedTypePack{});
2022-12-02 10:46:05 +00:00
TypePackId argPack = arena->addTypePack(TypePack{args, argTail});
2023-01-03 17:33:19 +00:00
FunctionType ftv(TypeLevel{}, scope.get(), argPack, rets);
2022-10-27 23:22:49 +01:00
TypeId inferredFnType = arena->addType(ftv);
2022-11-10 22:04:44 +00:00
unqueuedConstraints.push_back(
2022-10-27 23:22:49 +01:00
std::make_unique<Constraint>(NotNull{scope.get()}, call->func->location, InstantiationConstraint{instantiatedType, fnType}));
2022-11-10 22:04:44 +00:00
NotNull<const Constraint> ic(unqueuedConstraints.back().get());
2022-10-27 23:22:49 +01:00
2022-11-10 22:04:44 +00:00
unqueuedConstraints.push_back(
2022-12-02 10:46:05 +00:00
std::make_unique<Constraint>(NotNull{scope.get()}, call->func->location, SubtypeConstraint{instantiatedType, inferredFnType}));
2022-11-10 22:04:44 +00:00
NotNull<Constraint> sc(unqueuedConstraints.back().get());
2022-10-27 23:22:49 +01:00
2022-12-02 10:46:05 +00:00
NotNull<Constraint> fcc = addConstraint(scope, call->func->location,
2022-10-27 23:22:49 +01:00
FunctionCallConstraint{
{ic, sc},
fnType,
argPack,
rets,
call,
2023-02-10 18:50:54 +00:00
std::move(discriminantTypes),
2022-10-27 23:22:49 +01:00
});
2022-12-02 10:46:05 +00:00
// 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.
forEachConstraint(fnEndCheckpoint, argEndCheckpoint, this, [fcc](const ConstraintPtr& constraint) {
fcc->dependencies.emplace_back(constraint.get());
});
2023-02-10 18:50:54 +00:00
return InferencePack{rets, {refinementArena.variadic(returnRefinements)}};
2022-09-23 19:32:10 +01:00
}
2022-10-27 23:22:49 +01:00
}
2022-11-04 17:02:37 +00:00
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExpr* expr, std::optional<TypeId> expectedType, bool forceSingleton)
2022-10-27 23:22:49 +01:00
{
RecursionCounter counter{&recursionCount};
if (recursionCount >= FInt::LuauCheckRecursionLimit)
{
reportCodeTooComplex(expr->location);
2023-01-03 17:33:19 +00:00
return Inference{builtinTypes->errorRecoveryType()};
2022-10-27 23:22:49 +01:00
}
Inference result;
if (auto group = expr->as<AstExprGroup>())
2022-11-04 17:02:37 +00:00
result = check(scope, group->expr, expectedType, forceSingleton);
2022-10-27 23:22:49 +01:00
else if (auto stringExpr = expr->as<AstExprConstantString>())
2022-11-04 17:02:37 +00:00
result = check(scope, stringExpr, expectedType, forceSingleton);
2022-10-27 23:22:49 +01:00
else if (expr->is<AstExprConstantNumber>())
2023-01-03 17:33:19 +00:00
result = Inference{builtinTypes->numberType};
2022-10-27 23:22:49 +01:00
else if (auto boolExpr = expr->as<AstExprConstantBool>())
2022-11-04 17:02:37 +00:00
result = check(scope, boolExpr, expectedType, forceSingleton);
2022-06-17 01:54:42 +01:00
else if (expr->is<AstExprConstantNil>())
2023-01-03 17:33:19 +00:00
result = Inference{builtinTypes->nilType};
2022-10-21 18:33:43 +01:00
else if (auto local = expr->as<AstExprLocal>())
result = check(scope, local);
else if (auto global = expr->as<AstExprGlobal>())
result = check(scope, global);
2022-07-01 00:29:02 +01:00
else if (expr->is<AstExprVarargs>())
result = flattenPack(scope, expr->location, checkPack(scope, expr));
2022-10-27 23:22:49 +01:00
else if (auto call = expr->as<AstExprCall>())
{
std::vector<TypeId> expectedTypes;
if (expectedType)
expectedTypes.push_back(*expectedType);
result = flattenPack(scope, expr->location, checkPack(scope, call, expectedTypes)); // TODO: needs predicates too
}
2022-06-17 01:54:42 +01:00
else if (auto a = expr->as<AstExprFunction>())
{
2022-12-02 10:46:05 +00:00
Checkpoint startCheckpoint = checkpoint(this);
FunctionSignature sig = checkFunctionSignature(scope, a, expectedType);
2022-07-01 00:29:02 +01:00
checkFunctionBody(sig.bodyScope, a);
2022-12-02 10:46:05 +00:00
Checkpoint endCheckpoint = checkpoint(this);
2023-01-03 17:33:19 +00:00
TypeId generalizedTy = arena->addType(BlockedType{});
2022-12-02 10:46:05 +00:00
NotNull<Constraint> gc = addConstraint(scope, expr->location, GeneralizationConstraint{generalizedTy, sig.signature});
forEachConstraint(startCheckpoint, endCheckpoint, this, [gc](const ConstraintPtr& constraint) {
gc->dependencies.emplace_back(constraint.get());
});
2023-02-03 12:34:12 +00:00
result = Inference{generalizedTy};
2022-06-17 01:54:42 +01:00
}
else if (auto indexName = expr->as<AstExprIndexName>())
result = check(scope, indexName);
2022-07-01 00:29:02 +01:00
else if (auto indexExpr = expr->as<AstExprIndexExpr>())
result = check(scope, indexExpr);
2022-06-17 01:54:42 +01:00
else if (auto table = expr->as<AstExprTable>())
2022-09-23 19:32:10 +01:00
result = check(scope, table, expectedType);
2022-07-01 00:29:02 +01:00
else if (auto unary = expr->as<AstExprUnary>())
result = check(scope, unary);
else if (auto binary = expr->as<AstExprBinary>())
2022-10-21 18:33:43 +01:00
result = check(scope, binary, expectedType);
2022-08-18 22:04:33 +01:00
else if (auto ifElse = expr->as<AstExprIfElse>())
2022-09-23 19:32:10 +01:00
result = check(scope, ifElse, expectedType);
2022-08-11 21:42:54 +01:00
else if (auto typeAssert = expr->as<AstExprTypeAssertion>())
result = check(scope, typeAssert);
2023-01-27 21:28:45 +00:00
else if (auto interpString = expr->as<AstExprInterpString>())
result = check(scope, interpString);
2022-07-01 00:29:02 +01:00
else if (auto err = expr->as<AstExprError>())
{
// Open question: Should we traverse into this?
2022-09-29 23:11:54 +01:00
for (AstExpr* subExpr : err->expressions)
check(scope, subExpr);
2023-01-03 17:33:19 +00:00
result = Inference{builtinTypes->errorRecoveryType()};
2022-06-17 01:54:42 +01:00
}
else
{
LUAU_ASSERT(0);
2022-10-27 23:22:49 +01:00
result = Inference{freshType(scope)};
2022-06-17 01:54:42 +01:00
}
2022-10-27 23:22:49 +01:00
LUAU_ASSERT(result.ty);
2023-02-03 12:34:12 +00:00
module->astTypes[expr] = result.ty;
2023-01-27 21:28:45 +00:00
if (expectedType)
2023-02-03 12:34:12 +00:00
module->astExpectedTypes[expr] = *expectedType;
2022-06-17 01:54:42 +01:00
return result;
}
2022-11-04 17:02:37 +00:00
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprConstantString* string, std::optional<TypeId> expectedType, bool forceSingleton)
2022-10-27 23:22:49 +01:00
{
2022-11-04 17:02:37 +00:00
if (forceSingleton)
2023-01-03 17:33:19 +00:00
return Inference{arena->addType(SingletonType{StringSingleton{std::string{string->value.data, string->value.size}}})};
2022-11-04 17:02:37 +00:00
2022-10-27 23:22:49 +01:00
if (expectedType)
{
const TypeId expectedTy = follow(*expectedType);
2023-01-03 17:33:19 +00:00
if (get<BlockedType>(expectedTy) || get<PendingExpansionType>(expectedTy))
2022-10-27 23:22:49 +01:00
{
2023-01-03 17:33:19 +00:00
TypeId ty = arena->addType(BlockedType{});
TypeId singletonType = arena->addType(SingletonType(StringSingleton{std::string(string->value.data, string->value.size)}));
addConstraint(scope, string->location, PrimitiveTypeConstraint{ty, expectedTy, singletonType, builtinTypes->stringType});
2022-10-27 23:22:49 +01:00
return Inference{ty};
}
else if (maybeSingleton(expectedTy))
2023-01-03 17:33:19 +00:00
return Inference{arena->addType(SingletonType{StringSingleton{std::string{string->value.data, string->value.size}}})};
2022-10-27 23:22:49 +01:00
2023-01-03 17:33:19 +00:00
return Inference{builtinTypes->stringType};
2022-10-27 23:22:49 +01:00
}
2023-01-03 17:33:19 +00:00
return Inference{builtinTypes->stringType};
2022-10-27 23:22:49 +01:00
}
2022-11-04 17:02:37 +00:00
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprConstantBool* boolExpr, std::optional<TypeId> expectedType, bool forceSingleton)
2022-10-27 23:22:49 +01:00
{
2023-01-03 17:33:19 +00:00
const TypeId singletonType = boolExpr->value ? builtinTypes->trueType : builtinTypes->falseType;
2022-11-04 17:02:37 +00:00
if (forceSingleton)
return Inference{singletonType};
2022-10-27 23:22:49 +01:00
if (expectedType)
{
const TypeId expectedTy = follow(*expectedType);
2023-01-03 17:33:19 +00:00
if (get<BlockedType>(expectedTy) || get<PendingExpansionType>(expectedTy))
2022-10-27 23:22:49 +01:00
{
2023-01-03 17:33:19 +00:00
TypeId ty = arena->addType(BlockedType{});
addConstraint(scope, boolExpr->location, PrimitiveTypeConstraint{ty, expectedTy, singletonType, builtinTypes->booleanType});
2022-10-27 23:22:49 +01:00
return Inference{ty};
}
else if (maybeSingleton(expectedTy))
return Inference{singletonType};
2023-01-03 17:33:19 +00:00
return Inference{builtinTypes->booleanType};
2022-10-27 23:22:49 +01:00
}
2023-01-03 17:33:19 +00:00
return Inference{builtinTypes->booleanType};
2022-10-27 23:22:49 +01:00
}
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprLocal* local)
2022-10-21 18:33:43 +01:00
{
std::optional<TypeId> resultTy;
2022-11-04 17:02:37 +00:00
auto def = dfg->getDef(local);
if (def)
2022-10-21 18:33:43 +01:00
resultTy = scope->lookup(*def);
if (!resultTy)
{
if (auto ty = scope->lookup(local->local))
resultTy = *ty;
}
if (!resultTy)
2023-01-03 17:33:19 +00:00
return Inference{builtinTypes->errorRecoveryType()}; // TODO: replace with ice, locals should never exist before its definition.
2022-10-21 18:33:43 +01:00
2022-11-04 17:02:37 +00:00
if (def)
2023-02-03 12:34:12 +00:00
return Inference{*resultTy, refinementArena.proposition(*def, builtinTypes->truthyType)};
2022-11-04 17:02:37 +00:00
else
return Inference{*resultTy};
2022-10-21 18:33:43 +01:00
}
2022-10-27 23:22:49 +01:00
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprGlobal* global)
2022-10-21 18:33:43 +01:00
{
if (std::optional<TypeId> ty = scope->lookup(global->name))
2022-10-27 23:22:49 +01:00
return Inference{*ty};
2022-10-21 18:33: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(global->location, UnknownSymbol{global->name.value});
2023-01-03 17:33:19 +00:00
return Inference{builtinTypes->errorRecoveryType()};
2022-10-21 18:33:43 +01:00
}
2022-10-27 23:22:49 +01:00
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprIndexName* indexName)
2022-06-17 01:54:42 +01:00
{
2022-10-27 23:22:49 +01:00
TypeId obj = check(scope, indexName->expr).ty;
2023-02-10 18:50:54 +00:00
TypeId result = freshType(scope);
2022-12-02 10:46:05 +00:00
std::optional<DefId> def = dfg->getDef(indexName);
if (def)
{
if (auto ty = scope->lookup(*def))
2023-02-03 12:34:12 +00:00
return Inference{*ty, refinementArena.proposition(*def, builtinTypes->truthyType)};
2022-12-02 10:46:05 +00:00
else
scope->dcrRefinements[*def] = result;
}
2022-06-17 01:54:42 +01:00
2023-01-03 17:33:19 +00:00
TableType::Props props{{indexName->index.value, Property{result}}};
2022-06-17 01:54:42 +01:00
const std::optional<TableIndexer> indexer;
2023-01-03 17:33:19 +00:00
TableType ttv{std::move(props), indexer, TypeLevel{}, scope.get(), TableState::Free};
2022-06-17 01:54:42 +01:00
TypeId expectedTableType = arena->addType(std::move(ttv));
2022-06-03 21:32:20 +01:00
2022-09-02 00:00:14 +01:00
addConstraint(scope, indexName->expr->location, SubtypeConstraint{obj, expectedTableType});
2022-06-17 01:54:42 +01:00
2022-12-02 10:46:05 +00:00
if (def)
2023-02-03 12:34:12 +00:00
return Inference{result, refinementArena.proposition(*def, builtinTypes->truthyType)};
2022-12-02 10:46:05 +00:00
else
return Inference{result};
2022-06-17 01:54:42 +01:00
}
2022-10-27 23:22:49 +01:00
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprIndexExpr* indexExpr)
2022-07-01 00:29:02 +01:00
{
2022-10-27 23:22:49 +01:00
TypeId obj = check(scope, indexExpr->expr).ty;
TypeId indexType = check(scope, indexExpr->index).ty;
2022-07-01 00:29:02 +01:00
TypeId result = freshType(scope);
TableIndexer indexer{indexType, result};
2023-01-03 17:33:19 +00:00
TypeId tableType = arena->addType(TableType{TableType::Props{}, TableIndexer{indexType, result}, TypeLevel{}, scope.get(), TableState::Free});
2022-07-01 00:29:02 +01:00
2022-09-02 00:00:14 +01:00
addConstraint(scope, indexExpr->expr->location, SubtypeConstraint{obj, tableType});
2022-07-01 00:29:02 +01:00
2022-10-27 23:22:49 +01:00
return Inference{result};
2022-07-01 00:29:02 +01:00
}
2022-10-27 23:22:49 +01:00
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprUnary* unary)
2022-07-01 00:29:02 +01:00
{
2023-02-03 12:34:12 +00:00
auto [operandType, refinement] = check(scope, unary->expr);
2023-01-03 17:33:19 +00:00
TypeId resultType = arena->addType(BlockedType{});
2022-09-23 19:32:10 +01:00
addConstraint(scope, unary->location, UnaryConstraint{unary->op, operandType, resultType});
2022-11-04 17:02:37 +00:00
if (unary->op == AstExprUnary::Not)
2023-02-03 12:34:12 +00:00
return Inference{resultType, refinementArena.negation(refinement)};
2022-11-04 17:02:37 +00:00
else
return Inference{resultType};
2022-10-21 18:33:43 +01:00
}
2022-10-27 23:22:49 +01:00
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprBinary* binary, std::optional<TypeId> expectedType)
2022-10-21 18:33:43 +01:00
{
2023-02-03 12:34:12 +00:00
auto [leftType, rightType, refinement] = checkBinary(scope, binary, expectedType);
2022-10-21 18:33:43 +01:00
2023-01-03 17:33:19 +00:00
TypeId resultType = arena->addType(BlockedType{});
addConstraint(scope, binary->location,
2023-02-03 12:34:12 +00:00
BinaryConstraint{binary->op, leftType, rightType, resultType, binary, &module->astOriginalCallTypes, &module->astOverloadResolvedTypes});
return Inference{resultType, std::move(refinement)};
2022-07-01 00:29:02 +01:00
}
2022-10-27 23:22:49 +01:00
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprIfElse* ifElse, std::optional<TypeId> expectedType)
2022-08-18 22:04:33 +01:00
{
2022-12-09 18:07:25 +00:00
ScopePtr condScope = childScope(ifElse->condition, scope);
2023-02-03 12:34:12 +00:00
auto [_, refinement] = check(scope, ifElse->condition);
2022-12-09 18:07:25 +00:00
ScopePtr thenScope = childScope(ifElse->trueExpr, scope);
2023-02-03 12:34:12 +00:00
applyRefinements(thenScope, ifElse->trueExpr->location, refinement);
2022-12-09 18:07:25 +00:00
TypeId thenType = check(thenScope, ifElse->trueExpr, expectedType).ty;
2022-08-18 22:04:33 +01:00
2022-12-09 18:07:25 +00:00
ScopePtr elseScope = childScope(ifElse->falseExpr, scope);
2023-02-03 12:34:12 +00:00
applyRefinements(elseScope, ifElse->falseExpr->location, refinementArena.negation(refinement));
2022-12-09 18:07:25 +00:00
TypeId elseType = check(elseScope, ifElse->falseExpr, expectedType).ty;
2022-08-18 22:04:33 +01:00
2023-02-03 12:34:12 +00:00
return Inference{expectedType ? *expectedType : arena->addType(UnionType{{thenType, elseType}})};
2022-08-18 22:04:33 +01:00
}
2022-10-27 23:22:49 +01:00
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprTypeAssertion* typeAssert)
2022-08-11 21:42:54 +01:00
{
2022-10-21 18:33:43 +01:00
check(scope, typeAssert->expr, std::nullopt);
2023-01-20 12:02:39 +00:00
return Inference{resolveType(scope, typeAssert->annotation, /* inTypeArguments */ false)};
2022-08-11 21:42:54 +01:00
}
2023-01-27 21:28:45 +00:00
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprInterpString* interpString)
{
for (AstExpr* expr : interpString->expressions)
check(scope, expr);
return Inference{builtinTypes->stringType};
}
2023-02-03 12:34:12 +00:00
std::tuple<TypeId, TypeId, RefinementId> ConstraintGraphBuilder::checkBinary(
2022-11-04 17:02:37 +00:00
const ScopePtr& scope, AstExprBinary* binary, std::optional<TypeId> expectedType)
{
if (binary->op == AstExprBinary::And)
{
2023-02-03 12:34:12 +00:00
auto [leftType, leftRefinement] = check(scope, binary->left, expectedType);
2022-11-04 17:02:37 +00:00
ScopePtr rightScope = childScope(binary->right, scope);
2023-02-03 12:34:12 +00:00
applyRefinements(rightScope, binary->right->location, leftRefinement);
auto [rightType, rightRefinement] = check(rightScope, binary->right, expectedType);
2022-11-04 17:02:37 +00:00
2023-02-03 12:34:12 +00:00
return {leftType, rightType, refinementArena.conjunction(leftRefinement, rightRefinement)};
2022-11-04 17:02:37 +00:00
}
else if (binary->op == AstExprBinary::Or)
{
2023-02-03 12:34:12 +00:00
auto [leftType, leftRefinement] = check(scope, binary->left, expectedType);
2022-11-04 17:02:37 +00:00
ScopePtr rightScope = childScope(binary->right, scope);
2023-02-03 12:34:12 +00:00
applyRefinements(rightScope, binary->right->location, refinementArena.negation(leftRefinement));
auto [rightType, rightRefinement] = check(rightScope, binary->right, expectedType);
2022-11-04 17:02:37 +00:00
2023-02-03 12:34:12 +00:00
return {leftType, rightType, refinementArena.disjunction(leftRefinement, rightRefinement)};
2022-11-04 17:02:37 +00:00
}
2022-11-10 22:04:44 +00:00
else if (auto typeguard = matchTypeGuard(binary))
{
TypeId leftType = check(scope, binary->left).ty;
TypeId rightType = check(scope, binary->right).ty;
std::optional<DefId> def = dfg->getDef(typeguard->target);
if (!def)
return {leftType, rightType, nullptr};
2023-01-03 17:33:19 +00:00
TypeId discriminantTy = builtinTypes->neverType;
2022-11-10 22:04:44 +00:00
if (typeguard->type == "nil")
2023-01-03 17:33:19 +00:00
discriminantTy = builtinTypes->nilType;
2022-11-10 22:04:44 +00:00
else if (typeguard->type == "string")
2023-01-03 17:33:19 +00:00
discriminantTy = builtinTypes->stringType;
2022-11-10 22:04:44 +00:00
else if (typeguard->type == "number")
2023-01-03 17:33:19 +00:00
discriminantTy = builtinTypes->numberType;
2022-11-10 22:04:44 +00:00
else if (typeguard->type == "boolean")
2023-01-03 17:33:19 +00:00
discriminantTy = builtinTypes->threadType;
2022-11-10 22:04:44 +00:00
else if (typeguard->type == "table")
2023-01-27 21:28:45 +00:00
discriminantTy = builtinTypes->tableType;
2022-11-10 22:04:44 +00:00
else if (typeguard->type == "function")
2023-01-03 17:33:19 +00:00
discriminantTy = builtinTypes->functionType;
2022-11-10 22:04:44 +00:00
else if (typeguard->type == "userdata")
{
// For now, we don't really care about being accurate with userdata if the typeguard was using typeof
2023-01-03 17:33:19 +00:00
discriminantTy = builtinTypes->neverType; // TODO: replace with top class type
2022-11-10 22:04:44 +00:00
}
else if (!typeguard->isTypeof && typeguard->type == "vector")
2023-01-03 17:33:19 +00:00
discriminantTy = builtinTypes->neverType; // TODO: figure out a way to deal with this quirky type
2022-11-10 22:04:44 +00:00
else if (!typeguard->isTypeof)
2023-01-03 17:33:19 +00:00
discriminantTy = builtinTypes->neverType;
2022-11-10 22:04:44 +00:00
else if (auto typeFun = globalScope->lookupType(typeguard->type); typeFun && typeFun->typeParams.empty() && typeFun->typePackParams.empty())
{
TypeId ty = follow(typeFun->type);
// We're only interested in the root class of any classes.
2023-01-20 12:02:39 +00:00
if (auto ctv = get<ClassType>(ty); !ctv || (FFlag::LuauNegatedClassTypes ? (ctv->parent == builtinTypes->classType) : !ctv->parent))
2022-11-10 22:04:44 +00:00
discriminantTy = ty;
}
2023-02-03 12:34:12 +00:00
RefinementId proposition = refinementArena.proposition(*def, discriminantTy);
2022-11-10 22:04:44 +00:00
if (binary->op == AstExprBinary::CompareEq)
return {leftType, rightType, proposition};
else if (binary->op == AstExprBinary::CompareNe)
2023-02-03 12:34:12 +00:00
return {leftType, rightType, refinementArena.negation(proposition)};
2022-11-10 22:04:44 +00:00
else
ice->ice("matchTypeGuard should only return a Some under `==` or `~=`!");
}
2022-11-04 17:02:37 +00:00
else if (binary->op == AstExprBinary::CompareEq || binary->op == AstExprBinary::CompareNe)
{
TypeId leftType = check(scope, binary->left, expectedType, true).ty;
TypeId rightType = check(scope, binary->right, expectedType, true).ty;
2023-02-03 12:34:12 +00:00
RefinementId leftRefinement = nullptr;
2022-11-04 17:02:37 +00:00
if (auto def = dfg->getDef(binary->left))
2023-02-03 12:34:12 +00:00
leftRefinement = refinementArena.proposition(*def, rightType);
2022-11-04 17:02:37 +00:00
2023-02-03 12:34:12 +00:00
RefinementId rightRefinement = nullptr;
2022-11-04 17:02:37 +00:00
if (auto def = dfg->getDef(binary->right))
2023-02-03 12:34:12 +00:00
rightRefinement = refinementArena.proposition(*def, leftType);
2022-11-04 17:02:37 +00:00
if (binary->op == AstExprBinary::CompareNe)
{
2023-02-03 12:34:12 +00:00
leftRefinement = refinementArena.negation(leftRefinement);
rightRefinement = refinementArena.negation(rightRefinement);
2022-11-04 17:02:37 +00:00
}
2023-02-03 12:34:12 +00:00
return {leftType, rightType, refinementArena.equivalence(leftRefinement, rightRefinement)};
2022-11-04 17:02:37 +00:00
}
else
{
TypeId leftType = check(scope, binary->left, expectedType).ty;
TypeId rightType = check(scope, binary->right, expectedType).ty;
return {leftType, rightType, nullptr};
}
}
2022-10-21 18:33:43 +01:00
TypePackId ConstraintGraphBuilder::checkLValues(const ScopePtr& scope, AstArray<AstExpr*> exprs)
{
std::vector<TypeId> types;
types.reserve(exprs.size);
for (size_t i = 0; i < exprs.size; ++i)
{
AstExpr* const expr = exprs.data[i];
types.push_back(checkLValue(scope, expr));
}
return arena->addTypePack(std::move(types));
}
/**
* This function is mostly about identifying properties that are being inserted into unsealed tables.
*
* If expr has the form name.a.b.c
*/
TypeId ConstraintGraphBuilder::checkLValue(const ScopePtr& scope, AstExpr* expr)
{
if (auto indexExpr = expr->as<AstExprIndexExpr>())
{
if (auto constantString = indexExpr->index->as<AstExprConstantString>())
{
AstName syntheticIndex{constantString->value.data};
AstExprIndexName synthetic{
indexExpr->location, indexExpr->expr, syntheticIndex, constantString->location, indexExpr->expr->location.end, '.'};
return checkLValue(scope, &synthetic);
}
}
2022-11-18 18:45:14 +00:00
else if (!expr->is<AstExprIndexName>())
return check(scope, expr).ty;
2022-10-21 18:33:43 +01:00
2023-01-20 12:02:39 +00:00
Symbol sym;
std::vector<std::string> segments;
std::vector<AstExpr*> exprs;
AstExpr* e = expr;
while (e)
{
if (auto global = e->as<AstExprGlobal>())
{
sym = global->name;
break;
}
else if (auto local = e->as<AstExprLocal>())
{
sym = local->local;
break;
}
else if (auto indexName = e->as<AstExprIndexName>())
{
segments.push_back(indexName->index.value);
exprs.push_back(e);
e = indexName->expr;
}
else
return check(scope, expr).ty;
}
2022-10-21 18:33:43 +01:00
2022-11-18 18:45:14 +00:00
LUAU_ASSERT(!segments.empty());
2022-10-21 18:33:43 +01:00
2023-01-20 12:02:39 +00:00
std::reverse(begin(segments), end(segments));
std::reverse(begin(exprs), end(exprs));
2022-10-21 18:33:43 +01:00
auto lookupResult = scope->lookupEx(sym);
if (!lookupResult)
2022-10-27 23:22:49 +01:00
return check(scope, expr).ty;
2022-11-18 18:45:14 +00:00
const auto [subjectType, symbolScope] = std::move(*lookupResult);
2022-10-21 18:33:43 +01:00
2022-11-18 18:45:14 +00:00
TypeId propTy = freshType(scope);
2022-10-21 18:33:43 +01:00
2022-11-18 18:45:14 +00:00
std::vector<std::string> segmentStrings(begin(segments), end(segments));
2023-01-03 17:33:19 +00:00
TypeId updatedType = arena->addType(BlockedType{});
2022-11-18 18:45:14 +00:00
addConstraint(scope, expr->location, SetPropConstraint{updatedType, subjectType, std::move(segmentStrings), propTy});
2022-10-21 18:33:43 +01:00
2023-01-20 12:02:39 +00:00
TypeId prevSegmentTy = updatedType;
for (size_t i = 0; i < segments.size(); ++i)
{
TypeId segmentTy = arena->addType(BlockedType{});
2023-02-03 12:34:12 +00:00
module->astTypes[exprs[i]] = segmentTy;
2023-01-20 12:02:39 +00:00
addConstraint(scope, expr->location, HasPropConstraint{segmentTy, prevSegmentTy, segments[i]});
prevSegmentTy = segmentTy;
}
2023-02-03 12:34:12 +00:00
module->astTypes[expr] = prevSegmentTy;
module->astTypes[e] = updatedType;
2023-02-10 18:50:54 +00:00
symbolScope->bindings[sym].typeId = updatedType;
std::optional<DefId> def = dfg->getDef(sym);
if (def)
{
// This can fail if the user is erroneously trying to augment a builtin
// table like os or string.
symbolScope->dcrRefinements[*def] = updatedType;
}
2022-11-18 18:45:14 +00:00
return propTy;
2022-10-21 18:33:43 +01:00
}
2022-10-27 23:22:49 +01:00
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprTable* expr, std::optional<TypeId> expectedType)
2022-06-17 01:54:42 +01:00
{
2023-01-03 17:33:19 +00:00
TypeId ty = arena->addType(TableType{});
TableType* ttv = getMutable<TableType>(ty);
2022-06-17 01:54:42 +01:00
LUAU_ASSERT(ttv);
2022-09-29 23:11:54 +01:00
ttv->state = TableState::Unsealed;
ttv->scope = scope.get();
2022-09-02 00:00:14 +01:00
auto createIndexer = [this, scope, ttv](const Location& location, TypeId currentIndexType, TypeId currentResultType) {
2022-06-17 01:54:42 +01:00
if (!ttv->indexer)
2022-06-03 21:32:20 +01:00
{
2022-06-17 01:54:42 +01:00
TypeId indexType = this->freshType(scope);
TypeId resultType = this->freshType(scope);
ttv->indexer = TableIndexer{indexType, resultType};
2022-06-03 21:32:20 +01:00
}
2022-09-02 00:00:14 +01:00
addConstraint(scope, location, SubtypeConstraint{ttv->indexer->indexType, currentIndexType});
addConstraint(scope, location, SubtypeConstraint{ttv->indexer->indexResultType, currentResultType});
2022-06-17 01:54:42 +01:00
};
2022-06-03 21:32:20 +01:00
2023-02-10 18:50:54 +00:00
std::optional<TypeId> annotatedKeyType;
std::optional<TypeId> annotatedIndexResultType;
if (expectedType)
{
if (const TableType* ttv = get<TableType>(follow(*expectedType)))
{
if (ttv->indexer)
{
annotatedKeyType.emplace(follow(ttv->indexer->indexType));
annotatedIndexResultType.emplace(ttv->indexer->indexResultType);
}
}
}
bool isIndexedResultType = false;
std::optional<TypeId> pinnedIndexResultType;
2022-06-17 01:54:42 +01:00
for (const AstExprTable::Item& item : expr->items)
{
2022-09-23 19:32:10 +01:00
std::optional<TypeId> expectedValueType;
2023-02-10 18:50:54 +00:00
if (item.kind == AstExprTable::Item::Kind::General || item.kind == AstExprTable::Item::Kind::List)
isIndexedResultType = true;
2022-09-23 19:32:10 +01:00
if (item.key && expectedType)
{
if (auto stringKey = item.key->as<AstExprConstantString>())
{
2022-12-02 10:46:05 +00:00
ErrorVec errorVec;
std::optional<TypeId> propTy =
2023-01-03 17:33:19 +00:00
findTablePropertyRespectingMeta(builtinTypes, errorVec, follow(*expectedType), stringKey->value.data, item.value->location);
2022-12-02 10:46:05 +00:00
if (propTy)
expectedValueType = propTy;
else
{
2023-01-03 17:33:19 +00:00
expectedValueType = arena->addType(BlockedType{});
2022-12-02 10:46:05 +00:00
addConstraint(scope, item.value->location, HasPropConstraint{*expectedValueType, *expectedType, stringKey->value.data});
}
2022-09-23 19:32:10 +01:00
}
}
2023-02-10 18:50:54 +00:00
// We'll resolve the expected index result type here with the following priority:
// 1. Record table types - in which key, value pairs must be handled on a k,v pair basis.
// In this case, the above if-statement will populate expectedValueType
// 2. Someone places an annotation on a General or List table
// Trust the annotation and have the solver inform them if they get it wrong
// 3. Someone omits the annotation on a general or List table
// Use the type of the first indexResultType as the expected type
std::optional<TypeId> checkExpectedIndexResultType;
if (expectedValueType)
{
checkExpectedIndexResultType = expectedValueType;
}
else if (annotatedIndexResultType)
{
checkExpectedIndexResultType = annotatedIndexResultType;
}
else if (pinnedIndexResultType)
{
checkExpectedIndexResultType = pinnedIndexResultType;
}
TypeId itemTy = check(scope, item.value, checkExpectedIndexResultType).ty;
if (isIndexedResultType && !pinnedIndexResultType)
pinnedIndexResultType = itemTy;
2022-06-03 21:32:20 +01:00
2022-06-17 01:54:42 +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.
2023-02-10 18:50:54 +00:00
TypeId keyTy = check(scope, item.key, annotatedKeyType).ty;
2022-06-17 01:54:42 +01:00
if (AstExprConstantString* key = item.key->as<AstExprConstantString>())
{
ttv->props[key->value.begin()] = {itemTy};
}
else
{
2022-09-02 00:00:14 +01:00
createIndexer(item.key->location, keyTy, itemTy);
2022-06-17 01:54:42 +01:00
}
}
else
{
2023-01-03 17:33:19 +00:00
TypeId numberType = builtinTypes->numberType;
2022-09-02 00:00:14 +01:00
// FIXME? The location isn't quite right here. Not sure what is
// right.
createIndexer(item.value->location, numberType, itemTy);
2022-06-17 01:54:42 +01:00
}
2022-06-03 21:32:20 +01:00
}
2022-06-17 01:54:42 +01:00
2022-10-27 23:22:49 +01:00
return Inference{ty};
2022-06-17 01:54:42 +01:00
}
2022-12-02 10:46:05 +00:00
ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionSignature(
const ScopePtr& parent, AstExprFunction* fn, std::optional<TypeId> expectedType)
2022-06-17 01:54:42 +01:00
{
2022-07-29 04:41:13 +01:00
ScopePtr signatureScope = nullptr;
ScopePtr bodyScope = nullptr;
2022-07-01 00:29:02 +01:00
TypePackId returnType = nullptr;
std::vector<TypeId> genericTypes;
std::vector<TypePackId> genericTypePacks;
2022-12-02 10:46:05 +00:00
if (expectedType)
expectedType = follow(*expectedType);
2022-07-01 00:29:02 +01:00
bool hasGenerics = fn->generics.size > 0 || fn->genericPacks.size > 0;
2022-12-02 10:46:05 +00:00
signatureScope = childScope(fn, parent);
2022-07-01 00:29:02 +01:00
2022-12-02 10:46:05 +00:00
// We need to assign returnType before creating bodyScope so that the
// return type gets propogated to bodyScope.
returnType = freshTypePack(signatureScope);
signatureScope->returnType = returnType;
2022-07-01 00:29:02 +01:00
2022-12-02 10:46:05 +00:00
bodyScope = childScope(fn->body, signatureScope);
2022-07-01 00:29:02 +01:00
2022-12-02 10:46:05 +00:00
if (hasGenerics)
{
2022-07-29 04:41:13 +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:29:02 +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 21:42:54 +01:00
signatureScope->privateTypeBindings[name] = TypeFun{g.ty};
2022-07-01 00:29:02 +01:00
}
for (const auto& [name, g] : genericPackDefinitions)
{
genericTypePacks.push_back(g.tp);
2022-08-11 21:42:54 +01:00
signatureScope->privateTypePackBindings[name] = g.tp;
2022-07-01 00:29:02 +01:00
}
2022-12-02 10:46:05 +00:00
2022-12-09 18:07:25 +00:00
// Local variable works around an odd gcc 11.3 warning: <anonymous> may be used uninitialized
std::optional<TypeId> none = std::nullopt;
expectedType = none;
2022-07-01 00:29:02 +01:00
}
2022-12-02 10:46:05 +00:00
std::vector<TypeId> argTypes;
TypePack expectedArgPack;
2023-01-03 17:33:19 +00:00
const FunctionType* expectedFunction = expectedType ? get<FunctionType>(*expectedType) : nullptr;
2022-12-02 10:46:05 +00:00
if (expectedFunction)
2022-07-01 00:29:02 +01:00
{
2023-01-03 17:33:19 +00:00
expectedArgPack = extendTypePack(*arena, builtinTypes, expectedFunction->argTypes, fn->args.size);
2022-12-02 10:46:05 +00:00
genericTypes = expectedFunction->generics;
genericTypePacks = expectedFunction->genericPacks;
}
2022-07-01 00:29:02 +01:00
2022-12-02 10:46:05 +00:00
for (size_t i = 0; i < fn->args.size; ++i)
{
AstLocal* local = fn->args.data[i];
TypeId t = freshType(signatureScope);
argTypes.push_back(t);
signatureScope->bindings[local] = Binding{t, local->location};
TypeId annotationTy = t;
if (local->annotation)
{
2023-01-20 12:02:39 +00:00
annotationTy = resolveType(signatureScope, local->annotation, /* inTypeArguments */ false);
2023-02-03 12:34:12 +00:00
// If we provide an annotation that is wrong, type inference should ignore the annotation
// and try to infer a fresh type, like in the old solver
if (get<ErrorType>(follow(annotationTy)))
annotationTy = freshType(signatureScope);
2022-12-02 10:46:05 +00:00
addConstraint(signatureScope, local->annotation->location, SubtypeConstraint{t, annotationTy});
}
else if (i < expectedArgPack.head.size())
{
addConstraint(signatureScope, local->location, SubtypeConstraint{t, expectedArgPack.head[i]});
}
2022-07-01 00:29:02 +01:00
2022-12-02 10:46:05 +00:00
// HACK: This is the one case where the type of the definition will diverge from the type of the binding.
// We need to do this because there are cases where type refinements needs to have the information available
// at constraint generation time.
if (auto def = dfg->getDef(local))
signatureScope->dcrRefinements[*def] = annotationTy;
2022-07-01 00:29:02 +01:00
}
2022-09-15 23:13:58 +01:00
TypePackId varargPack = nullptr;
2022-09-08 22:44:50 +01:00
if (fn->vararg)
{
if (fn->varargAnnotation)
{
2023-01-20 12:02:39 +00:00
TypePackId annotationType = resolveTypePack(signatureScope, fn->varargAnnotation, /* inTypeArguments */ false);
2022-09-08 22:44:50 +01:00
varargPack = annotationType;
}
2022-12-02 10:46:05 +00:00
else if (expectedArgPack.tail && get<VariadicTypePack>(*expectedArgPack.tail))
varargPack = *expectedArgPack.tail;
2022-09-08 22:44:50 +01:00
else
2023-01-03 17:33:19 +00:00
varargPack = builtinTypes->anyTypePack;
2022-09-08 22:44:50 +01:00
signatureScope->varargPack = varargPack;
2022-12-02 10:46:05 +00:00
bodyScope->varargPack = varargPack;
2022-09-08 22:44:50 +01:00
}
2022-09-15 23:13:58 +01:00
else
{
2023-01-03 17:33:19 +00:00
varargPack = arena->addTypePack(VariadicTypePack{builtinTypes->anyType, /*hidden*/ true});
2022-09-15 23:13:58 +01:00
// We do not add to signatureScope->varargPack because ... is not valid
// in functions without an explicit ellipsis.
2022-12-02 10:46:05 +00:00
signatureScope->varargPack = std::nullopt;
bodyScope->varargPack = std::nullopt;
2022-09-15 23:13:58 +01:00
}
LUAU_ASSERT(nullptr != varargPack);
2022-09-08 22:44:50 +01:00
2022-12-02 10:46:05 +00:00
// If there is both an annotation and an expected type, the annotation wins.
// Type checking will sort out any discrepancies later.
2022-06-24 02:44:07 +01:00
if (fn->returnAnnotation)
{
2023-01-20 12:02:39 +00:00
TypePackId annotatedRetType = resolveTypePack(signatureScope, *fn->returnAnnotation, /* inTypeArguments */ false);
2022-09-23 19:32:10 +01:00
// We bind the annotated type directly here so that, when we need to
// generate constraints for return types, we have a guarantee that we
// know the annotated return type already, if one was provided.
LUAU_ASSERT(get<FreeTypePack>(returnType));
asMutable(returnType)->ty.emplace<BoundTypePack>(annotatedRetType);
2022-06-24 02:44:07 +01:00
}
2022-12-02 10:46:05 +00:00
else if (expectedFunction)
2022-06-03 21:32:20 +01:00
{
2022-12-02 10:46:05 +00:00
asMutable(returnType)->ty.emplace<BoundTypePack>(expectedFunction->retTypes);
2022-06-17 01:54:42 +01:00
}
2022-07-01 00:29:02 +01:00
// TODO: Preserve argument names in the function's type.
2022-06-24 02:44:07 +01:00
2023-01-03 17:33:19 +00:00
FunctionType actualFunction{TypeLevel{}, parent.get(), arena->addTypePack(argTypes, varargPack), returnType};
2022-07-01 00:29:02 +01:00
actualFunction.hasNoGenerics = !hasGenerics;
actualFunction.generics = std::move(genericTypes);
actualFunction.genericPacks = std::move(genericTypePacks);
2022-06-17 01:54:42 +01:00
TypeId actualFunctionType = arena->addType(std::move(actualFunction));
LUAU_ASSERT(actualFunctionType);
2023-02-03 12:34:12 +00:00
module->astTypes[fn] = actualFunctionType;
2022-06-17 01:54:42 +01:00
2023-01-03 17:33:19 +00:00
if (expectedType && get<FreeType>(*expectedType))
2022-12-02 10:46:05 +00:00
{
2023-01-03 17:33:19 +00:00
asMutable(*expectedType)->ty.emplace<BoundType>(actualFunctionType);
2022-12-02 10:46:05 +00:00
}
2022-07-01 00:29:02 +01:00
return {
/* signature */ actualFunctionType,
2022-12-02 10:46:05 +00:00
/* signatureScope */ signatureScope,
2022-07-29 04:41:13 +01:00
/* bodyScope */ bodyScope,
2022-07-01 00:29:02 +01:00
};
2022-06-17 01:54:42 +01:00
}
2022-07-29 04:41:13 +01:00
void ConstraintGraphBuilder::checkFunctionBody(const ScopePtr& scope, AstExprFunction* fn)
2022-06-17 01:54:42 +01:00
{
2022-07-01 00:29:02 +01:00
visitBlockWithoutChildScope(scope, fn->body);
2022-06-17 01:54:42 +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:00:14 +01:00
addConstraint(scope, fn->location, PackSubtypeConstraint{scope->returnType, empty});
2022-06-24 02:44:07 +01:00
}
}
2023-01-20 12:02:39 +00:00
TypeId ConstraintGraphBuilder::resolveType(const ScopePtr& scope, AstType* ty, bool inTypeArguments)
2022-06-24 02:44:07 +01:00
{
TypeId result = nullptr;
if (auto ref = ty->as<AstTypeReference>())
{
2022-09-29 23:11:54 +01:00
if (FFlag::DebugLuauMagicTypes)
{
if (ref->name == "_luau_ice")
ice->ice("_luau_ice encountered", ty->location);
else if (ref->name == "_luau_print")
{
if (ref->parameters.size != 1 || !ref->parameters.data[0].type)
{
reportError(ty->location, GenericError{"_luau_print requires one generic parameter"});
2023-01-03 17:33:19 +00:00
return builtinTypes->errorRecoveryType();
2022-09-29 23:11:54 +01:00
}
else
2023-01-20 12:02:39 +00:00
return resolveType(scope, ref->parameters.data[0].type, inTypeArguments);
2022-09-29 23:11:54 +01:00
}
}
2022-10-27 23:22:49 +01:00
std::optional<TypeFun> alias;
2022-08-04 22:27:28 +01:00
2022-10-27 23:22:49 +01:00
if (ref->prefix.has_value())
{
alias = scope->lookupImportedType(ref->prefix->value, ref->name.value);
}
else
{
alias = scope->lookupType(ref->name.value);
}
if (alias.has_value())
2022-08-04 22:27:28 +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:00:14 +01:00
if (alias.has_value() && alias->typeParams.empty() && alias->typePackParams.empty())
2022-08-04 22:27:28 +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)
{
2023-01-20 12:02:39 +00:00
parameters.push_back(resolveType(scope, p.type, /* inTypeArguments */ true));
2022-08-04 22:27:28 +01:00
}
else if (p.typePack)
{
2023-01-20 12:02:39 +00:00
packParameters.push_back(resolveTypePack(scope, p.typePack, /* inTypeArguments */ true));
2022-08-04 22:27:28 +01:00
}
else
{
// This indicates a parser bug: one of these two pointers
// should be set.
LUAU_ASSERT(false);
}
}
2023-01-03 17:33:19 +00:00
result = arena->addType(PendingExpansionType{ref->prefix, ref->name, parameters, packParameters});
2022-08-04 22:27:28 +01:00
2023-01-20 12:02:39 +00:00
// If we're not in a type argument context, we need to create a constraint that expands this.
// The dispatching of the above constraint will queue up additional constraints for nested
// type function applications.
if (!inTypeArguments)
2022-09-02 00:00:14 +01:00
addConstraint(scope, ty->location, TypeAliasExpansionConstraint{/* target */ result});
2022-08-04 22:27:28 +01:00
}
}
else
{
2022-10-27 23:22:49 +01:00
std::string typeName;
if (ref->prefix)
typeName = std::string(ref->prefix->value) + ".";
typeName += ref->name.value;
2023-01-03 17:33:19 +00:00
result = builtinTypes->errorRecoveryType();
2022-08-04 22:27:28 +01:00
}
2022-06-03 21:32:20 +01:00
}
2022-06-24 02:44:07 +01:00
else if (auto tab = ty->as<AstTypeTable>())
{
2023-01-03 17:33:19 +00:00
TableType::Props props;
2022-06-24 02:44:07 +01:00
std::optional<TableIndexer> indexer;
for (const AstTableProp& prop : tab->props)
{
std::string name = prop.name.value;
// TODO: Recursion limit.
2023-01-20 12:02:39 +00:00
TypeId propTy = resolveType(scope, prop.type, inTypeArguments);
2022-06-24 02:44:07 +01:00
// TODO: Fill in location.
props[name] = {propTy};
}
if (tab->indexer)
{
// TODO: Recursion limit.
indexer = TableIndexer{
2023-01-20 12:02:39 +00:00
resolveType(scope, tab->indexer->indexType, inTypeArguments),
resolveType(scope, tab->indexer->resultType, inTypeArguments),
2022-06-24 02:44:07 +01:00
};
}
2023-01-03 17:33:19 +00:00
result = arena->addType(TableType{props, indexer, scope->level, scope.get(), TableState::Sealed});
2022-06-24 02:44:07 +01:00
}
else if (auto fn = ty->as<AstTypeFunction>())
{
// TODO: Recursion limit.
2022-07-01 00:29:02 +01:00
bool hasGenerics = fn->generics.size > 0 || fn->genericPacks.size > 0;
2022-07-29 04:41:13 +01:00
ScopePtr signatureScope = nullptr;
2022-07-01 00:29:02 +01:00
std::vector<TypeId> genericTypes;
std::vector<TypePackId> genericTypePacks;
2022-06-24 02:44:07 +01:00
2022-07-01 00:29:02 +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:04:33 +01:00
signatureScope = childScope(fn, scope);
2022-07-01 00:29:02 +01:00
2022-07-29 04:41:13 +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:44:07 +01:00
2022-07-01 00:29:02 +01:00
for (const auto& [name, g] : genericDefinitions)
{
genericTypes.push_back(g.ty);
2022-08-11 21:42:54 +01:00
signatureScope->privateTypeBindings[name] = TypeFun{g.ty};
2022-07-01 00:29:02 +01:00
}
for (const auto& [name, g] : genericPackDefinitions)
{
genericTypePacks.push_back(g.tp);
2022-08-11 21:42:54 +01:00
signatureScope->privateTypePackBindings[name] = g.tp;
2022-07-01 00:29:02 +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 04:41:13 +01:00
signatureScope = scope;
2022-07-01 00:29:02 +01:00
}
2023-01-20 12:02:39 +00:00
TypePackId argTypes = resolveTypePack(signatureScope, fn->argTypes, inTypeArguments);
TypePackId returnTypes = resolveTypePack(signatureScope, fn->returnTypes, inTypeArguments);
2022-07-01 00:29:02 +01:00
2023-01-03 17:33:19 +00:00
// TODO: FunctionType needs a pointer to the scope so that we know
2022-07-01 00:29:02 +01:00
// how to quantify/instantiate it.
2023-01-03 17:33:19 +00:00
FunctionType ftv{TypeLevel{}, scope.get(), {}, {}, argTypes, returnTypes};
2022-07-01 00:29:02 +01:00
2023-01-03 17:33:19 +00:00
// This replicates the behavior of the appropriate FunctionType
2022-07-01 00:29:02 +01:00
// constructors.
ftv.hasNoGenerics = !hasGenerics;
ftv.generics = std::move(genericTypes);
ftv.genericPacks = std::move(genericTypePacks);
ftv.argNames.reserve(fn->argNames.size);
2022-06-24 02:44:07 +01:00
for (const auto& el : fn->argNames)
{
if (el)
{
const auto& [name, location] = *el;
2022-07-01 00:29:02 +01:00
ftv.argNames.push_back(FunctionArgument{name.value, location});
2022-06-24 02:44:07 +01:00
}
else
{
2022-07-01 00:29:02 +01:00
ftv.argNames.push_back(std::nullopt);
2022-06-24 02:44:07 +01:00
}
}
2022-07-01 00:29:02 +01:00
result = arena->addType(std::move(ftv));
2022-06-24 02:44:07 +01:00
}
else if (auto tof = ty->as<AstTypeTypeof>())
{
// TODO: Recursion limit.
2022-10-27 23:22:49 +01:00
TypeId exprType = check(scope, tof->expr).ty;
2022-06-24 02:44:07 +01:00
result = exprType;
}
else if (auto unionAnnotation = ty->as<AstTypeUnion>())
{
std::vector<TypeId> parts;
for (AstType* part : unionAnnotation->types)
{
// TODO: Recursion limit.
2023-01-20 12:02:39 +00:00
parts.push_back(resolveType(scope, part, inTypeArguments));
2022-06-24 02:44:07 +01:00
}
2023-01-03 17:33:19 +00:00
result = arena->addType(UnionType{parts});
2022-06-24 02:44:07 +01:00
}
else if (auto intersectionAnnotation = ty->as<AstTypeIntersection>())
{
std::vector<TypeId> parts;
for (AstType* part : intersectionAnnotation->types)
{
// TODO: Recursion limit.
2023-01-20 12:02:39 +00:00
parts.push_back(resolveType(scope, part, inTypeArguments));
2022-06-24 02:44:07 +01:00
}
2023-01-03 17:33:19 +00:00
result = arena->addType(IntersectionType{parts});
2022-06-24 02:44:07 +01:00
}
else if (auto boolAnnotation = ty->as<AstTypeSingletonBool>())
{
2023-01-03 17:33:19 +00:00
result = arena->addType(SingletonType(BooleanSingleton{boolAnnotation->value}));
2022-06-24 02:44:07 +01:00
}
else if (auto stringAnnotation = ty->as<AstTypeSingletonString>())
{
2023-01-03 17:33:19 +00:00
result = arena->addType(SingletonType(StringSingleton{std::string(stringAnnotation->value.data, stringAnnotation->value.size)}));
2022-06-24 02:44:07 +01:00
}
else if (ty->is<AstTypeError>())
{
2023-01-03 17:33:19 +00:00
result = builtinTypes->errorRecoveryType();
2022-06-24 02:44:07 +01:00
}
else
{
LUAU_ASSERT(0);
2023-01-03 17:33:19 +00:00
result = builtinTypes->errorRecoveryType();
2022-06-24 02:44:07 +01:00
}
2023-02-03 12:34:12 +00:00
module->astResolvedTypes[ty] = result;
2022-06-24 02:44:07 +01:00
return result;
}
2023-01-20 12:02:39 +00:00
TypePackId ConstraintGraphBuilder::resolveTypePack(const ScopePtr& scope, AstTypePack* tp, bool inTypeArgument)
2022-06-24 02:44:07 +01:00
{
TypePackId result;
if (auto expl = tp->as<AstTypePackExplicit>())
{
2023-01-20 12:02:39 +00:00
result = resolveTypePack(scope, expl->typeList, inTypeArgument);
2022-06-24 02:44:07 +01:00
}
else if (auto var = tp->as<AstTypePackVariadic>())
{
2023-01-20 12:02:39 +00:00
TypeId ty = resolveType(scope, var->variadicType, inTypeArgument);
2022-06-24 02:44:07 +01:00
result = arena->addTypePack(TypePackVar{VariadicTypePack{ty}});
}
else if (auto gen = tp->as<AstTypePackGeneric>())
{
2022-08-11 21:42:54 +01:00
if (std::optional<TypePackId> lookup = scope->lookupPack(gen->genericName.value))
2022-08-04 22:27:28 +01:00
{
result = *lookup;
}
else
{
reportError(tp->location, UnknownSymbol{gen->genericName.value, UnknownSymbol::Context::Type});
2023-01-03 17:33:19 +00:00
result = builtinTypes->errorRecoveryTypePack();
2022-08-04 22:27:28 +01:00
}
2022-06-24 02:44:07 +01:00
}
else
{
LUAU_ASSERT(0);
2023-01-03 17:33:19 +00:00
result = builtinTypes->errorRecoveryTypePack();
2022-06-24 02:44:07 +01:00
}
2023-02-03 12:34:12 +00:00
module->astResolvedTypePacks[tp] = result;
2022-06-24 02:44:07 +01:00
return result;
}
2023-01-20 12:02:39 +00:00
TypePackId ConstraintGraphBuilder::resolveTypePack(const ScopePtr& scope, const AstTypeList& list, bool inTypeArguments)
2022-06-24 02:44:07 +01:00
{
std::vector<TypeId> head;
for (AstType* headTy : list.types)
{
2023-01-20 12:02:39 +00:00
head.push_back(resolveType(scope, headTy, inTypeArguments));
2022-06-24 02:44:07 +01:00
}
std::optional<TypePackId> tail = std::nullopt;
if (list.tailType)
{
2023-01-20 12:02:39 +00:00
tail = resolveTypePack(scope, list.tailType, inTypeArguments);
2022-06-24 02:44:07 +01:00
}
return arena->addTypePack(TypePack{head, tail});
2022-06-03 21:32:20 +01:00
}
2022-07-29 04:41:13 +01:00
std::vector<std::pair<Name, GenericTypeDefinition>> ConstraintGraphBuilder::createGenerics(const ScopePtr& scope, AstArray<AstGenericType> generics)
2022-07-01 00:29:02 +01:00
{
std::vector<std::pair<Name, GenericTypeDefinition>> result;
for (const auto& generic : generics)
{
2023-01-03 17:33:19 +00:00
TypeId genericTy = arena->addType(GenericType{scope.get(), generic.name.value});
2022-07-01 00:29:02 +01:00
std::optional<TypeId> defaultTy = std::nullopt;
if (generic.defaultValue)
2023-01-20 12:02:39 +00:00
defaultTy = resolveType(scope, generic.defaultValue, /* inTypeArguments */ false);
2022-07-01 00:29:02 +01:00
2022-10-27 23:22:49 +01:00
result.push_back({generic.name.value, GenericTypeDefinition{genericTy, defaultTy}});
2022-07-01 00:29:02 +01:00
}
return result;
}
std::vector<std::pair<Name, GenericTypePackDefinition>> ConstraintGraphBuilder::createGenericPacks(
2022-07-29 04:41:13 +01:00
const ScopePtr& scope, AstArray<AstGenericTypePack> generics)
2022-07-01 00:29:02 +01:00
{
std::vector<std::pair<Name, GenericTypePackDefinition>> result;
for (const auto& generic : generics)
{
2022-07-29 04:41:13 +01:00
TypePackId genericTy = arena->addTypePack(TypePackVar{GenericTypePack{scope.get(), generic.name.value}});
2022-07-01 00:29:02 +01:00
std::optional<TypePackId> defaultTy = std::nullopt;
if (generic.defaultValue)
2023-01-20 12:02:39 +00:00
defaultTy = resolveTypePack(scope, generic.defaultValue, /* inTypeArguments */ false);
2022-07-01 00:29:02 +01:00
2022-10-27 23:22:49 +01:00
result.push_back({generic.name.value, GenericTypePackDefinition{genericTy, defaultTy}});
2022-07-01 00:29:02 +01:00
}
return result;
}
2022-10-27 23:22:49 +01:00
Inference ConstraintGraphBuilder::flattenPack(const ScopePtr& scope, Location location, InferencePack pack)
2022-07-01 00:29:02 +01:00
{
2023-02-03 12:34:12 +00:00
const auto& [tp, refinements] = pack;
RefinementId refinement = nullptr;
if (!refinements.empty())
refinement = refinements[0];
2022-11-04 17:02:37 +00:00
2022-07-01 00:29:02 +01:00
if (auto f = first(tp))
2023-02-03 12:34:12 +00:00
return Inference{*f, refinement};
2022-07-01 00:29:02 +01:00
TypeId typeResult = freshType(scope);
TypePack onePack{{typeResult}, freshTypePack(scope)};
TypePackId oneTypePack = arena->addTypePack(std::move(onePack));
2022-09-02 00:00:14 +01:00
addConstraint(scope, location, PackSubtypeConstraint{tp, oneTypePack});
2022-07-01 00:29:02 +01:00
2023-02-03 12:34:12 +00:00
return Inference{typeResult, refinement};
2022-07-01 00:29:02 +01:00
}
void ConstraintGraphBuilder::reportError(Location location, TypeErrorData err)
{
errors.push_back(TypeError{location, moduleName, std::move(err)});
2022-09-08 22:44:50 +01:00
if (FFlag::DebugLuauLogSolverToJson)
logger->captureGenerationError(errors.back());
2022-07-01 00:29:02 +01:00
}
void ConstraintGraphBuilder::reportCodeTooComplex(Location location)
{
errors.push_back(TypeError{location, moduleName, CodeTooComplex{}});
2022-09-08 22:44:50 +01:00
if (FFlag::DebugLuauLogSolverToJson)
logger->captureGenerationError(errors.back());
2022-07-01 00:29:02 +01:00
}
struct GlobalPrepopulator : AstVisitor
{
2022-07-29 04:41:13 +01:00
const NotNull<Scope> globalScope;
2022-07-01 00:29:02 +01:00
const NotNull<TypeArena> arena;
2022-07-29 04:41:13 +01:00
GlobalPrepopulator(NotNull<Scope> globalScope, NotNull<TypeArena> arena)
2022-07-01 00:29:02 +01:00
: globalScope(globalScope)
, arena(arena)
{
}
bool visit(AstStatFunction* function) override
{
if (AstExprGlobal* g = function->name->as<AstExprGlobal>())
2023-01-03 17:33:19 +00:00
globalScope->bindings[g->name] = Binding{arena->addType(BlockedType{})};
2022-07-01 00:29:02 +01:00
return true;
}
};
2022-07-29 04:41:13 +01:00
void ConstraintGraphBuilder::prepopulateGlobalScope(const ScopePtr& globalScope, AstStatBlock* program)
2022-07-01 00:29:02 +01:00
{
2022-07-29 04:41:13 +01:00
GlobalPrepopulator gp{NotNull{globalScope.get()}, arena};
2022-07-01 00:29:02 +01:00
program->visit(&gp);
}
2022-11-10 22:04:44 +00:00
std::vector<NotNull<Constraint>> borrowConstraints(const std::vector<ConstraintPtr>& constraints)
2022-06-03 21:32:20 +01:00
{
2022-11-10 22:04:44 +00:00
std::vector<NotNull<Constraint>> result;
result.reserve(constraints.size());
2022-06-03 21:32:20 +01:00
2022-11-10 22:04:44 +00:00
for (const auto& c : constraints)
result.emplace_back(c.get());
2022-06-03 21:32:20 +01:00
return result;
}
} // namespace Luau