mirror of
https://github.com/luau-lang/luau.git
synced 2024-12-13 13:30:40 +00:00
a36a3c41cc
### What's New? * Fragment Autocomplete: a new API allows for type checking a small fragment of code against an existing file, significantly speeding up autocomplete performance in large files. ### New Solver * E-Graphs have landed: this is an ongoing approach to make the new type solver simplify types in a more consistent and principled manner, based on similar work (see: https://egraphs-good.github.io/). * Adds support for exporting / local user type functions (previously they were always exported). * Fixes a set of bugs in which the new solver will fail to complete inference for simple expressions with just literals and operators. ### General Updates * Requiring a path with a ".lua" or ".luau" extension will now have a bespoke error suggesting to remove said extension. * Fixes a bug in which whether two `Luau::Symbol`s are equal depends on whether the new solver is enabled. --- Internal Contributors: Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: David Cope <dcope@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>
385 lines
7.7 KiB
C++
385 lines
7.7 KiB
C++
// 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... 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 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;
|
|
};
|
|
|
|
} // namespace Luau::EqSat
|