#include "engine/terms.h"
#include "engine/engine.h"
#include "engine/identifiers.h"
#include "engine/resolving_output.h"
#include "engine/symlevel/cbc_file.h"
#include "engine/symlevel/definitions.h"
#include "engine/symlevel/io/file_id.h"
#include "engine/symlevel/io/stream_file_reader.h"
#include "engine/symlevel/reader.h"
#include "engine/symlevel/region_data.h"
#include "engine/symlevel/type_kind.h"
#include "string.h"
#include "utils/assertion.h"
#include "utils/heap.h"
#include "utils/iterators.h"
#include "utils/ostream.h"
#include <alloca.h>
#include <cstdint>
#include <cstdlib>
#include <memory>
#include <mutex>
#include <optional>
#include <unordered_set>
namespace Engine {
struct TermData {
TermId identifier;
uint32_t hash;
uint16_t length;
TermFlags flags;
Term subterms[];
void InitAfterSubterms(TermId identifier, uint16_t length, TermFlags flags)
{
uint32_t hash = 0;
for (int i = 0; i < length; i++) {
hash = 31 * hash + subterms->Hash();
}
Init(identifier, identifier.Hash() ^ hash, length, flags);
}
void Init(TermId identifier, uint32_t hash, uint16_t length, TermFlags flags)
{
this->identifier = identifier;
this->length = length;
this->hash = hash;
this->flags = flags;
}
};
enum Tag : uint8_t {
NIL = 0x0,
REF = 0x1,
AOT_REF = 0x2,
CANGJIE_ARRAY = 0x3,
VARRAY = 0x4,
ENUM_WRAPPER = 0x5,
C_POINTER = 0x6,
FUNC_TYPE_VAR = 0x7,
CLASS_TYPE_VAR = 0x8,
GENERIC_RECORD = 0x9,
GENERIC_REFERENCE = 0xa,
NULLABLE = 0xb,
FUNCTIONAL = 0xc,
REC = 0xd,
AOT_REC = 0xe,
NON_NULLABLE = 0xf,
GENERIC_AOT_REF = 0x10,
GENERIC_AOT_REC = 0x11,
TUPLE = 0x12,
BOX = 0x13,
FST = 0x14,
};
static TermData* AllocateTerm(Memory::Heap& allocator, size_t subtermCount = 0)
{
return static_cast<TermData*>(allocator.Allocate(sizeof(TermData) + subtermCount * sizeof(Term), alignof(TermData))
);
}
struct BuiltinTerms {
void* memory;
void* primitives;
void* classTypeVars;
void* funcTypeVars;
static constexpr size_t TV_COUNT = 256;
static constexpr size_t PRIM_COUNT = FIRST_NON_PRIMITIVE;
BuiltinTerms(BuiltinTerms const&) = delete;
~BuiltinTerms() { std::free(memory); }
inline static TermData* DataAt(void* memory, size_t idx)
{
char* ptr = reinterpret_cast<char*>(memory);
ptr += sizeof(TermData) * idx;
return reinterpret_cast<TermData*>(ptr);
}
inline TermData* Primitive(size_t i) const
{
ASSERT(i < PRIM_COUNT);
return DataAt(primitives, i);
}
inline TermData* ClassTv(size_t i) const
{
ASSERT(i < TV_COUNT);
return DataAt(classTypeVars, i);
}
inline TermData* FuncTv(size_t i) const
{
ASSERT(i < TV_COUNT);
return DataAt(funcTypeVars, i);
}
BuiltinTerms()
{
size_t seed = 0xf123123a;
auto hash = [&seed]() {
constexpr size_t multiplier = 3202034522624059733L;
constexpr size_t addend = 0x421L;
auto next = (multiplier * seed + addend);
return seed = next;
};
void* memory = std::malloc(sizeof(TermData) * (TV_COUNT + TV_COUNT + PRIM_COUNT));
if (!memory)
FATAL("Failed to allocate builtin terms");
void* primitives = DataAt(memory, 0);
void* classTypeVars = DataAt(memory, PRIM_COUNT);
void* funcTypeVars = DataAt(memory, PRIM_COUNT + TV_COUNT);
TermFlags primFlags = {0};
TermFlags tvFlags = {
.isReference = true,
.isGeneric = true,
};
for (size_t i = 0; i < PRIM_COUNT; i++) {
auto kind = TermKind(i);
auto data = DataAt(primitives, i);
data->hash = hash();
data->length = 0;
data->identifier = TagTermId(kind);
data->flags = primFlags;
}
for (size_t i = 0; i < TV_COUNT; i++) {
auto data = DataAt(classTypeVars, i);
data->hash = hash();
data->length = 0;
data->identifier = ClassTvTermId(i);
data->flags = tvFlags;
}
for (size_t i = 0; i < TV_COUNT; i++) {
auto data = DataAt(funcTypeVars, i);
data->hash = hash();
data->length = 0;
data->identifier = FuncTvTermId(i);
data->flags = tvFlags;
}
this->memory = memory;
this->primitives = primitives;
this->classTypeVars = classTypeVars;
this->funcTypeVars = funcTypeVars;
}
};
static BuiltinTerms g_Builtins;
GlobalTerm Term::Predefined(TermKind tk)
{
int num = static_cast<int>(tk);
return GlobalTerm(g_Builtins.Primitive(num));
}
Term Term::ClassTypeVariable(uint8_t tv) { return GlobalTerm(g_Builtins.ClassTv(tv)); }
Term Term::FuncTypeVariable(uint8_t tv) { return GlobalTerm(g_Builtins.FuncTv(tv)); }
Term Term::Definition(Session& session, Identifier<Symlevel::TypeDefinition> type)
{
auto def = Symlevel::Reader::Read(session, type);
bool isRec = def.GetFlags().Is(Symlevel::TypeKind::RECORD);
auto arity = def->arity;
auto* data = AllocateTerm(session.Allocator(), arity);
for (uint8_t i = 0; i < arity; i++) {
data->subterms[i] = ClassTypeVariable(i);
}
data->InitAfterSubterms(
TypeTermId(type),
arity,
{
.isLocal = true,
.isReference = !isRec,
.isAotPromoted = false,
.isGeneric = (arity > 0),
}
);
return LocalTerm(data);
}
static Term Undefined(Session& session, RefIdentifier<Term> termId)
{
auto* data = AllocateTerm(session.Allocator());
data->InitAfterSubterms(
UndefTermId(termId),
0,
{
.isLocal = true,
.isReference = !false,
.isAotPromoted = false,
.isGeneric = false,
}
);
return LocalTerm(data);
}
static bool CompareTermData(TermData* origin, TermData* another)
{
if (another == origin) {
return true;
} else if (another->hash != origin->hash) {
return false;
} else if (another->identifier != origin->identifier) {
return false;
} else if (another->length != origin->length) {
return false;
} else {
auto length = origin->length;
for (auto i = 0; i < length; i++) {
if (!CompareTermData(another->subterms[i].data, origin->subterms[i].data)) {
return false;
}
}
return true;
}
}
Term::Term() : Term(Term::Predefined(TermKind::NIL)) {}
Term::Term(TermData* data) : data(data) {}
LocalTerm Term::AsLocal()
{
ASSERT(IsLocal());
return LocalTerm(data);
}
GlobalTerm Term::AsGlobal()
{
ASSERT(!IsLocal());
return GlobalTerm(data);
}
Term Term::Subterm(uint32_t i) const { return data->subterms[i]; }
TermId Term::GetId() const { return data->identifier; }
TermKind Term::GetKind() const { return data->identifier.GetKind(); }
bool Term::IsFloat() const
{
switch (GetKind()) {
case TermKind::F32:
case TermKind::F64: return true;
default: return false;
}
}
uint32_t Term::GetLength() const { return data->length; }
uint32_t Term::Hash() const { return data->hash; }
std::string Term::GetName(Session& session) const
{
Stream::StringBuffer buf;
GetName(session, buf);
return buf.ToString();
}
void Term::GetName(Session& session, Stream::Output& out) const
{
Stream::ResolvingOutput stream(session, out);
auto printSubTerms = [&](std::string_view prefix, std::string_view suffix, int len) {
stream << prefix;
auto separator = "";
for (int i = 0; i < len; i++) {
stream << separator << Subterm(i);
separator = ", ";
}
stream << suffix;
};
using TK = TermKind;
auto kind = GetKind();
switch (kind) {
case TK::NIL: stream << "Nil"; break;
case TK::VOID: stream << "Void"; break;
case TK::UNIT: stream << "Unit"; break;
case TK::NOTHING: stream << "Nothing"; break;
case TK::BOOLEAN: stream << "Bool"; break;
case TK::I8: stream << "Int8"; break;
case TK::U8: stream << "UInt8"; break;
case TK::I16: stream << "Int16"; break;
case TK::U16: stream << "UInt16"; break;
case TK::I32: stream << "Int32"; break;
case TK::U32: stream << "UInt32"; break;
case TK::UCHAR32: stream << "UChar32"; break;
case TK::I64: stream << "Int64"; break;
case TK::U64: stream << "UInt64"; break;
case TK::IADDR: stream << "IAddr"; break;
case TK::UADDR: stream << "UAddr"; break;
case TK::BSTRING: stream << "BString"; break;
case TK::F16: stream << "Float16"; break;
case TK::F32: stream << "Float32"; break;
case TK::F64: stream << "Float64"; break;
case TK::UNDEFINED: {
auto undef = UndefTermId(*this).GetIdentifier();
auto file = undef.GetFileId();
auto region = undef.GetIndex().GetRegion();
auto index = undef.GetIndex().GetIndex();
out.PrintFmt("$unresolved<%u,%u,%u>", file.id, region, index);
break;
}
case TK::BOX: {
stream << "$box<" << Subterm(0) << '>';
break;
}
case TK::C_POINTER: {
stream << "CPointer<" << Subterm(0) << '>';
break;
}
case TK::NULLABLE: {
stream << "$nullable<" << Subterm(0) << '>';
break;
}
case TK::CANGJIE_ARRAY: {
stream << "$array<" << Subterm(0) << '>';
break;
}
case TK::FUNCTIONAL: {
printSubTerms("(", ") -> ", GetLength() - 1);
stream << Subterm(GetLength() - 1);
break;
}
case TK::TUPLE: {
printSubTerms("(", ")", GetLength());
break;
}
case TK::TYPE: {
auto ident = TypeTermId(*this).GetIdentifier();
auto type = Symlevel::TypeDefinition::Resolve(session, ident);
stream << Symlevel::Reader::Read(session, type.GetName());
if (int len = GetLength(); len > 0) {
printSubTerms("<", ">", len);
}
break;
}
case TK::AOT_TYPE: {
auto& manager = TermManager::Of(session);
std::string_view name = manager.GetNameOfAotType(AotTermId(*this));
stream << name;
if (int len = GetLength(); len > 0) {
printSubTerms("<", ">", len);
}
break;
}
case TK::CLASS_TYPE_VAR: {
auto tv = ClassTvTermId(*this).GetNum();
stream << "%" << tv;
break;
}
case TK::FUNC_TYPE_VAR: {
auto tv = FuncTvTermId(*this).GetNum();
stream << "%%" << tv;
break;
}
default: {
FATAL("Unexpected case %d", GetKind());
}
}
}
bool Term::operator!=(const Term& another) const { return !(*this == another); }
bool Term::operator==(const Term& another) const { return CompareTermData(this->data, another.data); }
bool Term::IsLocal() const { return data->flags.isLocal; }
LocalTerm::LocalTerm(TermData* data) : Term(data) { ASSERT(data->flags.isLocal); }
GlobalTerm::GlobalTerm(TermData* data) : Term(data) { ASSERT(!data->flags.isLocal); }
GlobalTerm LocalTerm::Publish(Session& session)
{
Term term(*this);
return TermManager::Of(session).Globalize(term);
}
bool GlobalTerm::operator==(const GlobalTerm& another) const { return data == another.data; }
bool GlobalTerm::operator!=(const GlobalTerm& another) const { return data != another.data; }
GlobalTerm TermManager::Globalize(Term& term)
{
if (!term.IsLocal()) {
return term.AsGlobal();
}
auto termData = term.data;
for (int i = 0; i < term.GetLength(); i++) {
termData->subterms[i] = Globalize(termData->subterms[i]);
}
std::lock_guard guard(lock);
auto it = cache.find(termData);
if (it != cache.end()) {
return GlobalTerm(*it);
}
auto data = static_cast<TermData*>(malloc(sizeof(TermData) + termData->length * sizeof(Term)));
if (data == nullptr) {
FATAL("Out of memory");
}
for (int i = 0; i < term.GetLength(); i++) {
data->subterms[i] = termData->subterms[i];
}
auto flags = termData->flags;
flags.isLocal = false;
data->Init(termData->identifier, termData->hash, termData->length, flags);
cache.insert(data);
term.data = data;
return term.AsGlobal();
}
static bool IsProperTypeReference(Symlevel::TypeDefinition& def, bool isReference, int arity)
{
if ((def.GetFlags().Is(Symlevel::TypeKind::RECORD)) == isReference) {
return false;
} else if (def->arity != arity) {
return false;
}
return true;
}
Term TermManager::NewAotTerm(
Session& session, std::string_view name, std::vector<Term> const& subterms, bool isReference
)
{
auto& heap = session.Allocator();
auto data = AllocateTerm(heap, subterms.size());
bool isGeneric = false;
auto arity = subterms.size();
for (int i = 0; i < arity; i++) {
data->subterms[i] = subterms[i];
isGeneric = isGeneric || subterms[i].IsGeneric();
}
TermId id = TagTermId(TermKind::NOTHING);
TermFlags flags = {
.isLocal = true,
.isReference = isReference,
.isGeneric = isGeneric,
};
auto type = session.GetEngine().FindType(session, name);
if (type.has_value()) {
ASSERT([&]() -> bool {
auto def = Symlevel::TypeDefinition::Resolve(session, type.value());
return IsProperTypeReference(def, isReference, arity);
}());
id = TypeTermId(*type);
flags.isAotPromoted = true;
} else {
id = AotTermId(InternString(name));
}
data->InitAfterSubterms(id, arity, flags);
return Term(LocalTerm(data));
}
Term TermManager::NewTermWithId(Session& session, TermId id, bool isReference, std::vector<Term> const& subterms)
{
auto& heap = session.Allocator();
auto data = AllocateTerm(heap, subterms.size());
bool isGeneric = false;
auto arity = subterms.size();
for (int i = 0; i < arity; i++) {
data->subterms[i] = subterms[i];
isGeneric = isGeneric || subterms[i].IsGeneric();
}
TermFlags flags = {
.isLocal = true,
.isReference = isReference,
.isGeneric = isGeneric,
};
data->InitAfterSubterms(id, arity, flags);
return Term(LocalTerm(data));
}
uint64_t TermManager::Hasher::operator()(TermData* const& data) const { return data->hash; }
bool TermManager::Comparator::operator()(TermData* const& left, TermData* const& right) const
{
if (left == right) {
return true;
} else if (left->hash != right->hash) {
return false;
} else if (left->length != right->length) {
return false;
} else {
auto len = left->length;
for (int i = 0; i < len; i++) {
auto lhs = left->subterms[i];
auto rhs = right->subterms[i];
ASSERT(!lhs.IsLocal());
ASSERT(!rhs.IsLocal());
if (lhs.data != rhs.data) {
return false;
}
}
return true;
}
}
struct TermResolver {
Symlevel::RegionData const& regionData;
Session& session;
Memory::Heap& heap;
IO::FileId fileId;
uint8_t region;
IO::RandomAccessFile& raf;
Symlevel::CbcFile& file;
TermManager& manager;
Term NewUndefined(Symlevel::RefId<Term> refId) { return Undefined(session, RefIdentifier(refId, fileId)); }
bool ReadSubTerms(TermData* data, bool* isGenericLoc, int length, IO::StreamFileReader& reader)
{
using namespace Symlevel;
bool isGeneric = false;
for (int i = 0; i < length; i++) {
auto subtermIdx = reader.ReadULEB();
auto subterm = Resolve(RefId<Term>(region, subtermIdx));
if (subterm.GetKind() == TermKind::UNDEFINED) {
return false;
}
data->subterms[i] = subterm;
isGeneric = isGeneric || subterm.IsGeneric();
}
*isGenericLoc = isGeneric;
return true;
}
Term ResolveTypeDefTerm(
IO::StreamFileReader& reader,
Symlevel::Offset<Symlevel::String> nameOffs,
int expectedLength,
bool isReference,
Symlevel::RefId<Term> refId,
bool wasAot
)
{
using namespace Symlevel;
auto name = Reader::Read(session, fileId, nameOffs);
return ResolveTypeDefTerm(reader, name, expectedLength, isReference, refId, wasAot);
}
Term ResolveTypeDefTerm(
IO::StreamFileReader& reader,
Symlevel::String name,
int expectedLength,
bool isReference,
Symlevel::RefId<Term> refId,
bool wasAot
)
{
using namespace Symlevel;
auto type = session.GetEngine().FindType(session, name);
if (!type.has_value()) {
return NewUndefined(refId);
}
auto identifier = type.value();
auto def = Symlevel::TypeDefinition::Resolve(session, identifier);
bool undefined = !IsProperTypeReference(def, isReference, expectedLength);
if (undefined && wasAot) {
FATAL("Unexpected mismatch of resolved type definition and aot term");
}
if (undefined) {
return NewUndefined(refId);
}
auto data = AllocateTerm(heap, expectedLength);
bool isGeneric = false;
if (!ReadSubTerms(data, &isGeneric, expectedLength, reader)) {
return NewUndefined(refId);
}
data->InitAfterSubterms(
TypeTermId(identifier),
expectedLength,
{
.isLocal = true,
.isReference = isReference,
.isAotPromoted = wasAot,
.isGeneric = isGeneric,
}
);
return Term(LocalTerm(data));
}
Term ResolveAotType(
IO::StreamFileReader& reader,
Symlevel::Offset<Symlevel::String> nameOffs,
int length,
bool isReference,
Symlevel::RefId<Term> refId
)
{
auto name = Symlevel::Reader::Read(session, fileId, nameOffs);
auto term = ResolveTypeDefTerm(reader, name, length, isReference, refId, true);
if (term.GetKind() != TermKind::UNDEFINED) {
return term;
}
auto data = AllocateTerm(heap, length);
bool isGeneric = false;
if (!ReadSubTerms(data, &isGeneric, length, reader)) {
return NewUndefined(refId);
}
TermFlags flags = {
.isLocal = true,
.isReference = isReference,
.isGeneric = isGeneric,
};
auto internedName = manager.InternString(name);
data->InitAfterSubterms(AotTermId(internedName), length, flags);
return Term(LocalTerm(data));
}
Term Resolve(Symlevel::RefId<Term> refId)
{
using namespace Symlevel;
if (refId.GetIndex() < FIRST_NON_PRIMITIVE) {
return Term::Predefined(TermKind(refId.GetIndex()));
}
auto offset = regionData.Query(session, refId);
IO::StreamFileReader reader(raf, file.GetTermSectionOffs() + offset);
auto tag = static_cast<Tag>(reader.ReadU8());
switch (tag) {
case REC:
case REF: {
auto nameOffs = Offset<String>(reader.ReadULEB());
return ResolveTypeDefTerm(reader, nameOffs, 0, tag == REF, refId, false);
}
case GENERIC_RECORD:
case GENERIC_REFERENCE: {
auto nameOffs = Offset<String>(reader.ReadULEB());
auto length = reader.ReadU8();
return ResolveTypeDefTerm(reader, nameOffs, length, tag == GENERIC_REFERENCE, refId, false);
}
case AOT_REC:
case AOT_REF: {
auto nameOffs = Offset<String>(reader.ReadULEB());
return ResolveAotType(reader, nameOffs, 0, tag == AOT_REF, refId);
}
case GENERIC_AOT_REC:
case GENERIC_AOT_REF: {
auto nameOffs = Offset<String>(reader.ReadULEB());
auto length = reader.ReadU8();
bool isRef = tag == GENERIC_AOT_REF;
return ResolveAotType(reader, nameOffs, length, isRef, refId);
}
case FUNCTIONAL: {
auto len = reader.ReadU8() + 1;
auto* data = AllocateTerm(heap, len);
bool isGeneric = false;
if (!ReadSubTerms(data, &isGeneric, len, reader)) {
return NewUndefined(refId);
}
TermFlags flags = {
.isLocal = true,
.isReference = true,
.isGeneric = isGeneric,
};
data->InitAfterSubterms(TagTermId(TermKind::FUNCTIONAL), len, flags);
return Term(LocalTerm(data));
}
case TUPLE: {
auto len = reader.ReadULEB();
auto* data = AllocateTerm(heap, len);
bool isGeneric = false;
if (!ReadSubTerms(data, &isGeneric, len, reader)) {
return NewUndefined(refId);
}
TermFlags flags = {
.isLocal = true,
.isGeneric = isGeneric,
};
data->InitAfterSubterms(TagTermId(TermKind::TUPLE), len, flags);
return Term(LocalTerm(data));
}
case NULLABLE: {
auto* data = AllocateTerm(heap, 1);
bool isGeneric = false;
if (!ReadSubTerms(data, &isGeneric, 1, reader)) {
return NewUndefined(refId);
}
TermFlags flags = {
.isLocal = true,
.isReference = true,
.isGeneric = isGeneric,
};
data->InitAfterSubterms(TagTermId(TermKind::NULLABLE), 1, flags);
return Term(LocalTerm(data));
}
case CLASS_TYPE_VAR: {
auto id = reader.ReadU8();
return Term::ClassTypeVariable(id);
}
case FUNC_TYPE_VAR: {
auto id = reader.ReadU8();
return Term::FuncTypeVariable(id);
}
case CANGJIE_ARRAY: {
auto* data = AllocateTerm(heap, 1);
bool isGeneric = false;
if (!ReadSubTerms(data, &isGeneric, 1, reader)) {
return NewUndefined(refId);
}
TermFlags flags = {
.isLocal = true,
.isReference = true,
.isGeneric = isGeneric,
};
data->InitAfterSubterms(TagTermId(TermKind::CANGJIE_ARRAY), 1, flags);
return Term(LocalTerm(data));
}
case BOX: {
auto* data = AllocateTerm(heap, 1);
bool isGeneric = false;
if (!ReadSubTerms(data, &isGeneric, 1, reader)) {
return NewUndefined(refId);
}
TermFlags flags = {
.isLocal = true,
.isReference = true,
.isGeneric = isGeneric,
};
data->InitAfterSubterms(TagTermId(TermKind::BOX), 1, flags);
return Term(LocalTerm(data));
}
case FST: {
auto subtermIdx = reader.ReadULEB();
auto subterm = Resolve(RefId<Term>(region, subtermIdx));
if (subterm.GetKind() == TermKind::UNDEFINED) {
return NewUndefined(refId);
}
subterm.data->flags.isFixedSize = true;
return subterm;
}
default: {
FATAL("Not implemented for tag %d", tag);
return NewUndefined(refId);
}
}
}
};
size_t TermManager::InternString(std::string_view str)
{
std::lock_guard guard(lock);
return internTable.InternAndGetId(str);
}
Utils::StringPool::String TermManager::GetNameOfAotType(AotTermId type)
{
std::lock_guard guard(lock);
return internTable.GetStringById(type.GetNum());
}
Term TermManager::Resolve(Session& session, RefIdentifier<Term> ident)
{
auto index = ident.GetIndex();
auto region = index.GetRegion();
auto& raf = session.FileOf(ident.GetFileId());
auto& file = session.CbcFileOf(ident.GetFileId());
auto& manager = TermManager::Of(session);
TermResolver resolver {
.regionData = file.GetRegionData(),
.session = session,
.heap = session.Allocator(),
.fileId = ident.GetFileId(),
.region = ident.GetIndex().GetRegion(),
.raf = *raf,
.file = file,
.manager = manager,
};
return resolver.Resolve(ident.GetIndex());
}
bool Term::IsReference() const { return data->flags.isReference; }
bool Term::IsAotPromoted() const { return data->flags.isAotPromoted; }
bool Term::IsGeneric() const { return data->flags.isGeneric; }
TermFlags Term::Flags() const { return data->flags; }
Term::Range Term::SubTerms() const { return Iterators::MakeRange(Term::SubTermGenerator { data, 0, GetLength() }); }
std::optional<Term> Term::SubTermGenerator::operator()()
{
if (cursor < end) {
return term->subterms[cursor++];
}
return std::nullopt;
}
Term Substitution::Substitute(Term term)
{
if (!term.IsGeneric()) {
return term;
} else if (term.GetKind() == TermKind::CLASS_TYPE_VAR) {
return SubstituteClassTv(ClassTvTermId(term).GetNum());
} else if (term.GetKind() == TermKind::FUNC_TYPE_VAR) {
return SubstituteFuncTv(FuncTvTermId(term).GetNum());
} else if (term.GetLength() == 0) {
return term;
} else {
auto data = term.data;
auto length = data->length;
auto newData = AllocateTerm(session.Allocator(), length);
auto flags = data->flags;
auto isGeneric = false;
for (int i = 0; i < length; i++) {
newData->subterms[i] = Substitute(data->subterms[i]);
isGeneric = isGeneric || newData->subterms[i].IsGeneric();
}
flags.isLocal = true;
flags.isGeneric = isGeneric;
newData->InitAfterSubterms(data->identifier, length, flags);
return LocalTerm(newData);
}
}
Substitution::Substitution(Session& session) : session(session) {}
ClassSubstitution::ClassSubstitution(Session& session, Term term) : Substitution(session), term(term) {}
Term ClassSubstitution::SubstituteClassTv(uint8_t typeVar)
{
ASSERT(typeVar < term.GetLength());
return term.Subterm(typeVar);
}
Term ClassSubstitution::SubstituteFuncTv(uint8_t typeVar) { return Term::FuncTypeVariable(typeVar); }
ArraySubstitution::ArraySubstitution(Session& session, std::vector<Term> const& terms)
: Substitution(session),
terms(terms)
{}
Term ArraySubstitution::SubstituteFuncTv(uint8_t typeVar) { return Term::FuncTypeVariable(typeVar); }
Term ArraySubstitution::SubstituteClassTv(uint8_t typeVar) { return terms.at(typeVar); }
}