mirror of
https://github.com/luau-lang/luau.git
synced 2025-01-19 17:28:06 +00:00
Merge branch 'master' into merge
This commit is contained in:
commit
79966e94a7
32 changed files with 1316 additions and 61 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -14,3 +14,5 @@
|
||||||
/luau-analyze
|
/luau-analyze
|
||||||
/luau-compile
|
/luau-compile
|
||||||
__pycache__
|
__pycache__
|
||||||
|
.cache
|
||||||
|
.clangd
|
||||||
|
|
|
@ -499,10 +499,11 @@ struct ClassType
|
||||||
Tags tags;
|
Tags tags;
|
||||||
std::shared_ptr<ClassUserData> userData;
|
std::shared_ptr<ClassUserData> userData;
|
||||||
ModuleName definitionModuleName;
|
ModuleName definitionModuleName;
|
||||||
|
std::optional<Location> definitionLocation;
|
||||||
std::optional<TableIndexer> indexer;
|
std::optional<TableIndexer> indexer;
|
||||||
|
|
||||||
ClassType(Name name, Props props, std::optional<TypeId> parent, std::optional<TypeId> metatable, Tags tags,
|
ClassType(Name name, Props props, std::optional<TypeId> parent, std::optional<TypeId> metatable, Tags tags,
|
||||||
std::shared_ptr<ClassUserData> userData, ModuleName definitionModuleName)
|
std::shared_ptr<ClassUserData> userData, ModuleName definitionModuleName, std::optional<Location> definitionLocation)
|
||||||
: name(name)
|
: name(name)
|
||||||
, props(props)
|
, props(props)
|
||||||
, parent(parent)
|
, parent(parent)
|
||||||
|
@ -510,11 +511,13 @@ struct ClassType
|
||||||
, tags(tags)
|
, tags(tags)
|
||||||
, userData(userData)
|
, userData(userData)
|
||||||
, definitionModuleName(definitionModuleName)
|
, definitionModuleName(definitionModuleName)
|
||||||
|
, definitionLocation(definitionLocation)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassType(Name name, Props props, std::optional<TypeId> parent, std::optional<TypeId> metatable, Tags tags,
|
ClassType(Name name, Props props, std::optional<TypeId> parent, std::optional<TypeId> metatable, Tags tags,
|
||||||
std::shared_ptr<ClassUserData> userData, ModuleName definitionModuleName, std::optional<TableIndexer> indexer)
|
std::shared_ptr<ClassUserData> userData, ModuleName definitionModuleName, std::optional<Location> definitionLocation,
|
||||||
|
std::optional<TableIndexer> indexer)
|
||||||
: name(name)
|
: name(name)
|
||||||
, props(props)
|
, props(props)
|
||||||
, parent(parent)
|
, parent(parent)
|
||||||
|
@ -522,6 +525,7 @@ struct ClassType
|
||||||
, tags(tags)
|
, tags(tags)
|
||||||
, userData(userData)
|
, userData(userData)
|
||||||
, definitionModuleName(definitionModuleName)
|
, definitionModuleName(definitionModuleName)
|
||||||
|
, definitionLocation(definitionLocation)
|
||||||
, indexer(indexer)
|
, indexer(indexer)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -1349,7 +1349,7 @@ ControlFlow ConstraintGenerator::visit(const ScopePtr& scope, AstStatDeclareClas
|
||||||
|
|
||||||
Name className(declaredClass->name.value);
|
Name className(declaredClass->name.value);
|
||||||
|
|
||||||
TypeId classTy = arena->addType(ClassType(className, {}, superTy, std::nullopt, {}, {}, module->name));
|
TypeId classTy = arena->addType(ClassType(className, {}, superTy, std::nullopt, {}, {}, module->name, declaredClass->location));
|
||||||
ClassType* ctv = getMutable<ClassType>(classTy);
|
ClassType* ctv = getMutable<ClassType>(classTy);
|
||||||
|
|
||||||
TypeId metaTy = arena->addType(TableType{TableState::Sealed, scope->level, scope.get()});
|
TypeId metaTy = arena->addType(TableType{TableState::Sealed, scope->level, scope.get()});
|
||||||
|
|
|
@ -117,7 +117,7 @@ static TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log, bool a
|
||||||
{
|
{
|
||||||
if (alwaysClone)
|
if (alwaysClone)
|
||||||
{
|
{
|
||||||
ClassType clone{a.name, a.props, a.parent, a.metatable, a.tags, a.userData, a.definitionModuleName, a.indexer};
|
ClassType clone{a.name, a.props, a.parent, a.metatable, a.tags, a.userData, a.definitionModuleName, a.definitionLocation, a.indexer};
|
||||||
return dest.addType(std::move(clone));
|
return dest.addType(std::move(clone));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -969,7 +969,7 @@ BuiltinTypes::BuiltinTypes()
|
||||||
, threadType(arena->addType(Type{PrimitiveType{PrimitiveType::Thread}, /*persistent*/ true}))
|
, threadType(arena->addType(Type{PrimitiveType{PrimitiveType::Thread}, /*persistent*/ true}))
|
||||||
, bufferType(arena->addType(Type{PrimitiveType{PrimitiveType::Buffer}, /*persistent*/ true}))
|
, bufferType(arena->addType(Type{PrimitiveType{PrimitiveType::Buffer}, /*persistent*/ true}))
|
||||||
, functionType(arena->addType(Type{PrimitiveType{PrimitiveType::Function}, /*persistent*/ true}))
|
, functionType(arena->addType(Type{PrimitiveType{PrimitiveType::Function}, /*persistent*/ true}))
|
||||||
, classType(arena->addType(Type{ClassType{"class", {}, std::nullopt, std::nullopt, {}, {}, {}}, /*persistent*/ true}))
|
, classType(arena->addType(Type{ClassType{"class", {}, std::nullopt, std::nullopt, {}, {}, {}, {}}, /*persistent*/ true}))
|
||||||
, tableType(arena->addType(Type{PrimitiveType{PrimitiveType::Table}, /*persistent*/ true}))
|
, tableType(arena->addType(Type{PrimitiveType{PrimitiveType::Table}, /*persistent*/ true}))
|
||||||
, emptyTableType(arena->addType(Type{TableType{TableState::Sealed, TypeLevel{}, nullptr}, /*persistent*/ true}))
|
, emptyTableType(arena->addType(Type{TableType{TableState::Sealed, TypeLevel{}, nullptr}, /*persistent*/ true}))
|
||||||
, trueType(arena->addType(Type{SingletonType{BooleanSingleton{true}}, /*persistent*/ true}))
|
, trueType(arena->addType(Type{SingletonType{BooleanSingleton{true}}, /*persistent*/ true}))
|
||||||
|
|
|
@ -1733,7 +1733,7 @@ void TypeChecker::prototype(const ScopePtr& scope, const AstStatDeclareClass& de
|
||||||
|
|
||||||
Name className(declaredClass.name.value);
|
Name className(declaredClass.name.value);
|
||||||
|
|
||||||
TypeId classTy = addType(ClassType(className, {}, superTy, std::nullopt, {}, {}, currentModule->name));
|
TypeId classTy = addType(ClassType(className, {}, superTy, std::nullopt, {}, {}, currentModule->name, declaredClass.location));
|
||||||
ClassType* ctv = getMutable<ClassType>(classTy);
|
ClassType* ctv = getMutable<ClassType>(classTy);
|
||||||
TypeId metaTy = addType(TableType{TableState::Sealed, scope->level});
|
TypeId metaTy = addType(TableType{TableState::Sealed, scope->level});
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ add_library(Luau.Ast STATIC)
|
||||||
add_library(Luau.Compiler STATIC)
|
add_library(Luau.Compiler STATIC)
|
||||||
add_library(Luau.Config STATIC)
|
add_library(Luau.Config STATIC)
|
||||||
add_library(Luau.Analysis STATIC)
|
add_library(Luau.Analysis STATIC)
|
||||||
|
add_library(Luau.EqSat STATIC)
|
||||||
add_library(Luau.CodeGen STATIC)
|
add_library(Luau.CodeGen STATIC)
|
||||||
add_library(Luau.VM STATIC)
|
add_library(Luau.VM STATIC)
|
||||||
add_library(isocline STATIC)
|
add_library(isocline STATIC)
|
||||||
|
@ -83,7 +84,11 @@ target_link_libraries(Luau.Config PUBLIC Luau.Ast)
|
||||||
|
|
||||||
target_compile_features(Luau.Analysis PUBLIC cxx_std_17)
|
target_compile_features(Luau.Analysis PUBLIC cxx_std_17)
|
||||||
target_include_directories(Luau.Analysis PUBLIC Analysis/include)
|
target_include_directories(Luau.Analysis PUBLIC Analysis/include)
|
||||||
target_link_libraries(Luau.Analysis PUBLIC Luau.Ast Luau.Config)
|
target_link_libraries(Luau.Analysis PUBLIC Luau.Ast Luau.EqSat Luau.Config)
|
||||||
|
|
||||||
|
target_compile_features(Luau.EqSat PUBLIC cxx_std_17)
|
||||||
|
target_include_directories(Luau.EqSat PUBLIC EqSat/include)
|
||||||
|
target_link_libraries(Luau.EqSat PUBLIC Luau.Common)
|
||||||
|
|
||||||
target_compile_features(Luau.CodeGen PRIVATE cxx_std_17)
|
target_compile_features(Luau.CodeGen PRIVATE cxx_std_17)
|
||||||
target_include_directories(Luau.CodeGen PUBLIC CodeGen/include)
|
target_include_directories(Luau.CodeGen PUBLIC CodeGen/include)
|
||||||
|
@ -141,6 +146,7 @@ endif()
|
||||||
|
|
||||||
target_compile_options(Luau.Ast PRIVATE ${LUAU_OPTIONS})
|
target_compile_options(Luau.Ast PRIVATE ${LUAU_OPTIONS})
|
||||||
target_compile_options(Luau.Analysis PRIVATE ${LUAU_OPTIONS})
|
target_compile_options(Luau.Analysis PRIVATE ${LUAU_OPTIONS})
|
||||||
|
target_compile_options(Luau.EqSat PRIVATE ${LUAU_OPTIONS})
|
||||||
target_compile_options(Luau.CLI.lib PRIVATE ${LUAU_OPTIONS})
|
target_compile_options(Luau.CLI.lib PRIVATE ${LUAU_OPTIONS})
|
||||||
target_compile_options(Luau.CodeGen PRIVATE ${LUAU_OPTIONS})
|
target_compile_options(Luau.CodeGen PRIVATE ${LUAU_OPTIONS})
|
||||||
target_compile_options(Luau.VM PRIVATE ${LUAU_OPTIONS})
|
target_compile_options(Luau.VM PRIVATE ${LUAU_OPTIONS})
|
||||||
|
@ -263,13 +269,13 @@ endif()
|
||||||
add_subdirectory(fuzz)
|
add_subdirectory(fuzz)
|
||||||
|
|
||||||
# validate dependencies for internal libraries
|
# validate dependencies for internal libraries
|
||||||
foreach(LIB Luau.Ast Luau.Compiler Luau.Config Luau.Analysis Luau.CodeGen Luau.VM)
|
foreach(LIB Luau.Ast Luau.Compiler Luau.Config Luau.Analysis Luau.EqSat Luau.CodeGen Luau.VM)
|
||||||
if(TARGET ${LIB})
|
if(TARGET ${LIB})
|
||||||
get_target_property(DEPENDS ${LIB} LINK_LIBRARIES)
|
get_target_property(DEPENDS ${LIB} LINK_LIBRARIES)
|
||||||
if(LIB MATCHES "CodeGen|VM" AND DEPENDS MATCHES "Ast|Analysis|Config|Compiler")
|
if(LIB MATCHES "CodeGen|VM" AND DEPENDS MATCHES "Ast|Analysis|Config|Compiler")
|
||||||
message(FATAL_ERROR ${LIB} " is a runtime component but it depends on one of the offline components")
|
message(FATAL_ERROR ${LIB} " is a runtime component but it depends on one of the offline components")
|
||||||
endif()
|
endif()
|
||||||
if(LIB MATCHES "Ast|Analysis|Compiler" AND DEPENDS MATCHES "CodeGen|VM")
|
if(LIB MATCHES "Ast|Analysis|EqSat|Compiler" AND DEPENDS MATCHES "CodeGen|VM")
|
||||||
message(FATAL_ERROR ${LIB} " is an offline component but it depends on one of the runtime components")
|
message(FATAL_ERROR ${LIB} " is an offline component but it depends on one of the runtime components")
|
||||||
endif()
|
endif()
|
||||||
if(LIB MATCHES "Ast|Compiler" AND DEPENDS MATCHES "Analysis|Config")
|
if(LIB MATCHES "Ast|Compiler" AND DEPENDS MATCHES "Analysis|Config")
|
||||||
|
|
228
EqSat/include/Luau/EGraph.h
Normal file
228
EqSat/include/Luau/EGraph.h
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Luau/Common.h"
|
||||||
|
#include "Luau/Id.h"
|
||||||
|
#include "Luau/Language.h"
|
||||||
|
#include "Luau/UnionFind.h"
|
||||||
|
#include "Luau/VecDeque.h"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Luau::EqSat
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename L, typename N>
|
||||||
|
struct EGraph;
|
||||||
|
|
||||||
|
template<typename L, typename N>
|
||||||
|
struct Analysis final
|
||||||
|
{
|
||||||
|
N analysis;
|
||||||
|
|
||||||
|
using D = typename N::Data;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static D fnMake(const N& analysis, const EGraph<L, N>& egraph, const L& enode)
|
||||||
|
{
|
||||||
|
return analysis.make(egraph, *enode.template get<T>());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Ts>
|
||||||
|
D make(const EGraph<L, N>& egraph, const Language<Ts...>& enode) const
|
||||||
|
{
|
||||||
|
using FnMake = D (*)(const N&, const EGraph<L, N>&, const L&);
|
||||||
|
static constexpr FnMake tableMake[sizeof...(Ts)] = {&fnMake<Ts>...};
|
||||||
|
|
||||||
|
return tableMake[enode.index()](analysis, egraph, enode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void join(D& a, const D& b) const
|
||||||
|
{
|
||||||
|
return analysis.join(a, b);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Each e-class is a set of e-nodes representing equivalent terms from a given language,
|
||||||
|
/// and an e-node is a function symbol paired with a list of children e-classes.
|
||||||
|
template<typename L, typename D>
|
||||||
|
struct EClass final
|
||||||
|
{
|
||||||
|
Id id;
|
||||||
|
std::vector<L> nodes;
|
||||||
|
D data;
|
||||||
|
std::vector<std::pair<L, Id>> parents;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// See <https://arxiv.org/pdf/2004.03082>.
|
||||||
|
template<typename L, typename N>
|
||||||
|
struct EGraph final
|
||||||
|
{
|
||||||
|
Id find(Id id) const
|
||||||
|
{
|
||||||
|
return unionfind.find(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Id> lookup(const L& enode) const
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(isCanonical(enode));
|
||||||
|
|
||||||
|
if (auto it = hashcons.find(enode); it != hashcons.end())
|
||||||
|
return it->second;
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
Id add(L enode)
|
||||||
|
{
|
||||||
|
canonicalize(enode);
|
||||||
|
|
||||||
|
if (auto id = lookup(enode))
|
||||||
|
return *id;
|
||||||
|
|
||||||
|
Id id = makeEClass(enode);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void merge(Id id1, Id id2)
|
||||||
|
{
|
||||||
|
id1 = find(id1);
|
||||||
|
id2 = find(id2);
|
||||||
|
if (id1 == id2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
unionfind.merge(id1, id2);
|
||||||
|
|
||||||
|
EClass<L, typename N::Data>& eclass1 = get(id1);
|
||||||
|
EClass<L, typename N::Data> eclass2 = std::move(get(id2));
|
||||||
|
classes.erase(id2);
|
||||||
|
|
||||||
|
worklist.reserve(worklist.size() + eclass2.parents.size());
|
||||||
|
for (auto [enode, id] : eclass2.parents)
|
||||||
|
worklist.push_back({std::move(enode), id});
|
||||||
|
|
||||||
|
analysis.join(eclass1.data, eclass2.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rebuild()
|
||||||
|
{
|
||||||
|
while (!worklist.empty())
|
||||||
|
{
|
||||||
|
auto [enode, id] = worklist.back();
|
||||||
|
worklist.pop_back();
|
||||||
|
repair(get(find(id)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size() const
|
||||||
|
{
|
||||||
|
return classes.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
EClass<L, typename N::Data>& operator[](Id id)
|
||||||
|
{
|
||||||
|
return get(find(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
const EClass<L, typename N::Data>& operator[](Id id) const
|
||||||
|
{
|
||||||
|
return const_cast<EGraph*>(this)->get(find(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Analysis<L, N> analysis;
|
||||||
|
|
||||||
|
/// A union-find data structure 𝑈 stores an equivalence relation over e-class ids.
|
||||||
|
UnionFind unionfind;
|
||||||
|
|
||||||
|
/// The e-class map 𝑀 maps e-class ids to e-classes. All equivalent e-class ids map to the same
|
||||||
|
/// e-class, i.e., 𝑎 ≡id 𝑏 iff 𝑀[𝑎] is the same set as 𝑀[𝑏]. An e-class id 𝑎 is said to refer to the
|
||||||
|
/// e-class 𝑀[find(𝑎)].
|
||||||
|
std::unordered_map<Id, EClass<L, typename N::Data>> classes;
|
||||||
|
|
||||||
|
/// The hashcons 𝐻 is a map from e-nodes to e-class ids.
|
||||||
|
std::unordered_map<L, Id, typename L::Hash> hashcons;
|
||||||
|
|
||||||
|
VecDeque<std::pair<L, Id>> worklist;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void canonicalize(L& enode)
|
||||||
|
{
|
||||||
|
// An e-node 𝑛 is canonical iff 𝑛 = canonicalize(𝑛), where
|
||||||
|
// canonicalize(𝑓(𝑎1, 𝑎2, ...)) = 𝑓(find(𝑎1), find(𝑎2), ...).
|
||||||
|
for (Id& id : enode.operands())
|
||||||
|
id = find(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isCanonical(const L& enode) const
|
||||||
|
{
|
||||||
|
bool canonical = true;
|
||||||
|
for (Id id : enode.operands())
|
||||||
|
canonical &= (id == find(id));
|
||||||
|
return canonical;
|
||||||
|
}
|
||||||
|
|
||||||
|
Id makeEClass(const L& enode)
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(isCanonical(enode));
|
||||||
|
|
||||||
|
Id id = unionfind.makeSet();
|
||||||
|
|
||||||
|
classes.insert_or_assign(id, EClass<L, typename N::Data>{
|
||||||
|
id,
|
||||||
|
{enode},
|
||||||
|
analysis.make(*this, enode),
|
||||||
|
{},
|
||||||
|
});
|
||||||
|
|
||||||
|
for (Id operand : enode.operands())
|
||||||
|
get(operand).parents.push_back({enode, id});
|
||||||
|
|
||||||
|
worklist.push_back({enode, id});
|
||||||
|
hashcons.insert_or_assign(enode, id);
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks up for an eclass from a given non-canonicalized `id`.
|
||||||
|
// For a canonicalized eclass, use `get(find(id))` or `egraph[id]`.
|
||||||
|
EClass<L, typename N::Data>& get(Id id)
|
||||||
|
{
|
||||||
|
return classes.at(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void repair(EClass<L, typename N::Data>& eclass)
|
||||||
|
{
|
||||||
|
// In the egg paper, the `repair` function makes use of two loops over the `eclass.parents`
|
||||||
|
// by first erasing the old enode entry, and adding back the canonicalized enode with the canonical id.
|
||||||
|
// And then in another loop that follows, deduplicate it.
|
||||||
|
//
|
||||||
|
// Here, we unify the two loops. I think it's equivalent?
|
||||||
|
|
||||||
|
// After canonicalizing the enodes, the eclass may contain multiple enodes that are equivalent.
|
||||||
|
std::unordered_map<L, Id, typename L::Hash> map;
|
||||||
|
for (auto& [enode, id] : eclass.parents)
|
||||||
|
{
|
||||||
|
// By removing the old enode from the hashcons map, we will always find our new canonicalized eclass id.
|
||||||
|
hashcons.erase(enode);
|
||||||
|
canonicalize(enode);
|
||||||
|
hashcons.insert_or_assign(enode, find(id));
|
||||||
|
|
||||||
|
if (auto it = map.find(enode); it != map.end())
|
||||||
|
merge(id, it->second);
|
||||||
|
|
||||||
|
map.insert_or_assign(enode, find(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
eclass.parents.clear();
|
||||||
|
for (auto it = map.begin(); it != map.end();)
|
||||||
|
{
|
||||||
|
auto node = map.extract(it++);
|
||||||
|
eclass.parents.emplace_back(std::move(node.key()), node.mapped());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Luau::EqSat
|
29
EqSat/include/Luau/Id.h
Normal file
29
EqSat/include/Luau/Id.h
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace Luau::EqSat
|
||||||
|
{
|
||||||
|
|
||||||
|
struct Id final
|
||||||
|
{
|
||||||
|
explicit Id(size_t id);
|
||||||
|
|
||||||
|
explicit operator size_t() const;
|
||||||
|
|
||||||
|
bool operator==(Id rhs) const;
|
||||||
|
bool operator!=(Id rhs) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t id;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Luau::EqSat
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct std::hash<Luau::EqSat::Id>
|
||||||
|
{
|
||||||
|
size_t operator()(Luau::EqSat::Id id) const;
|
||||||
|
};
|
304
EqSat/include/Luau/Language.h
Normal file
304
EqSat/include/Luau/Language.h
Normal file
|
@ -0,0 +1,304 @@
|
||||||
|
// 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 <array>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#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_FIELD(name) \
|
||||||
|
struct name : public ::Luau::EqSat::Field<name> \
|
||||||
|
{ \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LUAU_EQSAT_NODE_FIELDS(name, ...) \
|
||||||
|
struct name : public ::Luau::EqSat::NodeFields<name, __VA_ARGS__> \
|
||||||
|
{ \
|
||||||
|
static constexpr const char* tag = #name; \
|
||||||
|
using NodeFields::NodeFields; \
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Luau::EqSat
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename Phantom, typename T>
|
||||||
|
struct Atom
|
||||||
|
{
|
||||||
|
Atom(const T& value)
|
||||||
|
: _value(value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const T& value() const
|
||||||
|
{
|
||||||
|
return _value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
Slice<Id> operands()
|
||||||
|
{
|
||||||
|
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 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> operands()
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Empty base class just for static_asserts.
|
||||||
|
struct FieldBase
|
||||||
|
{
|
||||||
|
FieldBase() = delete;
|
||||||
|
|
||||||
|
FieldBase(FieldBase&&) = delete;
|
||||||
|
FieldBase& operator=(FieldBase&&) = delete;
|
||||||
|
|
||||||
|
FieldBase(const FieldBase&) = delete;
|
||||||
|
FieldBase& operator=(const FieldBase&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Phantom>
|
||||||
|
struct Field : FieldBase
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Phantom, typename... Fields>
|
||||||
|
struct NodeFields
|
||||||
|
{
|
||||||
|
static_assert(std::conjunction<std::is_base_of<FieldBase, Fields>...>::value);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static constexpr int getIndex()
|
||||||
|
{
|
||||||
|
constexpr int N = sizeof...(Fields);
|
||||||
|
constexpr bool is[N] = {std::is_same_v<std::decay_t<T>, Fields>...};
|
||||||
|
|
||||||
|
for (int i = 0; i < N; ++i)
|
||||||
|
if (is[i])
|
||||||
|
return i;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<typename... Args>
|
||||||
|
NodeFields(Args&&... args)
|
||||||
|
: array{std::forward<Args>(args)...}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Slice<Id> operands()
|
||||||
|
{
|
||||||
|
return Slice{array};
|
||||||
|
}
|
||||||
|
|
||||||
|
Slice<const Id> operands() const
|
||||||
|
{
|
||||||
|
return Slice{array.data(), array.size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
Id field() const
|
||||||
|
{
|
||||||
|
static_assert(std::disjunction_v<std::is_same<std::decay_t<T>, Fields>...>);
|
||||||
|
return array[getIndex<T>()];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const NodeFields& rhs) const
|
||||||
|
{
|
||||||
|
return array == rhs.array;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const NodeFields& rhs) const
|
||||||
|
{
|
||||||
|
return !(*this == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Hash
|
||||||
|
{
|
||||||
|
size_t operator()(const NodeFields& value) const
|
||||||
|
{
|
||||||
|
return languageHash(value.array);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::array<Id, sizeof...(Fields)> array;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Ts>
|
||||||
|
struct Language final
|
||||||
|
{
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Language(const Language&) noexcept = default;
|
||||||
|
Language& operator=(const Language&) noexcept = default;
|
||||||
|
|
||||||
|
Language(Language&&) noexcept = default;
|
||||||
|
Language& operator=(Language&&) noexcept = default;
|
||||||
|
|
||||||
|
int index() const noexcept
|
||||||
|
{
|
||||||
|
return v.index();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// You should never call this function with the intention of mutating the `Id`.
|
||||||
|
/// Reading is ok, but you should also never assume that these `Id`s are stable.
|
||||||
|
Slice<Id> operands() noexcept
|
||||||
|
{
|
||||||
|
return visit([](auto&& v) -> Slice<Id> {
|
||||||
|
return v.operands();
|
||||||
|
}, 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:
|
||||||
|
Variant<Ts...> v;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Luau::EqSat
|
56
EqSat/include/Luau/LanguageHash.h
Normal file
56
EqSat/include/Luau/LanguageHash.h
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace Luau::EqSat
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct LanguageHash
|
||||||
|
{
|
||||||
|
size_t operator()(const T& t, decltype(std::hash<T>{}(std::declval<T>()))* = 0) const
|
||||||
|
{
|
||||||
|
return std::hash<T>{}(t);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
size_t languageHash(const T& lang)
|
||||||
|
{
|
||||||
|
return LanguageHash<T>{}(lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void hashCombine(size_t& seed, size_t hash)
|
||||||
|
{
|
||||||
|
// Golden Ratio constant used for better hash scattering
|
||||||
|
// See https://softwareengineering.stackexchange.com/a/402543
|
||||||
|
seed ^= hash + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, size_t I>
|
||||||
|
struct LanguageHash<std::array<T, I>>
|
||||||
|
{
|
||||||
|
size_t operator()(const std::array<T, I>& array) const
|
||||||
|
{
|
||||||
|
size_t seed = 0;
|
||||||
|
for (const T& t : array)
|
||||||
|
hashCombine(seed, languageHash(t));
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct LanguageHash<std::vector<T>>
|
||||||
|
{
|
||||||
|
size_t operator()(const std::vector<T>& vector) const
|
||||||
|
{
|
||||||
|
size_t seed = 0;
|
||||||
|
for (const T& t : vector)
|
||||||
|
hashCombine(seed, languageHash(t));
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Luau::EqSat
|
78
EqSat/include/Luau/Slice.h
Normal file
78
EqSat/include/Luau/Slice.h
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Luau/Common.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
namespace Luau::EqSat
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct Slice final
|
||||||
|
{
|
||||||
|
Slice()
|
||||||
|
: _data(nullptr)
|
||||||
|
, _size(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Use this constructor if you have a dynamically sized vector.
|
||||||
|
/// The slice is valid for as long as the backing vector has not moved
|
||||||
|
/// elsewhere in memory.
|
||||||
|
///
|
||||||
|
/// In general, a slice should never be used from vectors except for
|
||||||
|
/// any vectors whose size are statically unknown, but remains fixed
|
||||||
|
/// upon the construction of such a slice over a vector.
|
||||||
|
Slice(T* first, size_t last)
|
||||||
|
: _data(first)
|
||||||
|
, _size(last)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t I>
|
||||||
|
explicit Slice(std::array<T, I>& array)
|
||||||
|
: _data(array.data())
|
||||||
|
, _size(array.size())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
T* data() const
|
||||||
|
{
|
||||||
|
return _data;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size() const
|
||||||
|
{
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const
|
||||||
|
{
|
||||||
|
return _size == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
T& operator[](size_t i) const
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(i < _size);
|
||||||
|
return _data[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
T* _data;
|
||||||
|
size_t _size;
|
||||||
|
|
||||||
|
public:
|
||||||
|
T* begin() const
|
||||||
|
{
|
||||||
|
return _data;
|
||||||
|
}
|
||||||
|
|
||||||
|
T* end() const
|
||||||
|
{
|
||||||
|
return _data + _size;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Luau::EqSat
|
27
EqSat/include/Luau/UnionFind.h
Normal file
27
EqSat/include/Luau/UnionFind.h
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
// 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 <vector>
|
||||||
|
|
||||||
|
namespace Luau::EqSat
|
||||||
|
{
|
||||||
|
|
||||||
|
/// See <https://dl.acm.org/doi/pdf/10.1145/321879.321884>.
|
||||||
|
struct UnionFind final
|
||||||
|
{
|
||||||
|
Id makeSet();
|
||||||
|
Id find(Id id) const;
|
||||||
|
Id find(Id id);
|
||||||
|
void merge(Id a, Id b);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Id> parents;
|
||||||
|
std::vector<int> ranks;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Id canonicalize(Id id) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Luau::EqSat
|
32
EqSat/src/Id.cpp
Normal file
32
EqSat/src/Id.cpp
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
|
#include "Luau/Id.h"
|
||||||
|
|
||||||
|
namespace Luau::EqSat
|
||||||
|
{
|
||||||
|
|
||||||
|
Id::Id(size_t id)
|
||||||
|
: id(id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Id::operator size_t() const
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Id::operator==(Id rhs) const
|
||||||
|
{
|
||||||
|
return id == rhs.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Id::operator!=(Id rhs) const
|
||||||
|
{
|
||||||
|
return id != rhs.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Luau::EqSat
|
||||||
|
|
||||||
|
size_t std::hash<Luau::EqSat::Id>::operator()(Luau::EqSat::Id id) const
|
||||||
|
{
|
||||||
|
return std::hash<size_t>()(size_t(id));
|
||||||
|
}
|
68
EqSat/src/UnionFind.cpp
Normal file
68
EqSat/src/UnionFind.cpp
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
|
#include "Luau/UnionFind.h"
|
||||||
|
|
||||||
|
#include "Luau/Common.h"
|
||||||
|
|
||||||
|
namespace Luau::EqSat
|
||||||
|
{
|
||||||
|
|
||||||
|
Id UnionFind::makeSet()
|
||||||
|
{
|
||||||
|
Id id{parents.size()};
|
||||||
|
parents.push_back(id);
|
||||||
|
ranks.push_back(0);
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
Id UnionFind::find(Id id) const
|
||||||
|
{
|
||||||
|
return canonicalize(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Id UnionFind::find(Id id)
|
||||||
|
{
|
||||||
|
Id set = canonicalize(id);
|
||||||
|
|
||||||
|
// An e-class id 𝑎 is canonical iff find(𝑎) = 𝑎.
|
||||||
|
while (id != parents[size_t(id)])
|
||||||
|
{
|
||||||
|
// Note: we don't update the ranks here since a rank
|
||||||
|
// represents the upper bound on the maximum depth of a tree
|
||||||
|
Id parent = parents[size_t(id)];
|
||||||
|
parents[size_t(id)] = set;
|
||||||
|
id = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnionFind::merge(Id a, Id b)
|
||||||
|
{
|
||||||
|
Id aSet = find(a);
|
||||||
|
Id bSet = find(b);
|
||||||
|
if (aSet == bSet)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Ensure that the rank of set A is greater than the rank of set B
|
||||||
|
if (ranks[size_t(aSet)] < ranks[size_t(bSet)])
|
||||||
|
std::swap(aSet, bSet);
|
||||||
|
|
||||||
|
parents[size_t(bSet)] = aSet;
|
||||||
|
|
||||||
|
if (ranks[size_t(aSet)] == ranks[size_t(bSet)])
|
||||||
|
ranks[size_t(aSet)]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Id UnionFind::canonicalize(Id id) const
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(size_t(id) < parents.size());
|
||||||
|
|
||||||
|
// An e-class id 𝑎 is canonical iff find(𝑎) = 𝑎.
|
||||||
|
while (id != parents[size_t(id)])
|
||||||
|
id = parents[size_t(id)];
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Luau::EqSat
|
28
Makefile
28
Makefile
|
@ -26,6 +26,10 @@ ANALYSIS_SOURCES=$(wildcard Analysis/src/*.cpp)
|
||||||
ANALYSIS_OBJECTS=$(ANALYSIS_SOURCES:%=$(BUILD)/%.o)
|
ANALYSIS_OBJECTS=$(ANALYSIS_SOURCES:%=$(BUILD)/%.o)
|
||||||
ANALYSIS_TARGET=$(BUILD)/libluauanalysis.a
|
ANALYSIS_TARGET=$(BUILD)/libluauanalysis.a
|
||||||
|
|
||||||
|
EQSAT_SOURCES=$(wildcard EqSat/src/*.cpp)
|
||||||
|
EQSAT_OBJECTS=$(EQSAT_SOURCES:%=$(BUILD)/%.o)
|
||||||
|
EQSAT_TARGET=$(BUILD)/libluaueqsat.a
|
||||||
|
|
||||||
CODEGEN_SOURCES=$(wildcard CodeGen/src/*.cpp)
|
CODEGEN_SOURCES=$(wildcard CodeGen/src/*.cpp)
|
||||||
CODEGEN_OBJECTS=$(CODEGEN_SOURCES:%=$(BUILD)/%.o)
|
CODEGEN_OBJECTS=$(CODEGEN_SOURCES:%=$(BUILD)/%.o)
|
||||||
CODEGEN_TARGET=$(BUILD)/libluaucodegen.a
|
CODEGEN_TARGET=$(BUILD)/libluaucodegen.a
|
||||||
|
@ -69,7 +73,7 @@ ifneq ($(opt),)
|
||||||
TESTS_ARGS+=-O$(opt)
|
TESTS_ARGS+=-O$(opt)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
OBJECTS=$(AST_OBJECTS) $(COMPILER_OBJECTS) $(CONFIG_OBJECTS) $(ANALYSIS_OBJECTS) $(CODEGEN_OBJECTS) $(VM_OBJECTS) $(ISOCLINE_OBJECTS) $(TESTS_OBJECTS) $(REPL_CLI_OBJECTS) $(ANALYZE_CLI_OBJECTS) $(COMPILE_CLI_OBJECTS) $(BYTECODE_CLI_OBJECTS) $(FUZZ_OBJECTS)
|
OBJECTS=$(AST_OBJECTS) $(COMPILER_OBJECTS) $(CONFIG_OBJECTS) $(ANALYSIS_OBJECTS) $(EQSAT_OBJECTS) $(CODEGEN_OBJECTS) $(VM_OBJECTS) $(ISOCLINE_OBJECTS) $(TESTS_OBJECTS) $(REPL_CLI_OBJECTS) $(ANALYZE_CLI_OBJECTS) $(COMPILE_CLI_OBJECTS) $(BYTECODE_CLI_OBJECTS) $(FUZZ_OBJECTS)
|
||||||
EXECUTABLE_ALIASES = luau luau-analyze luau-compile luau-bytecode luau-tests
|
EXECUTABLE_ALIASES = luau luau-analyze luau-compile luau-bytecode luau-tests
|
||||||
|
|
||||||
# common flags
|
# common flags
|
||||||
|
@ -138,16 +142,17 @@ endif
|
||||||
$(AST_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include
|
$(AST_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include
|
||||||
$(COMPILER_OBJECTS): CXXFLAGS+=-std=c++17 -ICompiler/include -ICommon/include -IAst/include
|
$(COMPILER_OBJECTS): CXXFLAGS+=-std=c++17 -ICompiler/include -ICommon/include -IAst/include
|
||||||
$(CONFIG_OBJECTS): CXXFLAGS+=-std=c++17 -IConfig/include -ICommon/include -IAst/include
|
$(CONFIG_OBJECTS): CXXFLAGS+=-std=c++17 -IConfig/include -ICommon/include -IAst/include
|
||||||
$(ANALYSIS_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -IAnalysis/include -IConfig/include
|
$(ANALYSIS_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -IAnalysis/include -IEqSat/include -IConfig/include
|
||||||
|
$(EQSAT_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IEqSat/include
|
||||||
$(CODEGEN_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -ICodeGen/include -IVM/include -IVM/src # Code generation needs VM internals
|
$(CODEGEN_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -ICodeGen/include -IVM/include -IVM/src # Code generation needs VM internals
|
||||||
$(VM_OBJECTS): CXXFLAGS+=-std=c++11 -ICommon/include -IVM/include
|
$(VM_OBJECTS): CXXFLAGS+=-std=c++11 -ICommon/include -IVM/include
|
||||||
$(ISOCLINE_OBJECTS): CXXFLAGS+=-Wno-unused-function -Iextern/isocline/include
|
$(ISOCLINE_OBJECTS): CXXFLAGS+=-Wno-unused-function -Iextern/isocline/include
|
||||||
$(TESTS_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -ICompiler/include -IConfig/include -IAnalysis/include -ICodeGen/include -IVM/include -ICLI -Iextern -DDOCTEST_CONFIG_DOUBLE_STRINGIFY
|
$(TESTS_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -ICompiler/include -IConfig/include -IAnalysis/include -IEqSat/include -ICodeGen/include -IVM/include -ICLI -Iextern -DDOCTEST_CONFIG_DOUBLE_STRINGIFY
|
||||||
$(REPL_CLI_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -ICompiler/include -IVM/include -ICodeGen/include -Iextern -Iextern/isocline/include
|
$(REPL_CLI_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -ICompiler/include -IVM/include -ICodeGen/include -Iextern -Iextern/isocline/include
|
||||||
$(ANALYZE_CLI_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -IAnalysis/include -IConfig/include -Iextern
|
$(ANALYZE_CLI_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -IAnalysis/include -IEqSat/include -IConfig/include -Iextern
|
||||||
$(COMPILE_CLI_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -ICompiler/include -IVM/include -ICodeGen/include
|
$(COMPILE_CLI_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -ICompiler/include -IVM/include -ICodeGen/include
|
||||||
$(BYTECODE_CLI_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -ICompiler/include -IVM/include -ICodeGen/include
|
$(BYTECODE_CLI_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -ICompiler/include -IVM/include -ICodeGen/include
|
||||||
$(FUZZ_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -ICompiler/include -IAnalysis/include -IVM/include -ICodeGen/include -IConfig/include
|
$(FUZZ_OBJECTS): CXXFLAGS+=-std=c++17 -ICommon/include -IAst/include -ICompiler/include -IAnalysis/include -IEqSat/include -IVM/include -ICodeGen/include -IConfig/include
|
||||||
|
|
||||||
$(TESTS_TARGET): LDFLAGS+=-lpthread
|
$(TESTS_TARGET): LDFLAGS+=-lpthread
|
||||||
$(REPL_CLI_TARGET): LDFLAGS+=-lpthread
|
$(REPL_CLI_TARGET): LDFLAGS+=-lpthread
|
||||||
|
@ -218,9 +223,9 @@ luau-tests: $(TESTS_TARGET)
|
||||||
ln -fs $^ $@
|
ln -fs $^ $@
|
||||||
|
|
||||||
# executable targets
|
# executable targets
|
||||||
$(TESTS_TARGET): $(TESTS_OBJECTS) $(ANALYSIS_TARGET) $(COMPILER_TARGET) $(CONFIG_TARGET) $(AST_TARGET) $(CODEGEN_TARGET) $(VM_TARGET) $(ISOCLINE_TARGET)
|
$(TESTS_TARGET): $(TESTS_OBJECTS) $(ANALYSIS_TARGET) $(EQSAT_TARGET) $(COMPILER_TARGET) $(CONFIG_TARGET) $(AST_TARGET) $(CODEGEN_TARGET) $(VM_TARGET) $(ISOCLINE_TARGET)
|
||||||
$(REPL_CLI_TARGET): $(REPL_CLI_OBJECTS) $(COMPILER_TARGET) $(CONFIG_TARGET) $(AST_TARGET) $(CODEGEN_TARGET) $(VM_TARGET) $(ISOCLINE_TARGET)
|
$(REPL_CLI_TARGET): $(REPL_CLI_OBJECTS) $(COMPILER_TARGET) $(CONFIG_TARGET) $(AST_TARGET) $(CODEGEN_TARGET) $(VM_TARGET) $(ISOCLINE_TARGET)
|
||||||
$(ANALYZE_CLI_TARGET): $(ANALYZE_CLI_OBJECTS) $(ANALYSIS_TARGET) $(AST_TARGET) $(CONFIG_TARGET)
|
$(ANALYZE_CLI_TARGET): $(ANALYZE_CLI_OBJECTS) $(ANALYSIS_TARGET) $(EQSAT_TARGET) $(AST_TARGET) $(CONFIG_TARGET)
|
||||||
$(COMPILE_CLI_TARGET): $(COMPILE_CLI_OBJECTS) $(COMPILER_TARGET) $(AST_TARGET) $(CODEGEN_TARGET) $(VM_TARGET)
|
$(COMPILE_CLI_TARGET): $(COMPILE_CLI_OBJECTS) $(COMPILER_TARGET) $(AST_TARGET) $(CODEGEN_TARGET) $(VM_TARGET)
|
||||||
$(BYTECODE_CLI_TARGET): $(BYTECODE_CLI_OBJECTS) $(COMPILER_TARGET) $(AST_TARGET) $(CODEGEN_TARGET) $(VM_TARGET)
|
$(BYTECODE_CLI_TARGET): $(BYTECODE_CLI_OBJECTS) $(COMPILER_TARGET) $(AST_TARGET) $(CODEGEN_TARGET) $(VM_TARGET)
|
||||||
|
|
||||||
|
@ -228,22 +233,23 @@ $(TESTS_TARGET) $(REPL_CLI_TARGET) $(ANALYZE_CLI_TARGET) $(COMPILE_CLI_TARGET) $
|
||||||
$(CXX) $^ $(LDFLAGS) -o $@
|
$(CXX) $^ $(LDFLAGS) -o $@
|
||||||
|
|
||||||
# executable targets for fuzzing
|
# executable targets for fuzzing
|
||||||
fuzz-%: $(BUILD)/fuzz/%.cpp.o $(ANALYSIS_TARGET) $(COMPILER_TARGET) $(AST_TARGET) $(CONFIG_TARGET) $(CODEGEN_TARGET) $(VM_TARGET)
|
fuzz-%: $(BUILD)/fuzz/%.cpp.o $(ANALYSIS_TARGET) $(EQSAT_TARGET) $(COMPILER_TARGET) $(AST_TARGET) $(CONFIG_TARGET) $(CODEGEN_TARGET) $(VM_TARGET)
|
||||||
$(CXX) $^ $(LDFLAGS) -o $@
|
$(CXX) $^ $(LDFLAGS) -o $@
|
||||||
|
|
||||||
fuzz-proto: $(BUILD)/fuzz/proto.cpp.o $(BUILD)/fuzz/protoprint.cpp.o $(BUILD)/fuzz/luau.pb.cpp.o $(ANALYSIS_TARGET) $(COMPILER_TARGET) $(AST_TARGET) $(CONFIG_TARGET) $(VM_TARGET) | build/libprotobuf-mutator
|
fuzz-proto: $(BUILD)/fuzz/proto.cpp.o $(BUILD)/fuzz/protoprint.cpp.o $(BUILD)/fuzz/luau.pb.cpp.o $(ANALYSIS_TARGET) $(EQSAT_TARGET) $(COMPILER_TARGET) $(AST_TARGET) $(CONFIG_TARGET) $(VM_TARGET) | build/libprotobuf-mutator
|
||||||
fuzz-prototest: $(BUILD)/fuzz/prototest.cpp.o $(BUILD)/fuzz/protoprint.cpp.o $(BUILD)/fuzz/luau.pb.cpp.o $(ANALYSIS_TARGET) $(COMPILER_TARGET) $(AST_TARGET) $(CONFIG_TARGET) $(VM_TARGET) | build/libprotobuf-mutator
|
fuzz-prototest: $(BUILD)/fuzz/prototest.cpp.o $(BUILD)/fuzz/protoprint.cpp.o $(BUILD)/fuzz/luau.pb.cpp.o $(ANALYSIS_TARGET) $(EQSAT_TARGET) $(COMPILER_TARGET) $(AST_TARGET) $(CONFIG_TARGET) $(VM_TARGET) | build/libprotobuf-mutator
|
||||||
|
|
||||||
# static library targets
|
# static library targets
|
||||||
$(AST_TARGET): $(AST_OBJECTS)
|
$(AST_TARGET): $(AST_OBJECTS)
|
||||||
$(COMPILER_TARGET): $(COMPILER_OBJECTS)
|
$(COMPILER_TARGET): $(COMPILER_OBJECTS)
|
||||||
$(CONFIG_TARGET): $(CONFIG_OBJECTS)
|
$(CONFIG_TARGET): $(CONFIG_OBJECTS)
|
||||||
$(ANALYSIS_TARGET): $(ANALYSIS_OBJECTS)
|
$(ANALYSIS_TARGET): $(ANALYSIS_OBJECTS)
|
||||||
|
$(EQSAT_TARGET): $(EQSAT_OBJECTS)
|
||||||
$(CODEGEN_TARGET): $(CODEGEN_OBJECTS)
|
$(CODEGEN_TARGET): $(CODEGEN_OBJECTS)
|
||||||
$(VM_TARGET): $(VM_OBJECTS)
|
$(VM_TARGET): $(VM_OBJECTS)
|
||||||
$(ISOCLINE_TARGET): $(ISOCLINE_OBJECTS)
|
$(ISOCLINE_TARGET): $(ISOCLINE_OBJECTS)
|
||||||
|
|
||||||
$(AST_TARGET) $(COMPILER_TARGET) $(CONFIG_TARGET) $(ANALYSIS_TARGET) $(CODEGEN_TARGET) $(VM_TARGET) $(ISOCLINE_TARGET):
|
$(AST_TARGET) $(COMPILER_TARGET) $(CONFIG_TARGET) $(ANALYSIS_TARGET) $(EQSAT_TARGET) $(CODEGEN_TARGET) $(VM_TARGET) $(ISOCLINE_TARGET):
|
||||||
ar rcs $@ $^
|
ar rcs $@ $^
|
||||||
|
|
||||||
# object file targets
|
# object file targets
|
||||||
|
|
|
@ -7,6 +7,7 @@ if(NOT ${CMAKE_VERSION} VERSION_LESS "3.19")
|
||||||
Common/include/Luau/BytecodeUtils.h
|
Common/include/Luau/BytecodeUtils.h
|
||||||
Common/include/Luau/DenseHash.h
|
Common/include/Luau/DenseHash.h
|
||||||
Common/include/Luau/ExperimentalFlags.h
|
Common/include/Luau/ExperimentalFlags.h
|
||||||
|
Common/include/Luau/Variant.h
|
||||||
Common/include/Luau/VecDeque.h
|
Common/include/Luau/VecDeque.h
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
@ -232,7 +233,6 @@ target_sources(Luau.Analysis PRIVATE
|
||||||
Analysis/include/Luau/Unifier.h
|
Analysis/include/Luau/Unifier.h
|
||||||
Analysis/include/Luau/Unifier2.h
|
Analysis/include/Luau/Unifier2.h
|
||||||
Analysis/include/Luau/UnifierSharedState.h
|
Analysis/include/Luau/UnifierSharedState.h
|
||||||
Analysis/include/Luau/Variant.h
|
|
||||||
Analysis/include/Luau/VisitType.h
|
Analysis/include/Luau/VisitType.h
|
||||||
|
|
||||||
Analysis/src/Anyification.cpp
|
Analysis/src/Anyification.cpp
|
||||||
|
@ -295,6 +295,19 @@ target_sources(Luau.Analysis PRIVATE
|
||||||
Analysis/src/Unifier2.cpp
|
Analysis/src/Unifier2.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Luau.EqSat Sources
|
||||||
|
target_sources(Luau.EqSat PRIVATE
|
||||||
|
EqSat/include/Luau/EGraph.h
|
||||||
|
EqSat/include/Luau/Id.h
|
||||||
|
EqSat/include/Luau/Language.h
|
||||||
|
EqSat/include/Luau/LanguageHash.h
|
||||||
|
EqSat/include/Luau/Slice.h
|
||||||
|
EqSat/include/Luau/UnionFind.h
|
||||||
|
|
||||||
|
EqSat/src/Id.cpp
|
||||||
|
EqSat/src/UnionFind.cpp
|
||||||
|
)
|
||||||
|
|
||||||
# Luau.VM Sources
|
# Luau.VM Sources
|
||||||
target_sources(Luau.VM PRIVATE
|
target_sources(Luau.VM PRIVATE
|
||||||
VM/include/lua.h
|
VM/include/lua.h
|
||||||
|
@ -418,6 +431,9 @@ if(TARGET Luau.UnitTest)
|
||||||
tests/DiffAsserts.cpp
|
tests/DiffAsserts.cpp
|
||||||
tests/DiffAsserts.h
|
tests/DiffAsserts.h
|
||||||
tests/Differ.test.cpp
|
tests/Differ.test.cpp
|
||||||
|
tests/EqSat.language.test.cpp
|
||||||
|
tests/EqSat.propositional.test.cpp
|
||||||
|
tests/EqSat.slice.test.cpp
|
||||||
tests/Error.test.cpp
|
tests/Error.test.cpp
|
||||||
tests/Fixture.cpp
|
tests/Fixture.cpp
|
||||||
tests/Fixture.h
|
tests/Fixture.h
|
||||||
|
|
|
@ -124,7 +124,7 @@ int registerTypes(Luau::Frontend& frontend, Luau::GlobalTypes& globals, bool for
|
||||||
// Vector3 stub
|
// Vector3 stub
|
||||||
TypeId vector3MetaType = arena.addType(TableType{});
|
TypeId vector3MetaType = arena.addType(TableType{});
|
||||||
|
|
||||||
TypeId vector3InstanceType = arena.addType(ClassType{"Vector3", {}, nullopt, vector3MetaType, {}, {}, "Test"});
|
TypeId vector3InstanceType = arena.addType(ClassType{"Vector3", {}, nullopt, vector3MetaType, {}, {}, "Test", {}});
|
||||||
getMutable<ClassType>(vector3InstanceType)->props = {
|
getMutable<ClassType>(vector3InstanceType)->props = {
|
||||||
{"X", {builtinTypes.numberType}},
|
{"X", {builtinTypes.numberType}},
|
||||||
{"Y", {builtinTypes.numberType}},
|
{"Y", {builtinTypes.numberType}},
|
||||||
|
@ -138,7 +138,7 @@ int registerTypes(Luau::Frontend& frontend, Luau::GlobalTypes& globals, bool for
|
||||||
globals.globalScope->exportedTypeBindings["Vector3"] = TypeFun{{}, vector3InstanceType};
|
globals.globalScope->exportedTypeBindings["Vector3"] = TypeFun{{}, vector3InstanceType};
|
||||||
|
|
||||||
// Instance stub
|
// Instance stub
|
||||||
TypeId instanceType = arena.addType(ClassType{"Instance", {}, nullopt, nullopt, {}, {}, "Test"});
|
TypeId instanceType = arena.addType(ClassType{"Instance", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
||||||
getMutable<ClassType>(instanceType)->props = {
|
getMutable<ClassType>(instanceType)->props = {
|
||||||
{"Name", {builtinTypes.stringType}},
|
{"Name", {builtinTypes.stringType}},
|
||||||
};
|
};
|
||||||
|
@ -146,7 +146,7 @@ int registerTypes(Luau::Frontend& frontend, Luau::GlobalTypes& globals, bool for
|
||||||
globals.globalScope->exportedTypeBindings["Instance"] = TypeFun{{}, instanceType};
|
globals.globalScope->exportedTypeBindings["Instance"] = TypeFun{{}, instanceType};
|
||||||
|
|
||||||
// Part stub
|
// Part stub
|
||||||
TypeId partType = arena.addType(ClassType{"Part", {}, instanceType, nullopt, {}, {}, "Test"});
|
TypeId partType = arena.addType(ClassType{"Part", {}, instanceType, nullopt, {}, {}, "Test", {}});
|
||||||
getMutable<ClassType>(partType)->props = {
|
getMutable<ClassType>(partType)->props = {
|
||||||
{"Position", {vector3InstanceType}},
|
{"Position", {vector3InstanceType}},
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,9 +18,9 @@ ClassFixture::ClassFixture()
|
||||||
|
|
||||||
unfreeze(arena);
|
unfreeze(arena);
|
||||||
|
|
||||||
TypeId connectionType = arena.addType(ClassType{"Connection", {}, nullopt, nullopt, {}, {}, "Connection"});
|
TypeId connectionType = arena.addType(ClassType{"Connection", {}, nullopt, nullopt, {}, {}, "Connection", {}});
|
||||||
|
|
||||||
TypeId baseClassInstanceType = arena.addType(ClassType{"BaseClass", {}, nullopt, nullopt, {}, {}, "Test"});
|
TypeId baseClassInstanceType = arena.addType(ClassType{"BaseClass", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
||||||
getMutable<ClassType>(baseClassInstanceType)->props = {
|
getMutable<ClassType>(baseClassInstanceType)->props = {
|
||||||
{"BaseMethod", Property::readonly(makeFunction(arena, baseClassInstanceType, {numberType}, {}))},
|
{"BaseMethod", Property::readonly(makeFunction(arena, baseClassInstanceType, {numberType}, {}))},
|
||||||
{"BaseField", {numberType}},
|
{"BaseField", {numberType}},
|
||||||
|
@ -31,7 +31,7 @@ ClassFixture::ClassFixture()
|
||||||
getMutable<ClassType>(connectionType)->props = {
|
getMutable<ClassType>(connectionType)->props = {
|
||||||
{"Connect", {makeFunction(arena, connectionType, {makeFunction(arena, nullopt, {baseClassInstanceType}, {})}, {})}}};
|
{"Connect", {makeFunction(arena, connectionType, {makeFunction(arena, nullopt, {baseClassInstanceType}, {})}, {})}}};
|
||||||
|
|
||||||
TypeId baseClassType = arena.addType(ClassType{"BaseClass", {}, nullopt, nullopt, {}, {}, "Test"});
|
TypeId baseClassType = arena.addType(ClassType{"BaseClass", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
||||||
getMutable<ClassType>(baseClassType)->props = {
|
getMutable<ClassType>(baseClassType)->props = {
|
||||||
{"StaticMethod", {makeFunction(arena, nullopt, {}, {numberType})}},
|
{"StaticMethod", {makeFunction(arena, nullopt, {}, {numberType})}},
|
||||||
{"Clone", {makeFunction(arena, nullopt, {baseClassInstanceType}, {baseClassInstanceType})}},
|
{"Clone", {makeFunction(arena, nullopt, {baseClassInstanceType}, {baseClassInstanceType})}},
|
||||||
|
@ -40,48 +40,48 @@ ClassFixture::ClassFixture()
|
||||||
globals.globalScope->exportedTypeBindings["BaseClass"] = TypeFun{{}, baseClassInstanceType};
|
globals.globalScope->exportedTypeBindings["BaseClass"] = TypeFun{{}, baseClassInstanceType};
|
||||||
addGlobalBinding(globals, "BaseClass", baseClassType, "@test");
|
addGlobalBinding(globals, "BaseClass", baseClassType, "@test");
|
||||||
|
|
||||||
TypeId childClassInstanceType = arena.addType(ClassType{"ChildClass", {}, baseClassInstanceType, nullopt, {}, {}, "Test"});
|
TypeId childClassInstanceType = arena.addType(ClassType{"ChildClass", {}, baseClassInstanceType, nullopt, {}, {}, "Test", {}});
|
||||||
|
|
||||||
getMutable<ClassType>(childClassInstanceType)->props = {
|
getMutable<ClassType>(childClassInstanceType)->props = {
|
||||||
{"Method", {makeFunction(arena, childClassInstanceType, {}, {stringType})}},
|
{"Method", {makeFunction(arena, childClassInstanceType, {}, {stringType})}},
|
||||||
};
|
};
|
||||||
|
|
||||||
TypeId childClassType = arena.addType(ClassType{"ChildClass", {}, baseClassType, nullopt, {}, {}, "Test"});
|
TypeId childClassType = arena.addType(ClassType{"ChildClass", {}, baseClassType, nullopt, {}, {}, "Test", {}});
|
||||||
getMutable<ClassType>(childClassType)->props = {
|
getMutable<ClassType>(childClassType)->props = {
|
||||||
{"New", {makeFunction(arena, nullopt, {}, {childClassInstanceType})}},
|
{"New", {makeFunction(arena, nullopt, {}, {childClassInstanceType})}},
|
||||||
};
|
};
|
||||||
globals.globalScope->exportedTypeBindings["ChildClass"] = TypeFun{{}, childClassInstanceType};
|
globals.globalScope->exportedTypeBindings["ChildClass"] = TypeFun{{}, childClassInstanceType};
|
||||||
addGlobalBinding(globals, "ChildClass", childClassType, "@test");
|
addGlobalBinding(globals, "ChildClass", childClassType, "@test");
|
||||||
|
|
||||||
TypeId grandChildInstanceType = arena.addType(ClassType{"GrandChild", {}, childClassInstanceType, nullopt, {}, {}, "Test"});
|
TypeId grandChildInstanceType = arena.addType(ClassType{"GrandChild", {}, childClassInstanceType, nullopt, {}, {}, "Test", {}});
|
||||||
|
|
||||||
getMutable<ClassType>(grandChildInstanceType)->props = {
|
getMutable<ClassType>(grandChildInstanceType)->props = {
|
||||||
{"Method", {makeFunction(arena, grandChildInstanceType, {}, {stringType})}},
|
{"Method", {makeFunction(arena, grandChildInstanceType, {}, {stringType})}},
|
||||||
};
|
};
|
||||||
|
|
||||||
TypeId grandChildType = arena.addType(ClassType{"GrandChild", {}, baseClassType, nullopt, {}, {}, "Test"});
|
TypeId grandChildType = arena.addType(ClassType{"GrandChild", {}, baseClassType, nullopt, {}, {}, "Test", {}});
|
||||||
getMutable<ClassType>(grandChildType)->props = {
|
getMutable<ClassType>(grandChildType)->props = {
|
||||||
{"New", {makeFunction(arena, nullopt, {}, {grandChildInstanceType})}},
|
{"New", {makeFunction(arena, nullopt, {}, {grandChildInstanceType})}},
|
||||||
};
|
};
|
||||||
globals.globalScope->exportedTypeBindings["GrandChild"] = TypeFun{{}, grandChildInstanceType};
|
globals.globalScope->exportedTypeBindings["GrandChild"] = TypeFun{{}, grandChildInstanceType};
|
||||||
addGlobalBinding(globals, "GrandChild", childClassType, "@test");
|
addGlobalBinding(globals, "GrandChild", childClassType, "@test");
|
||||||
|
|
||||||
TypeId anotherChildInstanceType = arena.addType(ClassType{"AnotherChild", {}, baseClassInstanceType, nullopt, {}, {}, "Test"});
|
TypeId anotherChildInstanceType = arena.addType(ClassType{"AnotherChild", {}, baseClassInstanceType, nullopt, {}, {}, "Test", {}});
|
||||||
|
|
||||||
getMutable<ClassType>(anotherChildInstanceType)->props = {
|
getMutable<ClassType>(anotherChildInstanceType)->props = {
|
||||||
{"Method", {makeFunction(arena, anotherChildInstanceType, {}, {stringType})}},
|
{"Method", {makeFunction(arena, anotherChildInstanceType, {}, {stringType})}},
|
||||||
};
|
};
|
||||||
|
|
||||||
TypeId anotherChildType = arena.addType(ClassType{"AnotherChild", {}, baseClassType, nullopt, {}, {}, "Test"});
|
TypeId anotherChildType = arena.addType(ClassType{"AnotherChild", {}, baseClassType, nullopt, {}, {}, "Test", {}});
|
||||||
getMutable<ClassType>(anotherChildType)->props = {
|
getMutable<ClassType>(anotherChildType)->props = {
|
||||||
{"New", {makeFunction(arena, nullopt, {}, {anotherChildInstanceType})}},
|
{"New", {makeFunction(arena, nullopt, {}, {anotherChildInstanceType})}},
|
||||||
};
|
};
|
||||||
globals.globalScope->exportedTypeBindings["AnotherChild"] = TypeFun{{}, anotherChildInstanceType};
|
globals.globalScope->exportedTypeBindings["AnotherChild"] = TypeFun{{}, anotherChildInstanceType};
|
||||||
addGlobalBinding(globals, "AnotherChild", childClassType, "@test");
|
addGlobalBinding(globals, "AnotherChild", childClassType, "@test");
|
||||||
|
|
||||||
TypeId unrelatedClassInstanceType = arena.addType(ClassType{"UnrelatedClass", {}, nullopt, nullopt, {}, {}, "Test"});
|
TypeId unrelatedClassInstanceType = arena.addType(ClassType{"UnrelatedClass", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
||||||
|
|
||||||
TypeId unrelatedClassType = arena.addType(ClassType{"UnrelatedClass", {}, nullopt, nullopt, {}, {}, "Test"});
|
TypeId unrelatedClassType = arena.addType(ClassType{"UnrelatedClass", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
||||||
getMutable<ClassType>(unrelatedClassType)->props = {
|
getMutable<ClassType>(unrelatedClassType)->props = {
|
||||||
{"New", {makeFunction(arena, nullopt, {}, {unrelatedClassInstanceType})}},
|
{"New", {makeFunction(arena, nullopt, {}, {unrelatedClassInstanceType})}},
|
||||||
};
|
};
|
||||||
|
@ -90,13 +90,13 @@ ClassFixture::ClassFixture()
|
||||||
|
|
||||||
TypeId vector2MetaType = arena.addType(TableType{});
|
TypeId vector2MetaType = arena.addType(TableType{});
|
||||||
|
|
||||||
vector2InstanceType = arena.addType(ClassType{"Vector2", {}, nullopt, vector2MetaType, {}, {}, "Test"});
|
vector2InstanceType = arena.addType(ClassType{"Vector2", {}, nullopt, vector2MetaType, {}, {}, "Test", {}});
|
||||||
getMutable<ClassType>(vector2InstanceType)->props = {
|
getMutable<ClassType>(vector2InstanceType)->props = {
|
||||||
{"X", {numberType}},
|
{"X", {numberType}},
|
||||||
{"Y", {numberType}},
|
{"Y", {numberType}},
|
||||||
};
|
};
|
||||||
|
|
||||||
vector2Type = arena.addType(ClassType{"Vector2", {}, nullopt, nullopt, {}, {}, "Test"});
|
vector2Type = arena.addType(ClassType{"Vector2", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
||||||
getMutable<ClassType>(vector2Type)->props = {
|
getMutable<ClassType>(vector2Type)->props = {
|
||||||
{"New", {makeFunction(arena, nullopt, {numberType, numberType}, {vector2InstanceType})}},
|
{"New", {makeFunction(arena, nullopt, {numberType, numberType}, {vector2InstanceType})}},
|
||||||
};
|
};
|
||||||
|
@ -110,7 +110,7 @@ ClassFixture::ClassFixture()
|
||||||
addGlobalBinding(globals, "Vector2", vector2Type, "@test");
|
addGlobalBinding(globals, "Vector2", vector2Type, "@test");
|
||||||
|
|
||||||
TypeId callableClassMetaType = arena.addType(TableType{});
|
TypeId callableClassMetaType = arena.addType(TableType{});
|
||||||
TypeId callableClassType = arena.addType(ClassType{"CallableClass", {}, nullopt, callableClassMetaType, {}, {}, "Test"});
|
TypeId callableClassType = arena.addType(ClassType{"CallableClass", {}, nullopt, callableClassMetaType, {}, {}, "Test", {}});
|
||||||
getMutable<TableType>(callableClassMetaType)->props = {
|
getMutable<TableType>(callableClassMetaType)->props = {
|
||||||
{"__call", {makeFunction(arena, nullopt, {callableClassType, stringType}, {numberType})}},
|
{"__call", {makeFunction(arena, nullopt, {callableClassType, stringType}, {numberType})}},
|
||||||
};
|
};
|
||||||
|
@ -119,7 +119,7 @@ ClassFixture::ClassFixture()
|
||||||
auto addIndexableClass = [&arena, &globals](const char* className, TypeId keyType, TypeId returnType) {
|
auto addIndexableClass = [&arena, &globals](const char* className, TypeId keyType, TypeId returnType) {
|
||||||
TypeId indexableClassMetaType = arena.addType(TableType{});
|
TypeId indexableClassMetaType = arena.addType(TableType{});
|
||||||
TypeId indexableClassType =
|
TypeId indexableClassType =
|
||||||
arena.addType(ClassType{className, {}, nullopt, indexableClassMetaType, {}, {}, "Test", TableIndexer{keyType, returnType}});
|
arena.addType(ClassType{className, {}, nullopt, indexableClassMetaType, {}, {}, "Test", {}, TableIndexer{keyType, returnType}});
|
||||||
globals.globalScope->exportedTypeBindings[className] = TypeFun{{}, indexableClassType};
|
globals.globalScope->exportedTypeBindings[className] = TypeFun{{}, indexableClassType};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
144
tests/EqSat.language.test.cpp
Normal file
144
tests/EqSat.language.test.cpp
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
|
#include <doctest.h>
|
||||||
|
|
||||||
|
#include "Luau/Id.h"
|
||||||
|
#include "Luau/Language.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
LUAU_EQSAT_ATOM(I32, int);
|
||||||
|
LUAU_EQSAT_ATOM(Bool, bool);
|
||||||
|
LUAU_EQSAT_ATOM(Str, std::string);
|
||||||
|
|
||||||
|
LUAU_EQSAT_FIELD(Left);
|
||||||
|
LUAU_EQSAT_FIELD(Right);
|
||||||
|
LUAU_EQSAT_NODE_FIELDS(Add, Left, Right);
|
||||||
|
|
||||||
|
using namespace Luau;
|
||||||
|
|
||||||
|
using Value = EqSat::Language<I32, Bool, Str, Add>;
|
||||||
|
|
||||||
|
TEST_SUITE_BEGIN("EqSatLanguage");
|
||||||
|
|
||||||
|
TEST_CASE("atom_equality")
|
||||||
|
{
|
||||||
|
CHECK(I32{0} == I32{0});
|
||||||
|
CHECK(I32{0} != I32{1});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("node_equality")
|
||||||
|
{
|
||||||
|
CHECK(Add{EqSat::Id{0}, EqSat::Id{0}} == Add{EqSat::Id{0}, EqSat::Id{0}});
|
||||||
|
CHECK(Add{EqSat::Id{1}, EqSat::Id{0}} != Add{EqSat::Id{0}, EqSat::Id{0}});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("language_get")
|
||||||
|
{
|
||||||
|
Value v{I32{5}};
|
||||||
|
|
||||||
|
auto i = v.get<I32>();
|
||||||
|
REQUIRE(i);
|
||||||
|
CHECK(i->value());
|
||||||
|
|
||||||
|
CHECK(!v.get<Bool>());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("language_copy_ctor")
|
||||||
|
{
|
||||||
|
Value v1{I32{5}};
|
||||||
|
Value v2 = v1;
|
||||||
|
|
||||||
|
auto i1 = v1.get<I32>();
|
||||||
|
auto i2 = v2.get<I32>();
|
||||||
|
REQUIRE(i1);
|
||||||
|
REQUIRE(i2);
|
||||||
|
CHECK(i1->value() == i2->value());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("language_move_ctor")
|
||||||
|
{
|
||||||
|
Value v1{Str{"hello"}};
|
||||||
|
{
|
||||||
|
auto s1 = v1.get<Str>();
|
||||||
|
REQUIRE(s1);
|
||||||
|
CHECK(s1->value() == "hello");
|
||||||
|
}
|
||||||
|
|
||||||
|
Value v2 = std::move(v1);
|
||||||
|
|
||||||
|
auto s1 = v1.get<Str>();
|
||||||
|
REQUIRE(s1);
|
||||||
|
CHECK(s1->value() == ""); // this also tests the dtor.
|
||||||
|
|
||||||
|
auto s2 = v2.get<Str>();
|
||||||
|
REQUIRE(s2);
|
||||||
|
CHECK(s2->value() == "hello");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("language_equality")
|
||||||
|
{
|
||||||
|
Value v1{I32{0}};
|
||||||
|
Value v2{I32{0}};
|
||||||
|
Value v3{I32{1}};
|
||||||
|
Value v4{Bool{true}};
|
||||||
|
Value v5{Add{EqSat::Id{0}, EqSat::Id{1}}};
|
||||||
|
|
||||||
|
CHECK(v1 == v2);
|
||||||
|
CHECK(v2 != v3);
|
||||||
|
CHECK(v3 != v4);
|
||||||
|
CHECK(v4 != v5);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("language_is_mappable")
|
||||||
|
{
|
||||||
|
std::unordered_map<Value, int, Value::Hash> map;
|
||||||
|
|
||||||
|
Value v1{I32{5}};
|
||||||
|
Value v2{I32{5}};
|
||||||
|
Value v3{Bool{true}};
|
||||||
|
Value v4{Add{EqSat::Id{0}, EqSat::Id{1}}};
|
||||||
|
|
||||||
|
map[v1] = 1;
|
||||||
|
map[v2] = 2;
|
||||||
|
map[v3] = 42;
|
||||||
|
map[v4] = 37;
|
||||||
|
|
||||||
|
CHECK(map[v1] == 2);
|
||||||
|
CHECK(map[v2] == 2);
|
||||||
|
CHECK(map[v3] == 42);
|
||||||
|
CHECK(map[v4] == 37);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("node_field")
|
||||||
|
{
|
||||||
|
EqSat::Id left{0};
|
||||||
|
EqSat::Id right{1};
|
||||||
|
|
||||||
|
Add add{left, right};
|
||||||
|
|
||||||
|
EqSat::Id left2 = add.field<Left>();
|
||||||
|
EqSat::Id right2 = add.field<Right>();
|
||||||
|
|
||||||
|
CHECK(left == left2);
|
||||||
|
CHECK(left != right2);
|
||||||
|
CHECK(right == right2);
|
||||||
|
CHECK(right != left2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("language_operands")
|
||||||
|
{
|
||||||
|
Value v1{I32{0}};
|
||||||
|
CHECK(v1.operands().empty());
|
||||||
|
|
||||||
|
Value v2{Add{EqSat::Id{0}, EqSat::Id{1}}};
|
||||||
|
const Add* add = v2.get<Add>();
|
||||||
|
REQUIRE(add);
|
||||||
|
|
||||||
|
EqSat::Slice<EqSat::Id> actual = v2.operands();
|
||||||
|
CHECK(actual.size() == 2);
|
||||||
|
CHECK(actual[0] == add->field<Left>());
|
||||||
|
CHECK(actual[1] == add->field<Right>());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_SUITE_END();
|
197
tests/EqSat.propositional.test.cpp
Normal file
197
tests/EqSat.propositional.test.cpp
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
|
#include <doctest.h>
|
||||||
|
|
||||||
|
#include "Luau/EGraph.h"
|
||||||
|
#include "Luau/Id.h"
|
||||||
|
#include "Luau/Language.h"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
LUAU_EQSAT_ATOM(Var, std::string);
|
||||||
|
LUAU_EQSAT_ATOM(Bool, bool);
|
||||||
|
LUAU_EQSAT_NODE_ARRAY(Not, 1);
|
||||||
|
LUAU_EQSAT_NODE_ARRAY(And, 2);
|
||||||
|
LUAU_EQSAT_NODE_ARRAY(Or, 2);
|
||||||
|
LUAU_EQSAT_NODE_ARRAY(Implies, 2);
|
||||||
|
|
||||||
|
using namespace Luau;
|
||||||
|
|
||||||
|
using PropositionalLogic = EqSat::Language<Var, Bool, Not, And, Or, Implies>;
|
||||||
|
|
||||||
|
using EGraph = EqSat::EGraph<PropositionalLogic, struct ConstantFold>;
|
||||||
|
|
||||||
|
struct ConstantFold
|
||||||
|
{
|
||||||
|
using Data = std::optional<bool>;
|
||||||
|
|
||||||
|
Data make(const EGraph& egraph, const Var& var) const
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
Data make(const EGraph& egraph, const Bool& b) const
|
||||||
|
{
|
||||||
|
return b.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
Data make(const EGraph& egraph, const Not& n) const
|
||||||
|
{
|
||||||
|
Data data = egraph[n[0]].data;
|
||||||
|
if (data)
|
||||||
|
return !*data;
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
Data make(const EGraph& egraph, const And& a) const
|
||||||
|
{
|
||||||
|
Data l = egraph[a[0]].data;
|
||||||
|
Data r = egraph[a[1]].data;
|
||||||
|
if (l && r)
|
||||||
|
return *l && *r;
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
Data make(const EGraph& egraph, const Or& o) const
|
||||||
|
{
|
||||||
|
Data l = egraph[o[0]].data;
|
||||||
|
Data r = egraph[o[1]].data;
|
||||||
|
if (l && r)
|
||||||
|
return *l || *r;
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
Data make(const EGraph& egraph, const Implies& i) const
|
||||||
|
{
|
||||||
|
Data antecedent = egraph[i[0]].data;
|
||||||
|
Data consequent = egraph[i[1]].data;
|
||||||
|
if (antecedent && consequent)
|
||||||
|
return !*antecedent || *consequent;
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void join(Data& a, const Data& b) const
|
||||||
|
{
|
||||||
|
if (!a && b)
|
||||||
|
a = b;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_SUITE_BEGIN("EqSatPropositionalLogic");
|
||||||
|
|
||||||
|
TEST_CASE("egraph_hashconsing")
|
||||||
|
{
|
||||||
|
EGraph egraph;
|
||||||
|
|
||||||
|
EqSat::Id id1 = egraph.add(Bool{true});
|
||||||
|
EqSat::Id id2 = egraph.add(Bool{true});
|
||||||
|
EqSat::Id id3 = egraph.add(Bool{false});
|
||||||
|
|
||||||
|
CHECK(id1 == id2);
|
||||||
|
CHECK(id2 != id3);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("egraph_data")
|
||||||
|
{
|
||||||
|
EGraph egraph;
|
||||||
|
|
||||||
|
EqSat::Id id1 = egraph.add(Bool{true});
|
||||||
|
EqSat::Id id2 = egraph.add(Bool{false});
|
||||||
|
|
||||||
|
CHECK(egraph[id1].data == true);
|
||||||
|
CHECK(egraph[id2].data == false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("egraph_merge")
|
||||||
|
{
|
||||||
|
EGraph egraph;
|
||||||
|
|
||||||
|
EqSat::Id id1 = egraph.add(Var{"a"});
|
||||||
|
EqSat::Id id2 = egraph.add(Bool{true});
|
||||||
|
egraph.merge(id1, id2);
|
||||||
|
|
||||||
|
CHECK(egraph[id1].data == true);
|
||||||
|
CHECK(egraph[id2].data == true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("const_fold_true_and_true")
|
||||||
|
{
|
||||||
|
EGraph egraph;
|
||||||
|
|
||||||
|
EqSat::Id id1 = egraph.add(Bool{true});
|
||||||
|
EqSat::Id id2 = egraph.add(Bool{true});
|
||||||
|
EqSat::Id id3 = egraph.add(And{id1, id2});
|
||||||
|
|
||||||
|
CHECK(egraph[id3].data == true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("const_fold_true_and_false")
|
||||||
|
{
|
||||||
|
EGraph egraph;
|
||||||
|
|
||||||
|
EqSat::Id id1 = egraph.add(Bool{true});
|
||||||
|
EqSat::Id id2 = egraph.add(Bool{false});
|
||||||
|
EqSat::Id id3 = egraph.add(And{id1, id2});
|
||||||
|
|
||||||
|
CHECK(egraph[id3].data == false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("const_fold_false_and_false")
|
||||||
|
{
|
||||||
|
EGraph egraph;
|
||||||
|
|
||||||
|
EqSat::Id id1 = egraph.add(Bool{false});
|
||||||
|
EqSat::Id id2 = egraph.add(Bool{false});
|
||||||
|
EqSat::Id id3 = egraph.add(And{id1, id2});
|
||||||
|
|
||||||
|
CHECK(egraph[id3].data == false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("implications")
|
||||||
|
{
|
||||||
|
EGraph egraph;
|
||||||
|
|
||||||
|
EqSat::Id t = egraph.add(Bool{true});
|
||||||
|
EqSat::Id f = egraph.add(Bool{false});
|
||||||
|
|
||||||
|
EqSat::Id a = egraph.add(Implies{t, t}); // true
|
||||||
|
EqSat::Id b = egraph.add(Implies{t, f}); // false
|
||||||
|
EqSat::Id c = egraph.add(Implies{f, t}); // true
|
||||||
|
EqSat::Id d = egraph.add(Implies{f, f}); // true
|
||||||
|
|
||||||
|
CHECK(egraph[a].data == true);
|
||||||
|
CHECK(egraph[b].data == false);
|
||||||
|
CHECK(egraph[c].data == true);
|
||||||
|
CHECK(egraph[d].data == true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("merge_x_and_y")
|
||||||
|
{
|
||||||
|
EGraph egraph;
|
||||||
|
|
||||||
|
EqSat::Id x = egraph.add(Var{"x"});
|
||||||
|
EqSat::Id y = egraph.add(Var{"y"});
|
||||||
|
|
||||||
|
EqSat::Id a = egraph.add(Var{"a"});
|
||||||
|
EqSat::Id ax = egraph.add(And{a, x});
|
||||||
|
EqSat::Id ay = egraph.add(And{a, y});
|
||||||
|
|
||||||
|
egraph.merge(x, y); // [x y] [ax] [ay] [a]
|
||||||
|
CHECK_EQ(egraph.size(), 4);
|
||||||
|
CHECK_EQ(egraph.find(x), egraph.find(y));
|
||||||
|
CHECK_NE(egraph.find(ax), egraph.find(ay));
|
||||||
|
CHECK_NE(egraph.find(a), egraph.find(x));
|
||||||
|
CHECK_NE(egraph.find(a), egraph.find(y));
|
||||||
|
|
||||||
|
egraph.rebuild(); // [x y] [ax ay] [a]
|
||||||
|
CHECK_EQ(egraph.size(), 3);
|
||||||
|
CHECK_EQ(egraph.find(x), egraph.find(y));
|
||||||
|
CHECK_EQ(egraph.find(ax), egraph.find(ay));
|
||||||
|
CHECK_NE(egraph.find(a), egraph.find(x));
|
||||||
|
CHECK_NE(egraph.find(a), egraph.find(y));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_SUITE_END();
|
58
tests/EqSat.slice.test.cpp
Normal file
58
tests/EqSat.slice.test.cpp
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
|
#include <doctest.h>
|
||||||
|
|
||||||
|
#include "Luau/Slice.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace Luau;
|
||||||
|
|
||||||
|
TEST_SUITE_BEGIN("EqSatSlice");
|
||||||
|
|
||||||
|
TEST_CASE("slice_is_a_view_over_array")
|
||||||
|
{
|
||||||
|
std::array<int, 8> a{1, 2, 3, 4, 5, 6, 7, 8};
|
||||||
|
|
||||||
|
EqSat::Slice<int> slice{a};
|
||||||
|
|
||||||
|
CHECK(slice.data() == a.data());
|
||||||
|
CHECK(slice.size() == a.size());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < a.size(); ++i)
|
||||||
|
{
|
||||||
|
CHECK(slice[i] == a[i]);
|
||||||
|
CHECK(&slice[i] == &a[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("slice_is_a_view_over_vector")
|
||||||
|
{
|
||||||
|
std::vector<int> vector{1, 2, 3, 4, 5, 6, 7, 8};
|
||||||
|
|
||||||
|
EqSat::Slice<int> slice{vector.data(), vector.size()};
|
||||||
|
|
||||||
|
CHECK(slice.data() == vector.data());
|
||||||
|
CHECK(slice.size() == vector.size());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < vector.size(); ++i)
|
||||||
|
{
|
||||||
|
CHECK(slice[i] == vector[i]);
|
||||||
|
CHECK(&slice[i] == &vector[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("mutate_via_slice")
|
||||||
|
{
|
||||||
|
std::array<int, 2> a{1, 2};
|
||||||
|
CHECK(a[0] == 1);
|
||||||
|
CHECK(a[1] == 2);
|
||||||
|
|
||||||
|
EqSat::Slice<int> slice{a};
|
||||||
|
slice[0] = 42;
|
||||||
|
slice[1] = 37;
|
||||||
|
|
||||||
|
CHECK(a[0] == 42);
|
||||||
|
CHECK(a[1] == 37);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_SUITE_END();
|
|
@ -658,7 +658,7 @@ void createSomeClasses(Frontend* frontend)
|
||||||
|
|
||||||
ScopePtr moduleScope = globals.globalScope;
|
ScopePtr moduleScope = globals.globalScope;
|
||||||
|
|
||||||
TypeId parentType = arena.addType(ClassType{"Parent", {}, frontend->builtinTypes->classType, std::nullopt, {}, nullptr, "Test"});
|
TypeId parentType = arena.addType(ClassType{"Parent", {}, frontend->builtinTypes->classType, std::nullopt, {}, nullptr, "Test", {}});
|
||||||
|
|
||||||
ClassType* parentClass = getMutable<ClassType>(parentType);
|
ClassType* parentClass = getMutable<ClassType>(parentType);
|
||||||
parentClass->props["method"] = {makeFunction(arena, parentType, {}, {})};
|
parentClass->props["method"] = {makeFunction(arena, parentType, {}, {})};
|
||||||
|
@ -668,17 +668,17 @@ void createSomeClasses(Frontend* frontend)
|
||||||
addGlobalBinding(globals, "Parent", {parentType});
|
addGlobalBinding(globals, "Parent", {parentType});
|
||||||
moduleScope->exportedTypeBindings["Parent"] = TypeFun{{}, parentType};
|
moduleScope->exportedTypeBindings["Parent"] = TypeFun{{}, parentType};
|
||||||
|
|
||||||
TypeId childType = arena.addType(ClassType{"Child", {}, parentType, std::nullopt, {}, nullptr, "Test"});
|
TypeId childType = arena.addType(ClassType{"Child", {}, parentType, std::nullopt, {}, nullptr, "Test", {}});
|
||||||
|
|
||||||
addGlobalBinding(globals, "Child", {childType});
|
addGlobalBinding(globals, "Child", {childType});
|
||||||
moduleScope->exportedTypeBindings["Child"] = TypeFun{{}, childType};
|
moduleScope->exportedTypeBindings["Child"] = TypeFun{{}, childType};
|
||||||
|
|
||||||
TypeId anotherChildType = arena.addType(ClassType{"AnotherChild", {}, parentType, std::nullopt, {}, nullptr, "Test"});
|
TypeId anotherChildType = arena.addType(ClassType{"AnotherChild", {}, parentType, std::nullopt, {}, nullptr, "Test", {}});
|
||||||
|
|
||||||
addGlobalBinding(globals, "AnotherChild", {anotherChildType});
|
addGlobalBinding(globals, "AnotherChild", {anotherChildType});
|
||||||
moduleScope->exportedTypeBindings["AnotherChild"] = TypeFun{{}, anotherChildType};
|
moduleScope->exportedTypeBindings["AnotherChild"] = TypeFun{{}, anotherChildType};
|
||||||
|
|
||||||
TypeId unrelatedType = arena.addType(ClassType{"Unrelated", {}, frontend->builtinTypes->classType, std::nullopt, {}, nullptr, "Test"});
|
TypeId unrelatedType = arena.addType(ClassType{"Unrelated", {}, frontend->builtinTypes->classType, std::nullopt, {}, nullptr, "Test", {}});
|
||||||
|
|
||||||
addGlobalBinding(globals, "Unrelated", {unrelatedType});
|
addGlobalBinding(globals, "Unrelated", {unrelatedType});
|
||||||
moduleScope->exportedTypeBindings["Unrelated"] = TypeFun{{}, unrelatedType};
|
moduleScope->exportedTypeBindings["Unrelated"] = TypeFun{{}, unrelatedType};
|
||||||
|
|
|
@ -112,7 +112,7 @@ TEST_CASE_FIXTURE(GeneralizationFixture, "dont_traverse_into_class_types_when_ge
|
||||||
{
|
{
|
||||||
auto [propTy, _] = freshType();
|
auto [propTy, _] = freshType();
|
||||||
|
|
||||||
TypeId cursedClass = arena.addType(ClassType{"Cursed", {{"oh_no", Property::readonly(propTy)}}, std::nullopt, std::nullopt, {}, {}, ""});
|
TypeId cursedClass = arena.addType(ClassType{"Cursed", {{"oh_no", Property::readonly(propTy)}}, std::nullopt, std::nullopt, {}, {}, "", {}});
|
||||||
|
|
||||||
auto genClass = generalize(cursedClass);
|
auto genClass = generalize(cursedClass);
|
||||||
REQUIRE(genClass);
|
REQUIRE(genClass);
|
||||||
|
|
|
@ -1485,7 +1485,7 @@ TEST_CASE_FIXTURE(Fixture, "LintHygieneUAF")
|
||||||
TEST_CASE_FIXTURE(BuiltinsFixture, "DeprecatedApiTyped")
|
TEST_CASE_FIXTURE(BuiltinsFixture, "DeprecatedApiTyped")
|
||||||
{
|
{
|
||||||
unfreeze(frontend.globals.globalTypes);
|
unfreeze(frontend.globals.globalTypes);
|
||||||
TypeId instanceType = frontend.globals.globalTypes.addType(ClassType{"Instance", {}, std::nullopt, std::nullopt, {}, {}, "Test"});
|
TypeId instanceType = frontend.globals.globalTypes.addType(ClassType{"Instance", {}, std::nullopt, std::nullopt, {}, {}, "Test", {}});
|
||||||
persist(instanceType);
|
persist(instanceType);
|
||||||
frontend.globals.globalScope->exportedTypeBindings["Instance"] = TypeFun{{}, instanceType};
|
frontend.globals.globalScope->exportedTypeBindings["Instance"] = TypeFun{{}, instanceType};
|
||||||
|
|
||||||
|
|
|
@ -244,13 +244,13 @@ TEST_CASE_FIXTURE(Fixture, "clone_class")
|
||||||
{
|
{
|
||||||
{"__add", {builtinTypes->anyType}},
|
{"__add", {builtinTypes->anyType}},
|
||||||
},
|
},
|
||||||
std::nullopt, std::nullopt, {}, {}, "Test"}};
|
std::nullopt, std::nullopt, {}, {}, "Test", {}}};
|
||||||
Type exampleClass{ClassType{"ExampleClass",
|
Type exampleClass{ClassType{"ExampleClass",
|
||||||
{
|
{
|
||||||
{"PropOne", {builtinTypes->numberType}},
|
{"PropOne", {builtinTypes->numberType}},
|
||||||
{"PropTwo", {builtinTypes->stringType}},
|
{"PropTwo", {builtinTypes->stringType}},
|
||||||
},
|
},
|
||||||
std::nullopt, &exampleMetaClass, {}, {}, "Test"}};
|
std::nullopt, &exampleMetaClass, {}, {}, "Test", {}}};
|
||||||
|
|
||||||
TypeArena dest;
|
TypeArena dest;
|
||||||
CloneState cloneState{builtinTypes};
|
CloneState cloneState{builtinTypes};
|
||||||
|
|
|
@ -146,7 +146,7 @@ struct SubtypeFixture : Fixture
|
||||||
|
|
||||||
TypeId cls(const std::string& name, std::optional<TypeId> parent = std::nullopt)
|
TypeId cls(const std::string& name, std::optional<TypeId> parent = std::nullopt)
|
||||||
{
|
{
|
||||||
return arena.addType(ClassType{name, {}, parent.value_or(builtinTypes->classType), {}, {}, nullptr, ""});
|
return arena.addType(ClassType{name, {}, parent.value_or(builtinTypes->classType), {}, {}, nullptr, "", {}});
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeId cls(const std::string& name, ClassType::Props&& props)
|
TypeId cls(const std::string& name, ClassType::Props&& props)
|
||||||
|
|
|
@ -21,13 +21,13 @@ struct ToDotClassFixture : Fixture
|
||||||
|
|
||||||
TypeId baseClassMetaType = arena.addType(TableType{});
|
TypeId baseClassMetaType = arena.addType(TableType{});
|
||||||
|
|
||||||
TypeId baseClassInstanceType = arena.addType(ClassType{"BaseClass", {}, std::nullopt, baseClassMetaType, {}, {}, "Test"});
|
TypeId baseClassInstanceType = arena.addType(ClassType{"BaseClass", {}, std::nullopt, baseClassMetaType, {}, {}, "Test", {}});
|
||||||
getMutable<ClassType>(baseClassInstanceType)->props = {
|
getMutable<ClassType>(baseClassInstanceType)->props = {
|
||||||
{"BaseField", {builtinTypes->numberType}},
|
{"BaseField", {builtinTypes->numberType}},
|
||||||
};
|
};
|
||||||
frontend.globals.globalScope->exportedTypeBindings["BaseClass"] = TypeFun{{}, baseClassInstanceType};
|
frontend.globals.globalScope->exportedTypeBindings["BaseClass"] = TypeFun{{}, baseClassInstanceType};
|
||||||
|
|
||||||
TypeId childClassInstanceType = arena.addType(ClassType{"ChildClass", {}, baseClassInstanceType, std::nullopt, {}, {}, "Test"});
|
TypeId childClassInstanceType = arena.addType(ClassType{"ChildClass", {}, baseClassInstanceType, std::nullopt, {}, {}, "Test", {}});
|
||||||
getMutable<ClassType>(childClassInstanceType)->props = {
|
getMutable<ClassType>(childClassInstanceType)->props = {
|
||||||
{"ChildField", {builtinTypes->stringType}},
|
{"ChildField", {builtinTypes->stringType}},
|
||||||
};
|
};
|
||||||
|
|
|
@ -706,19 +706,19 @@ TEST_CASE_FIXTURE(Fixture, "read_write_class_properties")
|
||||||
|
|
||||||
unfreeze(arena);
|
unfreeze(arena);
|
||||||
|
|
||||||
TypeId instanceType = arena.addType(ClassType{"Instance", {}, nullopt, nullopt, {}, {}, "Test"});
|
TypeId instanceType = arena.addType(ClassType{"Instance", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
||||||
getMutable<ClassType>(instanceType)->props = {{"Parent", Property::rw(instanceType)}};
|
getMutable<ClassType>(instanceType)->props = {{"Parent", Property::rw(instanceType)}};
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
TypeId workspaceType = arena.addType(ClassType{"Workspace", {}, nullopt, nullopt, {}, {}, "Test"});
|
TypeId workspaceType = arena.addType(ClassType{"Workspace", {}, nullopt, nullopt, {}, {}, "Test", {}});
|
||||||
|
|
||||||
TypeId scriptType =
|
TypeId scriptType =
|
||||||
arena.addType(ClassType{"Script", {{"Parent", Property::rw(workspaceType, instanceType)}}, instanceType, nullopt, {}, {}, "Test"});
|
arena.addType(ClassType{"Script", {{"Parent", Property::rw(workspaceType, instanceType)}}, instanceType, nullopt, {}, {}, "Test", {}});
|
||||||
|
|
||||||
TypeId partType = arena.addType(
|
TypeId partType = arena.addType(
|
||||||
ClassType{"Part", {{"BrickColor", Property::rw(builtinTypes->stringType)}, {"Parent", Property::rw(workspaceType, instanceType)}},
|
ClassType{"Part", {{"BrickColor", Property::rw(builtinTypes->stringType)}, {"Parent", Property::rw(workspaceType, instanceType)}},
|
||||||
instanceType, nullopt, {}, {}, "Test"});
|
instanceType, nullopt, {}, {}, "Test", {}});
|
||||||
|
|
||||||
getMutable<ClassType>(workspaceType)->props = {{"Script", Property::readonly(scriptType)}, {"Part", Property::readonly(partType)}};
|
getMutable<ClassType>(workspaceType)->props = {{"Script", Property::readonly(scriptType)}, {"Part", Property::readonly(partType)}};
|
||||||
|
|
||||||
|
|
|
@ -66,14 +66,14 @@ struct RefinementClassFixture : BuiltinsFixture
|
||||||
std::optional<TypeId> rootSuper = std::make_optional(builtinTypes->classType);
|
std::optional<TypeId> rootSuper = std::make_optional(builtinTypes->classType);
|
||||||
|
|
||||||
unfreeze(arena);
|
unfreeze(arena);
|
||||||
TypeId vec3 = arena.addType(ClassType{"Vector3", {}, rootSuper, std::nullopt, {}, nullptr, "Test"});
|
TypeId vec3 = arena.addType(ClassType{"Vector3", {}, rootSuper, std::nullopt, {}, nullptr, "Test", {}});
|
||||||
getMutable<ClassType>(vec3)->props = {
|
getMutable<ClassType>(vec3)->props = {
|
||||||
{"X", Property{builtinTypes->numberType}},
|
{"X", Property{builtinTypes->numberType}},
|
||||||
{"Y", Property{builtinTypes->numberType}},
|
{"Y", Property{builtinTypes->numberType}},
|
||||||
{"Z", Property{builtinTypes->numberType}},
|
{"Z", Property{builtinTypes->numberType}},
|
||||||
};
|
};
|
||||||
|
|
||||||
TypeId inst = arena.addType(ClassType{"Instance", {}, rootSuper, std::nullopt, {}, nullptr, "Test"});
|
TypeId inst = arena.addType(ClassType{"Instance", {}, rootSuper, std::nullopt, {}, nullptr, "Test", {}});
|
||||||
|
|
||||||
TypePackId isAParams = arena.addTypePack({inst, builtinTypes->stringType});
|
TypePackId isAParams = arena.addTypePack({inst, builtinTypes->stringType});
|
||||||
TypePackId isARets = arena.addTypePack({builtinTypes->booleanType});
|
TypePackId isARets = arena.addTypePack({builtinTypes->booleanType});
|
||||||
|
@ -86,8 +86,8 @@ struct RefinementClassFixture : BuiltinsFixture
|
||||||
{"IsA", Property{isA}},
|
{"IsA", Property{isA}},
|
||||||
};
|
};
|
||||||
|
|
||||||
TypeId folder = frontend.globals.globalTypes.addType(ClassType{"Folder", {}, inst, std::nullopt, {}, nullptr, "Test"});
|
TypeId folder = frontend.globals.globalTypes.addType(ClassType{"Folder", {}, inst, std::nullopt, {}, nullptr, "Test", {}});
|
||||||
TypeId part = frontend.globals.globalTypes.addType(ClassType{"Part", {}, inst, std::nullopt, {}, nullptr, "Test"});
|
TypeId part = frontend.globals.globalTypes.addType(ClassType{"Part", {}, inst, std::nullopt, {}, nullptr, "Test", {}});
|
||||||
getMutable<ClassType>(part)->props = {
|
getMutable<ClassType>(part)->props = {
|
||||||
{"Position", Property{vec3}},
|
{"Position", Property{vec3}},
|
||||||
};
|
};
|
||||||
|
|
|
@ -314,7 +314,7 @@ TEST_CASE("tagging_tables")
|
||||||
|
|
||||||
TEST_CASE("tagging_classes")
|
TEST_CASE("tagging_classes")
|
||||||
{
|
{
|
||||||
Type base{ClassType{"Base", {}, std::nullopt, std::nullopt, {}, nullptr, "Test"}};
|
Type base{ClassType{"Base", {}, std::nullopt, std::nullopt, {}, nullptr, "Test", {}}};
|
||||||
CHECK(!Luau::hasTag(&base, "foo"));
|
CHECK(!Luau::hasTag(&base, "foo"));
|
||||||
Luau::attachTag(&base, "foo");
|
Luau::attachTag(&base, "foo");
|
||||||
CHECK(Luau::hasTag(&base, "foo"));
|
CHECK(Luau::hasTag(&base, "foo"));
|
||||||
|
@ -322,8 +322,8 @@ TEST_CASE("tagging_classes")
|
||||||
|
|
||||||
TEST_CASE("tagging_subclasses")
|
TEST_CASE("tagging_subclasses")
|
||||||
{
|
{
|
||||||
Type base{ClassType{"Base", {}, std::nullopt, std::nullopt, {}, nullptr, "Test"}};
|
Type base{ClassType{"Base", {}, std::nullopt, std::nullopt, {}, nullptr, "Test", {}}};
|
||||||
Type derived{ClassType{"Derived", {}, &base, std::nullopt, {}, nullptr, "Test"}};
|
Type derived{ClassType{"Derived", {}, &base, std::nullopt, {}, nullptr, "Test", {}}};
|
||||||
|
|
||||||
CHECK(!Luau::hasTag(&base, "foo"));
|
CHECK(!Luau::hasTag(&base, "foo"));
|
||||||
CHECK(!Luau::hasTag(&derived, "foo"));
|
CHECK(!Luau::hasTag(&derived, "foo"));
|
||||||
|
|
Loading…
Reference in a new issue