luau/Analysis/src/Scope.cpp
Vighnesh-V 2e6fdd90a0
Some checks failed
benchmark / callgrind (map[branch:main name:luau-lang/benchmark-data], ubuntu-22.04) (push) Has been cancelled
build / macos (push) Has been cancelled
build / macos-arm (push) Has been cancelled
build / ubuntu (push) Has been cancelled
build / windows (Win32) (push) Has been cancelled
build / windows (x64) (push) Has been cancelled
build / coverage (push) Has been cancelled
build / web (push) Has been cancelled
release / macos (push) Has been cancelled
release / ubuntu (push) Has been cancelled
release / windows (push) Has been cancelled
release / web (push) Has been cancelled
Sync to upstream/release/655 (#1563)
## New Solver
* Type functions should be able to signal whether or not irreducibility
is due to an error
* Do not generate extra expansion constraint for uninvoked user-defined
type functions
* Print in a user-defined type function reports as an error instead of
logging to stdout
* Many e-graphs bugfixes and performance improvements
* Many general bugfixes and improvements to the new solver as a whole
* Fixed issue with used-defined type functions not being able to call
each other
* Infer types of globals under new type solver

## Fragment Autocomplete
* Miscellaneous fixes to make interop with the old solver better

## Runtime
* Support disabling specific built-in functions from being fast-called
or constant-evaluated (Closes #1538)
* New compiler option `disabledBuiltins` accepts a list of library
function names like "tonumber" or "math.cos"
* Added constant folding for vector arithmetic
* Added constant propagation and type inference for vector globals
(Fixes #1511)
* New compiler option `librariesWithKnownMembers` accepts a list of
libraries for members of which a request for constant value and/or type
will be made
* `libraryMemberTypeCb` callback is called to get the type of a global,
return one of the `LuauBytecodeType` values. 'boolean', 'number',
'string' and 'vector' type are supported.
* `libraryMemberConstantCb` callback is called to setup the constant
value of a global. To set a value, C API `luau_set_compile_constant_*`
or C++ API `setCompileConstant*` functions should be used.

---
Co-authored-by: Aaron Weiss <aaronweiss@roblox.com>
Co-authored-by: Andy Friesen <afriesen@roblox.com>
Co-authored-by: Aviral Goel <agoel@roblox.com>
Co-authored-by: Daniel Angel <danielangel@roblox.com>
Co-authored-by: Jonathan Kelaty <jkelaty@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Vighnesh Vijay <vvijay@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>

---------

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: David Cope <dcope@roblox.com>
Co-authored-by: Lily Brown <lbrown@roblox.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
Co-authored-by: Junseo Yoo <jyoo@roblox.com>
Co-authored-by: Hunter Goldstein <hgoldstein@roblox.com>
Co-authored-by: Varun Saini <61795485+vrn-sn@users.noreply.github.com>
Co-authored-by: Alexander Youngblood <ayoungblood@roblox.com>
Co-authored-by: Varun Saini <vsaini@roblox.com>
Co-authored-by: Andrew Miranti <amiranti@roblox.com>
Co-authored-by: Shiqi Ai <sai@roblox.com>
Co-authored-by: Yohoo Lin <yohoo@roblox.com>
Co-authored-by: Daniel Angel <danielangel@roblox.com>
Co-authored-by: Jonathan Kelaty <jkelaty@roblox.com>
2024-12-13 13:02:30 -08:00

242 lines
5.6 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(LuauSolverV2);
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)
{
if (TypeId* it = s->lvalueTypes.find(def))
return std::pair{*it, s};
else if (TypeId* it = s->rvalueRefinements.find(def))
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::LuauSolverV2)
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::LuauSolverV2)
{
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 Scope::shouldWarnGlobal(std::string name) const
{
for (const Scope* current = this; current; current = current->parent.get())
{
if (current->globalsToWarn.contains(name))
return true;
}
return false;
}
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