2022-06-03 23:15:45 +01:00
|
|
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
|
|
#include "Luau/ConstraintGraphBuilder.h"
|
2022-10-21 18:54:01 +01:00
|
|
|
|
2022-08-11 22:01:33 +01:00
|
|
|
#include "Luau/Ast.h"
|
2023-03-03 20:21:14 +00:00
|
|
|
#include "Luau/Breadcrumb.h"
|
2022-08-18 22:32:08 +01:00
|
|
|
#include "Luau/Common.h"
|
2022-08-11 22:01:33 +01:00
|
|
|
#include "Luau/Constraint.h"
|
2023-03-17 19:20:37 +00:00
|
|
|
#include "Luau/ControlFlow.h"
|
2022-10-21 18:54:01 +01:00
|
|
|
#include "Luau/DcrLogger.h"
|
2022-09-02 00:14:03 +01:00
|
|
|
#include "Luau/ModuleResolver.h"
|
2022-07-01 00:52:43 +01:00
|
|
|
#include "Luau/RecursionCounter.h"
|
2023-03-03 20:21:14 +00:00
|
|
|
#include "Luau/Refinement.h"
|
2022-10-21 18:54:01 +01:00
|
|
|
#include "Luau/Scope.h"
|
|
|
|
#include "Luau/TypeUtils.h"
|
2023-01-04 20:53:17 +00:00
|
|
|
#include "Luau/Type.h"
|
2023-05-19 20:37:30 +01:00
|
|
|
#include "Luau/TypeFamily.h"
|
|
|
|
#include "Luau/Simplify.h"
|
|
|
|
#include "Luau/VisitType.h"
|
2023-06-09 18:08:00 +01:00
|
|
|
#include "Luau/InsertionOrderedMap.h"
|
2022-07-01 00:52:43 +01:00
|
|
|
|
2023-02-17 23:41:51 +00:00
|
|
|
#include <algorithm>
|
|
|
|
|
2022-07-01 00:52:43 +01:00
|
|
|
LUAU_FASTINT(LuauCheckRecursionLimit);
|
2022-09-29 23:23:10 +01:00
|
|
|
LUAU_FASTFLAG(DebugLuauMagicTypes);
|
2023-06-12 21:02:54 +01:00
|
|
|
LUAU_FASTFLAG(LuauParseDeclareClassIndexer);
|
2022-06-03 23:15:45 +01:00
|
|
|
|
|
|
|
namespace Luau
|
|
|
|
{
|
|
|
|
|
2023-03-24 18:03:04 +00:00
|
|
|
bool doesCallError(const AstExprCall* call); // TypeInfer.cpp
|
2022-06-17 02:05:14 +01:00
|
|
|
const AstStat* getFallthrough(const AstStat* node); // TypeInfer.cpp
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
static std::optional<AstExpr*> matchRequire(const AstExprCall& call)
|
|
|
|
{
|
|
|
|
const char* require = "require";
|
|
|
|
|
|
|
|
if (call.args.size != 1)
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
const AstExprGlobal* funcAsGlobal = call.func->as<AstExprGlobal>();
|
|
|
|
if (!funcAsGlobal || funcAsGlobal->name != require)
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
if (call.args.size != 1)
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
return call.args.data[0];
|
|
|
|
}
|
|
|
|
|
2022-09-23 20:17:25 +01:00
|
|
|
static bool matchSetmetatable(const AstExprCall& call)
|
|
|
|
{
|
|
|
|
const char* smt = "setmetatable";
|
|
|
|
|
|
|
|
if (call.args.size != 2)
|
|
|
|
return false;
|
2022-10-14 20:48:41 +01:00
|
|
|
|
2022-09-23 20:17:25 +01:00
|
|
|
const AstExprGlobal* funcAsGlobal = call.func->as<AstExprGlobal>();
|
|
|
|
if (!funcAsGlobal || funcAsGlobal->name != smt)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-11-10 22:53:13 +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 19:40:38 +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:53:13 +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
|
|
|
|
|
2023-04-21 23:14:26 +01:00
|
|
|
ConstraintGraphBuilder::ConstraintGraphBuilder(ModulePtr module, TypeArena* arena, NotNull<ModuleResolver> moduleResolver,
|
2023-04-28 20:55:13 +01:00
|
|
|
NotNull<BuiltinTypes> builtinTypes, NotNull<InternalErrorReporter> ice, const ScopePtr& globalScope,
|
|
|
|
std::function<void(const ModuleName&, const ScopePtr&)> prepareModuleScope, DcrLogger* logger, NotNull<DataFlowGraph> dfg)
|
2023-04-21 23:14:26 +01:00
|
|
|
: module(module)
|
2023-01-04 20:53:17 +00:00
|
|
|
, builtinTypes(builtinTypes)
|
2022-06-03 23:15:45 +01:00
|
|
|
, arena(arena)
|
|
|
|
, rootScope(nullptr)
|
2022-10-21 18:54:01 +01:00
|
|
|
, dfg(dfg)
|
2022-09-02 00:14:03 +01:00
|
|
|
, moduleResolver(moduleResolver)
|
2022-07-01 00:52:43 +01:00
|
|
|
, ice(ice)
|
|
|
|
, globalScope(globalScope)
|
2023-04-28 20:55:13 +01:00
|
|
|
, prepareModuleScope(std::move(prepareModuleScope))
|
2022-09-08 23:14:25 +01:00
|
|
|
, logger(logger)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-08-18 22:32:08 +01:00
|
|
|
LUAU_ASSERT(module);
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
TypeId ConstraintGraphBuilder::freshType(const ScopePtr& scope)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
return arena->addType(FreeType{scope.get()});
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
TypePackId ConstraintGraphBuilder::freshTypePack(const ScopePtr& scope)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-07-29 05:24:07 +01:00
|
|
|
FreeTypePack f{scope.get()};
|
2022-06-03 23:15:45 +01:00
|
|
|
return arena->addTypePack(TypePackVar{std::move(f)});
|
|
|
|
}
|
|
|
|
|
2022-08-18 22:32:08 +01:00
|
|
|
ScopePtr ConstraintGraphBuilder::childScope(AstNode* node, const ScopePtr& parent)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-07-29 05:24:07 +01:00
|
|
|
auto scope = std::make_shared<Scope>(parent);
|
2022-08-18 22:32:08 +01:00
|
|
|
scopes.emplace_back(node->location, scope);
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
scope->returnType = parent->returnType;
|
2022-09-08 23:14:25 +01:00
|
|
|
scope->varargPack = parent->varargPack;
|
2022-08-18 22:32:08 +01:00
|
|
|
|
|
|
|
parent->children.push_back(NotNull{scope.get()});
|
|
|
|
module->astScopes[node] = scope.get();
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
return scope;
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
|
2022-10-21 18:54:01 +01:00
|
|
|
NotNull<Constraint> ConstraintGraphBuilder::addConstraint(const ScopePtr& scope, const Location& location, ConstraintV cv)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-11-10 22:53:13 +00:00
|
|
|
return NotNull{constraints.emplace_back(new Constraint{NotNull{scope.get()}, location, std::move(cv)}).get()};
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
|
2022-10-21 18:54:01 +01:00
|
|
|
NotNull<Constraint> ConstraintGraphBuilder::addConstraint(const ScopePtr& scope, std::unique_ptr<Constraint> c)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-11-10 22:53:13 +00:00
|
|
|
return NotNull{constraints.emplace_back(std::move(c)).get()};
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
struct RefinementPartition
|
|
|
|
{
|
|
|
|
// Types that we want to intersect against the type of the expression.
|
|
|
|
std::vector<TypeId> discriminantTypes;
|
|
|
|
|
|
|
|
// Sometimes the type we're discriminating against is implicitly nil.
|
|
|
|
bool shouldAppendNilType = false;
|
|
|
|
};
|
|
|
|
|
2023-06-09 18:08:00 +01:00
|
|
|
using RefinementContext = InsertionOrderedMap<DefId, RefinementPartition>;
|
2023-03-03 20:21:14 +00:00
|
|
|
|
2023-05-19 20:37:30 +01:00
|
|
|
static void unionRefinements(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, const RefinementContext& lhs, const RefinementContext& rhs,
|
|
|
|
RefinementContext& dest, std::vector<ConstraintV>* constraints)
|
2022-11-04 17:33:22 +00:00
|
|
|
{
|
2023-05-19 20:37:30 +01:00
|
|
|
const auto intersect = [&](const std::vector<TypeId>& types) {
|
|
|
|
if (1 == types.size())
|
|
|
|
return types[0];
|
|
|
|
else if (2 == types.size())
|
|
|
|
{
|
|
|
|
// TODO: It may be advantageous to create a RefineConstraint here when there are blockedTypes.
|
|
|
|
SimplifyResult sr = simplifyIntersection(builtinTypes, arena, types[0], types[1]);
|
|
|
|
if (sr.blockedTypes.empty())
|
|
|
|
return sr.result;
|
|
|
|
}
|
|
|
|
|
|
|
|
return arena->addType(IntersectionType{types});
|
|
|
|
};
|
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
for (auto& [def, partition] : lhs)
|
2022-11-04 17:33:22 +00:00
|
|
|
{
|
|
|
|
auto rhsIt = rhs.find(def);
|
|
|
|
if (rhsIt == rhs.end())
|
|
|
|
continue;
|
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
LUAU_ASSERT(!partition.discriminantTypes.empty());
|
|
|
|
LUAU_ASSERT(!rhsIt->second.discriminantTypes.empty());
|
2022-11-04 17:33:22 +00:00
|
|
|
|
2023-05-19 20:37:30 +01:00
|
|
|
TypeId leftDiscriminantTy = partition.discriminantTypes.size() == 1 ? partition.discriminantTypes[0] : intersect(partition.discriminantTypes);
|
2022-11-04 17:33:22 +00:00
|
|
|
|
2023-05-19 20:37:30 +01:00
|
|
|
TypeId rightDiscriminantTy =
|
|
|
|
rhsIt->second.discriminantTypes.size() == 1 ? rhsIt->second.discriminantTypes[0] : intersect(rhsIt->second.discriminantTypes);
|
2023-03-03 20:21:14 +00:00
|
|
|
|
2023-06-09 18:08:00 +01:00
|
|
|
dest.insert(def, {});
|
|
|
|
dest.get(def)->discriminantTypes.push_back(simplifyUnion(builtinTypes, arena, leftDiscriminantTy, rightDiscriminantTy).result);
|
|
|
|
dest.get(def)->shouldAppendNilType |= partition.shouldAppendNilType || rhsIt->second.shouldAppendNilType;
|
2022-11-04 17:33:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-19 20:37:30 +01:00
|
|
|
static void computeRefinement(NotNull<BuiltinTypes> builtinTypes, NotNull<TypeArena> arena, const ScopePtr& scope, RefinementId refinement,
|
|
|
|
RefinementContext* refis, bool sense, bool eq, std::vector<ConstraintV>* constraints)
|
2022-11-04 17:33:22 +00:00
|
|
|
{
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
if (!refinement)
|
2022-11-04 17:33:22 +00:00
|
|
|
return;
|
2023-02-10 19:40:38 +00:00
|
|
|
else if (auto variadic = get<Variadic>(refinement))
|
|
|
|
{
|
|
|
|
for (RefinementId refi : variadic->refinements)
|
2023-05-19 20:37:30 +01:00
|
|
|
computeRefinement(builtinTypes, arena, scope, refi, refis, sense, eq, constraints);
|
2023-02-10 19:40:38 +00:00
|
|
|
}
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
else if (auto negation = get<Negation>(refinement))
|
2023-05-19 20:37:30 +01:00
|
|
|
return computeRefinement(builtinTypes, arena, scope, negation->refinement, refis, !sense, eq, constraints);
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
else if (auto conjunction = get<Conjunction>(refinement))
|
2022-11-04 17:33:22 +00:00
|
|
|
{
|
2023-03-03 20:21:14 +00:00
|
|
|
RefinementContext lhsRefis;
|
|
|
|
RefinementContext rhsRefis;
|
2022-11-04 17:33:22 +00:00
|
|
|
|
2023-05-19 20:37:30 +01:00
|
|
|
computeRefinement(builtinTypes, arena, scope, conjunction->lhs, sense ? refis : &lhsRefis, sense, eq, constraints);
|
|
|
|
computeRefinement(builtinTypes, arena, scope, conjunction->rhs, sense ? refis : &rhsRefis, sense, eq, constraints);
|
2022-11-04 17:33:22 +00:00
|
|
|
|
|
|
|
if (!sense)
|
2023-05-19 20:37:30 +01:00
|
|
|
unionRefinements(builtinTypes, arena, lhsRefis, rhsRefis, *refis, constraints);
|
2022-11-04 17:33:22 +00:00
|
|
|
}
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
else if (auto disjunction = get<Disjunction>(refinement))
|
2022-11-04 17:33:22 +00:00
|
|
|
{
|
2023-03-03 20:21:14 +00:00
|
|
|
RefinementContext lhsRefis;
|
|
|
|
RefinementContext rhsRefis;
|
2022-11-04 17:33:22 +00:00
|
|
|
|
2023-05-19 20:37:30 +01:00
|
|
|
computeRefinement(builtinTypes, arena, scope, disjunction->lhs, sense ? &lhsRefis : refis, sense, eq, constraints);
|
|
|
|
computeRefinement(builtinTypes, arena, scope, disjunction->rhs, sense ? &rhsRefis : refis, sense, eq, constraints);
|
2022-11-04 17:33:22 +00:00
|
|
|
|
|
|
|
if (sense)
|
2023-05-19 20:37:30 +01:00
|
|
|
unionRefinements(builtinTypes, arena, lhsRefis, rhsRefis, *refis, constraints);
|
2022-11-04 17:33:22 +00:00
|
|
|
}
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
else if (auto equivalence = get<Equivalence>(refinement))
|
2022-11-04 17:33:22 +00:00
|
|
|
{
|
2023-05-19 20:37:30 +01:00
|
|
|
computeRefinement(builtinTypes, arena, scope, equivalence->lhs, refis, sense, true, constraints);
|
|
|
|
computeRefinement(builtinTypes, arena, scope, equivalence->rhs, refis, sense, true, constraints);
|
2022-11-04 17:33:22 +00:00
|
|
|
}
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
else if (auto proposition = get<Proposition>(refinement))
|
2022-11-04 17:33:22 +00:00
|
|
|
{
|
|
|
|
TypeId discriminantTy = proposition->discriminantTy;
|
|
|
|
if (!sense && !eq)
|
2023-01-04 20:53:17 +00:00
|
|
|
discriminantTy = arena->addType(NegationType{proposition->discriminantTy});
|
2022-12-02 18:09:59 +00:00
|
|
|
else if (eq)
|
2022-11-04 17:33:22 +00:00
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
discriminantTy = arena->addType(BlockedType{});
|
2022-12-02 18:09:59 +00:00
|
|
|
constraints->push_back(SingletonOrTopTypeConstraint{discriminantTy, proposition->discriminantTy, !sense});
|
2022-11-04 17:33:22 +00:00
|
|
|
}
|
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
RefinementContext uncommittedRefis;
|
2023-06-09 18:08:00 +01:00
|
|
|
uncommittedRefis.insert(proposition->breadcrumb->def, {});
|
|
|
|
uncommittedRefis.get(proposition->breadcrumb->def)->discriminantTypes.push_back(discriminantTy);
|
2022-11-04 17:33:22 +00:00
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
// When the top-level expression is `t[x]`, we want to refine it into `nil`, not `never`.
|
|
|
|
if ((sense || !eq) && getMetadata<SubscriptMetadata>(proposition->breadcrumb))
|
2023-06-09 18:08:00 +01:00
|
|
|
uncommittedRefis.get(proposition->breadcrumb->def)->shouldAppendNilType = true;
|
2022-12-02 18:09:59 +00:00
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
for (NullableBreadcrumbId current = proposition->breadcrumb; current && current->previous; current = current->previous)
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(get<Cell>(current->def));
|
2022-12-02 18:09:59 +00:00
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
// If this current breadcrumb has no metadata, it's no-op for the purpose of building a discriminant type.
|
|
|
|
if (!current->metadata)
|
|
|
|
continue;
|
|
|
|
else if (auto field = getMetadata<FieldMetadata>(current))
|
|
|
|
{
|
|
|
|
TableType::Props props{{field->prop, Property{discriminantTy}}};
|
|
|
|
discriminantTy = arena->addType(TableType{std::move(props), std::nullopt, TypeLevel{}, scope.get(), TableState::Sealed});
|
2023-06-09 18:08:00 +01:00
|
|
|
uncommittedRefis.insert(current->previous->def, {});
|
|
|
|
uncommittedRefis.get(current->previous->def)->discriminantTypes.push_back(discriminantTy);
|
2023-03-03 20:21:14 +00:00
|
|
|
}
|
|
|
|
}
|
2022-12-02 18:09:59 +00:00
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
// And now it's time to commit it.
|
|
|
|
for (auto& [def, partition] : uncommittedRefis)
|
|
|
|
{
|
2023-06-09 18:08:00 +01:00
|
|
|
(*refis).insert(def, {});
|
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
for (TypeId discriminantTy : partition.discriminantTypes)
|
2023-06-09 18:08:00 +01:00
|
|
|
(*refis).get(def)->discriminantTypes.push_back(discriminantTy);
|
2022-12-02 18:09:59 +00:00
|
|
|
|
2023-06-09 18:08:00 +01:00
|
|
|
(*refis).get(def)->shouldAppendNilType |= partition.shouldAppendNilType;
|
2023-03-03 20:21:14 +00:00
|
|
|
}
|
|
|
|
}
|
2022-12-02 18:09:59 +00:00
|
|
|
}
|
|
|
|
|
2023-05-19 20:37:30 +01:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Constraint generation may be called upon to simplify an intersection or union
|
|
|
|
* of types that are not sufficiently solved yet. We use
|
|
|
|
* FindSimplificationBlockers to recognize these types and defer the
|
|
|
|
* simplification until constraint solution.
|
|
|
|
*/
|
|
|
|
struct FindSimplificationBlockers : TypeOnceVisitor
|
|
|
|
{
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
bool visit(TypeId) override
|
|
|
|
{
|
|
|
|
return !found;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool visit(TypeId, const BlockedType&) override
|
|
|
|
{
|
|
|
|
found = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool visit(TypeId, const FreeType&) override
|
|
|
|
{
|
|
|
|
found = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool visit(TypeId, const PendingExpansionType&) override
|
|
|
|
{
|
|
|
|
found = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We do not need to know anything at all about a function's argument or
|
|
|
|
// return types in order to simplify it in an intersection or union.
|
|
|
|
bool visit(TypeId, const FunctionType&) override
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool visit(TypeId, const ClassType&) override
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
bool mustDeferIntersection(TypeId ty)
|
|
|
|
{
|
|
|
|
FindSimplificationBlockers bts;
|
|
|
|
bts.traverse(ty);
|
|
|
|
return bts.found;
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
void ConstraintGraphBuilder::applyRefinements(const ScopePtr& scope, Location location, RefinementId refinement)
|
2022-11-04 17:33:22 +00:00
|
|
|
{
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
if (!refinement)
|
2022-11-04 17:33:22 +00:00
|
|
|
return;
|
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
RefinementContext refinements;
|
2022-12-02 18:09:59 +00:00
|
|
|
std::vector<ConstraintV> constraints;
|
2023-05-19 20:37:30 +01:00
|
|
|
computeRefinement(builtinTypes, arena, scope, refinement, &refinements, /*sense*/ true, /*eq*/ false, &constraints);
|
2022-11-04 17:33:22 +00:00
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
for (auto& [def, partition] : refinements)
|
2022-11-04 17:33:22 +00:00
|
|
|
{
|
2023-03-03 20:21:14 +00:00
|
|
|
if (std::optional<TypeId> defTy = scope->lookup(def))
|
|
|
|
{
|
|
|
|
TypeId ty = *defTy;
|
|
|
|
if (partition.shouldAppendNilType)
|
|
|
|
ty = arena->addType(UnionType{{ty, builtinTypes->nilType}});
|
2022-11-04 17:33:22 +00:00
|
|
|
|
2023-05-19 20:37:30 +01:00
|
|
|
// Intersect ty with every discriminant type. If either type is not
|
|
|
|
// sufficiently solved, we queue the intersection up via an
|
|
|
|
// IntersectConstraint.
|
|
|
|
|
|
|
|
for (TypeId dt : partition.discriminantTypes)
|
|
|
|
{
|
|
|
|
if (mustDeferIntersection(ty) || mustDeferIntersection(dt))
|
|
|
|
{
|
|
|
|
TypeId r = arena->addType(BlockedType{});
|
|
|
|
addConstraint(scope, location, RefineConstraint{RefineConstraint::Intersection, r, ty, dt});
|
|
|
|
|
|
|
|
ty = r;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ty = simplifyIntersection(builtinTypes, arena, ty, dt).result;
|
|
|
|
}
|
|
|
|
|
|
|
|
scope->dcrRefinements[def] = ty;
|
2023-03-03 20:21:14 +00:00
|
|
|
}
|
2022-11-04 17:33:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (auto& c : constraints)
|
|
|
|
addConstraint(scope, location, c);
|
|
|
|
}
|
|
|
|
|
2022-06-03 23:15:45 +01:00
|
|
|
void ConstraintGraphBuilder::visit(AstStatBlock* block)
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(scopes.empty());
|
|
|
|
LUAU_ASSERT(rootScope == nullptr);
|
2022-08-11 22:01:33 +01:00
|
|
|
ScopePtr scope = std::make_shared<Scope>(globalScope);
|
2022-07-29 05:24:07 +01:00
|
|
|
rootScope = scope.get();
|
|
|
|
scopes.emplace_back(block->location, scope);
|
2022-08-18 22:32:08 +01:00
|
|
|
module->astScopes[block] = NotNull{scope.get()};
|
2022-07-01 00:52:43 +01:00
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
rootScope->returnType = freshTypePack(scope);
|
2022-07-01 00:52:43 +01:00
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
prepopulateGlobalScope(scope, block);
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
visitBlockWithoutChildScope(scope, block);
|
2023-02-24 21:49:38 +00:00
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
if (logger)
|
2023-02-24 21:49:38 +00:00
|
|
|
logger->captureGenerationModule(module);
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
ControlFlow ConstraintGraphBuilder::visitBlockWithoutChildScope(const ScopePtr& scope, AstStatBlock* block)
|
2022-07-01 00:52:43 +01:00
|
|
|
{
|
|
|
|
RecursionCounter counter{&recursionCount};
|
|
|
|
|
|
|
|
if (recursionCount >= FInt::LuauCheckRecursionLimit)
|
|
|
|
{
|
|
|
|
reportCodeTooComplex(block->location);
|
2023-03-17 19:20:37 +00:00
|
|
|
return ControlFlow::None;
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
|
|
|
|
2022-08-04 23:35:33 +01:00
|
|
|
std::unordered_map<Name, Location> aliasDefinitionLocations;
|
|
|
|
|
|
|
|
// In order to enable mutually-recursive type aliases, we need to
|
|
|
|
// populate the type bindings before we actually check any of the
|
2023-02-17 23:41:51 +00:00
|
|
|
// alias statements.
|
2022-08-04 23:35:33 +01:00
|
|
|
for (AstStat* stat : block->body)
|
|
|
|
{
|
|
|
|
if (auto alias = stat->as<AstStatTypeAlias>())
|
|
|
|
{
|
2023-02-17 23:41:51 +00:00
|
|
|
if (scope->exportedTypeBindings.count(alias->name.value) || scope->privateTypeBindings.count(alias->name.value))
|
2022-08-04 23:35:33 +01:00
|
|
|
{
|
|
|
|
auto it = aliasDefinitionLocations.find(alias->name.value);
|
|
|
|
LUAU_ASSERT(it != aliasDefinitionLocations.end());
|
|
|
|
reportError(alias->location, DuplicateTypeDefinition{alias->name.value, it->second});
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2023-02-17 23:41:51 +00:00
|
|
|
ScopePtr defnScope = childScope(alias, scope);
|
2022-08-04 23:35:33 +01:00
|
|
|
|
2023-02-17 23:41:51 +00:00
|
|
|
TypeId initialType = arena->addType(BlockedType{});
|
|
|
|
TypeFun initialFun{initialType};
|
2022-08-04 23:35:33 +01:00
|
|
|
|
2023-02-17 23:41:51 +00:00
|
|
|
for (const auto& [name, gen] : createGenerics(defnScope, alias->generics, /* useCache */ true))
|
2022-08-04 23:35:33 +01:00
|
|
|
{
|
|
|
|
initialFun.typeParams.push_back(gen);
|
|
|
|
}
|
|
|
|
|
2023-02-17 23:41:51 +00:00
|
|
|
for (const auto& [name, genPack] : createGenericPacks(defnScope, alias->genericPacks, /* useCache */ true))
|
2022-08-04 23:35:33 +01:00
|
|
|
{
|
|
|
|
initialFun.typePackParams.push_back(genPack);
|
|
|
|
}
|
|
|
|
|
2023-02-17 23:41:51 +00:00
|
|
|
if (alias->exported)
|
|
|
|
scope->exportedTypeBindings[alias->name.value] = std::move(initialFun);
|
|
|
|
else
|
|
|
|
scope->privateTypeBindings[alias->name.value] = std::move(initialFun);
|
|
|
|
|
2022-08-04 23:35:33 +01:00
|
|
|
astTypeAliasDefiningScopes[alias] = defnScope;
|
|
|
|
aliasDefinitionLocations[alias->name.value] = alias->location;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
std::optional<ControlFlow> firstControlFlow;
|
2022-07-01 00:52:43 +01:00
|
|
|
for (AstStat* stat : block->body)
|
2023-03-17 19:20:37 +00:00
|
|
|
{
|
|
|
|
ControlFlow cf = visit(scope, stat);
|
|
|
|
if (cf != ControlFlow::None && !firstControlFlow)
|
|
|
|
firstControlFlow = cf;
|
|
|
|
}
|
|
|
|
|
|
|
|
return firstControlFlow.value_or(ControlFlow::None);
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
ControlFlow ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStat* stat)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-07-01 00:52:43 +01:00
|
|
|
RecursionLimiter limiter{&recursionCount, FInt::LuauCheckRecursionLimit};
|
2022-06-03 23:15:45 +01:00
|
|
|
|
|
|
|
if (auto s = stat->as<AstStatBlock>())
|
2023-03-17 19:20:37 +00:00
|
|
|
return visit(scope, s);
|
2023-02-17 23:41:51 +00:00
|
|
|
else if (auto i = stat->as<AstStatIf>())
|
2023-03-17 19:20:37 +00:00
|
|
|
return visit(scope, i);
|
2022-08-11 22:01:33 +01:00
|
|
|
else if (auto s = stat->as<AstStatWhile>())
|
2023-03-17 19:20:37 +00:00
|
|
|
return visit(scope, s);
|
2022-08-18 22:32:08 +01:00
|
|
|
else if (auto s = stat->as<AstStatRepeat>())
|
2023-03-17 19:20:37 +00:00
|
|
|
return visit(scope, s);
|
2023-02-17 23:41:51 +00:00
|
|
|
else if (stat->is<AstStatBreak>() || stat->is<AstStatContinue>())
|
|
|
|
{
|
|
|
|
// Nothing
|
2023-03-17 19:20:37 +00:00
|
|
|
return ControlFlow::None; // TODO: ControlFlow::Break/Continue
|
2023-02-17 23:41:51 +00:00
|
|
|
}
|
2022-06-03 23:15:45 +01:00
|
|
|
else if (auto r = stat->as<AstStatReturn>())
|
2023-03-17 19:20:37 +00:00
|
|
|
return visit(scope, r);
|
2023-02-17 23:41:51 +00:00
|
|
|
else if (auto e = stat->as<AstStatExpr>())
|
2023-03-17 19:20:37 +00:00
|
|
|
{
|
2023-02-17 23:41:51 +00:00
|
|
|
checkPack(scope, e->expr);
|
2023-03-17 19:20:37 +00:00
|
|
|
|
|
|
|
if (auto call = e->expr->as<AstExprCall>(); call && doesCallError(call))
|
|
|
|
return ControlFlow::Throws;
|
|
|
|
|
|
|
|
return ControlFlow::None;
|
|
|
|
}
|
2023-02-17 23:41:51 +00:00
|
|
|
else if (auto s = stat->as<AstStatLocal>())
|
2023-03-17 19:20:37 +00:00
|
|
|
return visit(scope, s);
|
2023-02-17 23:41:51 +00:00
|
|
|
else if (auto s = stat->as<AstStatFor>())
|
2023-03-17 19:20:37 +00:00
|
|
|
return visit(scope, s);
|
2023-02-17 23:41:51 +00:00
|
|
|
else if (auto s = stat->as<AstStatForIn>())
|
2023-03-17 19:20:37 +00:00
|
|
|
return visit(scope, s);
|
2022-06-17 02:05:14 +01:00
|
|
|
else if (auto a = stat->as<AstStatAssign>())
|
2023-03-17 19:20:37 +00:00
|
|
|
return visit(scope, a);
|
2022-08-18 22:32:08 +01:00
|
|
|
else if (auto a = stat->as<AstStatCompoundAssign>())
|
2023-03-17 19:20:37 +00:00
|
|
|
return visit(scope, a);
|
2023-02-17 23:41:51 +00:00
|
|
|
else if (auto f = stat->as<AstStatFunction>())
|
2023-03-17 19:20:37 +00:00
|
|
|
return visit(scope, f);
|
2023-02-17 23:41:51 +00:00
|
|
|
else if (auto f = stat->as<AstStatLocalFunction>())
|
2023-03-17 19:20:37 +00:00
|
|
|
return visit(scope, f);
|
2022-06-24 02:56:00 +01:00
|
|
|
else if (auto a = stat->as<AstStatTypeAlias>())
|
2023-03-17 19:20:37 +00:00
|
|
|
return visit(scope, a);
|
2022-08-04 23:35:33 +01:00
|
|
|
else if (auto s = stat->as<AstStatDeclareGlobal>())
|
2023-03-17 19:20:37 +00:00
|
|
|
return visit(scope, s);
|
2022-08-04 23:35:33 +01:00
|
|
|
else if (auto s = stat->as<AstStatDeclareFunction>())
|
2023-03-17 19:20:37 +00:00
|
|
|
return visit(scope, s);
|
2023-02-17 23:41:51 +00:00
|
|
|
else if (auto s = stat->as<AstStatDeclareClass>())
|
2023-03-17 19:20:37 +00:00
|
|
|
return visit(scope, s);
|
2022-09-29 23:23:10 +01:00
|
|
|
else if (auto s = stat->as<AstStatError>())
|
2023-03-17 19:20:37 +00:00
|
|
|
return visit(scope, s);
|
2022-06-03 23:15:45 +01:00
|
|
|
else
|
2023-03-17 19:20:37 +00:00
|
|
|
{
|
2023-02-17 23:41:51 +00:00
|
|
|
LUAU_ASSERT(0 && "Internal error: Unknown AstStat type");
|
2023-03-17 19:20:37 +00:00
|
|
|
return ControlFlow::None;
|
|
|
|
}
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
ControlFlow ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocal* local)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
|
|
|
std::vector<TypeId> varTypes;
|
2022-10-21 18:54:01 +01:00
|
|
|
varTypes.reserve(local->vars.size);
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2023-01-27 22:28:31 +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 23:15:45 +01:00
|
|
|
for (AstLocal* local : local->vars)
|
|
|
|
{
|
2022-09-23 20:17:25 +01:00
|
|
|
TypeId ty = nullptr;
|
2022-06-24 02:56:00 +01:00
|
|
|
|
|
|
|
if (local->annotation)
|
2023-01-20 20:27:03 +00:00
|
|
|
ty = resolveType(scope, local->annotation, /* inTypeArguments */ false);
|
2022-06-24 02:56:00 +01:00
|
|
|
|
2022-06-03 23:15:45 +01:00
|
|
|
varTypes.push_back(ty);
|
|
|
|
}
|
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
for (size_t i = 0; i < local->values.size; ++i)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-08-25 22:53:50 +01:00
|
|
|
AstExpr* value = local->values.data[i];
|
2022-09-23 20:17:25 +01:00
|
|
|
const bool hasAnnotation = i < local->vars.size && nullptr != local->vars.data[i]->annotation;
|
|
|
|
|
2022-08-25 22:53:50 +01:00
|
|
|
if (value->is<AstExprConstantNil>())
|
2022-06-17 02:05:14 +01:00
|
|
|
{
|
2022-11-04 17:33:22 +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:54:01 +01:00
|
|
|
|
|
|
|
if (!varTypes[i])
|
|
|
|
varTypes[i] = freshType(scope);
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
2022-11-04 17:33:22 +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);
|
|
|
|
|
2023-04-28 20:55:13 +01:00
|
|
|
TypeId exprType = check(scope, value, ValueContext::RValue, expectedType).ty;
|
2022-11-04 17:33:22 +00:00
|
|
|
if (i < varTypes.size())
|
|
|
|
{
|
|
|
|
if (varTypes[i])
|
|
|
|
addConstraint(scope, local->location, SubtypeConstraint{exprType, varTypes[i]});
|
|
|
|
else
|
|
|
|
varTypes[i] = exprType;
|
|
|
|
}
|
2023-01-27 22:28:31 +00:00
|
|
|
|
|
|
|
if (i == 0)
|
|
|
|
firstValueType = exprType;
|
2022-11-04 17:33:22 +00:00
|
|
|
}
|
|
|
|
else
|
2022-06-17 02:05:14 +01:00
|
|
|
{
|
2023-02-17 23:41:51 +00:00
|
|
|
std::vector<std::optional<TypeId>> expectedTypes;
|
2022-09-23 20:17:25 +01:00
|
|
|
if (hasAnnotation)
|
|
|
|
expectedTypes.insert(begin(expectedTypes), begin(varTypes) + i, end(varTypes));
|
|
|
|
|
2022-10-28 11:37:29 +01:00
|
|
|
TypePackId exprPack = checkPack(scope, value, expectedTypes).tp;
|
2022-06-17 02:05:14 +01:00
|
|
|
|
|
|
|
if (i < local->vars.size)
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
TypePack packTypes = extendTypePack(*arena, builtinTypes, exprPack, varTypes.size() - i);
|
2022-10-21 18:54:01 +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 18:09:59 +00:00
|
|
|
if (j - i < packTypes.head.size())
|
|
|
|
varTypes[j] = packTypes.head[j - i];
|
2022-10-21 18:54:01 +01:00
|
|
|
else
|
2023-02-24 21:49:38 +00:00
|
|
|
varTypes[j] = arena->addType(BlockedType{});
|
2022-10-21 18:54:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
std::vector<TypeId> tailValues{varTypes.begin() + i, varTypes.end()};
|
|
|
|
TypePackId tailPack = arena->addTypePack(std::move(tailValues));
|
2023-02-24 21:49:38 +00:00
|
|
|
addConstraint(scope, local->location, UnpackConstraint{tailPack, exprPack});
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
}
|
2022-09-02 00:14:03 +01:00
|
|
|
}
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
if (local->vars.size == 1 && local->values.size == 1 && firstValueType && scope.get() == rootScope)
|
2023-01-27 22:28:31 +00:00
|
|
|
{
|
|
|
|
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:54:01 +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.
|
2023-03-03 20:21:14 +00:00
|
|
|
BreadcrumbId bc = dfg->getBreadcrumb(l);
|
|
|
|
scope->dcrRefinements[bc->def] = varTypes[i];
|
2022-10-21 18:54:01 +01:00
|
|
|
}
|
|
|
|
|
2022-09-02 00:14:03 +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;
|
|
|
|
|
2023-04-21 23:14:26 +01:00
|
|
|
if (auto moduleInfo = moduleResolver->resolveModuleInfo(module->name, *require))
|
2022-09-02 00:14:03 +01:00
|
|
|
{
|
|
|
|
const Name name{local->vars.data[i]->name.value};
|
|
|
|
|
|
|
|
if (ModulePtr module = moduleResolver->getModule(moduleInfo->name))
|
2023-01-20 20:27:03 +00:00
|
|
|
{
|
2023-02-10 19:40:38 +00:00
|
|
|
scope->importedTypeBindings[name] = module->exportedTypeBindings;
|
2023-03-10 20:21:07 +00:00
|
|
|
scope->importedModules[name] = moduleInfo->name;
|
2023-01-20 20:27:03 +00:00
|
|
|
}
|
2022-09-02 00:14:03 +01:00
|
|
|
}
|
|
|
|
}
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
}
|
2023-03-17 19:20:37 +00:00
|
|
|
|
|
|
|
return ControlFlow::None;
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
ControlFlow ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatFor* for_)
|
2022-07-14 23:52:26 +01:00
|
|
|
{
|
2023-03-03 20:21:14 +00:00
|
|
|
TypeId annotationTy = builtinTypes->numberType;
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
if (for_->var->annotation)
|
2023-03-03 20:21:14 +00:00
|
|
|
annotationTy = resolveType(scope, for_->var->annotation, /* inTypeArguments */ false);
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
|
|
|
|
auto inferNumber = [&](AstExpr* expr) {
|
2022-07-14 23:52:26 +01:00
|
|
|
if (!expr)
|
|
|
|
return;
|
2022-08-04 23:35:33 +01:00
|
|
|
|
2022-10-28 11:37:29 +01:00
|
|
|
TypeId t = check(scope, expr).ty;
|
2023-01-04 20:53:17 +00:00
|
|
|
addConstraint(scope, expr->location, SubtypeConstraint{t, builtinTypes->numberType});
|
2022-07-14 23:52:26 +01:00
|
|
|
};
|
|
|
|
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
inferNumber(for_->from);
|
|
|
|
inferNumber(for_->to);
|
|
|
|
inferNumber(for_->step);
|
2022-07-14 23:52:26 +01:00
|
|
|
|
2022-08-18 22:32:08 +01:00
|
|
|
ScopePtr forScope = childScope(for_, scope);
|
2023-03-03 20:21:14 +00:00
|
|
|
forScope->bindings[for_->var] = Binding{annotationTy, for_->var->location};
|
|
|
|
|
|
|
|
BreadcrumbId bc = dfg->getBreadcrumb(for_->var);
|
|
|
|
forScope->dcrRefinements[bc->def] = annotationTy;
|
2022-07-14 23:52:26 +01:00
|
|
|
|
|
|
|
visit(forScope, for_->body);
|
2023-03-17 19:20:37 +00:00
|
|
|
|
|
|
|
return ControlFlow::None;
|
2022-07-14 23:52:26 +01:00
|
|
|
}
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
ControlFlow ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatForIn* forIn)
|
2022-09-02 00:14:03 +01:00
|
|
|
{
|
|
|
|
ScopePtr loopScope = childScope(forIn, scope);
|
|
|
|
|
2022-10-28 11:37:29 +01:00
|
|
|
TypePackId iterator = checkPack(scope, forIn->values).tp;
|
2022-09-02 00:14:03 +01:00
|
|
|
|
|
|
|
std::vector<TypeId> variableTypes;
|
|
|
|
variableTypes.reserve(forIn->vars.size);
|
|
|
|
for (AstLocal* var : forIn->vars)
|
|
|
|
{
|
2023-06-24 07:19:39 +01:00
|
|
|
TypeId ty = nullptr;
|
|
|
|
if (var->annotation)
|
|
|
|
ty = resolveType(loopScope, var->annotation, /*inTypeArguments*/ false);
|
|
|
|
else
|
|
|
|
ty = freshType(loopScope);
|
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
loopScope->bindings[var] = Binding{ty, var->location};
|
|
|
|
variableTypes.push_back(ty);
|
2022-11-10 22:53:13 +00:00
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
BreadcrumbId bc = dfg->getBreadcrumb(var);
|
|
|
|
loopScope->dcrRefinements[bc->def] = ty;
|
2022-09-02 00:14:03 +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()}));
|
|
|
|
|
2023-05-25 22:36:34 +01:00
|
|
|
addConstraint(
|
2023-06-24 07:19:39 +01:00
|
|
|
loopScope, getLocation(forIn->values), IterableConstraint{iterator, variablePack, forIn->values.data[0], &module->astForInNextTypes});
|
2022-09-02 00:14:03 +01:00
|
|
|
visit(loopScope, forIn->body);
|
2023-03-17 19:20:37 +00:00
|
|
|
|
|
|
|
return ControlFlow::None;
|
2022-09-02 00:14:03 +01:00
|
|
|
}
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
ControlFlow ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatWhile* while_)
|
2022-08-11 22:01:33 +01:00
|
|
|
{
|
2023-07-07 21:10:48 +01:00
|
|
|
RefinementId refinement = check(scope, while_->condition).refinement;
|
2022-08-11 22:01:33 +01:00
|
|
|
|
2022-08-18 22:32:08 +01:00
|
|
|
ScopePtr whileScope = childScope(while_, scope);
|
2023-07-07 21:10:48 +01:00
|
|
|
applyRefinements(whileScope, while_->condition->location, refinement);
|
2022-08-11 22:01:33 +01:00
|
|
|
|
|
|
|
visit(whileScope, while_->body);
|
2023-03-17 19:20:37 +00:00
|
|
|
|
|
|
|
return ControlFlow::None;
|
2022-08-11 22:01:33 +01:00
|
|
|
}
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
ControlFlow ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatRepeat* repeat)
|
2022-08-18 22:32:08 +01:00
|
|
|
{
|
|
|
|
ScopePtr repeatScope = childScope(repeat, scope);
|
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
visitBlockWithoutChildScope(repeatScope, repeat->body);
|
2022-08-18 22:32:08 +01:00
|
|
|
|
|
|
|
check(repeatScope, repeat->condition);
|
2023-03-17 19:20:37 +00:00
|
|
|
|
|
|
|
return ControlFlow::None;
|
2022-08-18 22:32:08 +01:00
|
|
|
}
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
ControlFlow ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocalFunction* function)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
|
|
|
// Local
|
|
|
|
// Global
|
|
|
|
// Dotted path
|
|
|
|
// Self?
|
|
|
|
|
|
|
|
TypeId functionType = nullptr;
|
|
|
|
auto ty = scope->lookup(function->name);
|
2022-07-01 00:52:43 +01:00
|
|
|
LUAU_ASSERT(!ty.has_value()); // The parser ensures that every local function has a distinct Symbol for its name.
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
functionType = arena->addType(BlockedType{});
|
2022-07-29 05:24:07 +01:00
|
|
|
scope->bindings[function->name] = Binding{functionType, function->name->location};
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2023-05-19 20:37:30 +01:00
|
|
|
FunctionSignature sig = checkFunctionSignature(scope, function->func, /* expectedType */ std::nullopt, function->name->location);
|
2022-07-29 05:24:07 +01:00
|
|
|
sig.bodyScope->bindings[function->name] = Binding{sig.signature, function->func->location};
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
BreadcrumbId bc = dfg->getBreadcrumb(function->name);
|
|
|
|
scope->dcrRefinements[bc->def] = functionType;
|
|
|
|
sig.bodyScope->dcrRefinements[bc->def] = sig.signature;
|
|
|
|
|
2022-12-02 18:09:59 +00:00
|
|
|
Checkpoint start = checkpoint(this);
|
2022-07-01 00:52:43 +01:00
|
|
|
checkFunctionBody(sig.bodyScope, function->func);
|
2022-12-02 18:09:59 +00:00
|
|
|
Checkpoint end = checkpoint(this);
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-08-18 22:32:08 +01:00
|
|
|
NotNull<Scope> constraintScope{sig.signatureScope ? sig.signatureScope.get() : sig.bodyScope.get()};
|
2022-09-02 00:14:03 +01:00
|
|
|
std::unique_ptr<Constraint> c =
|
|
|
|
std::make_unique<Constraint>(constraintScope, function->name->location, GeneralizationConstraint{functionType, sig.signature});
|
2022-11-10 22:53:13 +00:00
|
|
|
|
2023-07-07 21:10:48 +01:00
|
|
|
Constraint* previous = nullptr;
|
|
|
|
forEachConstraint(start, end, this, [&c, &previous](const ConstraintPtr& constraint) {
|
2022-11-10 22:53:13 +00:00
|
|
|
c->dependencies.push_back(NotNull{constraint.get()});
|
2023-07-07 21:10:48 +01:00
|
|
|
|
|
|
|
if (auto psc = get<PackSubtypeConstraint>(*constraint); psc && psc->returns)
|
|
|
|
{
|
|
|
|
if (previous)
|
|
|
|
constraint->dependencies.push_back(NotNull{previous});
|
|
|
|
|
|
|
|
previous = constraint.get();
|
|
|
|
}
|
2022-11-10 22:53:13 +00:00
|
|
|
});
|
2022-06-17 02:05:14 +01:00
|
|
|
|
|
|
|
addConstraint(scope, std::move(c));
|
2023-05-12 18:50:47 +01:00
|
|
|
module->astTypes[function->func] = functionType;
|
2023-03-17 19:20:37 +00:00
|
|
|
|
|
|
|
return ControlFlow::None;
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
ControlFlow ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatFunction* function)
|
2022-06-17 02:05:14 +01:00
|
|
|
{
|
|
|
|
// Name could be AstStatLocal, AstStatGlobal, AstStatIndexName.
|
|
|
|
// With or without self
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
TypeId generalizedType = arena->addType(BlockedType{});
|
2022-06-17 02:05:14 +01:00
|
|
|
|
2023-02-17 23:41:51 +00:00
|
|
|
Checkpoint start = checkpoint(this);
|
2023-05-19 20:37:30 +01:00
|
|
|
FunctionSignature sig = checkFunctionSignature(scope, function->func, /* expectedType */ std::nullopt, function->name->location);
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2023-02-24 21:49:38 +00:00
|
|
|
std::unordered_set<Constraint*> excludeList;
|
|
|
|
|
2023-05-19 20:37:30 +01:00
|
|
|
const NullableBreadcrumbId functionBreadcrumb = dfg->getBreadcrumb(function->name);
|
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
if (AstExprLocal* localName = function->name->as<AstExprLocal>())
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-06-17 02:05:14 +01:00
|
|
|
std::optional<TypeId> existingFunctionTy = scope->lookup(localName->local);
|
|
|
|
if (existingFunctionTy)
|
|
|
|
{
|
2022-12-02 18:09:59 +00:00
|
|
|
addConstraint(scope, function->name->location, SubtypeConstraint{generalizedType, *existingFunctionTy});
|
|
|
|
|
|
|
|
Symbol sym{localName->local};
|
|
|
|
scope->bindings[sym].typeId = generalizedType;
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
else
|
2022-12-02 18:09:59 +00:00
|
|
|
scope->bindings[localName->local] = Binding{generalizedType, localName->location};
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
sig.bodyScope->bindings[localName->local] = Binding{sig.signature, localName->location};
|
2023-05-19 20:37:30 +01:00
|
|
|
|
|
|
|
if (functionBreadcrumb)
|
|
|
|
sig.bodyScope->dcrRefinements[functionBreadcrumb->def] = sig.signature;
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
else if (AstExprGlobal* globalName = function->name->as<AstExprGlobal>())
|
|
|
|
{
|
|
|
|
std::optional<TypeId> existingFunctionTy = scope->lookup(globalName->name);
|
2022-12-02 18:09:59 +00:00
|
|
|
if (!existingFunctionTy)
|
|
|
|
ice->ice("prepopulateGlobalScope did not populate a global name", globalName->location);
|
|
|
|
|
|
|
|
generalizedType = *existingFunctionTy;
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
sig.bodyScope->bindings[globalName->name] = Binding{sig.signature, globalName->location};
|
2023-05-19 20:37:30 +01:00
|
|
|
|
|
|
|
if (functionBreadcrumb)
|
|
|
|
sig.bodyScope->dcrRefinements[functionBreadcrumb->def] = sig.signature;
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
else if (AstExprIndexName* indexName = function->name->as<AstExprIndexName>())
|
|
|
|
{
|
2023-02-24 21:49:38 +00:00
|
|
|
Checkpoint check1 = checkpoint(this);
|
2023-02-10 19:40:38 +00:00
|
|
|
TypeId lvalueType = checkLValue(scope, indexName);
|
2023-02-24 21:49:38 +00:00
|
|
|
Checkpoint check2 = checkpoint(this);
|
|
|
|
|
|
|
|
forEachConstraint(check1, check2, this, [&excludeList](const ConstraintPtr& c) {
|
|
|
|
excludeList.insert(c.get());
|
|
|
|
});
|
|
|
|
|
2023-02-10 19:40:38 +00:00
|
|
|
// TODO figure out how to populate the location field of the table Property.
|
2023-02-24 21:49:38 +00:00
|
|
|
|
|
|
|
if (get<FreeType>(lvalueType))
|
|
|
|
asMutable(lvalueType)->ty.emplace<BoundType>(generalizedType);
|
|
|
|
else
|
|
|
|
addConstraint(scope, indexName->location, SubtypeConstraint{lvalueType, generalizedType});
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
|
|
|
else if (AstExprError* err = function->name->as<AstExprError>())
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
generalizedType = builtinTypes->errorRecoveryType();
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
|
2022-12-02 18:09:59 +00:00
|
|
|
if (generalizedType == nullptr)
|
|
|
|
ice->ice("generalizedType == nullptr", function->location);
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2023-05-19 20:37:30 +01:00
|
|
|
if (functionBreadcrumb)
|
|
|
|
scope->dcrRefinements[functionBreadcrumb->def] = generalizedType;
|
2023-03-03 20:21:14 +00:00
|
|
|
|
2022-07-01 00:52:43 +01:00
|
|
|
checkFunctionBody(sig.bodyScope, function->func);
|
2022-12-02 18:09:59 +00:00
|
|
|
Checkpoint end = checkpoint(this);
|
2022-07-01 00:52:43 +01:00
|
|
|
|
2022-08-18 22:32:08 +01:00
|
|
|
NotNull<Scope> constraintScope{sig.signatureScope ? sig.signatureScope.get() : sig.bodyScope.get()};
|
2022-09-02 00:14:03 +01:00
|
|
|
std::unique_ptr<Constraint> c =
|
2022-12-02 18:09:59 +00:00
|
|
|
std::make_unique<Constraint>(constraintScope, function->name->location, GeneralizationConstraint{generalizedType, sig.signature});
|
2022-11-10 22:53:13 +00:00
|
|
|
|
2023-07-07 21:10:48 +01:00
|
|
|
Constraint* previous = nullptr;
|
|
|
|
forEachConstraint(start, end, this, [&c, &excludeList, &previous](const ConstraintPtr& constraint) {
|
2023-02-24 21:49:38 +00:00
|
|
|
if (!excludeList.count(constraint.get()))
|
|
|
|
c->dependencies.push_back(NotNull{constraint.get()});
|
2023-07-07 21:10:48 +01:00
|
|
|
|
|
|
|
if (auto psc = get<PackSubtypeConstraint>(*constraint); psc && psc->returns)
|
|
|
|
{
|
|
|
|
if (previous)
|
|
|
|
constraint->dependencies.push_back(NotNull{previous});
|
|
|
|
|
|
|
|
previous = constraint.get();
|
|
|
|
}
|
2022-11-10 22:53:13 +00:00
|
|
|
});
|
2022-06-03 23:15:45 +01:00
|
|
|
|
|
|
|
addConstraint(scope, std::move(c));
|
2023-03-17 19:20:37 +00:00
|
|
|
|
|
|
|
return ControlFlow::None;
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
ControlFlow ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatReturn* ret)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-09-23 20:17:25 +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.
|
2023-02-17 23:41:51 +00:00
|
|
|
std::vector<std::optional<TypeId>> expectedTypes;
|
2022-09-23 20:17:25 +01:00
|
|
|
for (TypeId ty : scope->returnType)
|
|
|
|
expectedTypes.push_back(ty);
|
|
|
|
|
2022-10-28 11:37:29 +01:00
|
|
|
TypePackId exprTypes = checkPack(scope, ret->list, expectedTypes).tp;
|
2023-07-07 21:10:48 +01:00
|
|
|
addConstraint(scope, ret->location, PackSubtypeConstraint{exprTypes, scope->returnType, /*returns*/ true});
|
2023-03-17 19:20:37 +00:00
|
|
|
|
|
|
|
return ControlFlow::Returns;
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
ControlFlow ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatBlock* block)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-08-18 22:32:08 +01:00
|
|
|
ScopePtr innerScope = childScope(block, scope);
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
ControlFlow flow = visitBlockWithoutChildScope(innerScope, block);
|
|
|
|
scope->inheritRefinements(innerScope);
|
|
|
|
|
|
|
|
return flow;
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
|
2023-02-24 21:49:38 +00:00
|
|
|
static void bindFreeType(TypeId a, TypeId b)
|
2022-06-17 02:05:14 +01:00
|
|
|
{
|
2023-02-24 21:49:38 +00:00
|
|
|
FreeType* af = getMutable<FreeType>(a);
|
|
|
|
FreeType* bf = getMutable<FreeType>(b);
|
2022-12-02 18:09:59 +00:00
|
|
|
|
2023-02-24 21:49:38 +00:00
|
|
|
LUAU_ASSERT(af || bf);
|
|
|
|
|
|
|
|
if (!bf)
|
|
|
|
asMutable(a)->ty.emplace<BoundType>(b);
|
|
|
|
else if (!af)
|
|
|
|
asMutable(b)->ty.emplace<BoundType>(a);
|
|
|
|
else if (subsumes(bf->scope, af->scope))
|
|
|
|
asMutable(a)->ty.emplace<BoundType>(b);
|
|
|
|
else if (subsumes(af->scope, bf->scope))
|
|
|
|
asMutable(b)->ty.emplace<BoundType>(a);
|
|
|
|
}
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
ControlFlow ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatAssign* assign)
|
2023-02-24 21:49:38 +00:00
|
|
|
{
|
|
|
|
std::vector<TypeId> varTypes = checkLValues(scope, assign->vars);
|
2023-02-17 23:41:51 +00:00
|
|
|
|
|
|
|
std::vector<std::optional<TypeId>> expectedTypes;
|
2023-02-24 21:49:38 +00:00
|
|
|
expectedTypes.reserve(varTypes.size());
|
2023-02-17 23:41:51 +00:00
|
|
|
|
2023-02-24 21:49:38 +00:00
|
|
|
for (TypeId ty : varTypes)
|
2023-02-17 23:41:51 +00:00
|
|
|
{
|
|
|
|
ty = follow(ty);
|
|
|
|
if (get<FreeType>(ty))
|
|
|
|
expectedTypes.push_back(std::nullopt);
|
|
|
|
else
|
|
|
|
expectedTypes.push_back(ty);
|
|
|
|
}
|
|
|
|
|
2023-02-24 21:49:38 +00:00
|
|
|
TypePackId exprPack = checkPack(scope, assign->values, expectedTypes).tp;
|
|
|
|
TypePackId varPack = arena->addTypePack({varTypes});
|
2022-06-17 02:05:14 +01:00
|
|
|
|
2023-02-24 21:49:38 +00:00
|
|
|
addConstraint(scope, assign->location, PackSubtypeConstraint{exprPack, varPack});
|
2023-03-17 19:20:37 +00:00
|
|
|
|
|
|
|
return ControlFlow::None;
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
ControlFlow ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatCompoundAssign* assign)
|
2022-08-18 22:32:08 +01:00
|
|
|
{
|
2023-01-06 21:14:35 +00:00
|
|
|
// We need to tweak the BinaryConstraint that we emit, so we cannot use the
|
|
|
|
// strategy of falsifying an AST fragment.
|
2023-03-03 20:21:14 +00:00
|
|
|
TypeId varTy = checkLValue(scope, assign->var);
|
|
|
|
TypeId valueTy = check(scope, assign->value).ty;
|
2022-08-18 22:32:08 +01:00
|
|
|
|
2023-01-06 21:14:35 +00:00
|
|
|
TypeId resultType = arena->addType(BlockedType{});
|
|
|
|
addConstraint(scope, assign->location,
|
2023-03-03 20:21:14 +00:00
|
|
|
BinaryConstraint{assign->op, varTy, valueTy, resultType, assign, &module->astOriginalCallTypes, &module->astOverloadResolvedTypes});
|
|
|
|
addConstraint(scope, assign->location, SubtypeConstraint{resultType, varTy});
|
2023-03-17 19:20:37 +00:00
|
|
|
|
|
|
|
return ControlFlow::None;
|
2022-08-18 22:32:08 +01:00
|
|
|
}
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
ControlFlow ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatIf* ifStatement)
|
2022-06-17 02:05:14 +01:00
|
|
|
{
|
2023-04-28 20:55:13 +01:00
|
|
|
RefinementId refinement = check(scope, ifStatement->condition, ValueContext::RValue, std::nullopt).refinement;
|
2022-06-17 02:05:14 +01:00
|
|
|
|
2022-08-18 22:32:08 +01:00
|
|
|
ScopePtr thenScope = childScope(ifStatement->thenbody, scope);
|
2023-02-10 19:40:38 +00:00
|
|
|
applyRefinements(thenScope, ifStatement->condition->location, refinement);
|
2022-06-17 02:05:14 +01:00
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
ScopePtr elseScope = childScope(ifStatement->elsebody ? ifStatement->elsebody : ifStatement, scope);
|
|
|
|
applyRefinements(elseScope, ifStatement->elseLocation.value_or(ifStatement->condition->location), refinementArena.negation(refinement));
|
|
|
|
|
|
|
|
ControlFlow thencf = visit(thenScope, ifStatement->thenbody);
|
|
|
|
ControlFlow elsecf = ControlFlow::None;
|
2022-06-17 02:05:14 +01:00
|
|
|
if (ifStatement->elsebody)
|
2023-03-17 19:20:37 +00:00
|
|
|
elsecf = visit(elseScope, ifStatement->elsebody);
|
|
|
|
|
|
|
|
if (matches(thencf, ControlFlow::Returns | ControlFlow::Throws) && elsecf == ControlFlow::None)
|
|
|
|
scope->inheritRefinements(elseScope);
|
|
|
|
else if (thencf == ControlFlow::None && matches(elsecf, ControlFlow::Returns | ControlFlow::Throws))
|
|
|
|
scope->inheritRefinements(thenScope);
|
|
|
|
|
|
|
|
if (matches(thencf, ControlFlow::Returns | ControlFlow::Throws) && matches(elsecf, ControlFlow::Returns | ControlFlow::Throws))
|
|
|
|
return ControlFlow::Returns;
|
|
|
|
else
|
|
|
|
return ControlFlow::None;
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
|
2023-02-17 23:41:51 +00:00
|
|
|
static bool occursCheck(TypeId needle, TypeId haystack)
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(get<BlockedType>(needle));
|
|
|
|
haystack = follow(haystack);
|
|
|
|
|
|
|
|
auto checkHaystack = [needle](TypeId haystack) {
|
|
|
|
return occursCheck(needle, haystack);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (needle == haystack)
|
|
|
|
return true;
|
|
|
|
else if (auto ut = get<UnionType>(haystack))
|
|
|
|
return std::any_of(begin(ut), end(ut), checkHaystack);
|
|
|
|
else if (auto it = get<IntersectionType>(haystack))
|
|
|
|
return std::any_of(begin(it), end(it), checkHaystack);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
ControlFlow ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatTypeAlias* alias)
|
2022-06-24 02:56:00 +01:00
|
|
|
{
|
2023-02-17 23:41:51 +00:00
|
|
|
ScopePtr* defnScope = astTypeAliasDefiningScopes.find(alias);
|
|
|
|
|
|
|
|
std::unordered_map<Name, TypeFun>* typeBindings;
|
|
|
|
if (alias->exported)
|
|
|
|
typeBindings = &scope->exportedTypeBindings;
|
|
|
|
else
|
|
|
|
typeBindings = &scope->privateTypeBindings;
|
|
|
|
|
2022-08-04 23:35:33 +01:00
|
|
|
// These will be undefined if the alias was a duplicate definition, in which
|
|
|
|
// case we just skip over it.
|
2023-02-17 23:41:51 +00:00
|
|
|
auto bindingIt = typeBindings->find(alias->name.value);
|
|
|
|
if (bindingIt == typeBindings->end() || defnScope == nullptr)
|
2023-03-17 19:20:37 +00:00
|
|
|
return ControlFlow::None;
|
2022-06-24 02:56:00 +01:00
|
|
|
|
2023-02-17 23:41:51 +00:00
|
|
|
TypeId ty = resolveType(*defnScope, alias->type, /* inTypeArguments */ false);
|
2022-08-04 23:35:33 +01:00
|
|
|
|
2023-02-17 23:41:51 +00:00
|
|
|
TypeId aliasTy = bindingIt->second.type;
|
|
|
|
LUAU_ASSERT(get<BlockedType>(aliasTy));
|
|
|
|
|
|
|
|
if (occursCheck(aliasTy, ty))
|
2022-09-02 00:14:03 +01:00
|
|
|
{
|
2023-02-17 23:41:51 +00:00
|
|
|
asMutable(aliasTy)->ty.emplace<BoundType>(builtinTypes->anyType);
|
|
|
|
reportError(alias->nameLocation, OccursCheckFailed{});
|
2022-09-02 00:14:03 +01:00
|
|
|
}
|
2023-02-17 23:41:51 +00:00
|
|
|
else
|
|
|
|
asMutable(aliasTy)->ty.emplace<BoundType>(ty);
|
|
|
|
|
|
|
|
std::vector<TypeId> typeParams;
|
2023-02-24 21:49:38 +00:00
|
|
|
for (auto tyParam : createGenerics(*defnScope, alias->generics, /* useCache */ true, /* addTypes */ false))
|
2023-02-17 23:41:51 +00:00
|
|
|
typeParams.push_back(tyParam.second.ty);
|
|
|
|
|
|
|
|
std::vector<TypePackId> typePackParams;
|
2023-02-24 21:49:38 +00:00
|
|
|
for (auto tpParam : createGenericPacks(*defnScope, alias->genericPacks, /* useCache */ true, /* addTypes */ false))
|
2023-02-17 23:41:51 +00:00
|
|
|
typePackParams.push_back(tpParam.second.tp);
|
|
|
|
|
|
|
|
addConstraint(scope, alias->type->location,
|
|
|
|
NameConstraint{
|
|
|
|
ty,
|
|
|
|
alias->name.value,
|
|
|
|
/*synthetic=*/false,
|
|
|
|
std::move(typeParams),
|
|
|
|
std::move(typePackParams),
|
|
|
|
});
|
2023-03-17 19:20:37 +00:00
|
|
|
|
|
|
|
return ControlFlow::None;
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
ControlFlow ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatDeclareGlobal* global)
|
2022-08-04 23:35:33 +01:00
|
|
|
{
|
|
|
|
LUAU_ASSERT(global->type);
|
|
|
|
|
2023-01-20 20:27:03 +00:00
|
|
|
TypeId globalTy = resolveType(scope, global->type, /* inTypeArguments */ false);
|
2022-08-11 22:01:33 +01:00
|
|
|
Name globalName(global->name.value);
|
|
|
|
|
|
|
|
module->declaredGlobals[globalName] = globalTy;
|
2023-03-03 20:21:14 +00:00
|
|
|
rootScope->bindings[global->name] = Binding{globalTy, global->location};
|
|
|
|
|
|
|
|
BreadcrumbId bc = dfg->getBreadcrumb(global);
|
|
|
|
rootScope->dcrRefinements[bc->def] = globalTy;
|
2023-03-17 19:20:37 +00:00
|
|
|
|
|
|
|
return ControlFlow::None;
|
2022-08-04 23:35:33 +01:00
|
|
|
}
|
|
|
|
|
2022-08-11 22:01:33 +01:00
|
|
|
static bool isMetamethod(const Name& name)
|
|
|
|
{
|
|
|
|
return name == "__index" || name == "__newindex" || name == "__call" || name == "__concat" || name == "__unm" || name == "__add" ||
|
|
|
|
name == "__sub" || name == "__mul" || name == "__div" || name == "__mod" || name == "__pow" || name == "__tostring" ||
|
|
|
|
name == "__metatable" || name == "__eq" || name == "__lt" || name == "__le" || name == "__mode" || name == "__iter" || name == "__len";
|
|
|
|
}
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
ControlFlow ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatDeclareClass* declaredClass)
|
2022-08-04 23:35:33 +01:00
|
|
|
{
|
2023-05-05 22:52:49 +01:00
|
|
|
std::optional<TypeId> superTy = std::make_optional(builtinTypes->classType);
|
2022-08-11 22:01:33 +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});
|
2023-03-17 19:20:37 +00:00
|
|
|
return ControlFlow::None;
|
2022-08-11 22:01:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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-04 20:53:17 +00:00
|
|
|
if (!get<ClassType>(follow(*superTy)))
|
2022-08-11 22:01:33 +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)});
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
return ControlFlow::None;
|
2022-08-11 22:01:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Name className(declaredClass->name.value);
|
|
|
|
|
2023-04-21 23:14:26 +01:00
|
|
|
TypeId classTy = arena->addType(ClassType(className, {}, superTy, std::nullopt, {}, {}, module->name));
|
2023-01-04 20:53:17 +00:00
|
|
|
ClassType* ctv = getMutable<ClassType>(classTy);
|
2022-08-11 22:01:33 +01:00
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
TypeId metaTy = arena->addType(TableType{TableState::Sealed, scope->level, scope.get()});
|
|
|
|
TableType* metatable = getMutable<TableType>(metaTy);
|
2022-08-11 22:01:33 +01:00
|
|
|
|
|
|
|
ctv->metatable = metaTy;
|
|
|
|
|
|
|
|
scope->exportedTypeBindings[className] = TypeFun{{}, classTy};
|
|
|
|
|
2023-06-12 21:02:54 +01:00
|
|
|
if (FFlag::LuauParseDeclareClassIndexer && declaredClass->indexer)
|
|
|
|
{
|
|
|
|
RecursionCounter counter{&recursionCount};
|
|
|
|
|
|
|
|
if (recursionCount >= FInt::LuauCheckRecursionLimit)
|
|
|
|
{
|
|
|
|
reportCodeTooComplex(declaredClass->indexer->location);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ctv->indexer = TableIndexer{
|
|
|
|
resolveType(scope, declaredClass->indexer->indexType, /* inTypeArguments */ false),
|
|
|
|
resolveType(scope, declaredClass->indexer->resultType, /* inTypeArguments */ false),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-11 22:01:33 +01:00
|
|
|
for (const AstDeclaredClassProp& prop : declaredClass->props)
|
|
|
|
{
|
|
|
|
Name propName(prop.name.value);
|
2023-01-20 20:27:03 +00:00
|
|
|
TypeId propTy = resolveType(scope, prop.ty, /* inTypeArguments */ false);
|
2022-08-11 22:01:33 +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-04 20:53:17 +00:00
|
|
|
if (FunctionType* ftv = getMutable<FunctionType>(propTy))
|
2022-08-11 22:01:33 +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
|
|
|
|
{
|
2023-04-28 20:55:13 +01:00
|
|
|
TypeId currentTy = assignToMetatable ? metatable->props[propName].type() : ctv->props[propName].type();
|
2022-08-11 22:01:33 +01:00
|
|
|
|
|
|
|
// We special-case this logic to keep the intersection flat; otherwise we
|
|
|
|
// would create a ton of nested intersection types.
|
2023-01-04 20:53:17 +00:00
|
|
|
if (const IntersectionType* itv = get<IntersectionType>(currentTy))
|
2022-08-11 22:01:33 +01:00
|
|
|
{
|
|
|
|
std::vector<TypeId> options = itv->parts;
|
|
|
|
options.push_back(propTy);
|
2023-01-04 20:53:17 +00:00
|
|
|
TypeId newItv = arena->addType(IntersectionType{std::move(options)});
|
2022-08-11 22:01:33 +01:00
|
|
|
|
|
|
|
if (assignToMetatable)
|
|
|
|
metatable->props[propName] = {newItv};
|
|
|
|
else
|
|
|
|
ctv->props[propName] = {newItv};
|
|
|
|
}
|
2023-01-04 20:53:17 +00:00
|
|
|
else if (get<FunctionType>(currentTy))
|
2022-08-11 22:01:33 +01:00
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
TypeId intersection = arena->addType(IntersectionType{{currentTy, propTy}});
|
2022-08-11 22:01:33 +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())});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-03-17 19:20:37 +00:00
|
|
|
|
|
|
|
return ControlFlow::None;
|
2022-08-04 23:35:33 +01:00
|
|
|
}
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
ControlFlow ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatDeclareFunction* global)
|
2022-08-04 23:35:33 +01:00
|
|
|
{
|
2022-08-11 22:01:33 +01:00
|
|
|
std::vector<std::pair<Name, GenericTypeDefinition>> generics = createGenerics(scope, global->generics);
|
|
|
|
std::vector<std::pair<Name, GenericTypePackDefinition>> genericPacks = createGenericPacks(scope, global->genericPacks);
|
|
|
|
|
|
|
|
std::vector<TypeId> genericTys;
|
|
|
|
genericTys.reserve(generics.size());
|
|
|
|
for (auto& [name, generic] : generics)
|
2022-09-08 23:14:25 +01:00
|
|
|
{
|
2022-08-11 22:01:33 +01:00
|
|
|
genericTys.push_back(generic.ty);
|
2022-09-08 23:14:25 +01:00
|
|
|
}
|
2022-08-11 22:01:33 +01:00
|
|
|
|
|
|
|
std::vector<TypePackId> genericTps;
|
|
|
|
genericTps.reserve(genericPacks.size());
|
|
|
|
for (auto& [name, generic] : genericPacks)
|
2022-09-08 23:14:25 +01:00
|
|
|
{
|
2022-08-11 22:01:33 +01:00
|
|
|
genericTps.push_back(generic.tp);
|
2022-09-08 23:14:25 +01:00
|
|
|
}
|
2022-08-11 22:01:33 +01:00
|
|
|
|
|
|
|
ScopePtr funScope = scope;
|
|
|
|
if (!generics.empty() || !genericPacks.empty())
|
2022-08-18 22:32:08 +01:00
|
|
|
funScope = childScope(global, scope);
|
2022-08-11 22:01:33 +01:00
|
|
|
|
2023-01-20 20:27:03 +00:00
|
|
|
TypePackId paramPack = resolveTypePack(funScope, global->params, /* inTypeArguments */ false);
|
|
|
|
TypePackId retPack = resolveTypePack(funScope, global->retTypes, /* inTypeArguments */ false);
|
2023-01-04 20:53:17 +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 22:01:33 +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};
|
2023-03-03 20:21:14 +00:00
|
|
|
|
|
|
|
BreadcrumbId bc = dfg->getBreadcrumb(global);
|
|
|
|
rootScope->dcrRefinements[bc->def] = fnType;
|
2023-03-17 19:20:37 +00:00
|
|
|
|
|
|
|
return ControlFlow::None;
|
2022-08-04 23:35:33 +01:00
|
|
|
}
|
|
|
|
|
2023-03-17 19:20:37 +00:00
|
|
|
ControlFlow ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatError* error)
|
2022-09-29 23:23:10 +01:00
|
|
|
{
|
|
|
|
for (AstStat* stat : error->statements)
|
|
|
|
visit(scope, stat);
|
|
|
|
for (AstExpr* expr : error->expressions)
|
|
|
|
check(scope, expr);
|
2023-03-17 19:20:37 +00:00
|
|
|
|
|
|
|
return ControlFlow::None;
|
2022-09-29 23:23:10 +01:00
|
|
|
}
|
|
|
|
|
2023-02-17 23:41:51 +00:00
|
|
|
InferencePack ConstraintGraphBuilder::checkPack(
|
|
|
|
const ScopePtr& scope, AstArray<AstExpr*> exprs, const std::vector<std::optional<TypeId>>& expectedTypes)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-09-02 00:14:03 +01:00
|
|
|
std::vector<TypeId> head;
|
|
|
|
std::optional<TypePackId> tail;
|
2022-06-17 02:05:14 +01:00
|
|
|
|
|
|
|
for (size_t i = 0; i < exprs.size; ++i)
|
|
|
|
{
|
|
|
|
AstExpr* expr = exprs.data[i];
|
|
|
|
if (i < exprs.size - 1)
|
2022-09-23 20:17:25 +01:00
|
|
|
{
|
|
|
|
std::optional<TypeId> expectedType;
|
|
|
|
if (i < expectedTypes.size())
|
|
|
|
expectedType = expectedTypes[i];
|
2023-04-28 20:55:13 +01:00
|
|
|
head.push_back(check(scope, expr, ValueContext::RValue, expectedType).ty);
|
2022-09-23 20:17:25 +01:00
|
|
|
}
|
2022-06-17 02:05:14 +01:00
|
|
|
else
|
2022-09-23 20:17:25 +01:00
|
|
|
{
|
2023-02-17 23:41:51 +00:00
|
|
|
std::vector<std::optional<TypeId>> expectedTailTypes;
|
2022-09-29 23:23:10 +01:00
|
|
|
if (i < expectedTypes.size())
|
|
|
|
expectedTailTypes.assign(begin(expectedTypes) + i, end(expectedTypes));
|
2022-10-28 11:37:29 +01:00
|
|
|
tail = checkPack(scope, expr, expectedTailTypes).tp;
|
2022-09-23 20:17:25 +01:00
|
|
|
}
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
if (head.empty() && tail)
|
2022-10-28 11:37:29 +01:00
|
|
|
return InferencePack{*tail};
|
2022-06-17 02:05:14 +01:00
|
|
|
else
|
2022-10-28 11:37:29 +01:00
|
|
|
return InferencePack{arena->addTypePack(TypePack{std::move(head), tail})};
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
|
2023-02-17 23:41:51 +00:00
|
|
|
InferencePack ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExpr* expr, const std::vector<std::optional<TypeId>>& expectedTypes)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-07-01 00:52:43 +01:00
|
|
|
RecursionCounter counter{&recursionCount};
|
|
|
|
|
|
|
|
if (recursionCount >= FInt::LuauCheckRecursionLimit)
|
|
|
|
{
|
|
|
|
reportCodeTooComplex(expr->location);
|
2023-01-04 20:53:17 +00:00
|
|
|
return InferencePack{builtinTypes->errorRecoveryTypePack()};
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-10-28 11:37:29 +01:00
|
|
|
InferencePack result;
|
2022-06-17 02:05:14 +01:00
|
|
|
|
|
|
|
if (AstExprCall* call = expr->as<AstExprCall>())
|
2023-02-17 23:41:51 +00:00
|
|
|
result = checkPack(scope, call);
|
2022-07-01 00:52:43 +01:00
|
|
|
else if (AstExprVarargs* varargs = expr->as<AstExprVarargs>())
|
|
|
|
{
|
|
|
|
if (scope->varargPack)
|
2022-10-28 11:37:29 +01:00
|
|
|
result = InferencePack{*scope->varargPack};
|
2022-07-01 00:52:43 +01:00
|
|
|
else
|
2023-01-04 20:53:17 +00:00
|
|
|
result = InferencePack{builtinTypes->errorRecoveryTypePack()};
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
2022-06-17 02:05:14 +01:00
|
|
|
else
|
|
|
|
{
|
2022-09-23 20:17:25 +01:00
|
|
|
std::optional<TypeId> expectedType;
|
|
|
|
if (!expectedTypes.empty())
|
|
|
|
expectedType = expectedTypes[0];
|
2023-04-28 20:55:13 +01:00
|
|
|
TypeId t = check(scope, expr, ValueContext::RValue, expectedType).ty;
|
2022-10-28 11:37:29 +01:00
|
|
|
result = InferencePack{arena->addTypePack({t})};
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
|
2022-10-28 11:37:29 +01:00
|
|
|
LUAU_ASSERT(result.tp);
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
module->astTypePacks[expr] = result.tp;
|
2022-06-17 02:05:14 +01:00
|
|
|
return result;
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
|
2023-02-17 23:41:51 +00:00
|
|
|
InferencePack ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstExprCall* call)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-12-02 18:09:59 +00:00
|
|
|
std::vector<AstExpr*> exprArgs;
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
std::vector<RefinementId> returnRefinements;
|
|
|
|
std::vector<std::optional<TypeId>> discriminantTypes;
|
|
|
|
|
2022-12-02 18:09:59 +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 19:40:38 +00:00
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
if (auto bc = dfg->getBreadcrumb(indexExpr->expr))
|
2023-02-10 19:40:38 +00:00
|
|
|
{
|
|
|
|
TypeId discriminantTy = arena->addType(BlockedType{});
|
2023-03-03 20:21:14 +00:00
|
|
|
returnRefinements.push_back(refinementArena.proposition(NotNull{bc}, discriminantTy));
|
2023-02-10 19:40:38 +00:00
|
|
|
discriminantTypes.push_back(discriminantTy);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
discriminantTypes.push_back(std::nullopt);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (AstExpr* arg : call->args)
|
|
|
|
{
|
|
|
|
exprArgs.push_back(arg);
|
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
if (auto bc = dfg->getBreadcrumb(arg))
|
2023-02-10 19:40:38 +00:00
|
|
|
{
|
|
|
|
TypeId discriminantTy = arena->addType(BlockedType{});
|
2023-03-03 20:21:14 +00:00
|
|
|
returnRefinements.push_back(refinementArena.proposition(NotNull{bc}, discriminantTy));
|
2023-02-10 19:40:38 +00:00
|
|
|
discriminantTypes.push_back(discriminantTy);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
discriminantTypes.push_back(std::nullopt);
|
2022-12-02 18:09:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Checkpoint startCheckpoint = checkpoint(this);
|
2022-10-28 11:37:29 +01:00
|
|
|
TypeId fnType = check(scope, call->func).ty;
|
2022-12-02 18:09:59 +00:00
|
|
|
Checkpoint fnEndCheckpoint = checkpoint(this);
|
2022-07-01 00:52:43 +01:00
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
std::vector<std::optional<TypeId>> expectedTypesForCall = getExpectedCallTypesForFunctionOverloads(fnType);
|
|
|
|
|
2023-02-10 19:40:38 +00:00
|
|
|
module->astOriginalCallTypes[call->func] = fnType;
|
2023-07-07 21:10:48 +01:00
|
|
|
module->astOriginalCallTypes[call] = fnType;
|
2023-02-10 19:40:38 +00:00
|
|
|
|
2022-12-02 18:09:59 +00:00
|
|
|
TypePackId expectedArgPack = arena->freshTypePack(scope.get());
|
|
|
|
TypePackId expectedRetPack = arena->freshTypePack(scope.get());
|
2023-02-24 21:49:38 +00:00
|
|
|
TypeId expectedFunctionType = arena->addType(FunctionType{expectedArgPack, expectedRetPack, std::nullopt, call->self});
|
2022-12-02 18:09:59 +00:00
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
TypeId instantiatedFnType = arena->addType(BlockedType{});
|
2022-12-02 18:09:59 +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-03-03 20:21:14 +00:00
|
|
|
expectedArgs = extendTypePack(*arena, builtinTypes, expectedArgPack, exprArgs.size(), expectedTypesForCall);
|
2022-12-02 18:09:59 +00:00
|
|
|
else
|
2023-03-03 20:21:14 +00:00
|
|
|
expectedArgs = extendTypePack(*arena, builtinTypes, expectedArgPack, exprArgs.size() - 1, expectedTypesForCall);
|
2022-10-28 11:37:29 +01:00
|
|
|
|
2022-12-02 18:09:59 +00:00
|
|
|
std::vector<TypeId> args;
|
|
|
|
std::optional<TypePackId> argTail;
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
std::vector<RefinementId> argumentRefinements;
|
2022-11-18 19:47:21 +00:00
|
|
|
|
2022-12-02 18:09:59 +00:00
|
|
|
Checkpoint argCheckpoint = checkpoint(this);
|
2022-11-18 19:47:21 +00:00
|
|
|
|
2022-12-02 18:09:59 +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.
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
TypeId* selfTy = module->astTypes.find(exprArgs[0]);
|
2022-12-02 18:09:59 +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 19:57:01 +00:00
|
|
|
{
|
2023-04-28 20:55:13 +01:00
|
|
|
auto [ty, refinement] = check(scope, arg, ValueContext::RValue, expectedType);
|
2022-12-09 19:57:01 +00:00
|
|
|
args.push_back(ty);
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
argumentRefinements.push_back(refinement);
|
2022-12-09 19:57:01 +00:00
|
|
|
}
|
2022-12-02 18:09:59 +00:00
|
|
|
else
|
2023-02-10 19:40:38 +00:00
|
|
|
{
|
2023-02-17 23:41:51 +00:00
|
|
|
auto [tp, refis] = checkPack(scope, arg, {});
|
2023-02-10 19:40:38 +00:00
|
|
|
argTail = tp;
|
|
|
|
argumentRefinements.insert(argumentRefinements.end(), refis.begin(), refis.end());
|
|
|
|
}
|
2022-11-18 19:47:21 +00:00
|
|
|
}
|
2022-06-17 02:05:14 +01:00
|
|
|
|
2022-12-02 18:09:59 +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-28 11:37:29 +01:00
|
|
|
if (matchSetmetatable(*call))
|
2022-09-23 20:17:25 +01:00
|
|
|
{
|
2022-12-02 18:09:59 +00:00
|
|
|
TypePack argTailPack;
|
|
|
|
if (argTail && args.size() < 2)
|
2023-01-04 20:53:17 +00:00
|
|
|
argTailPack = extendTypePack(*arena, builtinTypes, *argTail, 2 - args.size());
|
2022-12-02 18:09:59 +00:00
|
|
|
|
2023-03-24 18:03:04 +00:00
|
|
|
TypeId target = nullptr;
|
|
|
|
TypeId mt = nullptr;
|
2022-12-02 18:09:59 +00:00
|
|
|
|
2023-03-24 18:03:04 +00:00
|
|
|
if (args.size() + argTailPack.head.size() == 2)
|
|
|
|
{
|
|
|
|
target = args.size() > 0 ? args[0] : argTailPack.head[0];
|
|
|
|
mt = args.size() > 1 ? args[1] : argTailPack.head[args.size() == 0 ? 1 : 0];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::vector<TypeId> unpackedTypes;
|
|
|
|
if (args.size() > 0)
|
|
|
|
target = args[0];
|
|
|
|
else
|
|
|
|
{
|
|
|
|
target = arena->addType(BlockedType{});
|
|
|
|
unpackedTypes.emplace_back(target);
|
|
|
|
}
|
|
|
|
|
|
|
|
mt = arena->addType(BlockedType{});
|
|
|
|
unpackedTypes.emplace_back(mt);
|
|
|
|
TypePackId mtPack = arena->addTypePack(std::move(unpackedTypes));
|
|
|
|
|
|
|
|
addConstraint(scope, call->location, UnpackConstraint{mtPack, *argTail});
|
|
|
|
}
|
|
|
|
|
|
|
|
LUAU_ASSERT(target);
|
|
|
|
LUAU_ASSERT(mt);
|
2022-10-28 11:37:29 +01:00
|
|
|
|
|
|
|
AstExpr* targetExpr = call->args.data[0];
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
MetatableType mtv{target, mt};
|
2022-10-28 11:37:29 +01:00
|
|
|
TypeId resultTy = arena->addType(mtv);
|
|
|
|
|
|
|
|
if (AstExprLocal* targetLocal = targetExpr->as<AstExprLocal>())
|
2023-01-27 22:28:31 +00:00
|
|
|
{
|
2022-10-28 11:37:29 +01:00
|
|
|
scope->bindings[targetLocal->local].typeId = resultTy;
|
2023-03-03 20:21:14 +00:00
|
|
|
|
|
|
|
BreadcrumbId bc = dfg->getBreadcrumb(targetLocal);
|
|
|
|
scope->dcrRefinements[bc->def] = resultTy; // TODO: typestates: track this as an assignment
|
2023-01-27 22:28:31 +00:00
|
|
|
}
|
|
|
|
|
2023-02-10 19:40:38 +00:00
|
|
|
return InferencePack{arena->addTypePack({resultTy}), {refinementArena.variadic(returnRefinements)}};
|
2022-09-23 20:17:25 +01:00
|
|
|
}
|
2022-10-28 11:37:29 +01:00
|
|
|
else
|
2022-09-23 20:17:25 +01:00
|
|
|
{
|
2023-02-10 19:40:38 +00:00
|
|
|
if (matchAssert(*call) && !argumentRefinements.empty())
|
|
|
|
applyRefinements(scope, call->args.data[0]->location, argumentRefinements[0]);
|
2022-10-28 11:37:29 +01:00
|
|
|
|
|
|
|
// TODO: How do expectedTypes play into this? Do they?
|
|
|
|
TypePackId rets = arena->addTypePack(BlockedTypePack{});
|
2022-12-02 18:09:59 +00:00
|
|
|
TypePackId argPack = arena->addTypePack(TypePack{args, argTail});
|
2023-02-24 21:49:38 +00:00
|
|
|
FunctionType ftv(TypeLevel{}, scope.get(), argPack, rets, std::nullopt, call->self);
|
2022-10-28 11:37:29 +01:00
|
|
|
|
2022-12-02 18:09:59 +00:00
|
|
|
NotNull<Constraint> fcc = addConstraint(scope, call->func->location,
|
2022-10-28 11:37:29 +01:00
|
|
|
FunctionCallConstraint{
|
|
|
|
fnType,
|
|
|
|
argPack,
|
|
|
|
rets,
|
|
|
|
call,
|
2023-02-10 19:40:38 +00:00
|
|
|
std::move(discriminantTypes),
|
2023-05-05 22:52:49 +01:00
|
|
|
&module->astOverloadResolvedTypes,
|
2022-10-28 11:37:29 +01:00
|
|
|
});
|
|
|
|
|
2022-12-02 18:09:59 +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 19:40:38 +00:00
|
|
|
return InferencePack{rets, {refinementArena.variadic(returnRefinements)}};
|
2022-09-23 20:17:25 +01:00
|
|
|
}
|
2022-10-28 11:37:29 +01:00
|
|
|
}
|
|
|
|
|
2023-04-28 20:55:13 +01:00
|
|
|
Inference ConstraintGraphBuilder::check(
|
|
|
|
const ScopePtr& scope, AstExpr* expr, ValueContext context, std::optional<TypeId> expectedType, bool forceSingleton)
|
2022-10-28 11:37:29 +01:00
|
|
|
{
|
|
|
|
RecursionCounter counter{&recursionCount};
|
|
|
|
|
|
|
|
if (recursionCount >= FInt::LuauCheckRecursionLimit)
|
|
|
|
{
|
|
|
|
reportCodeTooComplex(expr->location);
|
2023-01-04 20:53:17 +00:00
|
|
|
return Inference{builtinTypes->errorRecoveryType()};
|
2022-10-28 11:37:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Inference result;
|
|
|
|
|
|
|
|
if (auto group = expr->as<AstExprGroup>())
|
2023-04-28 20:55:13 +01:00
|
|
|
result = check(scope, group->expr, ValueContext::RValue, expectedType, forceSingleton);
|
2022-10-28 11:37:29 +01:00
|
|
|
else if (auto stringExpr = expr->as<AstExprConstantString>())
|
2022-11-04 17:33:22 +00:00
|
|
|
result = check(scope, stringExpr, expectedType, forceSingleton);
|
2022-10-28 11:37:29 +01:00
|
|
|
else if (expr->is<AstExprConstantNumber>())
|
2023-01-04 20:53:17 +00:00
|
|
|
result = Inference{builtinTypes->numberType};
|
2022-10-28 11:37:29 +01:00
|
|
|
else if (auto boolExpr = expr->as<AstExprConstantBool>())
|
2022-11-04 17:33:22 +00:00
|
|
|
result = check(scope, boolExpr, expectedType, forceSingleton);
|
2022-06-17 02:05:14 +01:00
|
|
|
else if (expr->is<AstExprConstantNil>())
|
2023-01-04 20:53:17 +00:00
|
|
|
result = Inference{builtinTypes->nilType};
|
2022-10-21 18:54:01 +01:00
|
|
|
else if (auto local = expr->as<AstExprLocal>())
|
2023-04-28 20:55:13 +01:00
|
|
|
result = check(scope, local, context);
|
2022-10-21 18:54:01 +01:00
|
|
|
else if (auto global = expr->as<AstExprGlobal>())
|
|
|
|
result = check(scope, global);
|
2022-07-01 00:52:43 +01:00
|
|
|
else if (expr->is<AstExprVarargs>())
|
|
|
|
result = flattenPack(scope, expr->location, checkPack(scope, expr));
|
2022-10-28 11:37:29 +01:00
|
|
|
else if (auto call = expr->as<AstExprCall>())
|
2023-02-17 23:41:51 +00:00
|
|
|
result = flattenPack(scope, expr->location, checkPack(scope, call)); // TODO: needs predicates too
|
2022-06-17 02:05:14 +01:00
|
|
|
else if (auto a = expr->as<AstExprFunction>())
|
2023-05-19 20:37:30 +01:00
|
|
|
result = check(scope, a, expectedType);
|
2022-06-17 02:05:14 +01:00
|
|
|
else if (auto indexName = expr->as<AstExprIndexName>())
|
|
|
|
result = check(scope, indexName);
|
2022-07-01 00:52:43 +01:00
|
|
|
else if (auto indexExpr = expr->as<AstExprIndexExpr>())
|
|
|
|
result = check(scope, indexExpr);
|
2022-06-17 02:05:14 +01:00
|
|
|
else if (auto table = expr->as<AstExprTable>())
|
2022-09-23 20:17:25 +01:00
|
|
|
result = check(scope, table, expectedType);
|
2022-07-01 00:52:43 +01:00
|
|
|
else if (auto unary = expr->as<AstExprUnary>())
|
|
|
|
result = check(scope, unary);
|
|
|
|
else if (auto binary = expr->as<AstExprBinary>())
|
2022-10-21 18:54:01 +01:00
|
|
|
result = check(scope, binary, expectedType);
|
2022-08-18 22:32:08 +01:00
|
|
|
else if (auto ifElse = expr->as<AstExprIfElse>())
|
2022-09-23 20:17:25 +01:00
|
|
|
result = check(scope, ifElse, expectedType);
|
2022-08-11 22:01:33 +01:00
|
|
|
else if (auto typeAssert = expr->as<AstExprTypeAssertion>())
|
|
|
|
result = check(scope, typeAssert);
|
2023-01-27 22:28:31 +00:00
|
|
|
else if (auto interpString = expr->as<AstExprInterpString>())
|
|
|
|
result = check(scope, interpString);
|
2022-07-01 00:52:43 +01:00
|
|
|
else if (auto err = expr->as<AstExprError>())
|
|
|
|
{
|
|
|
|
// Open question: Should we traverse into this?
|
2022-09-29 23:23:10 +01:00
|
|
|
for (AstExpr* subExpr : err->expressions)
|
|
|
|
check(scope, subExpr);
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
result = Inference{builtinTypes->errorRecoveryType()};
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(0);
|
2022-10-28 11:37:29 +01:00
|
|
|
result = Inference{freshType(scope)};
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
|
2022-10-28 11:37:29 +01:00
|
|
|
LUAU_ASSERT(result.ty);
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
module->astTypes[expr] = result.ty;
|
2023-01-27 22:28:31 +00:00
|
|
|
if (expectedType)
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
module->astExpectedTypes[expr] = *expectedType;
|
2022-06-17 02:05:14 +01:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-11-04 17:33:22 +00:00
|
|
|
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprConstantString* string, std::optional<TypeId> expectedType, bool forceSingleton)
|
2022-10-28 11:37:29 +01:00
|
|
|
{
|
2022-11-04 17:33:22 +00:00
|
|
|
if (forceSingleton)
|
2023-01-04 20:53:17 +00:00
|
|
|
return Inference{arena->addType(SingletonType{StringSingleton{std::string{string->value.data, string->value.size}}})};
|
2022-11-04 17:33:22 +00:00
|
|
|
|
2022-10-28 11:37:29 +01:00
|
|
|
if (expectedType)
|
|
|
|
{
|
|
|
|
const TypeId expectedTy = follow(*expectedType);
|
2023-07-07 21:10:48 +01:00
|
|
|
if (get<BlockedType>(expectedTy) || get<PendingExpansionType>(expectedTy) || get<FreeType>(expectedTy))
|
2022-10-28 11:37:29 +01:00
|
|
|
{
|
2023-01-04 20:53:17 +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-28 11:37:29 +01:00
|
|
|
return Inference{ty};
|
|
|
|
}
|
|
|
|
else if (maybeSingleton(expectedTy))
|
2023-01-04 20:53:17 +00:00
|
|
|
return Inference{arena->addType(SingletonType{StringSingleton{std::string{string->value.data, string->value.size}}})};
|
2022-10-28 11:37:29 +01:00
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
return Inference{builtinTypes->stringType};
|
2022-10-28 11:37:29 +01:00
|
|
|
}
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
return Inference{builtinTypes->stringType};
|
2022-10-28 11:37:29 +01:00
|
|
|
}
|
|
|
|
|
2022-11-04 17:33:22 +00:00
|
|
|
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprConstantBool* boolExpr, std::optional<TypeId> expectedType, bool forceSingleton)
|
2022-10-28 11:37:29 +01:00
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
const TypeId singletonType = boolExpr->value ? builtinTypes->trueType : builtinTypes->falseType;
|
2022-11-04 17:33:22 +00:00
|
|
|
if (forceSingleton)
|
|
|
|
return Inference{singletonType};
|
|
|
|
|
2022-10-28 11:37:29 +01:00
|
|
|
if (expectedType)
|
|
|
|
{
|
|
|
|
const TypeId expectedTy = follow(*expectedType);
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
if (get<BlockedType>(expectedTy) || get<PendingExpansionType>(expectedTy))
|
2022-10-28 11:37:29 +01:00
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
TypeId ty = arena->addType(BlockedType{});
|
|
|
|
addConstraint(scope, boolExpr->location, PrimitiveTypeConstraint{ty, expectedTy, singletonType, builtinTypes->booleanType});
|
2022-10-28 11:37:29 +01:00
|
|
|
return Inference{ty};
|
|
|
|
}
|
|
|
|
else if (maybeSingleton(expectedTy))
|
|
|
|
return Inference{singletonType};
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
return Inference{builtinTypes->booleanType};
|
2022-10-28 11:37:29 +01:00
|
|
|
}
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
return Inference{builtinTypes->booleanType};
|
2022-10-28 11:37:29 +01:00
|
|
|
}
|
|
|
|
|
2023-04-28 20:55:13 +01:00
|
|
|
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprLocal* local, ValueContext context)
|
2022-10-21 18:54:01 +01:00
|
|
|
{
|
2023-03-03 20:21:14 +00:00
|
|
|
BreadcrumbId bc = dfg->getBreadcrumb(local);
|
2022-10-21 18:54:01 +01:00
|
|
|
|
2023-04-28 20:55:13 +01:00
|
|
|
if (auto ty = scope->lookup(bc->def); ty && context == ValueContext::RValue)
|
2023-03-03 20:21:14 +00:00
|
|
|
return Inference{*ty, refinementArena.proposition(bc, builtinTypes->truthyType)};
|
|
|
|
else if (auto ty = scope->lookup(local->local))
|
|
|
|
return Inference{*ty, refinementArena.proposition(bc, builtinTypes->truthyType)};
|
2022-11-04 17:33:22 +00:00
|
|
|
else
|
2023-03-03 20:21:14 +00:00
|
|
|
ice->ice("AstExprLocal came before its declaration?");
|
2022-10-21 18:54:01 +01:00
|
|
|
}
|
|
|
|
|
2022-10-28 11:37:29 +01:00
|
|
|
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprGlobal* global)
|
2022-10-21 18:54:01 +01:00
|
|
|
{
|
2023-03-03 20:21:14 +00:00
|
|
|
BreadcrumbId bc = dfg->getBreadcrumb(global);
|
2022-10-21 18:54:01 +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.
|
|
|
|
*/
|
2023-03-03 20:21:14 +00:00
|
|
|
if (auto ty = scope->lookup(bc->def))
|
|
|
|
return Inference{*ty, refinementArena.proposition(bc, builtinTypes->truthyType)};
|
|
|
|
else if (auto ty = scope->lookup(global->name))
|
|
|
|
{
|
|
|
|
rootScope->dcrRefinements[bc->def] = *ty;
|
|
|
|
return Inference{*ty, refinementArena.proposition(bc, builtinTypes->truthyType)};
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
reportError(global->location, UnknownSymbol{global->name.value});
|
|
|
|
return Inference{builtinTypes->errorRecoveryType()};
|
|
|
|
}
|
2022-10-21 18:54:01 +01:00
|
|
|
}
|
|
|
|
|
2022-10-28 11:37:29 +01:00
|
|
|
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprIndexName* indexName)
|
2022-06-17 02:05:14 +01:00
|
|
|
{
|
2022-10-28 11:37:29 +01:00
|
|
|
TypeId obj = check(scope, indexName->expr).ty;
|
2023-02-24 21:49:38 +00:00
|
|
|
TypeId result = arena->addType(BlockedType{});
|
2022-12-02 18:09:59 +00:00
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
NullableBreadcrumbId bc = dfg->getBreadcrumb(indexName);
|
|
|
|
if (bc)
|
2022-12-02 18:09:59 +00:00
|
|
|
{
|
2023-03-03 20:21:14 +00:00
|
|
|
if (auto ty = scope->lookup(bc->def))
|
|
|
|
return Inference{*ty, refinementArena.proposition(NotNull{bc}, builtinTypes->truthyType)};
|
|
|
|
|
|
|
|
scope->dcrRefinements[bc->def] = result;
|
2022-12-02 18:09:59 +00:00
|
|
|
}
|
2022-06-17 02:05:14 +01:00
|
|
|
|
2023-02-24 21:49:38 +00:00
|
|
|
addConstraint(scope, indexName->expr->location, HasPropConstraint{result, obj, indexName->index.value});
|
2022-06-17 02:05:14 +01:00
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
if (bc)
|
|
|
|
return Inference{result, refinementArena.proposition(NotNull{bc}, builtinTypes->truthyType)};
|
2022-12-02 18:09:59 +00:00
|
|
|
else
|
|
|
|
return Inference{result};
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
|
2022-10-28 11:37:29 +01:00
|
|
|
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprIndexExpr* indexExpr)
|
2022-07-01 00:52:43 +01:00
|
|
|
{
|
2022-10-28 11:37:29 +01:00
|
|
|
TypeId obj = check(scope, indexExpr->expr).ty;
|
|
|
|
TypeId indexType = check(scope, indexExpr->index).ty;
|
2022-07-01 00:52:43 +01:00
|
|
|
TypeId result = freshType(scope);
|
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
NullableBreadcrumbId bc = dfg->getBreadcrumb(indexExpr);
|
|
|
|
if (bc)
|
|
|
|
{
|
|
|
|
if (auto ty = scope->lookup(bc->def))
|
|
|
|
return Inference{*ty, refinementArena.proposition(NotNull{bc}, builtinTypes->truthyType)};
|
|
|
|
|
|
|
|
scope->dcrRefinements[bc->def] = result;
|
|
|
|
}
|
|
|
|
|
2022-07-01 00:52:43 +01:00
|
|
|
TableIndexer indexer{indexType, result};
|
2023-01-04 20:53:17 +00:00
|
|
|
TypeId tableType = arena->addType(TableType{TableType::Props{}, TableIndexer{indexType, result}, TypeLevel{}, scope.get(), TableState::Free});
|
2022-07-01 00:52:43 +01:00
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
addConstraint(scope, indexExpr->expr->location, SubtypeConstraint{obj, tableType});
|
2022-07-01 00:52:43 +01:00
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
if (bc)
|
|
|
|
return Inference{result, refinementArena.proposition(NotNull{bc}, builtinTypes->truthyType)};
|
|
|
|
else
|
|
|
|
return Inference{result};
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
|
|
|
|
2023-05-19 20:37:30 +01:00
|
|
|
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprFunction* func, std::optional<TypeId> expectedType)
|
|
|
|
{
|
|
|
|
Checkpoint startCheckpoint = checkpoint(this);
|
|
|
|
FunctionSignature sig = checkFunctionSignature(scope, func, expectedType);
|
|
|
|
checkFunctionBody(sig.bodyScope, func);
|
|
|
|
Checkpoint endCheckpoint = checkpoint(this);
|
|
|
|
|
|
|
|
TypeId generalizedTy = arena->addType(BlockedType{});
|
|
|
|
NotNull<Constraint> gc = addConstraint(sig.signatureScope, func->location, GeneralizationConstraint{generalizedTy, sig.signature});
|
|
|
|
|
2023-07-07 21:10:48 +01:00
|
|
|
Constraint* previous = nullptr;
|
|
|
|
forEachConstraint(startCheckpoint, endCheckpoint, this, [gc, &previous](const ConstraintPtr& constraint) {
|
2023-05-19 20:37:30 +01:00
|
|
|
gc->dependencies.emplace_back(constraint.get());
|
2023-07-07 21:10:48 +01:00
|
|
|
|
|
|
|
if (auto psc = get<PackSubtypeConstraint>(*constraint); psc && psc->returns)
|
|
|
|
{
|
|
|
|
if (previous)
|
|
|
|
constraint->dependencies.push_back(NotNull{previous});
|
|
|
|
|
|
|
|
previous = constraint.get();
|
|
|
|
}
|
2023-05-19 20:37:30 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
return Inference{generalizedTy};
|
|
|
|
}
|
|
|
|
|
2022-10-28 11:37:29 +01:00
|
|
|
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprUnary* unary)
|
2022-07-01 00:52:43 +01:00
|
|
|
{
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
auto [operandType, refinement] = check(scope, unary->expr);
|
2023-01-04 20:53:17 +00:00
|
|
|
TypeId resultType = arena->addType(BlockedType{});
|
2022-09-23 20:17:25 +01:00
|
|
|
addConstraint(scope, unary->location, UnaryConstraint{unary->op, operandType, resultType});
|
2022-11-04 17:33:22 +00:00
|
|
|
|
|
|
|
if (unary->op == AstExprUnary::Not)
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
return Inference{resultType, refinementArena.negation(refinement)};
|
2022-11-04 17:33:22 +00:00
|
|
|
else
|
|
|
|
return Inference{resultType};
|
2022-10-21 18:54:01 +01:00
|
|
|
}
|
|
|
|
|
2022-10-28 11:37:29 +01:00
|
|
|
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprBinary* binary, std::optional<TypeId> expectedType)
|
2022-10-21 18:54:01 +01:00
|
|
|
{
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
auto [leftType, rightType, refinement] = checkBinary(scope, binary, expectedType);
|
2022-10-21 18:54:01 +01:00
|
|
|
|
2023-05-19 20:37:30 +01:00
|
|
|
if (binary->op == AstExprBinary::Op::Add)
|
|
|
|
{
|
|
|
|
TypeId resultType = arena->addType(TypeFamilyInstanceType{
|
|
|
|
NotNull{&kBuiltinTypeFamilies.addFamily},
|
|
|
|
{leftType, rightType},
|
|
|
|
{},
|
|
|
|
});
|
|
|
|
addConstraint(scope, binary->location, ReduceConstraint{resultType});
|
|
|
|
return Inference{resultType, std::move(refinement)};
|
|
|
|
}
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
TypeId resultType = arena->addType(BlockedType{});
|
|
|
|
addConstraint(scope, binary->location,
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
BinaryConstraint{binary->op, leftType, rightType, resultType, binary, &module->astOriginalCallTypes, &module->astOverloadResolvedTypes});
|
|
|
|
return Inference{resultType, std::move(refinement)};
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
|
|
|
|
2022-10-28 11:37:29 +01:00
|
|
|
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprIfElse* ifElse, std::optional<TypeId> expectedType)
|
2022-08-18 22:32:08 +01:00
|
|
|
{
|
2022-12-09 19:57:01 +00:00
|
|
|
ScopePtr condScope = childScope(ifElse->condition, scope);
|
2023-03-03 20:21:14 +00:00
|
|
|
RefinementId refinement = check(condScope, ifElse->condition).refinement;
|
2022-12-09 19:57:01 +00:00
|
|
|
|
|
|
|
ScopePtr thenScope = childScope(ifElse->trueExpr, scope);
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
applyRefinements(thenScope, ifElse->trueExpr->location, refinement);
|
2023-04-28 20:55:13 +01:00
|
|
|
TypeId thenType = check(thenScope, ifElse->trueExpr, ValueContext::RValue, expectedType).ty;
|
2022-08-18 22:32:08 +01:00
|
|
|
|
2022-12-09 19:57:01 +00:00
|
|
|
ScopePtr elseScope = childScope(ifElse->falseExpr, scope);
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
applyRefinements(elseScope, ifElse->falseExpr->location, refinementArena.negation(refinement));
|
2023-04-28 20:55:13 +01:00
|
|
|
TypeId elseType = check(elseScope, ifElse->falseExpr, ValueContext::RValue, expectedType).ty;
|
2022-08-18 22:32:08 +01:00
|
|
|
|
2023-05-19 20:37:30 +01:00
|
|
|
return Inference{expectedType ? *expectedType : simplifyUnion(builtinTypes, arena, thenType, elseType).result};
|
2022-08-18 22:32:08 +01:00
|
|
|
}
|
|
|
|
|
2022-10-28 11:37:29 +01:00
|
|
|
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprTypeAssertion* typeAssert)
|
2022-08-11 22:01:33 +01:00
|
|
|
{
|
2023-04-28 20:55:13 +01:00
|
|
|
check(scope, typeAssert->expr, ValueContext::RValue, std::nullopt);
|
2023-01-20 20:27:03 +00:00
|
|
|
return Inference{resolveType(scope, typeAssert->annotation, /* inTypeArguments */ false)};
|
2022-08-11 22:01:33 +01:00
|
|
|
}
|
|
|
|
|
2023-01-27 22:28:31 +00:00
|
|
|
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprInterpString* interpString)
|
|
|
|
{
|
|
|
|
for (AstExpr* expr : interpString->expressions)
|
|
|
|
check(scope, expr);
|
|
|
|
|
|
|
|
return Inference{builtinTypes->stringType};
|
|
|
|
}
|
|
|
|
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
std::tuple<TypeId, TypeId, RefinementId> ConstraintGraphBuilder::checkBinary(
|
2022-11-04 17:33:22 +00:00
|
|
|
const ScopePtr& scope, AstExprBinary* binary, std::optional<TypeId> expectedType)
|
|
|
|
{
|
|
|
|
if (binary->op == AstExprBinary::And)
|
|
|
|
{
|
2023-04-28 20:55:13 +01:00
|
|
|
std::optional<TypeId> relaxedExpectedLhs;
|
|
|
|
|
|
|
|
if (expectedType)
|
|
|
|
relaxedExpectedLhs = arena->addType(UnionType{{builtinTypes->falsyType, *expectedType}});
|
|
|
|
|
|
|
|
auto [leftType, leftRefinement] = check(scope, binary->left, ValueContext::RValue, relaxedExpectedLhs);
|
2022-11-04 17:33:22 +00:00
|
|
|
|
|
|
|
ScopePtr rightScope = childScope(binary->right, scope);
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
applyRefinements(rightScope, binary->right->location, leftRefinement);
|
2023-04-28 20:55:13 +01:00
|
|
|
auto [rightType, rightRefinement] = check(rightScope, binary->right, ValueContext::RValue, expectedType);
|
2022-11-04 17:33:22 +00:00
|
|
|
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
return {leftType, rightType, refinementArena.conjunction(leftRefinement, rightRefinement)};
|
2022-11-04 17:33:22 +00:00
|
|
|
}
|
|
|
|
else if (binary->op == AstExprBinary::Or)
|
|
|
|
{
|
2023-04-28 20:55:13 +01:00
|
|
|
std::optional<TypeId> relaxedExpectedLhs;
|
|
|
|
|
|
|
|
if (expectedType)
|
|
|
|
relaxedExpectedLhs = arena->addType(UnionType{{builtinTypes->falsyType, *expectedType}});
|
|
|
|
|
|
|
|
auto [leftType, leftRefinement] = check(scope, binary->left, ValueContext::RValue, relaxedExpectedLhs);
|
2022-11-04 17:33:22 +00:00
|
|
|
|
|
|
|
ScopePtr rightScope = childScope(binary->right, scope);
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
applyRefinements(rightScope, binary->right->location, refinementArena.negation(leftRefinement));
|
2023-04-28 20:55:13 +01:00
|
|
|
auto [rightType, rightRefinement] = check(rightScope, binary->right, ValueContext::RValue, expectedType);
|
2022-11-04 17:33:22 +00:00
|
|
|
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
return {leftType, rightType, refinementArena.disjunction(leftRefinement, rightRefinement)};
|
2022-11-04 17:33:22 +00:00
|
|
|
}
|
2022-11-10 22:53:13 +00:00
|
|
|
else if (auto typeguard = matchTypeGuard(binary))
|
|
|
|
{
|
|
|
|
TypeId leftType = check(scope, binary->left).ty;
|
|
|
|
TypeId rightType = check(scope, binary->right).ty;
|
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
NullableBreadcrumbId bc = dfg->getBreadcrumb(typeguard->target);
|
|
|
|
if (!bc)
|
2022-11-10 22:53:13 +00:00
|
|
|
return {leftType, rightType, nullptr};
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
TypeId discriminantTy = builtinTypes->neverType;
|
2022-11-10 22:53:13 +00:00
|
|
|
if (typeguard->type == "nil")
|
2023-01-04 20:53:17 +00:00
|
|
|
discriminantTy = builtinTypes->nilType;
|
2022-11-10 22:53:13 +00:00
|
|
|
else if (typeguard->type == "string")
|
2023-01-04 20:53:17 +00:00
|
|
|
discriminantTy = builtinTypes->stringType;
|
2022-11-10 22:53:13 +00:00
|
|
|
else if (typeguard->type == "number")
|
2023-01-04 20:53:17 +00:00
|
|
|
discriminantTy = builtinTypes->numberType;
|
2022-11-10 22:53:13 +00:00
|
|
|
else if (typeguard->type == "boolean")
|
2023-02-24 21:49:38 +00:00
|
|
|
discriminantTy = builtinTypes->booleanType;
|
|
|
|
else if (typeguard->type == "thread")
|
2023-01-04 20:53:17 +00:00
|
|
|
discriminantTy = builtinTypes->threadType;
|
2022-11-10 22:53:13 +00:00
|
|
|
else if (typeguard->type == "table")
|
2023-01-27 22:28:31 +00:00
|
|
|
discriminantTy = builtinTypes->tableType;
|
2022-11-10 22:53:13 +00:00
|
|
|
else if (typeguard->type == "function")
|
2023-01-04 20:53:17 +00:00
|
|
|
discriminantTy = builtinTypes->functionType;
|
2022-11-10 22:53:13 +00:00
|
|
|
else if (typeguard->type == "userdata")
|
|
|
|
{
|
2023-02-24 21:49:38 +00:00
|
|
|
// For now, we don't really care about being accurate with userdata if the typeguard was using typeof.
|
|
|
|
discriminantTy = builtinTypes->classType;
|
2022-11-10 22:53:13 +00:00
|
|
|
}
|
|
|
|
else if (!typeguard->isTypeof && typeguard->type == "vector")
|
2023-01-04 20:53:17 +00:00
|
|
|
discriminantTy = builtinTypes->neverType; // TODO: figure out a way to deal with this quirky type
|
2022-11-10 22:53:13 +00:00
|
|
|
else if (!typeguard->isTypeof)
|
2023-01-04 20:53:17 +00:00
|
|
|
discriminantTy = builtinTypes->neverType;
|
2022-11-10 22:53:13 +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-05-05 22:52:49 +01:00
|
|
|
if (auto ctv = get<ClassType>(ty); !ctv || ctv->parent == builtinTypes->classType)
|
2022-11-10 22:53:13 +00:00
|
|
|
discriminantTy = ty;
|
|
|
|
}
|
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
RefinementId proposition = refinementArena.proposition(NotNull{bc}, discriminantTy);
|
2022-11-10 22:53:13 +00:00
|
|
|
if (binary->op == AstExprBinary::CompareEq)
|
|
|
|
return {leftType, rightType, proposition};
|
|
|
|
else if (binary->op == AstExprBinary::CompareNe)
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
return {leftType, rightType, refinementArena.negation(proposition)};
|
2022-11-10 22:53:13 +00:00
|
|
|
else
|
|
|
|
ice->ice("matchTypeGuard should only return a Some under `==` or `~=`!");
|
|
|
|
}
|
2022-11-04 17:33:22 +00:00
|
|
|
else if (binary->op == AstExprBinary::CompareEq || binary->op == AstExprBinary::CompareNe)
|
|
|
|
{
|
2023-05-05 22:52:49 +01:00
|
|
|
// We are checking a binary expression of the form a op b
|
|
|
|
// Just because a op b is epxected to return a bool, doesn't mean a, b are expected to be bools too
|
|
|
|
TypeId leftType = check(scope, binary->left, ValueContext::RValue, {}, true).ty;
|
|
|
|
TypeId rightType = check(scope, binary->right, ValueContext::RValue, {}, true).ty;
|
2022-11-04 17:33:22 +00:00
|
|
|
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
RefinementId leftRefinement = nullptr;
|
2023-03-03 20:21:14 +00:00
|
|
|
if (auto bc = dfg->getBreadcrumb(binary->left))
|
|
|
|
leftRefinement = refinementArena.proposition(NotNull{bc}, rightType);
|
2022-11-04 17:33:22 +00:00
|
|
|
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
RefinementId rightRefinement = nullptr;
|
2023-03-03 20:21:14 +00:00
|
|
|
if (auto bc = dfg->getBreadcrumb(binary->right))
|
|
|
|
rightRefinement = refinementArena.proposition(NotNull{bc}, leftType);
|
2022-11-04 17:33:22 +00:00
|
|
|
|
|
|
|
if (binary->op == AstExprBinary::CompareNe)
|
|
|
|
{
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
leftRefinement = refinementArena.negation(leftRefinement);
|
|
|
|
rightRefinement = refinementArena.negation(rightRefinement);
|
2022-11-04 17:33:22 +00:00
|
|
|
}
|
|
|
|
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
return {leftType, rightType, refinementArena.equivalence(leftRefinement, rightRefinement)};
|
2022-11-04 17:33:22 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-04-28 20:55:13 +01:00
|
|
|
TypeId leftType = check(scope, binary->left, ValueContext::RValue).ty;
|
|
|
|
TypeId rightType = check(scope, binary->right, ValueContext::RValue).ty;
|
2022-11-04 17:33:22 +00:00
|
|
|
return {leftType, rightType, nullptr};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-24 21:49:38 +00:00
|
|
|
std::vector<TypeId> ConstraintGraphBuilder::checkLValues(const ScopePtr& scope, AstArray<AstExpr*> exprs)
|
2022-10-21 18:54:01 +01:00
|
|
|
{
|
|
|
|
std::vector<TypeId> types;
|
|
|
|
types.reserve(exprs.size);
|
|
|
|
|
2023-02-24 21:49:38 +00:00
|
|
|
for (AstExpr* expr : exprs)
|
2022-10-21 18:54:01 +01:00
|
|
|
types.push_back(checkLValue(scope, expr));
|
|
|
|
|
2023-02-24 21:49:38 +00:00
|
|
|
return types;
|
2022-10-21 18:54:01 +01:00
|
|
|
}
|
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
static bool isIndexNameEquivalent(AstExpr* expr)
|
|
|
|
{
|
|
|
|
if (expr->is<AstExprIndexName>())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
AstExprIndexExpr* e = expr->as<AstExprIndexExpr>();
|
|
|
|
if (e == nullptr)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!e->index->is<AstExprConstantString>())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-10-21 18:54:01 +01:00
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2023-03-03 20:21:14 +00:00
|
|
|
if (auto indexExpr = expr->as<AstExprIndexExpr>(); indexExpr && !indexExpr->index->is<AstExprConstantString>())
|
2022-10-21 18:54:01 +01:00
|
|
|
{
|
2023-02-24 21:49:38 +00:00
|
|
|
// An indexer is only interesting in an lvalue-ey way if it is at the
|
|
|
|
// tail of an expression.
|
|
|
|
//
|
|
|
|
// If the indexer is not at the tail, then we are not interested in
|
|
|
|
// augmenting the lhs data structure with a new indexer. Constraint
|
|
|
|
// generation can treat it as an ordinary lvalue.
|
|
|
|
//
|
|
|
|
// eg
|
|
|
|
//
|
|
|
|
// a.b.c[1] = 44 -- lvalue
|
|
|
|
// a.b[4].c = 2 -- rvalue
|
|
|
|
|
|
|
|
TypeId resultType = arena->addType(BlockedType{});
|
|
|
|
TypeId subjectType = check(scope, indexExpr->expr).ty;
|
|
|
|
TypeId indexType = check(scope, indexExpr->index).ty;
|
|
|
|
TypeId propType = arena->addType(BlockedType{});
|
|
|
|
addConstraint(scope, expr->location, SetIndexerConstraint{resultType, subjectType, indexType, propType});
|
|
|
|
|
|
|
|
module->astTypes[expr] = propType;
|
|
|
|
|
|
|
|
return propType;
|
2022-10-21 18:54:01 +01:00
|
|
|
}
|
2023-03-03 20:21:14 +00:00
|
|
|
else if (!isIndexNameEquivalent(expr))
|
2023-04-28 20:55:13 +01:00
|
|
|
return check(scope, expr, ValueContext::LValue).ty;
|
2022-10-21 18:54:01 +01:00
|
|
|
|
2023-01-20 20:27:03 +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;
|
|
|
|
}
|
2023-03-03 20:21:14 +00:00
|
|
|
else if (auto indexExpr = e->as<AstExprIndexExpr>())
|
|
|
|
{
|
2023-05-19 20:37:30 +01:00
|
|
|
// We need to populate the type for the index value
|
|
|
|
check(scope, indexExpr->index, ValueContext::RValue);
|
2023-03-03 20:21:14 +00:00
|
|
|
if (auto strIndex = indexExpr->index->as<AstExprConstantString>())
|
|
|
|
{
|
|
|
|
segments.push_back(std::string(strIndex->value.data, strIndex->value.size));
|
|
|
|
exprs.push_back(e);
|
|
|
|
e = indexExpr->expr;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-04-28 20:55:13 +01:00
|
|
|
return check(scope, expr, ValueContext::LValue).ty;
|
2023-03-03 20:21:14 +00:00
|
|
|
}
|
|
|
|
}
|
2023-01-20 20:27:03 +00:00
|
|
|
else
|
2023-04-28 20:55:13 +01:00
|
|
|
return check(scope, expr, ValueContext::LValue).ty;
|
2023-01-20 20:27:03 +00:00
|
|
|
}
|
2022-10-21 18:54:01 +01:00
|
|
|
|
2022-11-18 19:47:21 +00:00
|
|
|
LUAU_ASSERT(!segments.empty());
|
2022-10-21 18:54:01 +01:00
|
|
|
|
2023-01-20 20:27:03 +00:00
|
|
|
std::reverse(begin(segments), end(segments));
|
|
|
|
std::reverse(begin(exprs), end(exprs));
|
|
|
|
|
2022-10-21 18:54:01 +01:00
|
|
|
auto lookupResult = scope->lookupEx(sym);
|
|
|
|
if (!lookupResult)
|
2023-04-28 20:55:13 +01:00
|
|
|
return check(scope, expr, ValueContext::LValue).ty;
|
2023-02-24 21:49:38 +00:00
|
|
|
const auto [subjectBinding, symbolScope] = std::move(*lookupResult);
|
|
|
|
TypeId subjectType = subjectBinding->typeId;
|
2022-10-21 18:54:01 +01:00
|
|
|
|
2022-11-18 19:47:21 +00:00
|
|
|
TypeId propTy = freshType(scope);
|
2022-10-21 18:54:01 +01:00
|
|
|
|
2022-11-18 19:47:21 +00:00
|
|
|
std::vector<std::string> segmentStrings(begin(segments), end(segments));
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
TypeId updatedType = arena->addType(BlockedType{});
|
2022-11-18 19:47:21 +00:00
|
|
|
addConstraint(scope, expr->location, SetPropConstraint{updatedType, subjectType, std::move(segmentStrings), propTy});
|
2022-10-21 18:54:01 +01:00
|
|
|
|
2023-01-20 20:27:03 +00:00
|
|
|
TypeId prevSegmentTy = updatedType;
|
|
|
|
for (size_t i = 0; i < segments.size(); ++i)
|
|
|
|
{
|
|
|
|
TypeId segmentTy = arena->addType(BlockedType{});
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
module->astTypes[exprs[i]] = segmentTy;
|
2023-01-20 20:27:03 +00:00
|
|
|
addConstraint(scope, expr->location, HasPropConstraint{segmentTy, prevSegmentTy, segments[i]});
|
|
|
|
prevSegmentTy = segmentTy;
|
|
|
|
}
|
|
|
|
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
module->astTypes[expr] = prevSegmentTy;
|
|
|
|
module->astTypes[e] = updatedType;
|
2023-02-10 19:40:38 +00:00
|
|
|
|
2023-02-24 21:49:38 +00:00
|
|
|
if (!subjectType->persistent)
|
2023-02-10 19:40:38 +00:00
|
|
|
{
|
2023-02-24 21:49:38 +00:00
|
|
|
symbolScope->bindings[sym].typeId = updatedType;
|
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
// This can fail if the user is erroneously trying to augment a builtin
|
|
|
|
// table like os or string.
|
|
|
|
if (auto bc = dfg->getBreadcrumb(e))
|
|
|
|
symbolScope->dcrRefinements[bc->def] = updatedType;
|
2023-02-10 19:40:38 +00:00
|
|
|
}
|
2022-11-18 19:47:21 +00:00
|
|
|
|
|
|
|
return propTy;
|
2022-10-21 18:54:01 +01:00
|
|
|
}
|
|
|
|
|
2022-10-28 11:37:29 +01:00
|
|
|
Inference ConstraintGraphBuilder::check(const ScopePtr& scope, AstExprTable* expr, std::optional<TypeId> expectedType)
|
2022-06-17 02:05:14 +01:00
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
TypeId ty = arena->addType(TableType{});
|
|
|
|
TableType* ttv = getMutable<TableType>(ty);
|
2022-06-17 02:05:14 +01:00
|
|
|
LUAU_ASSERT(ttv);
|
|
|
|
|
2022-09-29 23:23:10 +01:00
|
|
|
ttv->state = TableState::Unsealed;
|
|
|
|
ttv->scope = scope.get();
|
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
auto createIndexer = [this, scope, ttv](const Location& location, TypeId currentIndexType, TypeId currentResultType) {
|
2022-06-17 02:05:14 +01:00
|
|
|
if (!ttv->indexer)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-06-17 02:05:14 +01:00
|
|
|
TypeId indexType = this->freshType(scope);
|
|
|
|
TypeId resultType = this->freshType(scope);
|
|
|
|
ttv->indexer = TableIndexer{indexType, resultType};
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
|
2022-09-02 00:14:03 +01:00
|
|
|
addConstraint(scope, location, SubtypeConstraint{ttv->indexer->indexType, currentIndexType});
|
|
|
|
addConstraint(scope, location, SubtypeConstraint{ttv->indexer->indexResultType, currentResultType});
|
2022-06-17 02:05:14 +01:00
|
|
|
};
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2023-02-10 19:40:38 +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 02:05:14 +01:00
|
|
|
for (const AstExprTable::Item& item : expr->items)
|
|
|
|
{
|
2022-09-23 20:17:25 +01:00
|
|
|
std::optional<TypeId> expectedValueType;
|
2023-02-10 19:40:38 +00:00
|
|
|
if (item.kind == AstExprTable::Item::Kind::General || item.kind == AstExprTable::Item::Kind::List)
|
|
|
|
isIndexedResultType = true;
|
2022-09-23 20:17:25 +01:00
|
|
|
|
|
|
|
if (item.key && expectedType)
|
|
|
|
{
|
|
|
|
if (auto stringKey = item.key->as<AstExprConstantString>())
|
|
|
|
{
|
2022-12-02 18:09:59 +00:00
|
|
|
ErrorVec errorVec;
|
|
|
|
std::optional<TypeId> propTy =
|
2023-01-04 20:53:17 +00:00
|
|
|
findTablePropertyRespectingMeta(builtinTypes, errorVec, follow(*expectedType), stringKey->value.data, item.value->location);
|
2022-12-02 18:09:59 +00:00
|
|
|
if (propTy)
|
|
|
|
expectedValueType = propTy;
|
|
|
|
else
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
expectedValueType = arena->addType(BlockedType{});
|
2023-05-19 20:37:30 +01:00
|
|
|
addConstraint(scope, item.value->location,
|
|
|
|
HasPropConstraint{*expectedValueType, *expectedType, stringKey->value.data, /*suppressSimplification*/ true});
|
2022-12-02 18:09:59 +00:00
|
|
|
}
|
2022-09-23 20:17:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-10 19:40:38 +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;
|
|
|
|
}
|
|
|
|
|
2023-04-28 20:55:13 +01:00
|
|
|
TypeId itemTy = check(scope, item.value, ValueContext::RValue, checkExpectedIndexResultType).ty;
|
2023-02-10 19:40:38 +00:00
|
|
|
|
|
|
|
if (isIndexedResultType && !pinnedIndexResultType)
|
|
|
|
pinnedIndexResultType = itemTy;
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
if (item.key)
|
|
|
|
{
|
|
|
|
// Even though we don't need to use the type of the item's key if
|
|
|
|
// it's a string constant, we still want to check it to populate
|
|
|
|
// astTypes.
|
2023-04-28 20:55:13 +01:00
|
|
|
TypeId keyTy = check(scope, item.key, ValueContext::RValue, annotatedKeyType).ty;
|
2022-06-17 02:05:14 +01:00
|
|
|
|
|
|
|
if (AstExprConstantString* key = item.key->as<AstExprConstantString>())
|
|
|
|
{
|
|
|
|
ttv->props[key->value.begin()] = {itemTy};
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-09-02 00:14:03 +01:00
|
|
|
createIndexer(item.key->location, keyTy, itemTy);
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
TypeId numberType = builtinTypes->numberType;
|
2022-09-02 00:14:03 +01:00
|
|
|
// FIXME? The location isn't quite right here. Not sure what is
|
|
|
|
// right.
|
|
|
|
createIndexer(item.value->location, numberType, itemTy);
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
2022-06-17 02:05:14 +01:00
|
|
|
|
2022-10-28 11:37:29 +01:00
|
|
|
return Inference{ty};
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
|
2022-12-02 18:09:59 +00:00
|
|
|
ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionSignature(
|
2023-05-19 20:37:30 +01:00
|
|
|
const ScopePtr& parent, AstExprFunction* fn, std::optional<TypeId> expectedType, std::optional<Location> originalName)
|
2022-06-17 02:05:14 +01:00
|
|
|
{
|
2022-07-29 05:24:07 +01:00
|
|
|
ScopePtr signatureScope = nullptr;
|
|
|
|
ScopePtr bodyScope = nullptr;
|
2022-07-01 00:52:43 +01:00
|
|
|
TypePackId returnType = nullptr;
|
|
|
|
|
|
|
|
std::vector<TypeId> genericTypes;
|
|
|
|
std::vector<TypePackId> genericTypePacks;
|
|
|
|
|
2022-12-02 18:09:59 +00:00
|
|
|
if (expectedType)
|
|
|
|
expectedType = follow(*expectedType);
|
|
|
|
|
2022-07-01 00:52:43 +01:00
|
|
|
bool hasGenerics = fn->generics.size > 0 || fn->genericPacks.size > 0;
|
|
|
|
|
2022-12-02 18:09:59 +00:00
|
|
|
signatureScope = childScope(fn, parent);
|
2022-07-01 00:52:43 +01:00
|
|
|
|
2022-12-02 18:09:59 +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:52:43 +01:00
|
|
|
|
2022-12-02 18:09:59 +00:00
|
|
|
bodyScope = childScope(fn->body, signatureScope);
|
2022-07-01 00:52:43 +01:00
|
|
|
|
2022-12-02 18:09:59 +00:00
|
|
|
if (hasGenerics)
|
|
|
|
{
|
2022-07-29 05:24:07 +01:00
|
|
|
std::vector<std::pair<Name, GenericTypeDefinition>> genericDefinitions = createGenerics(signatureScope, fn->generics);
|
|
|
|
std::vector<std::pair<Name, GenericTypePackDefinition>> genericPackDefinitions = createGenericPacks(signatureScope, fn->genericPacks);
|
2022-07-01 00:52:43 +01:00
|
|
|
|
|
|
|
// We do not support default values on function generics, so we only
|
|
|
|
// care about the types involved.
|
|
|
|
for (const auto& [name, g] : genericDefinitions)
|
|
|
|
{
|
|
|
|
genericTypes.push_back(g.ty);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto& [name, g] : genericPackDefinitions)
|
|
|
|
{
|
|
|
|
genericTypePacks.push_back(g.tp);
|
|
|
|
}
|
2022-12-02 18:09:59 +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:52:43 +01:00
|
|
|
}
|
2022-12-02 18:09:59 +00:00
|
|
|
|
|
|
|
std::vector<TypeId> argTypes;
|
2023-02-17 23:41:51 +00:00
|
|
|
std::vector<std::optional<FunctionArgument>> argNames;
|
2022-12-02 18:09:59 +00:00
|
|
|
TypePack expectedArgPack;
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
const FunctionType* expectedFunction = expectedType ? get<FunctionType>(*expectedType) : nullptr;
|
2023-03-24 18:03:04 +00:00
|
|
|
// This check ensures that expectedType is precisely optional and not any (since any is also an optional type)
|
|
|
|
if (expectedType && isOptional(*expectedType) && !get<AnyType>(*expectedType))
|
|
|
|
{
|
|
|
|
auto ut = get<UnionType>(*expectedType);
|
|
|
|
for (auto u : ut)
|
|
|
|
{
|
|
|
|
if (get<FunctionType>(u) && !isNil(u))
|
|
|
|
{
|
|
|
|
expectedFunction = get<FunctionType>(u);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-12-02 18:09:59 +00:00
|
|
|
|
|
|
|
if (expectedFunction)
|
2022-07-01 00:52:43 +01:00
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
expectedArgPack = extendTypePack(*arena, builtinTypes, expectedFunction->argTypes, fn->args.size);
|
2022-12-02 18:09:59 +00:00
|
|
|
|
|
|
|
genericTypes = expectedFunction->generics;
|
|
|
|
genericTypePacks = expectedFunction->genericPacks;
|
|
|
|
}
|
2022-07-01 00:52:43 +01:00
|
|
|
|
2023-02-17 23:41:51 +00:00
|
|
|
if (fn->self)
|
|
|
|
{
|
|
|
|
TypeId selfType = freshType(signatureScope);
|
|
|
|
argTypes.push_back(selfType);
|
|
|
|
argNames.emplace_back(FunctionArgument{fn->self->name.value, fn->self->location});
|
|
|
|
signatureScope->bindings[fn->self] = Binding{selfType, fn->self->location};
|
2023-03-03 20:21:14 +00:00
|
|
|
|
|
|
|
BreadcrumbId bc = dfg->getBreadcrumb(fn->self);
|
|
|
|
signatureScope->dcrRefinements[bc->def] = selfType;
|
2023-02-17 23:41:51 +00:00
|
|
|
}
|
|
|
|
|
2022-12-02 18:09:59 +00:00
|
|
|
for (size_t i = 0; i < fn->args.size; ++i)
|
|
|
|
{
|
|
|
|
AstLocal* local = fn->args.data[i];
|
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
TypeId argTy = nullptr;
|
2022-12-02 18:09:59 +00:00
|
|
|
if (local->annotation)
|
2023-03-03 20:21:14 +00:00
|
|
|
argTy = resolveType(signatureScope, local->annotation, /* inTypeArguments */ false, /* replaceErrorWithFresh*/ true);
|
|
|
|
else
|
2022-12-02 18:09:59 +00:00
|
|
|
{
|
2023-03-03 20:21:14 +00:00
|
|
|
argTy = freshType(signatureScope);
|
|
|
|
|
|
|
|
if (i < expectedArgPack.head.size())
|
|
|
|
addConstraint(signatureScope, local->location, SubtypeConstraint{argTy, expectedArgPack.head[i]});
|
2022-12-02 18:09:59 +00:00
|
|
|
}
|
2023-03-03 20:21:14 +00:00
|
|
|
|
|
|
|
argTypes.push_back(argTy);
|
|
|
|
argNames.emplace_back(FunctionArgument{local->name.value, local->location});
|
|
|
|
signatureScope->bindings[local] = Binding{argTy, local->location};
|
|
|
|
|
|
|
|
BreadcrumbId bc = dfg->getBreadcrumb(local);
|
|
|
|
signatureScope->dcrRefinements[bc->def] = argTy;
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
|
|
|
|
2022-09-15 23:38:17 +01:00
|
|
|
TypePackId varargPack = nullptr;
|
2022-09-08 23:14:25 +01:00
|
|
|
|
|
|
|
if (fn->vararg)
|
|
|
|
{
|
|
|
|
if (fn->varargAnnotation)
|
|
|
|
{
|
2023-03-03 20:21:14 +00:00
|
|
|
TypePackId annotationType =
|
|
|
|
resolveTypePack(signatureScope, fn->varargAnnotation, /* inTypeArguments */ false, /* replaceErrorWithFresh */ true);
|
2022-09-08 23:14:25 +01:00
|
|
|
varargPack = annotationType;
|
|
|
|
}
|
2022-12-02 18:09:59 +00:00
|
|
|
else if (expectedArgPack.tail && get<VariadicTypePack>(*expectedArgPack.tail))
|
|
|
|
varargPack = *expectedArgPack.tail;
|
2022-09-08 23:14:25 +01:00
|
|
|
else
|
2023-01-04 20:53:17 +00:00
|
|
|
varargPack = builtinTypes->anyTypePack;
|
2022-09-08 23:14:25 +01:00
|
|
|
|
|
|
|
signatureScope->varargPack = varargPack;
|
2022-12-02 18:09:59 +00:00
|
|
|
bodyScope->varargPack = varargPack;
|
2022-09-08 23:14:25 +01:00
|
|
|
}
|
2022-09-15 23:38:17 +01:00
|
|
|
else
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
varargPack = arena->addTypePack(VariadicTypePack{builtinTypes->anyType, /*hidden*/ true});
|
2022-09-15 23:38:17 +01:00
|
|
|
// We do not add to signatureScope->varargPack because ... is not valid
|
|
|
|
// in functions without an explicit ellipsis.
|
2022-12-02 18:09:59 +00:00
|
|
|
|
|
|
|
signatureScope->varargPack = std::nullopt;
|
|
|
|
bodyScope->varargPack = std::nullopt;
|
2022-09-15 23:38:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
LUAU_ASSERT(nullptr != varargPack);
|
2022-09-08 23:14:25 +01:00
|
|
|
|
2022-12-02 18:09:59 +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:56:00 +01:00
|
|
|
if (fn->returnAnnotation)
|
|
|
|
{
|
2023-03-03 20:21:14 +00:00
|
|
|
TypePackId annotatedRetType =
|
|
|
|
resolveTypePack(signatureScope, *fn->returnAnnotation, /* inTypeArguments */ false, /* replaceErrorWithFresh*/ true);
|
2022-09-23 20:17:25 +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:56:00 +01:00
|
|
|
}
|
2022-12-02 18:09:59 +00:00
|
|
|
else if (expectedFunction)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-12-02 18:09:59 +00:00
|
|
|
asMutable(returnType)->ty.emplace<BoundTypePack>(expectedFunction->retTypes);
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
|
2022-07-01 00:52:43 +01:00
|
|
|
// TODO: Preserve argument names in the function's type.
|
2022-06-24 02:56:00 +01:00
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
FunctionType actualFunction{TypeLevel{}, parent.get(), arena->addTypePack(argTypes, varargPack), returnType};
|
2022-07-01 00:52:43 +01:00
|
|
|
actualFunction.generics = std::move(genericTypes);
|
|
|
|
actualFunction.genericPacks = std::move(genericTypePacks);
|
2023-02-17 23:41:51 +00:00
|
|
|
actualFunction.argNames = std::move(argNames);
|
2023-02-24 21:49:38 +00:00
|
|
|
actualFunction.hasSelf = fn->self != nullptr;
|
2022-07-01 00:52:43 +01:00
|
|
|
|
2023-05-19 20:37:30 +01:00
|
|
|
FunctionDefinition defn;
|
|
|
|
defn.definitionModuleName = module->name;
|
|
|
|
defn.definitionLocation = fn->location;
|
|
|
|
defn.varargLocation = fn->vararg ? std::make_optional(fn->varargLocation) : std::nullopt;
|
|
|
|
defn.originalNameLocation = originalName.value_or(Location(fn->location.begin, 0));
|
|
|
|
actualFunction.definition = defn;
|
|
|
|
|
2022-06-17 02:05:14 +01:00
|
|
|
TypeId actualFunctionType = arena->addType(std::move(actualFunction));
|
|
|
|
LUAU_ASSERT(actualFunctionType);
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
module->astTypes[fn] = actualFunctionType;
|
2022-06-17 02:05:14 +01:00
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
if (expectedType && get<FreeType>(*expectedType))
|
2023-02-24 21:49:38 +00:00
|
|
|
bindFreeType(*expectedType, actualFunctionType);
|
2022-12-02 18:09:59 +00:00
|
|
|
|
2022-07-01 00:52:43 +01:00
|
|
|
return {
|
|
|
|
/* signature */ actualFunctionType,
|
2022-12-02 18:09:59 +00:00
|
|
|
/* signatureScope */ signatureScope,
|
2022-07-29 05:24:07 +01:00
|
|
|
/* bodyScope */ bodyScope,
|
2022-07-01 00:52:43 +01:00
|
|
|
};
|
2022-06-17 02:05:14 +01:00
|
|
|
}
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
void ConstraintGraphBuilder::checkFunctionBody(const ScopePtr& scope, AstExprFunction* fn)
|
2022-06-17 02:05:14 +01:00
|
|
|
{
|
2022-07-01 00:52:43 +01:00
|
|
|
visitBlockWithoutChildScope(scope, fn->body);
|
2022-06-17 02:05:14 +01:00
|
|
|
|
|
|
|
// If it is possible for execution to reach the end of the function, the return type must be compatible with ()
|
|
|
|
|
|
|
|
if (nullptr != getFallthrough(fn->body))
|
|
|
|
{
|
2023-07-07 21:10:48 +01:00
|
|
|
TypePackId empty = arena->addTypePack({}); // TODO we could have CGB retain one of these forever
|
2022-09-02 00:14:03 +01:00
|
|
|
addConstraint(scope, fn->location, PackSubtypeConstraint{scope->returnType, empty});
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
TypeId ConstraintGraphBuilder::resolveType(const ScopePtr& scope, AstType* ty, bool inTypeArguments, bool replaceErrorWithFresh)
|
2022-06-24 02:56:00 +01:00
|
|
|
{
|
|
|
|
TypeId result = nullptr;
|
|
|
|
|
|
|
|
if (auto ref = ty->as<AstTypeReference>())
|
|
|
|
{
|
2022-09-29 23:23:10 +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-05-19 20:37:30 +01:00
|
|
|
module->astResolvedTypes[ty] = builtinTypes->errorRecoveryType();
|
2023-01-04 20:53:17 +00:00
|
|
|
return builtinTypes->errorRecoveryType();
|
2022-09-29 23:23:10 +01:00
|
|
|
}
|
|
|
|
else
|
2023-01-20 20:27:03 +00:00
|
|
|
return resolveType(scope, ref->parameters.data[0].type, inTypeArguments);
|
2022-09-29 23:23:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-28 11:37:29 +01:00
|
|
|
std::optional<TypeFun> alias;
|
2022-08-04 23:35:33 +01:00
|
|
|
|
2022-10-28 11:37:29 +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 23:35:33 +01:00
|
|
|
{
|
|
|
|
// If the alias is not generic, we don't need to set up a blocked
|
|
|
|
// type and an instantiation constraint.
|
2022-09-02 00:14:03 +01:00
|
|
|
if (alias.has_value() && alias->typeParams.empty() && alias->typePackParams.empty())
|
2022-08-04 23:35:33 +01:00
|
|
|
{
|
|
|
|
result = alias->type;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::vector<TypeId> parameters;
|
|
|
|
std::vector<TypePackId> packParameters;
|
|
|
|
|
|
|
|
for (const AstTypeOrPack& p : ref->parameters)
|
|
|
|
{
|
|
|
|
// We do not enforce the ordering of types vs. type packs here;
|
|
|
|
// that is done in the parser.
|
|
|
|
if (p.type)
|
|
|
|
{
|
2023-01-20 20:27:03 +00:00
|
|
|
parameters.push_back(resolveType(scope, p.type, /* inTypeArguments */ true));
|
2022-08-04 23:35:33 +01:00
|
|
|
}
|
|
|
|
else if (p.typePack)
|
|
|
|
{
|
2023-01-20 20:27:03 +00:00
|
|
|
packParameters.push_back(resolveTypePack(scope, p.typePack, /* inTypeArguments */ true));
|
2022-08-04 23:35:33 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// This indicates a parser bug: one of these two pointers
|
|
|
|
// should be set.
|
|
|
|
LUAU_ASSERT(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
result = arena->addType(PendingExpansionType{ref->prefix, ref->name, parameters, packParameters});
|
2022-08-04 23:35:33 +01:00
|
|
|
|
2023-01-20 20:27:03 +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:14:03 +01:00
|
|
|
addConstraint(scope, ty->location, TypeAliasExpansionConstraint{/* target */ result});
|
2022-08-04 23:35:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
result = builtinTypes->errorRecoveryType();
|
2023-03-03 20:21:14 +00:00
|
|
|
if (replaceErrorWithFresh)
|
|
|
|
result = freshType(scope);
|
2022-08-04 23:35:33 +01:00
|
|
|
}
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
2022-06-24 02:56:00 +01:00
|
|
|
else if (auto tab = ty->as<AstTypeTable>())
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
TableType::Props props;
|
2022-06-24 02:56:00 +01:00
|
|
|
std::optional<TableIndexer> indexer;
|
|
|
|
|
|
|
|
for (const AstTableProp& prop : tab->props)
|
|
|
|
{
|
|
|
|
std::string name = prop.name.value;
|
|
|
|
// TODO: Recursion limit.
|
2023-01-20 20:27:03 +00:00
|
|
|
TypeId propTy = resolveType(scope, prop.type, inTypeArguments);
|
2022-06-24 02:56:00 +01:00
|
|
|
// TODO: Fill in location.
|
|
|
|
props[name] = {propTy};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tab->indexer)
|
|
|
|
{
|
|
|
|
// TODO: Recursion limit.
|
|
|
|
indexer = TableIndexer{
|
2023-01-20 20:27:03 +00:00
|
|
|
resolveType(scope, tab->indexer->indexType, inTypeArguments),
|
|
|
|
resolveType(scope, tab->indexer->resultType, inTypeArguments),
|
2022-06-24 02:56:00 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
result = arena->addType(TableType{props, indexer, scope->level, scope.get(), TableState::Sealed});
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
else if (auto fn = ty->as<AstTypeFunction>())
|
|
|
|
{
|
|
|
|
// TODO: Recursion limit.
|
2022-07-01 00:52:43 +01:00
|
|
|
bool hasGenerics = fn->generics.size > 0 || fn->genericPacks.size > 0;
|
2022-07-29 05:24:07 +01:00
|
|
|
ScopePtr signatureScope = nullptr;
|
2022-07-01 00:52:43 +01:00
|
|
|
|
|
|
|
std::vector<TypeId> genericTypes;
|
|
|
|
std::vector<TypePackId> genericTypePacks;
|
2022-06-24 02:56:00 +01:00
|
|
|
|
2022-07-01 00:52:43 +01:00
|
|
|
// If we don't have generics, we do not need to generate a child scope
|
|
|
|
// for the generic bindings to live on.
|
|
|
|
if (hasGenerics)
|
|
|
|
{
|
2022-08-18 22:32:08 +01:00
|
|
|
signatureScope = childScope(fn, scope);
|
2022-07-01 00:52:43 +01:00
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
std::vector<std::pair<Name, GenericTypeDefinition>> genericDefinitions = createGenerics(signatureScope, fn->generics);
|
|
|
|
std::vector<std::pair<Name, GenericTypePackDefinition>> genericPackDefinitions = createGenericPacks(signatureScope, fn->genericPacks);
|
2022-06-24 02:56:00 +01:00
|
|
|
|
2022-07-01 00:52:43 +01:00
|
|
|
for (const auto& [name, g] : genericDefinitions)
|
|
|
|
{
|
|
|
|
genericTypes.push_back(g.ty);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto& [name, g] : genericPackDefinitions)
|
|
|
|
{
|
|
|
|
genericTypePacks.push_back(g.tp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// To eliminate the need to branch on hasGenerics below, we say that
|
|
|
|
// the signature scope is the parent scope if we don't have
|
|
|
|
// generics.
|
2022-07-29 05:24:07 +01:00
|
|
|
signatureScope = scope;
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
TypePackId argTypes = resolveTypePack(signatureScope, fn->argTypes, inTypeArguments, replaceErrorWithFresh);
|
|
|
|
TypePackId returnTypes = resolveTypePack(signatureScope, fn->returnTypes, inTypeArguments, replaceErrorWithFresh);
|
2022-07-01 00:52:43 +01:00
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
// TODO: FunctionType needs a pointer to the scope so that we know
|
2022-07-01 00:52:43 +01:00
|
|
|
// how to quantify/instantiate it.
|
2023-01-04 20:53:17 +00:00
|
|
|
FunctionType ftv{TypeLevel{}, scope.get(), {}, {}, argTypes, returnTypes};
|
2022-07-01 00:52:43 +01:00
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
// This replicates the behavior of the appropriate FunctionType
|
2022-07-01 00:52:43 +01:00
|
|
|
// constructors.
|
|
|
|
ftv.generics = std::move(genericTypes);
|
|
|
|
ftv.genericPacks = std::move(genericTypePacks);
|
|
|
|
|
|
|
|
ftv.argNames.reserve(fn->argNames.size);
|
2022-06-24 02:56:00 +01:00
|
|
|
for (const auto& el : fn->argNames)
|
|
|
|
{
|
|
|
|
if (el)
|
|
|
|
{
|
|
|
|
const auto& [name, location] = *el;
|
2022-07-01 00:52:43 +01:00
|
|
|
ftv.argNames.push_back(FunctionArgument{name.value, location});
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-07-01 00:52:43 +01:00
|
|
|
ftv.argNames.push_back(std::nullopt);
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
}
|
2022-07-01 00:52:43 +01:00
|
|
|
|
|
|
|
result = arena->addType(std::move(ftv));
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
else if (auto tof = ty->as<AstTypeTypeof>())
|
|
|
|
{
|
|
|
|
// TODO: Recursion limit.
|
2022-10-28 11:37:29 +01:00
|
|
|
TypeId exprType = check(scope, tof->expr).ty;
|
2022-06-24 02:56:00 +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 20:27:03 +00:00
|
|
|
parts.push_back(resolveType(scope, part, inTypeArguments));
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
result = arena->addType(UnionType{parts});
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
else if (auto intersectionAnnotation = ty->as<AstTypeIntersection>())
|
|
|
|
{
|
|
|
|
std::vector<TypeId> parts;
|
|
|
|
for (AstType* part : intersectionAnnotation->types)
|
|
|
|
{
|
|
|
|
// TODO: Recursion limit.
|
2023-01-20 20:27:03 +00:00
|
|
|
parts.push_back(resolveType(scope, part, inTypeArguments));
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
|
2023-01-04 20:53:17 +00:00
|
|
|
result = arena->addType(IntersectionType{parts});
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
else if (auto boolAnnotation = ty->as<AstTypeSingletonBool>())
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
result = arena->addType(SingletonType(BooleanSingleton{boolAnnotation->value}));
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
else if (auto stringAnnotation = ty->as<AstTypeSingletonString>())
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
result = arena->addType(SingletonType(StringSingleton{std::string(stringAnnotation->value.data, stringAnnotation->value.size)}));
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
else if (ty->is<AstTypeError>())
|
|
|
|
{
|
2023-01-04 20:53:17 +00:00
|
|
|
result = builtinTypes->errorRecoveryType();
|
2023-03-03 20:21:14 +00:00
|
|
|
if (replaceErrorWithFresh)
|
|
|
|
result = freshType(scope);
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(0);
|
2023-01-04 20:53:17 +00:00
|
|
|
result = builtinTypes->errorRecoveryType();
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
module->astResolvedTypes[ty] = result;
|
2022-06-24 02:56:00 +01:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
TypePackId ConstraintGraphBuilder::resolveTypePack(const ScopePtr& scope, AstTypePack* tp, bool inTypeArgument, bool replaceErrorWithFresh)
|
2022-06-24 02:56:00 +01:00
|
|
|
{
|
|
|
|
TypePackId result;
|
|
|
|
if (auto expl = tp->as<AstTypePackExplicit>())
|
|
|
|
{
|
2023-03-03 20:21:14 +00:00
|
|
|
result = resolveTypePack(scope, expl->typeList, inTypeArgument, replaceErrorWithFresh);
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
else if (auto var = tp->as<AstTypePackVariadic>())
|
|
|
|
{
|
2023-03-03 20:21:14 +00:00
|
|
|
TypeId ty = resolveType(scope, var->variadicType, inTypeArgument, replaceErrorWithFresh);
|
2022-06-24 02:56:00 +01:00
|
|
|
result = arena->addTypePack(TypePackVar{VariadicTypePack{ty}});
|
|
|
|
}
|
|
|
|
else if (auto gen = tp->as<AstTypePackGeneric>())
|
|
|
|
{
|
2022-08-11 22:01:33 +01:00
|
|
|
if (std::optional<TypePackId> lookup = scope->lookupPack(gen->genericName.value))
|
2022-08-04 23:35:33 +01:00
|
|
|
{
|
|
|
|
result = *lookup;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
reportError(tp->location, UnknownSymbol{gen->genericName.value, UnknownSymbol::Context::Type});
|
2023-01-04 20:53:17 +00:00
|
|
|
result = builtinTypes->errorRecoveryTypePack();
|
2022-08-04 23:35:33 +01:00
|
|
|
}
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(0);
|
2023-01-04 20:53:17 +00:00
|
|
|
result = builtinTypes->errorRecoveryTypePack();
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
module->astResolvedTypePacks[tp] = result;
|
2022-06-24 02:56:00 +01:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
TypePackId ConstraintGraphBuilder::resolveTypePack(const ScopePtr& scope, const AstTypeList& list, bool inTypeArguments, bool replaceErrorWithFresh)
|
2022-06-24 02:56:00 +01:00
|
|
|
{
|
|
|
|
std::vector<TypeId> head;
|
|
|
|
|
|
|
|
for (AstType* headTy : list.types)
|
|
|
|
{
|
2023-03-03 20:21:14 +00:00
|
|
|
head.push_back(resolveType(scope, headTy, inTypeArguments, replaceErrorWithFresh));
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<TypePackId> tail = std::nullopt;
|
|
|
|
if (list.tailType)
|
|
|
|
{
|
2023-03-03 20:21:14 +00:00
|
|
|
tail = resolveTypePack(scope, list.tailType, inTypeArguments, replaceErrorWithFresh);
|
2022-06-24 02:56:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return arena->addTypePack(TypePack{head, tail});
|
2022-06-03 23:15:45 +01:00
|
|
|
}
|
|
|
|
|
2023-02-17 23:41:51 +00:00
|
|
|
std::vector<std::pair<Name, GenericTypeDefinition>> ConstraintGraphBuilder::createGenerics(
|
2023-02-24 21:49:38 +00:00
|
|
|
const ScopePtr& scope, AstArray<AstGenericType> generics, bool useCache, bool addTypes)
|
2022-07-01 00:52:43 +01:00
|
|
|
{
|
|
|
|
std::vector<std::pair<Name, GenericTypeDefinition>> result;
|
|
|
|
for (const auto& generic : generics)
|
|
|
|
{
|
2023-02-17 23:41:51 +00:00
|
|
|
TypeId genericTy = nullptr;
|
|
|
|
|
|
|
|
if (auto it = scope->parent->typeAliasTypeParameters.find(generic.name.value); useCache && it != scope->parent->typeAliasTypeParameters.end())
|
|
|
|
genericTy = it->second;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
genericTy = arena->addType(GenericType{scope.get(), generic.name.value});
|
|
|
|
scope->parent->typeAliasTypeParameters[generic.name.value] = genericTy;
|
|
|
|
}
|
|
|
|
|
2022-07-01 00:52:43 +01:00
|
|
|
std::optional<TypeId> defaultTy = std::nullopt;
|
|
|
|
|
|
|
|
if (generic.defaultValue)
|
2023-01-20 20:27:03 +00:00
|
|
|
defaultTy = resolveType(scope, generic.defaultValue, /* inTypeArguments */ false);
|
2022-07-01 00:52:43 +01:00
|
|
|
|
2023-02-24 21:49:38 +00:00
|
|
|
if (addTypes)
|
|
|
|
scope->privateTypeBindings[generic.name.value] = TypeFun{genericTy};
|
|
|
|
|
2022-10-28 11:37:29 +01:00
|
|
|
result.push_back({generic.name.value, GenericTypeDefinition{genericTy, defaultTy}});
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::pair<Name, GenericTypePackDefinition>> ConstraintGraphBuilder::createGenericPacks(
|
2023-02-24 21:49:38 +00:00
|
|
|
const ScopePtr& scope, AstArray<AstGenericTypePack> generics, bool useCache, bool addTypes)
|
2022-07-01 00:52:43 +01:00
|
|
|
{
|
|
|
|
std::vector<std::pair<Name, GenericTypePackDefinition>> result;
|
|
|
|
for (const auto& generic : generics)
|
|
|
|
{
|
2023-02-17 23:41:51 +00:00
|
|
|
TypePackId genericTy;
|
|
|
|
|
|
|
|
if (auto it = scope->parent->typeAliasTypePackParameters.find(generic.name.value);
|
|
|
|
useCache && it != scope->parent->typeAliasTypePackParameters.end())
|
|
|
|
genericTy = it->second;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
genericTy = arena->addTypePack(TypePackVar{GenericTypePack{scope.get(), generic.name.value}});
|
|
|
|
scope->parent->typeAliasTypePackParameters[generic.name.value] = genericTy;
|
|
|
|
}
|
|
|
|
|
2022-07-01 00:52:43 +01:00
|
|
|
std::optional<TypePackId> defaultTy = std::nullopt;
|
|
|
|
|
|
|
|
if (generic.defaultValue)
|
2023-01-20 20:27:03 +00:00
|
|
|
defaultTy = resolveTypePack(scope, generic.defaultValue, /* inTypeArguments */ false);
|
2022-07-01 00:52:43 +01:00
|
|
|
|
2023-02-24 21:49:38 +00:00
|
|
|
if (addTypes)
|
|
|
|
scope->privateTypePackBindings[generic.name.value] = genericTy;
|
|
|
|
|
2022-10-28 11:37:29 +01:00
|
|
|
result.push_back({generic.name.value, GenericTypePackDefinition{genericTy, defaultTy}});
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-10-28 11:37:29 +01:00
|
|
|
Inference ConstraintGraphBuilder::flattenPack(const ScopePtr& scope, Location location, InferencePack pack)
|
2022-07-01 00:52:43 +01:00
|
|
|
{
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
const auto& [tp, refinements] = pack;
|
|
|
|
RefinementId refinement = nullptr;
|
|
|
|
if (!refinements.empty())
|
|
|
|
refinement = refinements[0];
|
2022-11-04 17:33:22 +00:00
|
|
|
|
2022-07-01 00:52:43 +01:00
|
|
|
if (auto f = first(tp))
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
return Inference{*f, refinement};
|
2022-07-01 00:52:43 +01:00
|
|
|
|
2023-02-24 21:49:38 +00:00
|
|
|
TypeId typeResult = arena->addType(BlockedType{});
|
|
|
|
TypePackId resultPack = arena->addTypePack({typeResult}, arena->freshTypePack(scope.get()));
|
|
|
|
addConstraint(scope, location, UnpackConstraint{resultPack, tp});
|
2022-07-01 00:52:43 +01:00
|
|
|
|
Sync to upstream/release/562 (#828)
* Fixed rare use-after-free in analysis during table unification
A lot of work these past months went into two new Luau components:
* A near full rewrite of the typechecker using a new deferred constraint
resolution system
* Native code generation for AoT/JiT compilation of VM bytecode into x64
(avx)/arm64 instructions
Both of these components are far from finished and we don't provide
documentation on building and using them at this point.
However, curious community members expressed interest in learning about
changes that go into these components each week, so we are now listing
them here in the 'sync' pull request descriptions.
---
New typechecker can be enabled by setting
DebugLuauDeferredConstraintResolution flag to 'true'.
It is considered unstable right now, so try it at your own risk.
Even though it already provides better type inference than the current
one in some cases, our main goal right now is to reach feature parity
with current typechecker.
Features which improve over the capabilities of the current typechecker
are marked as '(NEW)'.
Changes to new typechecker:
* Regular for loop index and parameters are now typechecked
* Invalid type annotations on local variables are ignored to improve
autocomplete
* Fixed missing autocomplete type suggestions for function arguments
* Type reduction is now performed to produce simpler types to be
presented to the user (error messages, custom LSPs)
* Internally, complex types like '((number | string) & ~(false?)) |
string' can be produced, which is just 'string | number' when simplified
* Fixed spots where support for unknown and never types was missing
* (NEW) Length operator '#' is now valid to use on top table type, this
type comes up when doing typeof(x) == "table" guards and isn't available
in current typechecker
---
Changes to native code generation:
* Additional math library fast calls are now lowered to x64: math.ldexp,
math.round, math.frexp, math.modf, math.sign and math.clamp
2023-02-03 19:26:13 +00:00
|
|
|
return Inference{typeResult, refinement};
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void ConstraintGraphBuilder::reportError(Location location, TypeErrorData err)
|
|
|
|
{
|
2023-04-21 23:14:26 +01:00
|
|
|
errors.push_back(TypeError{location, module->name, std::move(err)});
|
2022-09-08 23:14:25 +01:00
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
if (logger)
|
2022-09-08 23:14:25 +01:00
|
|
|
logger->captureGenerationError(errors.back());
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void ConstraintGraphBuilder::reportCodeTooComplex(Location location)
|
|
|
|
{
|
2023-04-21 23:14:26 +01:00
|
|
|
errors.push_back(TypeError{location, module->name, CodeTooComplex{}});
|
2022-09-08 23:14:25 +01:00
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
if (logger)
|
2022-09-08 23:14:25 +01:00
|
|
|
logger->captureGenerationError(errors.back());
|
2022-07-01 00:52:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
struct GlobalPrepopulator : AstVisitor
|
|
|
|
{
|
2022-07-29 05:24:07 +01:00
|
|
|
const NotNull<Scope> globalScope;
|
2022-07-01 00:52:43 +01:00
|
|
|
const NotNull<TypeArena> arena;
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
GlobalPrepopulator(NotNull<Scope> globalScope, NotNull<TypeArena> arena)
|
2022-07-01 00:52:43 +01:00
|
|
|
: globalScope(globalScope)
|
|
|
|
, arena(arena)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool visit(AstStatFunction* function) override
|
|
|
|
{
|
|
|
|
if (AstExprGlobal* g = function->name->as<AstExprGlobal>())
|
2023-01-04 20:53:17 +00:00
|
|
|
globalScope->bindings[g->name] = Binding{arena->addType(BlockedType{})};
|
2022-07-01 00:52:43 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-07-29 05:24:07 +01:00
|
|
|
void ConstraintGraphBuilder::prepopulateGlobalScope(const ScopePtr& globalScope, AstStatBlock* program)
|
2022-07-01 00:52:43 +01:00
|
|
|
{
|
2022-07-29 05:24:07 +01:00
|
|
|
GlobalPrepopulator gp{NotNull{globalScope.get()}, arena};
|
2022-07-01 00:52:43 +01:00
|
|
|
|
2023-04-28 20:55:13 +01:00
|
|
|
if (prepareModuleScope)
|
|
|
|
prepareModuleScope(module->name, globalScope);
|
|
|
|
|
2022-07-01 00:52:43 +01:00
|
|
|
program->visit(&gp);
|
|
|
|
}
|
|
|
|
|
2023-03-03 20:21:14 +00:00
|
|
|
std::vector<std::optional<TypeId>> ConstraintGraphBuilder::getExpectedCallTypesForFunctionOverloads(const TypeId fnType)
|
|
|
|
{
|
|
|
|
std::vector<TypeId> funTys;
|
|
|
|
if (auto it = get<IntersectionType>(follow(fnType)))
|
|
|
|
{
|
|
|
|
for (TypeId intersectionComponent : it)
|
|
|
|
{
|
|
|
|
funTys.push_back(intersectionComponent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::optional<TypeId>> expectedTypes;
|
|
|
|
// For a list of functions f_0 : e_0 -> r_0, ... f_n : e_n -> r_n,
|
|
|
|
// emit a list of arguments that the function could take at each position
|
|
|
|
// by unioning the arguments at each place
|
|
|
|
auto assignOption = [this, &expectedTypes](size_t index, TypeId ty) {
|
|
|
|
if (index == expectedTypes.size())
|
|
|
|
expectedTypes.push_back(ty);
|
|
|
|
else if (ty)
|
|
|
|
{
|
|
|
|
auto& el = expectedTypes[index];
|
|
|
|
|
|
|
|
if (!el)
|
|
|
|
el = ty;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::vector<TypeId> result = reduceUnion({*el, ty});
|
|
|
|
if (result.empty())
|
|
|
|
el = builtinTypes->neverType;
|
|
|
|
else if (result.size() == 1)
|
|
|
|
el = result[0];
|
|
|
|
else
|
|
|
|
el = module->internalTypes.addType(UnionType{std::move(result)});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
for (const TypeId overload : funTys)
|
|
|
|
{
|
|
|
|
if (const FunctionType* ftv = get<FunctionType>(follow(overload)))
|
|
|
|
{
|
|
|
|
auto [argsHead, argsTail] = flatten(ftv->argTypes);
|
|
|
|
size_t start = ftv->hasSelf ? 1 : 0;
|
|
|
|
size_t index = 0;
|
|
|
|
for (size_t i = start; i < argsHead.size(); ++i)
|
|
|
|
assignOption(index++, argsHead[i]);
|
|
|
|
if (argsTail)
|
|
|
|
{
|
|
|
|
argsTail = follow(*argsTail);
|
|
|
|
if (const VariadicTypePack* vtp = get<VariadicTypePack>(*argsTail))
|
|
|
|
{
|
|
|
|
while (index < funTys.size())
|
|
|
|
assignOption(index++, vtp->ty);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO vvijay Feb 24, 2023 apparently we have to demote the types here?
|
|
|
|
|
|
|
|
return expectedTypes;
|
|
|
|
}
|
|
|
|
|
2022-11-10 22:53:13 +00:00
|
|
|
std::vector<NotNull<Constraint>> borrowConstraints(const std::vector<ConstraintPtr>& constraints)
|
2022-06-03 23:15:45 +01:00
|
|
|
{
|
2022-11-10 22:53:13 +00:00
|
|
|
std::vector<NotNull<Constraint>> result;
|
|
|
|
result.reserve(constraints.size());
|
2022-06-03 23:15:45 +01:00
|
|
|
|
2022-11-10 22:53:13 +00:00
|
|
|
for (const auto& c : constraints)
|
|
|
|
result.emplace_back(c.get());
|
2022-06-03 23:15:45 +01:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Luau
|