mirror of
https://github.com/luau-lang/luau.git
synced 2024-12-13 21:40:43 +00:00
fd6250cf9d
### What's Changed - Improve readability of unions and intersections by limiting the number of elements of those types that can be presented on a single line (gated under `FFlag::LuauToStringSimpleCompositeTypesSingleLine`) - Adds a new option to the compiler `--record-stats` to record and output compilation statistics - `if...then...else` expressions are now optimized into `AND/OR` form when possible. ### VM - Add a new `buffer` type to Luau based on the [buffer RFC](https://github.com/Roblox/luau/pull/739) and additional C API functions to work with it; this release does not include the library. - Internal C API to work with string buffers has been updated to align with Lua version more closely ### Native Codegen - Added support for new X64 instruction (rev) and new A64 instruction (bswap) in the assembler - Simplified the way numerical loop condition is translated to IR ### New Type Solver - Operator inference now handled by type families - Created a new system called `Type Paths` to explain why subtyping tests fail in order to improve the quality of error messages. - Systematic changes to implement Data Flow analysis in the new solver (`Breadcrumb` removed and replaced with `RefinementKey`) --- 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: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com>
293 lines
8.3 KiB
C++
293 lines
8.3 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 <initializer_list>
|
|
#include <new>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
#include <stddef.h>
|
|
|
|
namespace Luau
|
|
{
|
|
|
|
template<typename... Ts>
|
|
class Variant
|
|
{
|
|
static_assert(sizeof...(Ts) > 0, "variant must have at least 1 type (empty variants are ill-formed)");
|
|
static_assert(std::disjunction_v<std::is_void<Ts>...> == false, "variant does not allow void as an alternative type");
|
|
static_assert(std::disjunction_v<std::is_reference<Ts>...> == false, "variant does not allow references as an alternative type");
|
|
static_assert(std::disjunction_v<std::is_array<Ts>...> == false, "variant does not allow arrays as an alternative type");
|
|
|
|
private:
|
|
template<typename T>
|
|
static constexpr int getTypeId()
|
|
{
|
|
using TT = std::decay_t<T>;
|
|
|
|
constexpr int N = sizeof...(Ts);
|
|
constexpr bool is[N] = {std::is_same_v<TT, Ts>...};
|
|
|
|
for (int i = 0; i < N; ++i)
|
|
if (is[i])
|
|
return i;
|
|
|
|
return -1;
|
|
}
|
|
|
|
template<typename T, typename... Tail>
|
|
struct First
|
|
{
|
|
using type = T;
|
|
};
|
|
|
|
public:
|
|
using first_alternative = typename First<Ts...>::type;
|
|
|
|
template<typename T>
|
|
static constexpr bool is_part_of_v = std::disjunction_v<typename std::is_same<std::decay_t<Ts>, T>...>;
|
|
|
|
Variant()
|
|
{
|
|
static_assert(std::is_default_constructible_v<first_alternative>, "first alternative type must be default constructible");
|
|
typeId = 0;
|
|
new (&storage) first_alternative();
|
|
}
|
|
|
|
template<typename T>
|
|
Variant(T&& value, std::enable_if_t<getTypeId<T>() >= 0>* = 0)
|
|
{
|
|
using TT = std::decay_t<T>;
|
|
|
|
constexpr int tid = getTypeId<T>();
|
|
typeId = tid;
|
|
new (&storage) TT(std::forward<T>(value));
|
|
}
|
|
|
|
Variant(const Variant& other)
|
|
{
|
|
static constexpr FnCopy table[sizeof...(Ts)] = {&fnCopy<Ts>...};
|
|
|
|
typeId = other.typeId;
|
|
table[typeId](&storage, &other.storage);
|
|
}
|
|
|
|
Variant(Variant&& other)
|
|
{
|
|
typeId = other.typeId;
|
|
tableMove[typeId](&storage, &other.storage);
|
|
}
|
|
|
|
~Variant()
|
|
{
|
|
tableDtor[typeId](&storage);
|
|
}
|
|
|
|
Variant& operator=(const Variant& other)
|
|
{
|
|
Variant copy(other);
|
|
// static_cast<T&&> is equivalent to std::move() but faster in Debug
|
|
return *this = static_cast<Variant&&>(copy);
|
|
}
|
|
|
|
Variant& operator=(Variant&& other)
|
|
{
|
|
if (this != &other)
|
|
{
|
|
tableDtor[typeId](&storage);
|
|
typeId = other.typeId;
|
|
tableMove[typeId](&storage, &other.storage); // nothrow
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template<typename T, typename... Args>
|
|
T& emplace(Args&&... args)
|
|
{
|
|
using TT = std::decay_t<T>;
|
|
constexpr int tid = getTypeId<T>();
|
|
static_assert(tid >= 0, "unsupported T");
|
|
|
|
tableDtor[typeId](&storage);
|
|
typeId = tid;
|
|
new (&storage) TT{std::forward<Args>(args)...};
|
|
|
|
return *reinterpret_cast<T*>(&storage);
|
|
}
|
|
|
|
template<typename T>
|
|
const T* get_if() const
|
|
{
|
|
constexpr int tid = getTypeId<T>();
|
|
static_assert(tid >= 0, "unsupported T");
|
|
|
|
return tid == typeId ? reinterpret_cast<const T*>(&storage) : nullptr;
|
|
}
|
|
|
|
template<typename T>
|
|
T* get_if()
|
|
{
|
|
constexpr int tid = getTypeId<T>();
|
|
static_assert(tid >= 0, "unsupported T");
|
|
|
|
return tid == typeId ? reinterpret_cast<T*>(&storage) : nullptr;
|
|
}
|
|
|
|
bool valueless_by_exception() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int index() const
|
|
{
|
|
return typeId;
|
|
}
|
|
|
|
bool operator==(const Variant& other) const
|
|
{
|
|
static constexpr FnPred table[sizeof...(Ts)] = {&fnPredEq<Ts>...};
|
|
|
|
return typeId == other.typeId && table[typeId](&storage, &other.storage);
|
|
}
|
|
|
|
bool operator!=(const Variant& other) const
|
|
{
|
|
return !(*this == other);
|
|
}
|
|
|
|
private:
|
|
static constexpr size_t cmax(std::initializer_list<size_t> l)
|
|
{
|
|
size_t res = 0;
|
|
for (size_t i : l)
|
|
res = (res < i) ? i : res;
|
|
return res;
|
|
}
|
|
|
|
static constexpr size_t storageSize = cmax({sizeof(Ts)...});
|
|
static constexpr size_t storageAlign = cmax({alignof(Ts)...});
|
|
|
|
using FnCopy = void (*)(void*, const void*);
|
|
using FnMove = void (*)(void*, void*);
|
|
using FnDtor = void (*)(void*);
|
|
using FnPred = bool (*)(const void*, const void*);
|
|
|
|
template<typename T>
|
|
static void fnCopy(void* dst, const void* src)
|
|
{
|
|
new (dst) T(*static_cast<const T*>(src));
|
|
}
|
|
|
|
template<typename T>
|
|
static void fnMove(void* dst, void* src)
|
|
{
|
|
// static_cast<T&&> is equivalent to std::move() but faster in Debug
|
|
new (dst) T(static_cast<T&&>(*static_cast<T*>(src)));
|
|
}
|
|
|
|
template<typename T>
|
|
static void fnDtor(void* dst)
|
|
{
|
|
static_cast<T*>(dst)->~T();
|
|
}
|
|
|
|
template<typename T>
|
|
static bool fnPredEq(const void* lhs, const void* rhs)
|
|
{
|
|
return *static_cast<const T*>(lhs) == *static_cast<const T*>(rhs);
|
|
}
|
|
|
|
static constexpr FnMove tableMove[sizeof...(Ts)] = {&fnMove<Ts>...};
|
|
static constexpr FnDtor tableDtor[sizeof...(Ts)] = {&fnDtor<Ts>...};
|
|
|
|
int typeId;
|
|
alignas(storageAlign) char storage[storageSize];
|
|
|
|
template<class Visitor, typename... _Ts>
|
|
friend auto visit(Visitor&& vis, const Variant<_Ts...>& var);
|
|
template<class Visitor, typename... _Ts>
|
|
friend auto visit(Visitor&& vis, Variant<_Ts...>& var);
|
|
};
|
|
|
|
template<typename T, typename... Ts>
|
|
const T* get_if(const Variant<Ts...>* var)
|
|
{
|
|
return var ? var->template get_if<T>() : nullptr;
|
|
}
|
|
|
|
template<typename T, typename... Ts>
|
|
T* get_if(Variant<Ts...>* var)
|
|
{
|
|
return var ? var->template get_if<T>() : nullptr;
|
|
}
|
|
|
|
template<typename Visitor, typename Result, typename T>
|
|
static void fnVisitR(Visitor& vis, Result& dst, std::conditional_t<std::is_const_v<T>, const void, void>* src)
|
|
{
|
|
dst = vis(*static_cast<T*>(src));
|
|
}
|
|
|
|
template<typename Visitor, typename T>
|
|
static void fnVisitV(Visitor& vis, std::conditional_t<std::is_const_v<T>, const void, void>* src)
|
|
{
|
|
vis(*static_cast<T*>(src));
|
|
}
|
|
|
|
template<class Visitor, typename... Ts>
|
|
auto visit(Visitor&& vis, const Variant<Ts...>& var)
|
|
{
|
|
static_assert(std::conjunction_v<std::is_invocable<Visitor, Ts>...>, "visitor must accept every alternative as an argument");
|
|
|
|
using Result = std::invoke_result_t<Visitor, typename Variant<Ts...>::first_alternative>;
|
|
static_assert(std::conjunction_v<std::is_same<Result, std::invoke_result_t<Visitor, Ts>>...>,
|
|
"visitor result type must be consistent between alternatives");
|
|
|
|
if constexpr (std::is_same_v<Result, void>)
|
|
{
|
|
using FnVisitV = void (*)(Visitor&, const void*);
|
|
static const FnVisitV tableVisit[sizeof...(Ts)] = {&fnVisitV<Visitor, const Ts>...};
|
|
|
|
tableVisit[var.typeId](vis, &var.storage);
|
|
}
|
|
else
|
|
{
|
|
using FnVisitR = void (*)(Visitor&, Result&, const void*);
|
|
static const FnVisitR tableVisit[sizeof...(Ts)] = {&fnVisitR<Visitor, Result, const Ts>...};
|
|
|
|
Result res;
|
|
tableVisit[var.typeId](vis, res, &var.storage);
|
|
return res;
|
|
}
|
|
}
|
|
|
|
template<class Visitor, typename... Ts>
|
|
auto visit(Visitor&& vis, Variant<Ts...>& var)
|
|
{
|
|
static_assert(std::conjunction_v<std::is_invocable<Visitor, Ts&>...>, "visitor must accept every alternative as an argument");
|
|
|
|
using Result = std::invoke_result_t<Visitor, typename Variant<Ts...>::first_alternative&>;
|
|
static_assert(std::conjunction_v<std::is_same<Result, std::invoke_result_t<Visitor, Ts&>>...>,
|
|
"visitor result type must be consistent between alternatives");
|
|
|
|
if constexpr (std::is_same_v<Result, void>)
|
|
{
|
|
using FnVisitV = void (*)(Visitor&, void*);
|
|
static const FnVisitV tableVisit[sizeof...(Ts)] = {&fnVisitV<Visitor, Ts>...};
|
|
|
|
tableVisit[var.typeId](vis, &var.storage);
|
|
}
|
|
else
|
|
{
|
|
using FnVisitR = void (*)(Visitor&, Result&, void*);
|
|
static const FnVisitR tableVisit[sizeof...(Ts)] = {&fnVisitR<Visitor, Result, Ts>...};
|
|
|
|
Result res;
|
|
tableVisit[var.typeId](vis, res, &var.storage);
|
|
return res;
|
|
}
|
|
}
|
|
|
|
template<class>
|
|
inline constexpr bool always_false_v = false;
|
|
|
|
} // namespace Luau
|