mirror of
https://github.com/luau-lang/luau.git
synced 2025-01-19 09:18:07 +00:00
74c532053f
New Solver * New algorithm for inferring the types of locals that have no annotations. This algorithm is very conservative by default, but is augmented with some control flow awareness to handle most common scenarios. * Fix bugs in type inference of tables * Improve performance of by switching out standard C++ containers for `DenseHashMap` * Infrastructure to support clearer error messages in strict mode Native Code Generation * Fix a lowering issue with buffer.writeu8 and 0x80-0xff values: A constant argument wasn't truncated to the target type range and that causes an assertion failure in `build.mov`. * Store full lightuserdata value in loop iteration protocol lowering * Add analysis to compute function bytecode distribution * This includes a class to analyze the bytecode operator distribution per function and a CLI tool that produces a JSON report. See the new cmake target `Luau.Bytecode.CLI` --------- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
231 lines
5.3 KiB
C++
231 lines
5.3 KiB
C++
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
|
|
#include "Luau/Scope.h"
|
|
|
|
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
|
|
|
|
namespace Luau
|
|
{
|
|
|
|
Scope::Scope(TypePackId returnType)
|
|
: parent(nullptr)
|
|
, returnType(returnType)
|
|
, level(TypeLevel())
|
|
{
|
|
}
|
|
|
|
Scope::Scope(const ScopePtr& parent, int subLevel)
|
|
: parent(parent)
|
|
, returnType(parent->returnType)
|
|
, level(parent->level.incr())
|
|
{
|
|
level = level.incr();
|
|
level.subLevel = subLevel;
|
|
}
|
|
|
|
void Scope::addBuiltinTypeBinding(const Name& name, const TypeFun& tyFun)
|
|
{
|
|
exportedTypeBindings[name] = tyFun;
|
|
builtinTypeNames.insert(name);
|
|
}
|
|
|
|
std::optional<TypeId> Scope::lookup(Symbol sym) const
|
|
{
|
|
auto r = const_cast<Scope*>(this)->lookupEx(sym);
|
|
if (r)
|
|
return r->first->typeId;
|
|
else
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<std::pair<TypeId, Scope*>> Scope::lookupEx(DefId def)
|
|
{
|
|
Scope* s = this;
|
|
|
|
while (true)
|
|
{
|
|
TypeId* it = s->lvalueTypes.find(def);
|
|
if (it)
|
|
return std::pair{*it, s};
|
|
|
|
if (s->parent)
|
|
s = s->parent.get();
|
|
else
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
std::optional<std::pair<Binding*, Scope*>> Scope::lookupEx(Symbol sym)
|
|
{
|
|
Scope* s = this;
|
|
|
|
while (true)
|
|
{
|
|
auto it = s->bindings.find(sym);
|
|
if (it != s->bindings.end())
|
|
return std::pair{&it->second, s};
|
|
|
|
if (s->parent)
|
|
s = s->parent.get();
|
|
else
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
std::optional<TypeId> Scope::lookupUnrefinedType(DefId def) const
|
|
{
|
|
for (const Scope* current = this; current; current = current->parent.get())
|
|
{
|
|
if (auto ty = current->lvalueTypes.find(def))
|
|
return *ty;
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<TypeId> Scope::lookup(DefId def) const
|
|
{
|
|
for (const Scope* current = this; current; current = current->parent.get())
|
|
{
|
|
if (auto ty = current->rvalueRefinements.find(def))
|
|
return *ty;
|
|
if (auto ty = current->lvalueTypes.find(def))
|
|
return *ty;
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<TypeFun> Scope::lookupType(const Name& name) const
|
|
{
|
|
const Scope* scope = this;
|
|
while (true)
|
|
{
|
|
auto it = scope->exportedTypeBindings.find(name);
|
|
if (it != scope->exportedTypeBindings.end())
|
|
return it->second;
|
|
|
|
it = scope->privateTypeBindings.find(name);
|
|
if (it != scope->privateTypeBindings.end())
|
|
return it->second;
|
|
|
|
if (scope->parent)
|
|
scope = scope->parent.get();
|
|
else
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
std::optional<TypeFun> Scope::lookupImportedType(const Name& moduleAlias, const Name& name) const
|
|
{
|
|
const Scope* scope = this;
|
|
while (scope)
|
|
{
|
|
auto it = scope->importedTypeBindings.find(moduleAlias);
|
|
if (it == scope->importedTypeBindings.end())
|
|
{
|
|
scope = scope->parent.get();
|
|
continue;
|
|
}
|
|
|
|
auto it2 = it->second.find(name);
|
|
if (it2 == it->second.end())
|
|
{
|
|
scope = scope->parent.get();
|
|
continue;
|
|
}
|
|
|
|
return it2->second;
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<TypePackId> Scope::lookupPack(const Name& name) const
|
|
{
|
|
const Scope* scope = this;
|
|
while (true)
|
|
{
|
|
auto it = scope->privateTypePackBindings.find(name);
|
|
if (it != scope->privateTypePackBindings.end())
|
|
return it->second;
|
|
|
|
if (scope->parent)
|
|
scope = scope->parent.get();
|
|
else
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
std::optional<Binding> Scope::linearSearchForBinding(const std::string& name, bool traverseScopeChain) const
|
|
{
|
|
const Scope* scope = this;
|
|
|
|
while (scope)
|
|
{
|
|
for (const auto& [n, binding] : scope->bindings)
|
|
{
|
|
if (n.local && n.local->name == name.c_str())
|
|
return binding;
|
|
else if (n.global.value && n.global == name.c_str())
|
|
return binding;
|
|
}
|
|
|
|
scope = scope->parent.get();
|
|
|
|
if (!traverseScopeChain)
|
|
break;
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
// Updates the `this` scope with the assignments from the `childScope` including ones that doesn't exist in `this`.
|
|
void Scope::inheritAssignments(const ScopePtr& childScope)
|
|
{
|
|
if (!FFlag::DebugLuauDeferredConstraintResolution)
|
|
return;
|
|
|
|
for (const auto& [k, a] : childScope->lvalueTypes)
|
|
lvalueTypes[k] = a;
|
|
}
|
|
|
|
// Updates the `this` scope with the refinements from the `childScope` excluding ones that doesn't exist in `this`.
|
|
void Scope::inheritRefinements(const ScopePtr& childScope)
|
|
{
|
|
if (FFlag::DebugLuauDeferredConstraintResolution)
|
|
{
|
|
for (const auto& [k, a] : childScope->rvalueRefinements)
|
|
{
|
|
if (lookup(NotNull{k}))
|
|
rvalueRefinements[k] = a;
|
|
}
|
|
}
|
|
|
|
for (const auto& [k, a] : childScope->refinements)
|
|
{
|
|
Symbol symbol = getBaseSymbol(k);
|
|
if (lookup(symbol))
|
|
refinements[k] = a;
|
|
}
|
|
}
|
|
|
|
bool subsumesStrict(Scope* left, Scope* right)
|
|
{
|
|
while (right)
|
|
{
|
|
if (right->parent.get() == left)
|
|
return true;
|
|
|
|
right = right->parent.get();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool subsumes(Scope* left, Scope* right)
|
|
{
|
|
return left == right || subsumesStrict(left, right);
|
|
}
|
|
|
|
} // namespace Luau
|