luau/EqSat/include/Luau/Language.h
Vighnesh 906a00d498 Sync to upstream/release/655
* General
- Fix the benchmark require wrapper function to work in Lua
- Fix memory leak in the new Luau C API test

* New Solver
- Luau: 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 should be reported 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 Luau used-defined type functions not having all environments initialized
- Infer types of globals under new type solver

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

* Runtime
- Support disabling specific Luau built-in functions from being
fast-called or constant-evaluated
- Added constant folding for vector arithmetic
- Added constant propagation and type inference for Vector3 globals

----------------------------------------------------------
9 contributors:

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>
2024-12-13 11:20:43 -08:00

424 lines
8.8 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Id.h"
#include "Luau/LanguageHash.h"
#include "Luau/Slice.h"
#include "Luau/Variant.h"
#include <algorithm>
#include <array>
#include <type_traits>
#include <unordered_set>
#include <utility>
#include <vector>
#define LUAU_EQSAT_UNIT(name) \
struct name : ::Luau::EqSat::Unit<name> \
{ \
static constexpr const char* tag = #name; \
using Unit::Unit; \
}
#define LUAU_EQSAT_ATOM(name, t) \
struct name : public ::Luau::EqSat::Atom<name, t> \
{ \
static constexpr const char* tag = #name; \
using Atom::Atom; \
}
#define LUAU_EQSAT_NODE_ARRAY(name, ops) \
struct name : public ::Luau::EqSat::NodeVector<name, std::array<::Luau::EqSat::Id, ops>> \
{ \
static constexpr const char* tag = #name; \
using NodeVector::NodeVector; \
}
#define LUAU_EQSAT_NODE_VECTOR(name) \
struct name : public ::Luau::EqSat::NodeVector<name, std::vector<::Luau::EqSat::Id>> \
{ \
static constexpr const char* tag = #name; \
using NodeVector::NodeVector; \
}
#define LUAU_EQSAT_NODE_SET(name) \
struct name : public ::Luau::EqSat::NodeSet<name, std::vector<::Luau::EqSat::Id>> \
{ \
static constexpr const char* tag = #name; \
using NodeSet::NodeSet; \
}
#define LUAU_EQSAT_NODE_ATOM_WITH_VECTOR(name, t) \
struct name : public ::Luau::EqSat::NodeAtomAndVector<name, t, std::vector<::Luau::EqSat::Id>> \
{ \
static constexpr const char* tag = #name; \
using NodeAtomAndVector::NodeAtomAndVector; \
}
namespace Luau::EqSat
{
template<typename Phantom>
struct Unit
{
Slice<Id> mutableOperands()
{
return {};
}
Slice<const Id> operands() const
{
return {};
}
bool operator==(const Unit& rhs) const
{
return true;
}
bool operator!=(const Unit& rhs) const
{
return false;
}
struct Hash
{
size_t operator()(const Unit& value) const
{
// chosen by fair dice roll.
// guaranteed to be random.
return 4;
}
};
};
template<typename Phantom, typename T>
struct Atom
{
Atom(const T& value)
: _value(value)
{
}
const T& value() const
{
return _value;
}
public:
Slice<Id> mutableOperands()
{
return {};
}
Slice<const Id> operands() const
{
return {};
}
bool operator==(const Atom& rhs) const
{
return _value == rhs._value;
}
bool operator!=(const Atom& rhs) const
{
return !(*this == rhs);
}
struct Hash
{
size_t operator()(const Atom& value) const
{
return languageHash(value._value);
}
};
private:
T _value;
};
template<typename Phantom, typename X, typename T>
struct NodeAtomAndVector
{
template<typename... Args>
NodeAtomAndVector(const X& value, Args&&... args)
: _value(value)
, vector{std::forward<Args>(args)...}
{
}
Id operator[](size_t i) const
{
return vector[i];
}
public:
const X& value() const
{
return _value;
}
Slice<Id> mutableOperands()
{
return Slice{vector.data(), vector.size()};
}
Slice<const Id> operands() const
{
return Slice{vector.data(), vector.size()};
}
bool operator==(const NodeAtomAndVector& rhs) const
{
return _value == rhs._value && vector == rhs.vector;
}
bool operator!=(const NodeAtomAndVector& rhs) const
{
return !(*this == rhs);
}
struct Hash
{
size_t operator()(const NodeAtomAndVector& value) const
{
size_t result = languageHash(value._value);
hashCombine(result, languageHash(value.vector));
return result;
}
};
private:
X _value;
T vector;
};
template<typename Phantom, typename T>
struct NodeVector
{
template<typename... Args>
NodeVector(Args&&... args)
: vector{std::forward<Args>(args)...}
{
}
Id operator[](size_t i) const
{
return vector[i];
}
public:
Slice<Id> mutableOperands()
{
return Slice{vector.data(), vector.size()};
}
Slice<const Id> operands() const
{
return Slice{vector.data(), vector.size()};
}
bool operator==(const NodeVector& rhs) const
{
return vector == rhs.vector;
}
bool operator!=(const NodeVector& rhs) const
{
return !(*this == rhs);
}
struct Hash
{
size_t operator()(const NodeVector& value) const
{
return languageHash(value.vector);
}
};
private:
T vector;
};
template<typename Phantom, typename T>
struct NodeSet
{
template <typename P_, typename T_, typename Find>
friend void canonicalize(NodeSet<P_, T_>& node, Find&& find);
template<typename... Args>
NodeSet(Args&&... args)
: vector{std::forward<Args>(args)...}
{
std::sort(begin(vector), end(vector));
auto it = std::unique(begin(vector), end(vector));
vector.erase(it, end(vector));
}
Id operator[](size_t i) const
{
return vector[i];
}
public:
Slice<Id> mutableOperands()
{
return Slice{vector.data(), vector.size()};
}
Slice<const Id> operands() const
{
return Slice{vector.data(), vector.size()};
}
bool operator==(const NodeSet& rhs) const
{
return vector == rhs.vector;
}
bool operator!=(const NodeSet& rhs) const
{
return !(*this == rhs);
}
struct Hash
{
size_t operator()(const NodeSet& value) const
{
return languageHash(value.vector);
}
};
protected:
T vector;
};
template<typename... Ts>
struct Language final
{
using VariantTy = Luau::Variant<Ts...>;
template<typename T>
using WithinDomain = std::disjunction<std::is_same<std::decay_t<T>, Ts>...>;
template <typename Find, typename... Vs>
friend void canonicalize(Language<Vs...>& enode, Find&& find);
template<typename T>
Language(T&& t, std::enable_if_t<WithinDomain<T>::value>* = 0) noexcept
: v(std::forward<T>(t))
{
}
int index() const noexcept
{
return v.index();
}
/// This should only be used in canonicalization!
/// Always prefer operands()
Slice<Id> mutableOperands() noexcept
{
return visit(
[](auto&& v) -> Slice<Id>
{
return v.mutableOperands();
},
v
);
}
Slice<const Id> operands() const noexcept
{
return visit(
[](auto&& v) -> Slice<const Id>
{
return v.operands();
},
v
);
}
template<typename T>
T* get() noexcept
{
static_assert(WithinDomain<T>::value);
return v.template get_if<T>();
}
template<typename T>
const T* get() const noexcept
{
static_assert(WithinDomain<T>::value);
return v.template get_if<T>();
}
bool operator==(const Language& rhs) const noexcept
{
return v == rhs.v;
}
bool operator!=(const Language& rhs) const noexcept
{
return !(*this == rhs);
}
public:
struct Hash
{
size_t operator()(const Language& language) const
{
size_t seed = std::hash<int>{}(language.index());
hashCombine(
seed,
visit(
[](auto&& v)
{
return typename std::decay_t<decltype(v)>::Hash{}(v);
},
language.v
)
);
return seed;
}
};
private:
VariantTy v;
};
template <typename Node, typename Find>
void canonicalize(Node& node, Find&& find)
{
// An e-node 𝑛 is canonical iff 𝑛 = canonicalize(𝑛), where
// canonicalize(𝑓(𝑎1, 𝑎2, ...)) = 𝑓(find(𝑎1), find(𝑎2), ...).
for (Id& id : node.mutableOperands())
id = find(id);
}
// Canonicalizing the Ids in a NodeSet may result in the set decreasing in size.
template <typename Phantom, typename T, typename Find>
void canonicalize(NodeSet<Phantom, T>& node, Find&& find)
{
for (Id& id : node.vector)
id = find(id);
std::sort(begin(node.vector), end(node.vector));
auto endIt = std::unique(begin(node.vector), end(node.vector));
node.vector.erase(endIt, end(node.vector));
}
template <typename Find, typename... Vs>
void canonicalize(Language<Vs...>& enode, Find&& find)
{
visit(
[&](auto&& v)
{
Luau::EqSat::canonicalize(v, find);
},
enode.v
);
}
} // namespace Luau::EqSat