summaryrefslogtreecommitdiff
path: root/tiger-compiler/src/object
diff options
context:
space:
mode:
authorMartial Simon <msimon_fr@hotmail.com>2025-09-15 01:07:58 +0200
committerMartial Simon <msimon_fr@hotmail.com>2025-09-15 01:07:58 +0200
commit967be9e750221ab2ab783f95df79bb26d290a45e (patch)
tree6802900a5e975f9f68b169f0f503f040056d6952 /tiger-compiler/src/object
add: added projectsHEADmain
Diffstat (limited to 'tiger-compiler/src/object')
-rw-r--r--tiger-compiler/src/object/binder.cc176
-rw-r--r--tiger-compiler/src/object/binder.hh98
-rw-r--r--tiger-compiler/src/object/desugar-visitor.cc1127
-rw-r--r--tiger-compiler/src/object/desugar-visitor.hh301
-rw-r--r--tiger-compiler/src/object/fwd.hh16
-rw-r--r--tiger-compiler/src/object/libobject.cc49
-rw-r--r--tiger-compiler/src/object/libobject.hh82
-rw-r--r--tiger-compiler/src/object/libobject.hxx48
-rw-r--r--tiger-compiler/src/object/local.am33
-rw-r--r--tiger-compiler/src/object/renamer.cc142
-rw-r--r--tiger-compiler/src/object/renamer.hh79
-rw-r--r--tiger-compiler/src/object/tasks.cc52
-rw-r--r--tiger-compiler/src/object/tasks.hh60
-rw-r--r--tiger-compiler/src/object/type-checker.cc405
-rw-r--r--tiger-compiler/src/object/type-checker.hh108
15 files changed, 2776 insertions, 0 deletions
diff --git a/tiger-compiler/src/object/binder.cc b/tiger-compiler/src/object/binder.cc
new file mode 100644
index 0000000..8220da5
--- /dev/null
+++ b/tiger-compiler/src/object/binder.cc
@@ -0,0 +1,176 @@
+/**
+ ** \file object/binder.cc
+ ** \brief Implementation of object::Binder.
+ */
+
+#include <ast/all.hh>
+#include <object/binder.hh>
+
+namespace object
+{
+ /*---------.
+ | Visits. |
+ `---------*/
+
+ /* Handle the case of `self'. If the variable is not named `self', handle it
+ like the normal `Binder'. If it is `self', it must be bound to a definiton
+ site, unless:
+ * it is inside a method,
+ * AND `self` is not overridden.
+ If those conditions are met, `self' is an implicitly defined instance of
+ the class.
+
+ Variable `self' will have a meaningful definition after the object
+ constructs have been desugared. */
+
+ void Binder::operator()(ast::SimpleVar& e)
+ {
+ // FIXME DONE: Some code was deleted here.
+ misc::symbol test_self("self");
+ if (e.name_get() == test_self && within_method_dec_ && !overrided_self_)
+ {
+ e.def_set(nullptr);
+ }
+ else
+ {
+ super_type::operator()(e);
+ }
+ }
+
+ // Handle the case of `Object'.
+ void Binder::operator()(ast::NameTy& e)
+ {
+ // FIXME DONE: Some code was deleted here.
+ misc::symbol test_obj("Object");
+ if (e.name_get() == test_obj)
+ {
+ e.def_set(nullptr);
+ }
+ else
+ {
+ super_type::operator()(e);
+ }
+ }
+
+ /*---------------.
+ | Visiting Exp. |
+ `---------------*/
+
+ void Binder::operator()(ast::ForExp& e)
+ {
+ bool saved_within_class_ty = within_class_ty_;
+ within_class_ty_ = false;
+ super_type::operator()(e);
+ within_class_ty_ = saved_within_class_ty;
+ }
+
+ /*-------------------.
+ | Visiting ClassTy. |
+ `-------------------*/
+
+ void Binder::operator()(ast::ClassTy& e)
+ {
+ // FIXME DONE: Some code was deleted here (Scope begins).
+ begin_scope();
+ e.super_get().accept(*this);
+ bool saved_within_class_ty = within_class_ty_;
+ within_class_ty_ = true;
+ bool saved_within_method_dec = within_method_dec_;
+ within_method_dec_ = false;
+ e.chunks_get().accept(*this);
+ within_method_dec_ = saved_within_method_dec;
+ within_class_ty_ = saved_within_class_ty;
+ // FIXME DONE: Some code was deleted here (Scope ends).
+ end_scope();
+ }
+
+ /*---------------.
+ | Visiting Dec. |
+ `---------------*/
+
+ void Binder::operator()(ast::VarDec& e)
+ {
+ if (within_class_ty_)
+ {
+ within_class_ty_ = false;
+ // Don't bind class attributes.
+ super_type::super_type::operator()(e);
+ within_class_ty_ = true;
+ }
+ else
+ {
+ // But still bind other variable declarations.
+ super_type::operator()(e);
+ if (e.name_get() == "self" && within_method_dec_)
+ overrided_self_ = true;
+ }
+ }
+
+ void Binder::operator()(ast::FunctionChunk& e)
+ {
+ chunk_visit<ast::FunctionDec>(e);
+ }
+
+ template <class D> void Binder::chunk_visit(ast::Chunk<D>& e)
+ {
+ // Shorthand.
+ using chunk_type = ast::Chunk<D>;
+ // FIXME: Some code was deleted here (Two passes: once on headers, then on bodies).
+ for (auto machala : e)
+ {
+ if (dynamic_cast<ast::MethodDec*>(machala))
+ {
+ scoped_map_fun_.put(machala->name_get(), machala);
+ }
+ }
+ super_type::operator()(e);
+ }
+
+ // This trampoline is needed, since `virtual' and `template' cannot
+ // be mixed.
+ template <>
+ void Binder::visit_dec_header<ast::FunctionDec>(ast::FunctionDec& e)
+ {
+ // FIXME DONE: Some code was deleted here (Call the super type).
+ super_type::operator()(e);
+ }
+
+ // Compute the bindings of this function's body.
+ template <> void Binder::visit_dec_body<ast::FunctionDec>(ast::FunctionDec& e)
+ {
+ bool saved_within_class_ty = within_class_ty_;
+ within_class_ty_ = false;
+ bool saved_within_method_dec = within_method_dec_;
+ within_method_dec_ = false;
+ // FIXME DONE: Some code was deleted here (Call the super type).
+ super_type::operator()(e);
+ within_method_dec_ = saved_within_method_dec;
+ within_class_ty_ = saved_within_class_ty;
+ }
+
+ /* We can't bind methods definitions without types, so we don't
+ store them. Nonetheless, object::Binder must still recurse
+ through the children of ast::MethodChunk to bind other names.
+
+ Note that as we defer the binding of methods to the
+ type-checkimg, there is no need to visit method in two-pass (like
+ bind::Binder does for functions for instance). */
+ void Binder::operator()(ast::MethodDec& e)
+ {
+ // FIXME DONE: Some code was deleted here (Scope begins).
+ begin_scope();
+ bool saved_within_class_ty = within_class_ty_;
+ within_class_ty_ = false;
+ bool saved_within_method_dec = within_method_dec_;
+ within_method_dec_ = true;
+ bool saved_overrided_self = overrided_self_;
+ overrided_self_ = false;
+ ast::DefaultVisitor::operator()(static_cast<ast::FunctionDec&>(e));
+ within_method_dec_ = saved_within_method_dec;
+ within_class_ty_ = saved_within_class_ty;
+ overrided_self_ = saved_overrided_self;
+ // FIXME DONE: Some code was deleted here (Scope ends).
+ end_scope();
+ }
+
+} // namespace object
diff --git a/tiger-compiler/src/object/binder.hh b/tiger-compiler/src/object/binder.hh
new file mode 100644
index 0000000..dad6b57
--- /dev/null
+++ b/tiger-compiler/src/object/binder.hh
@@ -0,0 +1,98 @@
+/**
+ ** \file object/binder.hh
+ ** \brief Declaration of object::Binder.
+ **/
+
+#pragma once
+
+#include <bind/binder.hh>
+
+namespace object
+{
+ /** \brief Computing bindings with support for objects.
+ **
+ ** Inheritance is declared virtual to enable diamond inheritance with
+ ** the combine::Binder (src/combine/binder.hh), inheriting from
+ ** overload::Binder and object::Binder, both inheriting from bind::Binder.
+ **/
+ class Binder : virtual public bind::Binder
+ {
+ public:
+ /// Super class type.
+ using super_type = ::bind::Binder;
+ /// Import all the overloaded operator() methods.
+ using super_type::operator();
+
+ /* The visiting methods. */
+ public:
+ // ---------------- //
+ // Visiting /Exp/. //
+ // ---------------- //
+
+ void operator()(ast::ForExp& e) override;
+
+ /// Visit a Variable instantiation.
+ void operator()(ast::SimpleVar& e) override;
+
+ // ---------------- //
+ // Visiting /Ty/. //
+ // ---------------- //
+
+ /// Visit a type name.
+ void operator()(ast::NameTy& e) override;
+
+ /// Visit a class definition.
+ void operator()(ast::ClassTy& e) override;
+
+ // ---------------- //
+ // Visiting /Dec/. //
+ // ---------------- //
+
+ /// Check a set of definitions: unique names, browse headers, then bodies.
+ template <class D> void chunk_visit(ast::Chunk<D>& e);
+
+ /// Check a Function declaration header.
+ template <class D> void visit_dec_header(D& e);
+
+ /// Check a Function declaration body.
+ template <class D> void visit_dec_body(D& e);
+
+ // Visit a variable declaration.
+ void operator()(ast::VarDec&) override;
+
+ /// Visit a chunk of Function declarations.
+ void operator()(ast::FunctionChunk&) override;
+
+ /// No longer used.
+ void operator()(ast::MethodDec&) override;
+
+ private:
+ /// Are we (immediately) within a class definition?
+ ///
+ /// This predicate is used to prevent the bindings of VarChunk that
+ /// are direct children of a ClassTy and which represent
+ /// attributes of that class definition; the binding of these
+ /// attributes is deferred to the type-checking phase (as for
+ /// records' fields). This predicate should be false within a
+ /// non-ClassTy node declaring variables, even if that node
+ /// belongs (indirectly) to the subtree of a ClassTy.
+ bool within_class_ty_ = false;
+
+ /// Are we (immediately) within a method definition?
+ ///
+ /// This predicate is used to determine whether `self' is a valid
+ /// variable. This is only the case for nodes that belong to the
+ /// subtree of a MethodDec node, except for FunctionDec and
+ /// ClassTy subtrees, which ``reset'' the context.
+ bool within_method_dec_ = false;
+
+ /// Is the implicit `self' formal of the current MethodDec overrided by an
+ /// other variable named `self' local to the current method?
+ ///
+ /// This predicate is used to determine whether a SimpleVar named `self`
+ /// should be bound to its potential definition or if it is bound to
+ /// nullptr, representing the current object.
+ bool overrided_self_ = false;
+ };
+
+} // namespace object
diff --git a/tiger-compiler/src/object/desugar-visitor.cc b/tiger-compiler/src/object/desugar-visitor.cc
new file mode 100644
index 0000000..db61a80
--- /dev/null
+++ b/tiger-compiler/src/object/desugar-visitor.cc
@@ -0,0 +1,1127 @@
+/**
+ ** \file object/desugar-visitor.cc
+ ** \brief Implementation of object::DesugarVisitor.
+ */
+
+#include <sstream>
+
+#include <algorithm>
+#include <ast/all.hh>
+#include <misc/contract.hh>
+#include <misc/set.hh>
+#include <misc/symbol.hh>
+#include <object/desugar-visitor.hh>
+#include <parse/libparse.hh>
+#include <parse/tweast.hh>
+#include <type/class.hh>
+#include <type/function.hh>
+#include <type/record.hh>
+
+namespace object
+{
+ DesugarVisitor::DesugarVisitor(const class_names_type& names)
+ : class_names_(names)
+ {}
+
+ /*---------------------------.
+ | Handling names and types. |
+ `---------------------------*/
+
+ namespace
+ {
+ // Desugar prefixes.
+
+ // Prefix of every class id (label).
+ const char* class_id_prefix = "_id_";
+ // Prefix of every record holding the contents of a desugared class.
+ const char* class_contents_prefix = "_contents_";
+ // Prefix of every variant storing the possible dynamic type for
+ // a given static type.
+ const char* class_variant_prefix = "_variant_";
+ // Prefix of the fields in the variant.
+ const char* variant_field_prefix = "field_";
+ // Prefix of constructors.
+ const char* class_ctor_prefix = "_new_";
+ // Prefix of methods.
+ const char* method_prefix = "_method_";
+
+ // Useful routines.
+
+ // Try to get the class type of \a t. If \a t is not a class
+ // type, return a null pointer.
+ const type::Class* class_type_query(const ast::Typable& t)
+ {
+ // FIXME DONE: Some code was deleted here.
+ return dynamic_cast<const type::Class*>(&t.type_get()->actual());
+ }
+
+ // Like class_type_query, but ensure the type is actually a class.
+ const type::Class* class_type_get(const ast::Typable& t)
+ {
+ const type::Class* class_type = class_type_query(t);
+ postcondition(class_type);
+ return class_type;
+ }
+
+ } // namespace
+
+ /*------------------.
+ | Code generation. |
+ `------------------*/
+
+ std::string DesugarVisitor::type_symbol(const type::Type* type)
+ {
+ // FIXME DONE: Some code was deleted here (Check int, then string, then class name).
+ if (dynamic_cast<const type::Int*>(type))
+ {
+ return "int";
+ }
+ else if (dynamic_cast<const type::String*>(type))
+ {
+ return "string";
+ }
+ else if (const auto named = dynamic_cast<const type::Named*>(type))
+ {
+ return named->name_get().get();
+ }
+ else
+ {
+ throw std::invalid_argument("type was neither a builtin or named");
+ }
+ }
+
+ std::string DesugarVisitor::upcast_fun_name(const type::Class* from,
+ const type::Class* to)
+ {
+ std::stringstream s;
+ s << "_upcast_" << class_names_(from) << "_to_" << class_names_(to);
+ return s.str();
+ }
+
+ std::string DesugarVisitor::downcast_fun_name(const type::Class* from,
+ const type::Class* to)
+ {
+ std::stringstream s;
+ s << "_downcast_" << class_names_(from) << "_to_" << class_names_(to);
+ return s.str();
+ }
+
+ std::string DesugarVisitor::dispatch_fun_name(const type::Class* owner,
+ const type::Method* method)
+ {
+ std::stringstream s;
+ // If an extension is found, add it to the dispatch suffix.
+ if (dispatch_map_.find(method) != dispatch_map_.end())
+ s << "_dispatch_" << dispatch_map_[method] << "_" << class_names_(owner)
+ << "_" << method->name_get();
+ else
+ s << "_dispatch_" << class_names_(owner) << "_" << method->name_get();
+
+ return s.str();
+ }
+
+ void DesugarVisitor::adapt_type(ast::Exp*& source_exp,
+ const type::Class* source_type,
+ const type::Class* target_type)
+ {
+ // If the source type is different from the target type, (up)cast
+ // the source expression to the latter.
+ if (source_type && target_type && source_type != target_type)
+ source_exp = parse::parse(parse::Tweast()
+ << upcast_fun_name(source_type, target_type)
+ << " (" << source_exp << ")");
+ }
+
+ ast::Exp* DesugarVisitor::variant_exp(const type::Class* static_type,
+ const std::string& exact_type,
+ const field_inits_type& inits)
+ {
+ parse::Tweast input;
+ misc::symbol static_type_name = class_names_(static_type);
+ input << " " << class_variant_prefix << static_type_name
+ << " {"
+ " exact_type = "
+ << exact_type;
+ /* For each field of the variant, store the corresponding
+ initialization value if one was given, otherwise set the field
+ to `nil'. */
+ // Fields of the static type.
+ for (const type::Class* c = static_type; c; c = c->super_get())
+ // Don't generate slots for classes with no data.
+ if (c->has_data())
+ {
+ input << ",\n" << variant_field_prefix << class_names_(c) << " = ";
+ // These fields must have a value (we don't need to put an
+ // assertion here, misc::map::operator() already handles this.
+ std::string init = inits.operator()(c);
+ input << init;
+ }
+ // Potential fields of the dynamic type (from subclasses of the
+ // static type).
+ for (const type::Class* subclass : static_type->subclasses_get())
+ // Don't generate slots for classes with no data.
+ if (subclass->has_data())
+ {
+ input << ",\n"
+ << variant_field_prefix << class_names_(subclass) << " = ";
+ // These fields might be nil.
+ const auto i = inits.find(subclass);
+ if (i != inits.end())
+ input << i->second;
+ else
+ input << "nil";
+ }
+ input << " }\n";
+ return parse::parse(input);
+ }
+
+ // Syntactic sugar.
+ ast::Exp* DesugarVisitor::variant_exp(const type::Class* static_type,
+ const type::Class* dynamic_type,
+ const field_inits_type& inits)
+ {
+ std::string exact_type = class_id_prefix + class_names_(dynamic_type).get();
+ return variant_exp(static_type, exact_type, inits);
+ }
+
+ void DesugarVisitor::fill_variant_fields(const type::Class* class_type,
+ parse::Tweast* input)
+ {
+ // Don't generate slots for classes with no data.
+ if (class_type->has_data())
+ {
+ misc::symbol class_name = class_names_(class_type);
+ *input << ",\n"
+ << " " << variant_field_prefix << class_name << " : "
+ << " " << class_contents_prefix << class_name;
+ }
+ }
+
+ // Desugar a class type as a variant (record) type.
+ parse::Tweast* DesugarVisitor::variant_ty(const type::Class* class_type)
+ {
+ auto input = new parse::Tweast;
+ *input << " {"
+ << " exact_type : int";
+ // Actual data slots.
+ /* First, populate the variant with mandatory fields (always non
+ nil) starting with the type of the visited class, then super
+ classes. */
+ for (const type::Class* c = class_type; c; c = c->super_get())
+ fill_variant_fields(c, input);
+ /* Then add all subclasses types. These might be nil, according to
+ the exact type of the object. */
+ for (const type::Class* subclass : class_type->subclasses_get())
+ fill_variant_fields(subclass, input);
+ *input << " }\n";
+ return input;
+ }
+
+ void DesugarVisitor::fill_init_list(const type::Class* class_type,
+ field_inits_type& inits)
+ {
+ // Populate the initialization list with classes
+ // owning actual data only.
+ if (class_type->has_data())
+ {
+ std::string field_name = class_names_(class_type);
+ misc::put(inits, class_type,
+ std::string("source.") + variant_field_prefix + field_name);
+ }
+ }
+
+ parse::Tweast* DesugarVisitor::cast_function(const std::string& name,
+ const type::Class* source,
+ const type::Class* target,
+ const type::Class* exact_type)
+ {
+ auto input = new parse::Tweast;
+ *input << " function " << name << " (source : " << class_variant_prefix
+ << class_names_(source)
+ << ") :"
+ " "
+ << class_variant_prefix << class_names_(target) << " = ";
+
+ // Copy all fields from the source.
+ field_inits_type inits;
+ // First, fields from the class and its super classes...
+ for (const type::Class* c = source; c; c = c->super_get())
+ fill_init_list(c, inits);
+ // ...then, fields from the subclasses.
+ for (const type::Class* c : source->subclasses_get())
+ fill_init_list(c, inits);
+ *input << variant_exp(target, exact_type, inits) << "\n";
+ return input;
+ }
+
+ parse::Tweast* DesugarVisitor::upcast_function(const type::Class* source,
+ const type::Class* target)
+ {
+ return cast_function(upcast_fun_name(source, target), source, target,
+ source);
+ }
+
+ parse::Tweast* DesugarVisitor::downcast_function(const type::Class* source,
+ const type::Class* target)
+ {
+ return cast_function(downcast_fun_name(source, target), source, target,
+ target);
+ }
+
+ ast::Exp* DesugarVisitor::dispatch_switch(const type::Class* class_type,
+ const type::Method* method,
+ const ast::TypeChunk* typechunk,
+ const type::Method* dispatch_method)
+ {
+ parse::Tweast input;
+ misc::symbol method_name = method->name_get();
+ // If a recursive call is needed, dispatch_method will not be
+ // nullptr. It means that during the dispatch function
+ // declaration, formals of the mother method have been used
+ // (because of the use of dispatch_map_, we cannot use child's
+ // formals.
+ const ast::MethodDec* def;
+ // FIXME DONE: Some code was deleted here (Initialize def).
+ def = dispatch_method ? dispatch_method->def_get() : method->def_get();
+ const ast::VarChunk& formals = def->formals_get();
+
+ // Create a case for each method redefinition.
+ classes_type overridings;
+ for (const type::Class* c : class_type->subclasses_get())
+ if (std::ranges::any_of(*typechunk, [c](const ast::TypeDec* tmp) {
+ return class_type_query(*tmp) == c;
+ }))
+ overridings.emplace_back(c);
+
+ if (overridings.empty())
+ // No dispatch is needed; simply call the method.
+ input << method_call(class_names_(class_type), method_name, "self",
+ formals);
+ else
+ {
+ // Emit code for self.
+ bool first = true;
+ // Emit code for other types.
+ for (auto c : overridings)
+ {
+ if (!first)
+ input << " else ";
+ input << " if self.exact_type = " << class_id_prefix
+ << class_names_(c) << " then ";
+ // We search for the nearest implementation of our method.
+ const type::Class* nearest_c = c;
+ while (nearest_c && !(nearest_c->owned_meth_find(method_name)))
+ nearest_c = nearest_c->super_get();
+ input << method_call(
+ class_names_(nearest_c), method_name,
+ ((*class_type != *nearest_c)
+ ? downcast_fun_name(class_type, nearest_c) + " (self)"
+ : "self"),
+ formals);
+ first = false;
+ }
+ input << " else ";
+ // If this is a sub dispatch function, call the last dispatch
+ // method built so we don't need to write every single test
+ // for exact_type.
+ if (dispatch_method)
+ {
+ input << "_dispatch_";
+ if ((dispatch_map_[method] - 1) != 1)
+ input << (dispatch_map_[method] - 1) << "_";
+ input << class_names_(class_type) << "_" << method->name_get()
+ << " (self";
+ // Get the other arguments.
+ for (const ast::VarDec* arg : formals)
+ input << ", " << arg->name_get();
+ input << ")";
+ }
+ else
+ input << method_call(class_names_(class_type), method_name, "self",
+ formals);
+ }
+ input << '\n';
+ return parse::parse(input);
+ }
+
+ parse::Tweast* DesugarVisitor::method_call(misc::symbol class_name,
+ misc::symbol method_name,
+ const std::string& target,
+ const ast::VarChunk& formals)
+ {
+ auto input = new parse::Tweast;
+ *input << method_prefix << class_name << "_" << method_name << " (";
+
+ // Pass the target.
+ // FIXME DONE: Some code was deleted here.
+ *input << target;
+
+ // Pass other arguments.
+ // FIXME DONE: Some code was deleted here.
+ for (const auto var : formals)
+ {
+ // FIXME: Is potentially wrong
+ *input << ", " << *recurse(*var);
+ }
+
+ *input << ")";
+ return input;
+ }
+
+ /*------------------------.
+ | Visiting declarations. |
+ `------------------------*/
+
+ void DesugarVisitor::operator()(const ast::ChunkList& e)
+ {
+ const ast::Location& location = e.location_get();
+ // This is an inlined and specialized version of
+ // astclone::Cloner::recurse_collection. Maybe we could factor
+ // it, but it's not easy to see whether we could benefit from
+ // this. (Maybe a variant would be appropriate.)
+ ast::ChunkList::list_type contents;
+ for (const ast::ChunkInterface* d : e)
+ {
+ d->accept(*this);
+ // The result can be either an ast::ChunkInterface* or an ast::ChunkList*.
+ auto chunk = dynamic_cast<ast::ChunkInterface*>(result_);
+ if (chunk)
+ contents.emplace_back(chunk);
+ else
+ {
+ auto chunklist = dynamic_cast<ast::ChunkList*>(result_);
+ if (chunklist)
+ {
+ contents.splice(contents.end(), chunklist->chunks_get());
+ delete chunklist;
+ }
+ else
+ abort();
+ }
+ }
+ result_ = new ast::ChunkList(location, contents);
+ }
+
+ /*-----------------------------.
+ | Desugar class declarations. |
+ `-----------------------------*/
+
+ void DesugarVisitor::desugar_constructor(parse::Tweast& functions,
+ const type::Class* cls,
+ misc::symbol class_name)
+ {
+ functions << " function " << class_ctor_prefix << class_name
+ << "() : "
+ " "
+ << class_variant_prefix << class_name
+ << " = "
+ " let";
+ // Initialize each mandatory field of the variant (i.e.,
+ // the fields holding the attributes of the classes and
+ // its super classes).
+ for (const type::Class* c = cls; c; c = c->super_get())
+ if (c->has_data())
+ {
+ functions << " var contents_" << class_names_(c) << " := "
+ << " " << class_contents_prefix << class_names_(c) << " { ";
+
+ for (auto a = c->attrs_get().begin(); a != c->attrs_get().end(); a++)
+ {
+ if (a != c->attrs_get().begin())
+ functions << ", ";
+ const ast::VarDec* attr;
+ // FIXME DONE: Some code was deleted here (Initialize attr).
+ attr = a->def_get();
+ misc::symbol attr_name = attr->name_get();
+ // Partially clone the contents of the VarDec
+ // (cloning the whole VarDec would leak memory).
+
+ ast::Exp* attr_init = recurse(attr->init_get());
+ // Cast the initialization value if needed.
+ if (attr->init_get() && attr->type_name_get())
+ adapt_type(attr_init, class_type_query(*attr->init_get()),
+ class_type_query(*attr->type_name_get()));
+ functions << attr_name << " = " << attr_init;
+ }
+ functions << " } ";
+ }
+ functions << " in ";
+ // Create a list of initializations for each field of the
+ // variant being constructed.
+ field_inits_type inits;
+ for (const type::Class* c = cls; c; c = c->super_get())
+ if (c->has_data())
+ {
+ // FIXME DONE: Some code was deleted here.
+ std::string base = "contents_";
+ std::string name = class_names_(c);
+ misc::put(inits, c, base + name);
+ }
+ // Create the contents of the variant.
+ functions << variant_exp(cls, cls, inits) << " end\n";
+ }
+
+ void DesugarVisitor::desugar_method(parse::Tweast& functions,
+ const type::Method* method,
+ misc::symbol class_name)
+ {
+ functions << " function " << method_prefix << class_name << "_"
+ << method->name_get() << " (self : " << class_variant_prefix
+ << class_name;
+ // Get the other arguments.
+ const ast::MethodDec* def;
+ // FIXME DONE: Some code was deleted here (Initiliaze def).
+ def = method->def_get();
+ for (const ast::VarDec* arg : def->formals_get())
+ functions << ", " << arg->name_get() << " : "
+ << recurse(*arg->type_name_get());
+ functions << ")";
+ if (def->result_get())
+ functions << " : " << recurse(def->result_get());
+ ast::Exp* body = recurse(def->body_get());
+ // Cast the return value of the function if needed.
+ if (def->result_get())
+ adapt_type(body, class_type_query(*def->body_get()),
+ class_type_query(*def->result_get()));
+ functions << " = " << body << "\n";
+ }
+
+ void DesugarVisitor::dispatch_function(parse::Tweast& functions,
+ const ast::TypeChunk& e,
+ const type::Class* cls,
+ const type::Method* method,
+ misc::symbol class_name,
+ dispatch_list_type& sub_dispatches)
+ {
+ for (const type::Class* c = cls->super_get(); c; c = c->super_get())
+ {
+ // If this class is not defined in the current chunk, then
+ // the subdispatch method is not needed yet (since it can
+ // only be called after the declaration of the said class.
+ if (std::ranges::any_of(e, [c](const ast::TypeDec* t) {
+ return class_type_query(*t) == c;
+ }))
+ continue;
+
+ // Determine if the class c implements our method. If not,
+ // we do not need to write a sub dispatch method for it.
+ auto meth_it = std::ranges::find_if(
+ c->meths_get(), [method](const type::Method* meth) {
+ return meth->name_get() == method->name_get();
+ });
+
+ if (meth_it == c->meths_get().end())
+ continue;
+
+ const type::Method* parent_method = *meth_it;
+
+ // Since we're looping inside a chunk, we do not rebuild an
+ // already built sub dispatch method.
+ auto disp_it = sub_dispatches.emplace(c, parent_method);
+
+ if (!disp_it.second)
+ continue;
+
+ // Increments the dispatch counter.
+ if (dispatch_map_.find(parent_method) == dispatch_map_.end())
+ dispatch_map_[parent_method] = 2;
+ else
+ dispatch_map_[parent_method] = dispatch_map_[parent_method] + 1;
+
+ // Keep track of the added dispatch.
+ dispatch_added_ += parent_method;
+
+ // We build the subdispatch method.
+ functions << " function " << dispatch_fun_name(c, parent_method)
+ << " (self : " << class_variant_prefix << class_names_(c);
+ // Get the other arguments.
+ const ast::MethodDec* def;
+ // FIXME DONE: Some code was deleted here (Initialize def).
+ def = parent_method->def_get();
+ for (const ast::VarDec* arg : def->formals_get())
+ functions << ", " << arg->name_get() << " : "
+ << recurse(*arg->type_name_get());
+ functions << ")";
+ if (def->result_get())
+ functions << " : " << recurse(def->result_get());
+ functions << " = " << dispatch_switch(c, parent_method, &e, method);
+ }
+
+ functions << " function " << dispatch_fun_name(cls, method)
+ << " (self : " << class_variant_prefix << class_name;
+ // Get the other arguments.
+ const ast::MethodDec* def;
+ // FIXME DONE: Some code was deleted here (Initialize def).
+ def = method->def_get();
+ for (const ast::VarDec* arg : def->formals_get())
+ functions << ", " << arg->name_get() << " : "
+ << recurse(*arg->type_name_get());
+ functions << ")";
+ if (def->result_get() != nullptr)
+ functions << " : " << recurse(def->result_get());
+ functions << " = " << dispatch_switch(cls, method, &e);
+ }
+
+ void DesugarVisitor::handle_class(const ast::TypeChunk& e,
+ const type::Class* cls,
+ parse::Tweast& functions,
+ dispatch_list_type& sub_dispatches)
+ {
+ misc::symbol class_name = class_names_(cls);
+
+ /*---------------------------.
+ | Introduce a new class id. |
+ `---------------------------*/
+
+ class_ids_ << " var " << class_id_prefix << class_name
+ << " := " << cls->id_get() << "\n";
+
+ /*----------------------------------------------------.
+ | Create a record holding the actual class contents. |
+ `----------------------------------------------------*/
+
+ if (cls->has_data())
+ {
+ types_ << " type " << class_contents_prefix << class_name << " ="
+ << " { ";
+ // FIXME DONE: Some code was deleted here (Populate the record with attributes (names and types)).
+ const auto& attributes = cls->attrs_get();
+ const auto& first = attributes.begin();
+
+ for (auto attribute = first; attribute != attributes.end(); ++attribute)
+ {
+ if (attribute != first)
+ types_ << ", ";
+
+ types_ << attribute->name_get() << " : "
+ << type_symbol(&attribute->type_get());
+ }
+
+ types_ << " }\n";
+ }
+
+ /*--------------------------------------------------------------.
+ | Create a variant able to store any dynamic type corresponding |
+ | to this (static) class type. |
+ `--------------------------------------------------------------*/
+
+ types_ << " type " << class_variant_prefix << class_name << " ="
+ << variant_ty(cls);
+
+ /*-----------------------.
+ | Create a constructor. |
+ `-----------------------*/
+
+ desugar_constructor(functions, cls, class_name);
+
+ /*-------------------------------------------------------------.
+ | Create conversion routines from the class type to any of its |
+ | super types. |
+ `-------------------------------------------------------------*/
+
+ for (const type::Class* super_type = cls->super_get(); super_type;
+ super_type = super_type->super_get())
+ funs_tweast << upcast_function(cls, super_type);
+
+ /*-------------------------------------------------------------.
+ | Create conversion routines from the class type to any of its |
+ | subtypes. |
+ `-------------------------------------------------------------*/
+
+ for (const type::Class* subclass : cls->subclasses_get())
+ functions << downcast_function(cls, subclass);
+
+ for (const type::Method* m : cls->meths_get())
+ {
+ desugar_method(functions, m, class_name);
+ dispatch_function(functions, e, cls, m, class_name, sub_dispatches);
+ }
+ }
+
+ /* All type-related code is emitted into the top-level chunklist, so
+ that all classes are stored in the same typechunk, allowing them
+ to see their subclasses and be able to build a variant for each
+ of them. */
+ void DesugarVisitor::operator()(const ast::TypeChunk& e)
+ {
+ parse::Tweast functions;
+ // Is used to know what class/method pair we already have seen for the
+ // sub dispatch functions.
+ dispatch_list_type sub_dispatches;
+ for (const ast::TypeDec* t : e)
+ {
+ const type::Class* cls = nullptr;
+ // FIXME DONE: Some code was deleted here (Get the ty's class type).
+ cls = class_type_get(t->ty_get());
+
+ if (cls)
+ handle_class(e, cls, functions, sub_dispatches);
+ else
+ {
+ /* FIXME: In the rest of the visitor, the
+ simply-clone-this-node case is handled before the
+ desugar-this-node case. */
+ // Otherwise, clone the type declaration.
+ ast::TypeDec* typedec = recurse(*t);
+ assertion(typedec);
+ types_ << *typedec << '\n';
+ }
+ }
+
+ ast::ChunkList* funs_list = parse::parse(functions);
+ result_ = funs_list;
+ }
+
+ /*------------------------------------------------.
+ | Desugar class instantiations and object usage. |
+ `------------------------------------------------*/
+
+ void DesugarVisitor::operator()(const ast::VarDec& e)
+ {
+ /* We don't desugar everything using concrete syntax here, because
+ it would require a lot of additional manipulations, as we
+ cannot easily produce a single VarDec from a parsing. Also
+ working from VarChunk (with an `s') doesn't help much either, as
+ VarDec (with no `s') are also found in FunctionDec. */
+
+ // If this is not an object instantiation, delegate to the cloner.
+
+ const type::Class* class_type = class_type_query(e);
+ if (!class_type)
+ return super_type::operator()(e);
+
+ // Otherwise, handle the different cases.
+ const ast::Location& location = e.location_get();
+ ast::NameTy* type_name = nullptr;
+ ast::Exp* init = nullptr;
+ if (e.init_get())
+ {
+ // Object (variable) declaration.
+ if (e.type_name_get())
+ {
+ type_name = recurse(e.type_name_get());
+ init = recurse(e.init_get());
+ // If the dynamic type is non-nil and different from the
+ // static type, cast INIT to the latter.
+ // FIXME DONE: Some code was deleted here.
+ if (!init)
+ {
+ unreachable();
+ }
+
+ const type::Class* name_type = class_type_get(*type_name);
+ const type::Class* init_type = class_type_get(*init);
+ if (name_type != init_type)
+ {
+ adapt_type(init, name_type, init_type);
+ }
+ // Up to here
+ }
+ else
+ // No manifest type: simply clone the declaration as-is.
+ return super_type::operator()(e);
+ }
+ else
+ // Formal declaration.
+ type_name = recurse(e.type_name_get());
+
+ misc::symbol name = e.name_get();
+
+ result_ = new ast::VarDec(location, name, type_name, init);
+ postcondition(type_name || init);
+ }
+
+ // Desugar a class instantiation as a call to the desugared ctor routine.
+ void DesugarVisitor::operator()(const ast::ObjectExp& e)
+ {
+ // FIXME DONE: Some code was deleted here.
+ const ast::Location location = e.location_get();
+
+ const std::string class_name = e.type_name_get().name_get();
+
+ const std::string call_name = class_ctor_prefix + class_name;
+ const auto args = new ast::exps_type;
+ result_ = new ast::CallExp(location, call_name, args);
+ }
+
+ void DesugarVisitor::operator()(const ast::IfExp& e)
+ {
+ // FIXME DONE: Some code was deleted here.
+ ast::Exp* cond = recurse(e.test_get());
+ ast::Exp* then_clause = recurse(e.thenclause_get());
+ ast::Exp* else_clause = recurse(e.elseclause_get());
+ result_ = new ast::IfExp(e.location_get(), cond, then_clause, else_clause);
+ }
+
+ void DesugarVisitor::operator()(const ast::AssignExp& e)
+ {
+ // If this is not an object assignment, delegate to the cloner.
+ const type::Class* lhs_class_type;
+ // FIXME DONE: Some code was deleted here.
+ lhs_class_type = class_type_query(e.var_get());
+
+ if (!lhs_class_type)
+ return super_type::operator()(e);
+
+ // Duplicate the subtrees of E.
+ ast::Var* var = nullptr;
+ // FIXME DONE: Some code was deleted here (Recurse).
+ var = recurse(e.var_get());
+
+ ast::Exp* exp = nullptr;
+ // FIXME DONE: Some code was deleted here (Recurse).
+ exp = recurse(e.exp_get());
+
+ // If the RHS type is non-nil and different from the LHS type,
+ // cast EXP to the latter.
+ // FIXME DONE: Some code was deleted here.
+ const type::Class* rhs_class_type = class_type_get(*exp);
+ if (lhs_class_type != rhs_class_type)
+ {
+ adapt_type(exp, rhs_class_type, lhs_class_type);
+ }
+ // Up to here
+
+ ast::Exp* assignment =
+ parse::parse(parse::Tweast() << var << " := " << exp);
+ result_ = assignment;
+ }
+
+ ast::exps_type* DesugarVisitor::recurse_args(const ast::exps_type& actuals,
+ const type::Record& formals)
+ {
+ // For each argument, check the type of the actual argument
+ // against the formal one. If they are two different class types,
+ // convert the actual argument to the expected type.
+ auto args = new ast::exps_type;
+ ast::exps_type::const_iterator i;
+ type::Record::const_iterator j;
+ for (i = actuals.begin(), j = formals.begin();
+ i != actuals.end() && j != formals.end(); ++i, ++j)
+ {
+ // Clone the argument.
+ ast::Exp* arg = recurse(**i);
+
+ // In the case of a class, handle the case of a (non-nil) actual
+ // argument having a different type than the corresponding
+ // formal.
+ const type::Type* formal_type = &j->type_get().actual();
+ auto formal_class_type = dynamic_cast<const type::Class*>(formal_type);
+ // FIXME DONE: Some code was deleted here.
+ // How to check if this arg is non nil ?
+ // Checking if arg is not a nullptr ?
+ if (formal_class_type && arg)
+ {
+ const type::Type* arg_type = &((*i)->type_get()->actual());
+ auto arg_class_type = dynamic_cast<const type::Class*>(arg_type);
+ adapt_type(arg, arg_class_type, formal_class_type);
+ }
+ // Up to here
+ args->emplace_back(arg);
+ }
+ return args;
+ }
+
+ void DesugarVisitor::operator()(const ast::ArrayExp& e)
+ {
+ // We want to allow this:
+ // let
+ // class A {}
+ // class B extends A {}
+ // type arrtype = array of A
+ // var arr := arrtype[10] of new B
+ // in
+ // arr[0] := new B;
+ // arr[1] := new A
+ // end
+ // FIXME DONE: Some code was deleted here.
+
+ parse::Tweast input;
+ const type::Type* contener_type = &(e.type_name_get().type_get()->actual());
+ const type::Type* init_type = &(e.init_get().type_get()->actual());
+
+ auto contener_class_type = dynamic_cast<const type::Class*>(contener_type);
+ auto init_class_type = dynamic_cast<const type::Class*>(init_type);
+
+ ast::Exp* reced = recurse(e.init_get());
+ if (contener_class_type && init_class_type && reced)
+ {
+ adapt_type(reced, init_class_type, contener_class_type);
+ }
+
+ result_ = recurse(e);
+ }
+
+ void DesugarVisitor::operator()(const ast::RecordExp& e)
+ {
+ // We want to allow this:
+ // let
+ // class A {}
+ // class B extends A {}
+ // type rectype = { a : A }
+ // var rec := rectype { a = new B }
+ // in
+ // end
+ // FIXME DONE: Some code was deleted here.
+
+ const type::Type* rec_type = &(e.type_get()->actual());
+ auto actual_rec_type = dynamic_cast<const type::Record*>(rec_type);
+
+ if (actual_rec_type)
+ {
+ ast::fieldinits_type::const_iterator i;
+ type::Record::const_iterator j;
+ for (i = e.fields_get().begin(), j = actual_rec_type->fields_get().begin();
+ i != e.fields_get().end() && j != actual_rec_type->fields_get().end();
+ ++i, ++j)
+ {
+ const type::Type* given_type = &((*i)->init_get().type_get()->actual());
+ const type::Type* asked_type = &j->type_get().actual();
+ auto class_given = dynamic_cast<const type::Class*>(given_type);
+ auto class_asked = dynamic_cast<const type::Class*>(asked_type);
+
+ ast::Exp* reced = recurse((*i)->init_get());
+
+ if (class_given && class_asked && reced)
+ {
+ adapt_type(reced, class_given, class_asked);
+ }
+ }
+ }
+
+ // Maybe need to change this because we may need to create another
+ result_ = recurse(e);
+ }
+
+ void DesugarVisitor::operator()(const ast::CallExp& e)
+ {
+ const type::Function* function_type;
+ // FIXME DONE: Some code was deleted here.
+ const ast::FunctionDec* interm = e.def_get();
+ function_type = dynamic_cast<const type::Function*>(interm);
+
+ if (!interm)
+ {
+ unreachable();
+ }
+
+ const ast::Location& location = e.location_get();
+ // FIXME DONE: Some code was deleted here (Grab name).
+ misc::symbol name = e.name_get();
+
+ // FIXME DONE: Some code was deleted here (Actual arguments).
+ const ast::exps_type& arguments = e.args_get();
+
+ // (Types of) formal arguments.
+ const type::Record& formals = function_type->formals_get();
+ // Desugar the arguments and handle possible polymorphic assignments.
+ // FIXME DONE: Some code was deleted here.
+ ast::exps_type* final_args = recurse_args(arguments, formals);
+
+ // FIXME DONE: Some code was deleted here (Instantiate into result).
+ result_ = new ast::CallExp(location, name, final_args);
+ }
+
+ /*------------------------------------.
+ | Desugar accesses to class members. |
+ `------------------------------------*/
+
+ void DesugarVisitor::operator()(const ast::FieldVar& e)
+ {
+ // Check the type of the variable to see whether it is a class or
+ // a record.
+ const type::Class* class_type;
+ // FIXME DONE: Some code was deleted here.
+ class_type = class_type_query(e.var_get());
+
+ // If this is not a class, delegate to the cloner.
+ if (!class_type)
+ return super_type::operator()(e);
+
+ // FIXME DONE: Some code was deleted here (Grab name).
+ misc::symbol name = e.name_get();
+
+ // Otherwise, desugar this FieldVar as an access to an attribute.
+
+ // Look for the attribute within the class and its base classes.
+ const type::Class* owner = nullptr;
+ for (const type::Class* c = class_type; c; c = c->super_get())
+ {
+ // FIXME DONE: Some code was deleted here.
+ if (c->attr_find(name))
+ {
+ owner = c;
+ break;
+ }
+ }
+ assertion(owner);
+
+ ast::Var* var;
+ // FIXME DONE: Some code was deleted here (Recurse).
+ var = recurse(e.var_get());
+
+ ast::Exp* attr_var =
+ parse::parse(parse::Tweast() << var << "." << variant_field_prefix
+ << class_names_(owner) << "."
+ // FIXME DONE ??: Some code was deleted here.
+ << name
+ );
+ result_ = attr_var;
+ }
+
+ void DesugarVisitor::operator()(const ast::LetExp& e)
+ {
+ // Save the current scope situation for dispatched methods
+ auto saved_dispatch_added_ = dispatch_added_;
+ dispatch_added_.clear();
+
+ super_type::operator()(e);
+
+ // After exiting the scope, remove unreachable dispatch methods
+ for (const auto* meth : dispatch_added_)
+ {
+ if (dispatch_map_[meth] == 2)
+ dispatch_map_.take(meth);
+ else
+ dispatch_map_[meth] -= 1;
+ }
+
+ // Resume the current scope
+ dispatch_added_ = saved_dispatch_added_;
+ }
+
+ void DesugarVisitor::operator()(const ast::MethodCallExp& e)
+ {
+ const type::Method* method_type;
+ // FIXME DONE: Some code was deleted here (Initialize method_type).
+ method_type = dynamic_cast<const type::Method*>(&e.def_get()->type_get()->actual());
+ // Maybe we need this ?
+ if (!method_type)
+ {
+ unreachable();
+ }
+
+ const type::Class* owner_type = method_type->owner_get();
+
+ const ast::Location& location = e.location_get();
+ std::string name = dispatch_fun_name(owner_type, method_type);
+
+ // FIXME DONE: Some code was deleted here (Fetch actual arguments).
+ // ???????????????????????????????????????????????????????????
+ const ast::exps_type actual_args = e.args_get();
+
+ // (Types of) formal arguments.
+ const type::Record& formals = method_type->formals_get();
+ // Desugar the arguments and handle possible polymorphic assignements.
+ ast::exps_type* args;
+ // FIXME DONE: Some code was deleted here (Initialize args).
+ args = recurse_args(actual_args, formals);
+
+ // Process the target of the method call, and convert it to the
+ // expected type if needed.
+ ast::Exp* object;
+ // FIXME DONE: Some code was deleted here (Recurse).
+ object = recurse(e.object_get());
+
+ // FIXME DONE: Some code was deleted here (Adapt type).
+ const type::Class* item_type = class_type_query(*object);
+ adapt_type(object, item_type, owner_type);
+ // Prepend the target to the actual arguments, as the desugared
+ // method expects to find it as its first arguments.
+ args->insert(args->begin(), object);
+
+ // Turn the method call into a function call to the desugared method.
+ // FIXME DONE: Some code was deleted here (Instanciate into result).
+ ast::Var* finial = dynamic_cast<ast::Var*>(object);
+ if (!finial)
+ {
+ unreachable();
+ }
+ result_ = new ast::MethodCallExp(location, name, args, finial);
+ }
+
+ /*--------------------------.
+ | New types and functions. |
+ `--------------------------*/
+
+ // Introduce a desugared builtin Object in the top-level function.
+ void DesugarVisitor::operator()(const ast::FunctionDec& e)
+ {
+ bool is_main;
+ // FIXME DONE: Some code was deleted here (Setup is_main).
+ is_main = e.name_get() == "_main";
+ if (is_main)
+ {
+ // Desugared data structures of the builtin Object.
+ types_ << " type " << class_variant_prefix
+ << "Object =" << variant_ty(&type::Class::object_instance());
+ // Object's class id.
+ class_ids_ << " var " << class_id_prefix
+ << "Object := "
+ " "
+ << type::Class::object_instance().id_get();
+ }
+
+ // Process E.
+ super_type::operator()(e);
+ if (is_main)
+ {
+ // Object's ctor.
+ funs_tweast << " function " << class_ctor_prefix
+ << "Object() :"
+ " "
+ << class_variant_prefix << "Object =";
+ // Initialize the variant (a single field is filled, the one
+ // corresponding to Object).
+ field_inits_type object_init;
+ misc::put(object_init, &type::Class::object_instance(),
+ std::string(class_contents_prefix) + "Object {}");
+ // Create the variant.
+ funs_tweast << variant_exp(&type::Class::object_instance(),
+ &type::Class::object_instance(),
+ object_init);
+
+ // Parse the built TWEASTs.
+ ast::ChunkList* types = parse::parse(types_);
+ ast::ChunkList* class_ids = parse::parse(class_ids_);
+ ast::ChunkList* funs = parse::parse(funs_tweast);
+ // Gather these declarations.
+ types->splice_back(*class_ids);
+ types->splice_back(*funs);
+ // Add them to the top of the program.
+ auto res = dynamic_cast<ast::FunctionDec*>(result_);
+ parse::Tweast input;
+ input << "let " << types << " in " << res->body_get() << " end";
+ res->body_set(parse::parse(input));
+ }
+
+ // Cast the return value of the function if needed.
+ if (e.body_get() && e.result_get())
+ {
+ const type::Class* body_type = class_type_query(*e.body_get());
+ const type::Class* result_type = class_type_query(*e.result_get());
+ if (body_type && result_type && body_type != result_type)
+ {
+ auto res = dynamic_cast<ast::FunctionDec*>(result_);
+ parse::Tweast input;
+ input << upcast_fun_name(body_type, result_type) << " ("
+ << res->body_get() << ")";
+ res->body_set(parse::parse(input));
+ }
+ }
+ }
+
+ void DesugarVisitor::operator()(const ast::NameTy& e)
+ {
+ // Check if E is the name of a class; if not, just clone it.
+ const type::Class* class_type = class_type_query(e);
+ if (!class_type)
+ return super_type::operator()(e);
+
+ // Otherwise, desugar the name of E.
+ const ast::Location& location = e.location_get();
+ result_ = new ast::NameTy(
+ location, class_variant_prefix + class_names_(class_type).get());
+ }
+
+} // namespace object
diff --git a/tiger-compiler/src/object/desugar-visitor.hh b/tiger-compiler/src/object/desugar-visitor.hh
new file mode 100644
index 0000000..3c2b455
--- /dev/null
+++ b/tiger-compiler/src/object/desugar-visitor.hh
@@ -0,0 +1,301 @@
+/**
+ ** \file object/desugar-visitor.hh
+ ** \brief Declaration of object::DesugarVisitor.
+ */
+
+#pragma once
+
+#include <astclone/cloner.hh>
+#include <object/fwd.hh>
+#include <parse/tweast.hh>
+
+namespace object
+{
+ /// \brief Desugar some object structures while duplicating an Ast.
+ class DesugarVisitor : public astclone::Cloner
+ {
+ public:
+ /// Superclass.
+ using super_type = astclone::Cloner;
+
+ // Import overloaded virtual functions.
+ using super_type::operator();
+
+ /// Build a DesugarVisitor.
+ DesugarVisitor(const class_names_type& class_names);
+
+ /// Special version of the visit of a ChunkList allowing the
+ /// transformation of an ast::ChunkInterface to either a single ast::ChunkInterface or
+ /// to an ast::ChunkList.
+ void operator()(const ast::ChunkList& e) override;
+
+ /// \name Desugar class declarations.
+ /// \{
+
+ /// Desugar class declarations.
+ void operator()(const ast::TypeChunk& e) override;
+ /// Handle the builtin Object.
+ void operator()(const ast::FunctionDec& e) override;
+ /// Desugar class names.
+ void operator()(const ast::NameTy& e) override;
+
+ /// \}
+
+ /// \name Desugar class instantiations and object usage.
+ /// \{
+
+ /// Desugar polymorphic initializations.
+ void operator()(const ast::VarDec& e) override;
+ /// Desugar manifest objects.
+ void operator()(const ast::ObjectExp& e) override;
+ /// Desugar polymorphic branching.
+ void operator()(const ast::IfExp& e) override;
+ /// Desugar polymorphic assignments.
+ void operator()(const ast::AssignExp& e) override;
+ /// Desugar polymorphic insertion.
+ void operator()(const ast::ArrayExp& e) override;
+ void operator()(const ast::RecordExp& e) override;
+ /// Add object casts around arguments when needed.
+ void operator()(const ast::CallExp& e) override;
+
+ /// \}
+
+ /// \name Desugar accesses to class members.
+ /// \{
+
+ /// Desugar accesses to attributes.
+ void operator()(const ast::FieldVar& e) override;
+
+ /// Handle dynamic dispatch within scope.
+ void operator()(const ast::LetExp& e) override;
+
+ /// Desugar calls to methods.
+ void operator()(const ast::MethodCallExp& e) override;
+
+ /// \}
+
+ /// \brief Desugar actuals arguments in routine calls.
+ ///
+ /// \param actuals the actual arguments of the initial AST
+ /// \param formals the formal arguments of the routine
+ /// \return the desugared actual arguments
+ ast::exps_type* recurse_args(const ast::exps_type& actuals,
+ const type::Record& formals);
+
+ /// A list of classes (e.g., useful to represent a set of subclasses).
+ using classes_type = std::vector<const type::Class*>;
+
+ private:
+ /// \name Code generation.
+ /// \{
+
+ using pair_class_method =
+ std::pair<const type::Class*, const type::Method*>;
+ using dispatch_list_type = std::set<pair_class_method>;
+
+ /// \brief Desugar the constructor of the current class
+ ///
+ /// \param functions the tweast containing the constructor being desugared
+ /// \param cls the class whose constructor is being desugared
+ /// \param class_name the desugared version of the class name
+ void desugar_constructor(parse::Tweast& functions,
+ const type::Class* cls,
+ misc::symbol class_name);
+
+ /// \brief Desugar the current method.
+ ///
+ /// \param functions the tweast containing the method being desugared
+ /// \param method the methods being desugared
+ /// \param class_name the desugared version of the class name
+ void desugar_method(parse::Tweast& functions,
+ const type::Method* method,
+ misc::symbol class_name);
+
+ /// \brief Dispatch the method between the different classes.
+ ///
+ /// \param functions the tweast containing the dispatch
+ /// \param e the block of type being processed
+ /// \param cls the cls being desugared
+ /// \param method the method being dispatched
+ /// \param class_name the desugared version of the class name
+ /// \param sub_dispatches the processed classes/methods for dispatch
+ void dispatch_function(parse::Tweast& functions,
+ const ast::TypeChunk& e,
+ const type::Class* cls,
+ const type::Method* method,
+ misc::symbol class_name,
+ dispatch_list_type& sub_dispatches);
+
+ /// \brief Handle a class declaration.
+ ///
+ /// This is a declaration of a class: remove it and replace it with
+ ///
+ /// (1) a class type label (integer constant),
+ /// (2) new data structures:
+ /// - an actual structure holding the data of the class,
+ /// - a variant able to store any concrete type for this
+ /// class;
+ /// (3) new functions:
+ /// - a constructor,
+ /// - conversion routines used in polymorphic assignments (upcasts),
+ /// - conversion routines used in dynamic dispatch (downcasts),
+ /// - (desugared) methods,
+ /// - dispatch functions.
+ ///
+ /// We must take care of the order in which these declarations
+ /// are injected, since (1), (2) and (3) are declaration of
+ /// different kinds (variable, type and function respectively).
+ /// Mixing them would break the block of type declarations
+ /// (TypeChunk, or ``chunk'' of TypeDec's) being currently
+ /// visited. Thus we collect all class definitions from E,
+ /// replace them by new data structures ((2)) and inject the
+ /// rest of the new material *after* the TypeChunk ((1) and
+ /// (3)).
+ void handle_class(const ast::TypeChunk& e,
+ const type::Class* cls,
+ parse::Tweast& functions,
+ dispatch_list_type& sub_dispatches);
+
+ /// \brief Return the name of a type.
+ ///
+ /// \param type either a builtin type or a type::Named
+ /// \result the name of the type
+ std::string type_symbol(const type::Type* type);
+
+ /// Return the name of the upcast function between types \a from
+ /// and \a to.
+ std::string upcast_fun_name(const type::Class* from, const type::Class* to);
+ /// Return the name of the downcast function between types \a from
+ /// and \a to.
+ std::string downcast_fun_name(const type::Class* from,
+ const type::Class* to);
+
+ /// Return the name of the dispatch function for \a method,
+ /// defined in class \a owner.
+ std::string dispatch_fun_name(const type::Class* owner,
+ const type::Method* method);
+
+ /// Check if the type \a source_type of \a source_exp matches
+ /// \a target_type, and generate a type conversion wrapper if
+ /// needed.
+ void adapt_type(ast::Exp*& source_exp,
+ const type::Class* source_type,
+ const type::Class* target_type);
+
+ /// The type of a list of initializations for the field of a variant.
+ using field_inits_type = misc::map<const type::Class*, std::string>;
+
+ /// \brief Generate a variant expression.
+ ///
+ /// \param static_type the type of the class whose variant is built
+ /// \param exact_type the exact type of the data stored in the variant
+ /// \param inits the initalization value of the variant (must be
+ /// of type \a dynamic_type).
+ /// \return the generated variant expression
+ ast::Exp* variant_exp(const type::Class* static_type,
+ const std::string& exact_type,
+ const field_inits_type& inits);
+
+ /// Syntactic sugar for the previous routine.
+ ast::Exp* variant_exp(const type::Class* static_type,
+ const type::Class* dynamic_type,
+ const field_inits_type& inits);
+
+ /// \brief Populate a variant fields.
+ ///
+ /// \param class_type the type of the class used to populate the variant
+ /// \param input the tweast containing the variant being built
+ void fill_variant_fields(const type::Class* class_type,
+ parse::Tweast* input);
+
+ /// \brief Generate code for a variant type on a TWEAST.
+ ///
+ /// Contrary to object::DesugarVisitor::variant_exp, we
+ /// cannot directly generate an expression, since the client
+ /// TWEAST won't accept an ast::Ty* as a metavariable.
+ ///
+ /// \param class_type the type of the class whose variant is built
+ /// \return the TWEAST on which the code is generated
+ parse::Tweast* variant_ty(const type::Class* class_type);
+
+ /// \brief Populate an initialization list.
+ ///
+ /// \param class_type the type of the class used to populate the list
+ /// \param inits the initialization list
+ void fill_init_list(const type::Class* class_type, field_inits_type& inits);
+
+ /// \brief Generate a conversion routine.
+ ///
+ /// \param name the type of the generated function
+ /// \param source the type of the converted value
+ /// \param target the target type of the conversion
+ /// \param exact_type the exact type of the returned variant
+ /// \return the TWEAST on which the code is generated
+ parse::Tweast* cast_function(const std::string& name,
+ const type::Class* source,
+ const type::Class* target,
+ const type::Class* exact_type);
+
+ /// Syntactic sugar for object::DesugarVisitor::cast_function,
+ /// used to generate an upcast function.
+ parse::Tweast* upcast_function(const type::Class* source,
+ const type::Class* target);
+
+ /// Syntactic sugar for object::DesugarVisitor::cast_function,
+ /// used to generate a downcast function.
+ parse::Tweast* downcast_function(const type::Class* source,
+ const type::Class* target);
+
+ /// \brief Generate an expression looking like a switch
+ /// expression, to dispatch a method call. Do not generate code
+ /// for classes outside typechunk. If this function is used to create a sub
+ /// dispatch function, recursive call should be true, in order to call
+ /// the previous dispatch function if nothing matches.
+ ///
+ /// \param class_type the static type of the class owning the method
+ /// \param method the called method
+ /// \param typechunk the current chunk
+ /// \param dispatch_method the recursively called dispatch method
+ /// \return the generated expression
+ ast::Exp* dispatch_switch(const type::Class* class_type,
+ const type::Method* method,
+ const ast::TypeChunk* typechunk,
+ const type::Method* dispatch_method = nullptr);
+
+ /// \brief Generate a (static) call to a desugared method.
+ ///
+ /// \note The dynamic dispatch must have been performed before
+ /// using this generator.
+ ///
+ /// \param class_name the class where the method resides
+ /// \param method_name the name of the method
+ /// \param target the name of the target (object)
+ /// \param formals the actual arguments (other than the target)
+ /// \return the TWEAST on which the code is generated
+ parse::Tweast* method_call(misc::symbol class_name,
+ misc::symbol method_name,
+ const std::string& target,
+ const ast::VarChunk& formals);
+
+ /// \}
+
+ private:
+ /// The names of the classes defined in the program.
+ class_names_type class_names_;
+
+ /// TWEAST of desugared types.
+ parse::Tweast types_;
+ /// TWEAST of class ids.
+ parse::Tweast class_ids_;
+
+ /// TWEAST of upcast functions.
+ parse::Tweast funs_tweast;
+
+ /// Vector keeping track of added dispatch functions within a scope.
+ misc::vector<const type::Method*> dispatch_added_;
+
+ /// Map / counter giving the correct current dispatch extensions for a method
+ misc::map<const type::Method*, unsigned int> dispatch_map_;
+ };
+
+} // namespace object
diff --git a/tiger-compiler/src/object/fwd.hh b/tiger-compiler/src/object/fwd.hh
new file mode 100644
index 0000000..7b7d779
--- /dev/null
+++ b/tiger-compiler/src/object/fwd.hh
@@ -0,0 +1,16 @@
+/**
+ ** \file object/fwd.hh
+ ** \brief Forward declarations of using types.
+ */
+
+#pragma once
+
+#include <misc/map.hh>
+#include <misc/symbol.hh>
+#include <type/class.hh>
+
+namespace object
+{
+ /// Names associated to class types.
+ using class_names_type = misc::map<const type::Class*, misc::symbol>;
+} // namespace object
diff --git a/tiger-compiler/src/object/libobject.cc b/tiger-compiler/src/object/libobject.cc
new file mode 100644
index 0000000..b7c7f72
--- /dev/null
+++ b/tiger-compiler/src/object/libobject.cc
@@ -0,0 +1,49 @@
+/**
+ ** \file object/libobject.cc
+ ** \brief Define exported object functions.
+ */
+
+// FIXME DONE: Some code was deleted here.
+#include <object/libobject.hh>
+#include <object/binder.hh>
+#include <object/renamer.hh>
+#include <object/type-checker.hh>
+
+namespace object
+{
+ /*-------.
+ | Bind. |
+ `-------*/
+
+ // FIXME DONE: Some code was deleted here.
+ misc::error bind_obj(ast::ChunkList* d)
+ {
+ Binder bdc = Binder();
+ bdc(d);
+ return bdc.error_get();
+ }
+
+ /*----------------.
+ | Compute types. |
+ `----------------*/
+
+ misc::error types_check(ast::Ast& tree)
+ {
+ TypeChecker type;
+ type(tree);
+ return type.error_get();
+ }
+
+ /*---------.
+ | Rename. |
+ `---------*/
+
+ class_names_type* rename(ast::Ast& tree)
+ {
+ // Rename.
+ Renamer rename;
+ rename(tree);
+ return rename.class_names_get();
+ }
+
+} // namespace object
diff --git a/tiger-compiler/src/object/libobject.hh b/tiger-compiler/src/object/libobject.hh
new file mode 100644
index 0000000..d153ef4
--- /dev/null
+++ b/tiger-compiler/src/object/libobject.hh
@@ -0,0 +1,82 @@
+/**
+ ** \file object/libobject.hh
+ ** \brief Declare functions and variables exported by object module.
+ */
+
+#pragma once
+
+#include <ast/fwd.hh>
+#include <misc/error.hh>
+#include <object/fwd.hh>
+
+namespace object
+{
+ /*-------.
+ | Bind. |
+ `-------*/
+
+ // FIXME DONE: Some code was deleted here.
+ /// \brief Bind the whole ast_object in place, return the error code
+ ///
+ /// \param last the ast you want to bind
+ ///
+ /// \return a misc::error that serve to indicate possible failure
+ misc::error bind_obj(ast::ChunkList* last);
+
+ /*----------------.
+ | Compute types. |
+ `----------------*/
+
+ /** \brief Check types allowing objects.
+
+ \param tree abstract syntax tree's root.
+
+ \return success of the type-checking. */
+ misc::error types_check(ast::Ast& tree);
+
+ /*---------.
+ | Rename. |
+ `---------*/
+
+ /// Rename the variables of an AST so that they each have a unique
+ /// name, with support for objects.
+ ///
+ /// \param tree abstract syntax tree's root, whose bindings and types
+ /// have been computed.
+ /// \return a newly allocated dictionnary of class names
+ class_names_type* rename(ast::Ast& tree);
+
+ /*------------------.
+ | Desugar objects. |
+ `------------------*/
+
+ /** \brief Remove objects constructs from an AST.
+
+ \param tree abstract syntax tree's root, whose bindings
+ and types have been computed, and whose
+ identifiers are all unique.
+ \param class_names the names of the class types of the AST
+
+ \return the desugared, bound and type-checked AST. */
+ template <typename A>
+ A* desugar(const A& tree, const class_names_type& class_names);
+
+ /** \brief Remove objects constructs from an AST without recomputing
+ its bindings nor its types.
+
+ This function acts like object::object_desugar, but stops just
+ after the desugaring step (in fact, object::desugar is built
+ upon this function). It is meant to be used as a test of
+ DesugarVisitor (i.e., even if the desugared tree is badly bound
+ or typed, it can still be pretty-printed).
+
+ \param tree AST to desugar.
+ \param class_names the names of the class types of the AST
+
+ \return the desugared AST. */
+ template <typename A>
+ A* raw_desugar(const A& tree, const class_names_type& class_names);
+
+} // namespace object
+
+#include <object/libobject.hxx>
diff --git a/tiger-compiler/src/object/libobject.hxx b/tiger-compiler/src/object/libobject.hxx
new file mode 100644
index 0000000..7c75b08
--- /dev/null
+++ b/tiger-compiler/src/object/libobject.hxx
@@ -0,0 +1,48 @@
+#pragma once
+
+/**
+ ** \file object/libobject.hxx
+ ** \brief Functions exported by the object module.
+ */
+
+#include <memory>
+
+#include <desugar/libdesugar.hh>
+#include <object/desugar-visitor.hh>
+#include <object/fwd.hh>
+
+namespace object
+{
+
+ /*------------------.
+ | Desugar objects. |
+ `------------------*/
+
+ template <typename A>
+ A* raw_desugar(const A& tree, const class_names_type& class_names)
+ {
+ // Desugar.
+ ::object::DesugarVisitor desugar(class_names);
+ desugar(tree);
+ return dynamic_cast<A*>(desugar.result_get());
+ }
+
+ template <typename A>
+ A* desugar(const A& tree, const class_names_type& class_names)
+ {
+ // Desugar.
+ A* desugared = raw_desugar(tree, class_names);
+ assertion(desugared);
+ std::unique_ptr<A> desugared_ptr(desugared);
+ // Recompute the bindings and the types.
+ ::desugar::bind_and_types_check(*desugared_ptr);
+ return desugared_ptr.release();
+ }
+
+ /// Explicit instantiations.
+ template ast::ChunkList* raw_desugar(const ast::ChunkList&,
+ const class_names_type&);
+ template ast::ChunkList* desugar(const ast::ChunkList&,
+ const class_names_type&);
+
+} // namespace object
diff --git a/tiger-compiler/src/object/local.am b/tiger-compiler/src/object/local.am
new file mode 100644
index 0000000..3c1eda9
--- /dev/null
+++ b/tiger-compiler/src/object/local.am
@@ -0,0 +1,33 @@
+## object module.
+
+src_libtc_la_SOURCES += \
+ %D%/libobject.hh \
+ %D%/libobject.cc \
+ %D%/libobject.hxx \
+ %D%/fwd.hh
+
+src_libtc_la_SOURCES += %D%/binder.hh %D%/binder.cc
+
+src_libtc_la_SOURCES += %D%/type-checker.hh %D%/type-checker.cc \
+ %D%/renamer.hh %D%/renamer.cc
+
+src_libtc_la_SOURCES += %D%/desugar-visitor.hh %D%/desugar-visitor.cc
+
+
+# Tests.
+check_PROGRAMS += %D%/test-parse
+%C%_test_parse_LDADD = src/libtc.la
+
+check_PROGRAMS += %D%/test-bind
+%C%_test_bind_LDADD = src/libtc.la
+
+check_PROGRAMS += %D%/test-type
+%C%_test_type_LDADD = src/libtc.la
+
+# FIXME: Add a test for object::Renamer.
+
+check_PROGRAMS += %D%/test-desugar
+%C%_test_desugar_LDADD = src/libtc.la
+
+
+TASKS += %D%/tasks.hh %D%/tasks.cc
diff --git a/tiger-compiler/src/object/renamer.cc b/tiger-compiler/src/object/renamer.cc
new file mode 100644
index 0000000..0abae10
--- /dev/null
+++ b/tiger-compiler/src/object/renamer.cc
@@ -0,0 +1,142 @@
+/**
+ ** \file object/renamer.cc
+ ** \brief Implementation of object::Renamer.
+ */
+
+#include <memory>
+
+#include <object/renamer.hh>
+
+namespace object
+{
+ using namespace ast;
+
+ Renamer::Renamer()
+ : super_type()
+ , class_names_(new class_names_type)
+ , within_class_ty_(false)
+ {
+ // Give a name to the built-in class Object.
+ misc::put(*class_names_, &type::Class::object_instance(), "Object");
+ }
+
+ /**
+ * Get the node's type as a class, if it references one
+ * @param ast Any typable node
+ * @return The node's referenced class type if there is one, nullptr if not
+ */
+ static inline const type::Class* get_referenced_class_type(const Typable& ast)
+ {
+ return dynamic_cast<const type::Class*>(&ast.type_get()->actual());
+ }
+
+ /*----------------------------.
+ | Visiting definition sites. |
+ `----------------------------*/
+
+ void Renamer::operator()(ast::VarDec& e)
+ {
+ if (within_class_ty_)
+ // Don't rename class attributes.
+ super_type::super_type::operator()(e);
+ else
+ // But still rename other variable declarations.
+ super_type::operator()(e);
+ }
+
+ void Renamer::operator()(ast::MethodChunk& e)
+ {
+ // FIXME DONE: Some code was deleted here (Just recurse on children nodes).
+ chunk_visit<MethodChunk>(e);
+ }
+
+ void Renamer::operator()(ast::MethodDec& e)
+ {
+ bool saved_within_class_ty = within_class_ty_;
+ within_class_ty_ = false;
+ /* We can't call bind::Binder::visit directly, because this method
+ delegates the recursion task to DefaultVisitor, which doesn't
+ have a proper operator() for MethodDec. This visitor however
+ knows how to handle a FunctionDec; therefore we upcast the
+ MethodDec to a FunctionDec before visiting it. */
+ ast::FunctionDec& fundec = e;
+ visit(fundec, &fundec);
+ within_class_ty_ = saved_within_class_ty;
+ }
+
+ void Renamer::operator()(ast::TypeDec& e)
+ {
+ // Rename.
+ // FIXME DONE: Some code was deleted here.
+ if (class_names_->find(get_referenced_class_type(e)) == class_names_->end())
+ super_type::operator()(e);
+
+ // Collect the name of the classes.
+ // FIXME DONE: Some code was deleted here.
+ if (auto classtype = get_referenced_class_type(e))
+ {
+ class_names_->insert(std::make_pair(classtype, e.name_get()));
+ }
+ }
+
+ /*-----------------------.
+ | Visiting usage sites. |
+ `-----------------------*/
+
+ void Renamer::operator()(ast::MethodCallExp& e)
+ {
+ // FIXME DONE: Some code was deleted here.
+ super_type::operator()(e.object_get());
+ e.name_set(e.def_get()->name_get());
+ CallExp& upcast = e;
+ precondition(upcast.def_get() == nullptr);
+ super_type::operator()(upcast);
+ }
+
+ /*--------------------------------------.
+ | Visiting other object-related nodes. |
+ `--------------------------------------*/
+
+ void Renamer::operator()(ast::ClassTy& e)
+ {
+ // FIXME DONE: Some code was deleted here.
+ bool saved_within_class_ty = within_class_ty_;
+ within_class_ty_ = true;
+ if (e.super_get().name_get() != "Object")
+ e.super_get().name_set(e.super_get().def_get()->name_get());
+ super_type::operator()(e.chunks_get());
+ within_class_ty_ = saved_within_class_ty;
+ }
+
+ void Renamer::operator()(ast::ObjectExp& e)
+ {
+ // FIXME DONE: Some code was deleted here.
+ if (auto name = class_names_->find(get_referenced_class_type(e));
+ name != class_names_->end())
+ e.type_name_get().name_set(name->second);
+ else
+ {
+ this->accept(e.type_name_get().def_get());
+ e.type_name_get().name_set(e.type_name_get().def_get()->name_get());
+ class_names_->insert(
+ std::make_pair(get_referenced_class_type(e),
+ e.type_name_get().name_get()));
+ }
+ }
+
+ void Renamer::operator()(ast::LetExp& e)
+ {
+ // FIXME DONE: Some code was deleted here.
+ bool saved_within_class_ty = within_class_ty_;
+ within_class_ty_ = false;
+ super_type::operator()(e);
+ within_class_ty_ = saved_within_class_ty;
+ }
+
+ /*--------------.
+ | Class names. |
+ `--------------*/
+
+ class_names_type* Renamer::class_names_get() const { return class_names_; }
+
+} // namespace object
diff --git a/tiger-compiler/src/object/renamer.hh b/tiger-compiler/src/object/renamer.hh
new file mode 100644
index 0000000..6c62a86
--- /dev/null
+++ b/tiger-compiler/src/object/renamer.hh
@@ -0,0 +1,79 @@
+/**
+ ** \file object/renamer.hh
+ ** \brief Implementation of object::Renamer.
+ */
+
+#pragma once
+
+#include <map>
+
+#include <bind/renamer.hh>
+#include <object/fwd.hh>
+
+namespace object
+{
+ /// \brief Perform identifier renaming within an AST (in place),
+ /// with support for objects.
+ class Renamer : public bind::Renamer
+ {
+ public:
+ using super_type = ::bind::Renamer;
+
+ // Import overloaded virtual functions.
+ using super_type::operator();
+
+ /// Build a Renamer.
+ Renamer();
+
+ // Visit methods.
+ /// \name Visiting definition sites.
+ /// \{
+ /// This method is like bind::Binder's, but prevent the renaming
+ /// of attributes.
+ void operator()(ast::VarDec& e) override;
+ /// Rename methods.
+ void operator()(ast::MethodChunk& e) override;
+ /// Rename a method.
+ void operator()(ast::MethodDec& e) override;
+ /// In addition to performing the renaming, collect the name of
+ /// the classes.
+ void operator()(ast::TypeDec& e) override;
+ /// \}
+
+ /// \name Visiting usage sites.
+ /// \{
+ void operator()(ast::MethodCallExp& e) override;
+ /// \}
+
+ /// \name Visiting other object-related nodes.
+ ///
+ /// These methods should be part of an ObjectDefaultVisitor, but
+ /// our current design makes the implementation (and the use) of
+ /// such a visitor difficult.
+ /// \{
+ void operator()(ast::ClassTy& e) override;
+ void operator()(ast::ObjectExp& e) override;
+ /// \}
+
+ /// \name Visiting LetExp.
+ ///
+ /// In order to handle variable declarations that might be
+ /// situated in a ClassTy and yet do not qualify as attributes.
+ /// \{
+ void operator()(ast::LetExp& e) override;
+ /// \}
+
+ /// Class names.
+ /// \{
+ /// Get the class names.
+ class_names_type* class_names_get() const;
+
+ private:
+ /// Dictionnary mapping class types to their names.
+ class_names_type* class_names_;
+ /// Are we in a class definition?
+ bool within_class_ty_;
+ /// \}
+ };
+
+} // namespace object
diff --git a/tiger-compiler/src/object/tasks.cc b/tiger-compiler/src/object/tasks.cc
new file mode 100644
index 0000000..c4d0929
--- /dev/null
+++ b/tiger-compiler/src/object/tasks.cc
@@ -0,0 +1,52 @@
+/**
+ ** \file object/tasks.cc
+ ** \brief Object module related tasks' implementation.
+ */
+
+#include <memory>
+
+#include <ast/tasks.hh>
+#include <astclone/libastclone.hh>
+#include <common.hh>
+#include <object/libobject.hh>
+#define DEFINE_TASKS 1
+#include <object/tasks.hh>
+#undef DEFINE_TASKS
+
+namespace object::tasks
+{
+ void object_parse() {}
+
+ // FIXME DONE: Some code was deleted here.
+ void object_bind()
+ {
+ task_error() << bind_obj(ast::tasks::the_program.get())
+ << &misc::error::exit_on_error;
+ }
+
+ void object_types_compute()
+ {
+ task_error() << ::object::types_check(*ast::tasks::the_program)
+ << &misc::error::exit_on_error;
+ }
+
+ static std::unique_ptr<class_names_type> class_names;
+
+ void object_rename()
+ {
+ class_names.reset(::object::rename(*ast::tasks::the_program));
+ }
+
+ void object_desugar()
+ {
+ astclone::apply(::object::desugar, ast::tasks::the_program,
+ *class_names.get());
+ }
+
+ void raw_object_desugar()
+ {
+ astclone::apply(::object::raw_desugar, ast::tasks::the_program,
+ *class_names.get());
+ }
+
+} // namespace object::tasks
diff --git a/tiger-compiler/src/object/tasks.hh b/tiger-compiler/src/object/tasks.hh
new file mode 100644
index 0000000..9a108f3
--- /dev/null
+++ b/tiger-compiler/src/object/tasks.hh
@@ -0,0 +1,60 @@
+/**
+ ** \file object/tasks.hh
+ ** \brief Object module related tasks.
+ */
+
+#pragma once
+
+#include <overload/binder.hh>
+#include <task/libtask.hh>
+
+namespace object::tasks
+{
+ TASK_GROUP("Object");
+
+ /// Enable object extensions.
+ BOOLEAN_TASK_DECLARE("o|object",
+ "enable object extensions",
+ enable_object_extensions_p,
+ "");
+
+ /// Parse the input file, allowing objects.
+ TASK_DECLARE("object-parse",
+ "parse a file, allowing objects",
+ object_parse,
+ "object parse");
+
+ // FIXME DONE: Some code was deleted here.
+ TASK_DECLARE("object-bindings-compute",
+ "bind the name uses to their definitions, allowing objects",
+ object_bind,
+ "object-parse");
+
+ /// Check for type violation, allowing objects.
+ TASK_DECLARE("object-types-compute",
+ "check for type violations, "
+ "allowing objects",
+ object_types_compute,
+ "object-bindings-compute");
+
+ /// Perform a renaming, before desugaring objects.
+ TASK_DECLARE("object-rename",
+ "rename identifiers to unique names, allowing objects",
+ object_rename,
+ "object-types-compute");
+
+ /// Remove syntactic sugar from the Ast.
+ TASK_DECLARE("object-desugar",
+ "remove object constructs from the program",
+ object_desugar,
+ "object-rename");
+
+ /// Remove syntactic sugar from the Ast without recomputing
+ /// bindings nor types.
+ TASK_DECLARE("raw-object-desugar",
+ "remove object constructs from the program "
+ "without recomputing bindings nor types",
+ raw_object_desugar,
+ "object-rename");
+
+} // namespace object::tasks
diff --git a/tiger-compiler/src/object/type-checker.cc b/tiger-compiler/src/object/type-checker.cc
new file mode 100644
index 0000000..76c4e88
--- /dev/null
+++ b/tiger-compiler/src/object/type-checker.cc
@@ -0,0 +1,405 @@
+/**
+ ** \file object/type-checker.cc
+ ** \brief Implementation for object/type-checker.hh.
+ */
+
+#include <iostream>
+#include <memory>
+#include <sstream>
+#include <boost/iterator/zip_iterator.hpp>
+
+#include <ast/all.hh>
+#include <object/type-checker.hh>
+#include <type/types.hh>
+
+namespace object
+{
+ TypeChecker::TypeChecker()
+ : super_type()
+ {
+ // Reset the subclasses of Object. This is required if several
+ // trees are processed during the compilation.
+ type::Class::object_instance().subclasses_clear();
+
+ // `self' variables are the only valid variables having a null
+ // declaration site. Use this property to tag them as read-only.
+ // FIXME DONE: Some code was deleted here.
+ var_read_only_.insert(nullptr);
+ }
+
+ /*--------------------------.
+ | The core of the visitor. |
+ `--------------------------*/
+
+ /*-----------------.
+ | Visiting /Var/. |
+ `-----------------*/
+
+ void TypeChecker::operator()(ast::SimpleVar& e)
+ {
+ // FIXME DONE: Some code was deleted here.
+ if (e.name_get() == "self")
+ type_default(e, current_);
+ else
+ super_type::operator()(e);
+ }
+
+ void TypeChecker::operator()(ast::FieldVar& e)
+ {
+ const type::Type* def_type = nullptr;
+ // FIXME DONE: Some code was deleted here (Grab type).
+ def_type = type(e.var_get());
+ auto class_type = dynamic_cast<const type::Class*>(&def_type->actual());
+
+ if (class_type)
+ {
+ // FIXME DONE: Some code was deleted here.
+ auto attr = class_type->attr_find(e.name_get());
+ if (attr == nullptr)
+ error(e, "No such attribute found");
+ else
+ type_default(e, attr->def_get()->type_get());
+ }
+ else
+ super_type::operator()(e);
+ }
+
+ /*----------------.
+ | Visiting /Ty/. |
+ `----------------*/
+
+ // Handle the case of `Object'.
+ void TypeChecker::operator()(ast::NameTy& e)
+ {
+ // FIXME DONE: Some code was deleted here.
+ if (e.name_get() == "Object")
+ {
+ type_default(e, &type::Class::object_instance());
+ }
+ else
+ super_type::operator()(e);
+ }
+
+ /*-----------------.
+ | Visiting /Exp/. |
+ `-----------------*/
+
+ void TypeChecker::operator()(ast::IfExp& e)
+ {
+ // We want to handle the following case
+ // let
+ // class A {}
+ // class B extends A { method print() = () }
+ // var a :=
+ // if 1 then
+ // new B
+ // else
+ // new A
+ // in
+ // a.print() /* error */
+ // end
+ // FIXME DONE: Some code was deleted here.
+ //Martial: Pré typage
+ super_type::operator()(e);
+ //Martial: Check mentionné au-dessus
+ if (auto then_class = dynamic_cast<type::Class*>(&e.thenclause_get());
+ then_class)
+ {
+ auto else_class = dynamic_cast<type::Class*>(&e.elseclause_get());
+ if (then_class->id_get() != else_class->id_get())
+ type_mismatch(e, "then clause", *e.thenclause_get().type_get(),
+ "else clause", *e.elseclause_get().type_get());
+ }
+ }
+
+ void TypeChecker::operator()(ast::OpExp& e)
+ {
+ // We want to only compare equal static object types.
+ // Otherwise, the desugarer emits wrong code on:
+ //
+ // let
+ // class A {}
+ // class B extends A {}
+ // var a := new A
+ // var b := new B
+ // in
+ // a = b
+ // end
+ // FIXME DONE: Some code was deleted here.
+ //Martial: Pré typage
+ super_type::operator()(e);
+ //Martial: Check mentionné au-dessus
+
+ const ast::OpExp::Oper operation_type = e.oper_get();
+ const auto a_class =
+ dynamic_cast<const type::Class*>(&e.left_get().type_get()->actual());
+ const auto b_class =
+ dynamic_cast<const type::Class*>(&e.right_get().type_get()->actual());
+
+ if (!a_class || !b_class || operation_type < ast::OpExp::Oper::eq
+ || operation_type > ast::OpExp::Oper::ne)
+ {
+ return;
+ }
+
+ if (a_class->id_get() != b_class->id_get())
+ {
+ type_mismatch(e, "left operand", *e.left_get().type_get(),
+ "right operand", *e.right_get().type_get());
+ }
+ }
+
+ void TypeChecker::operator()(ast::ObjectExp& e)
+ {
+ // FIXME DONE: Some code was deleted here.
+ type(e.type_name_get());
+ type_default(e, e.type_name_get().type_get());
+ }
+
+ void TypeChecker::operator()(ast::MethodCallExp& e)
+ {
+ // FIXME DONE: Some code was deleted here.
+ type(e.object_get());
+ for (ast::Exp* exp : e.args_get())
+ {
+ type(*exp);
+ }
+ const auto actual_params = e.args_get();
+
+ const auto obj =
+ dynamic_cast<const type::Class*>(&e.object_get().type_get()->actual());
+ assertion(obj != nullptr);
+ const auto meth = obj->meth_find(e.name_get());
+
+ if (!meth)
+ {
+ // std::string error_message = "method ";
+ error(e,
+ "method " + e.name_get().get()
+ + " does not exist within the "
+ "class");
+ type_default(e, &default_type);
+ return;
+ }
+
+ e.def_set(const_cast<ast::MethodDec*>(meth->def_get()));
+ const auto expected_params = e.def_get()->formals_get().decs_get();
+
+ if (actual_params.size() != expected_params.size())
+ {
+ error(e,
+ std::string(std::to_string(expected_params.size())
+ + " parameters expected but got "
+ + std::to_string(actual_params.size())));
+ }
+ else
+ {
+ std::for_each(
+ boost::make_zip_iterator(
+ boost::make_tuple(expected_params.begin(), actual_params.begin())),
+ boost::make_zip_iterator(
+ boost::make_tuple(expected_params.end(), actual_params.end())),
+ [this, &e](const boost::tuple<ast::VarDec*, ast::Exp*>& params) {
+ check_types(e, "expected", *params.get<0>(), "actual",
+ *params.get<1>());
+ });
+ }
+
+ type_default(e,
+ &dynamic_cast<const type::Method*>(e.def_get()->type_get())
+ ->result_get());
+ }
+
+ /*-----------------.
+ | Visiting /Dec/. |
+ `-----------------*/
+
+ /*--------------------.
+ | Visiting TypeChunk. |
+ `--------------------*/
+
+ void TypeChecker::operator()(ast::TypeChunk& e)
+ {
+ // Visit the header and the body of the typechunk, as in
+ // type::TypeChecker.
+ super_type::operator()(e);
+
+ // However, class members are not considered part of the body of
+ // their class here; they are processed separately to allow valid
+ // uses of the class from its members.
+ for (ast::TypeDec* typedec : e)
+ {
+ ast::Ty& ty = typedec->ty_get();
+ if (auto classty = dynamic_cast<ast::ClassTy*>(&ty))
+ visit_dec_members(*classty);
+ }
+ }
+
+ /*----------------------.
+ | Visiting MethodChunk. |
+ `----------------------*/
+
+ void TypeChecker::operator()(ast::MethodChunk& e)
+ {
+ precondition(within_class_body_p_);
+ within_class_body_p_ = false;
+
+ // Two passes: once on headers, then on bodies.
+ for (ast::MethodDec* m : e)
+ visit_dec_header(*m);
+ for (ast::MethodDec* m : e)
+ visit_dec_body(*m);
+
+ within_class_body_p_ = true;
+ }
+
+ // Store the type of this method.
+ void TypeChecker::visit_dec_header(ast::MethodDec& e)
+ {
+ assertion(current_);
+
+ // FIXME DONE: Some code was deleted here.
+ const type::Record* formals = type(e.formals_get());
+
+ const type::Type* return_type = e.result_get() != nullptr
+ ? type(*e.result_get())
+ : &type::Void::instance();
+
+ auto type =
+ std::make_unique<type::Method>(e.name_get(), current_, formals, *return_type, &e);
+
+ // Check for multiple definitions in the current class.
+ for (const type::Method* m : current_->meths_get())
+ if (m->name_get() == e.name_get())
+ return error(e, "method multiply defined", e.name_get());
+
+ // Check for signature conformance w.r.t. super class, if applicable.
+ const auto* super_meth_type =
+ dynamic_cast<const type::Method*>(current_->meth_type(e.name_get()));
+ // FIXME DONE: Some code was deleted here.
+ if (super_meth_type && !super_meth_type->compatible_with(*type.get()))
+ type_mismatch(e, "super class method signature", *super_meth_type,
+ "child class method signature", *type.get());
+ else
+ type_default(e, type.get());
+ current_->meth_add(type.release());
+ }
+
+ // Type check this method's body.
+ void TypeChecker::visit_dec_body(ast::MethodDec& e)
+ {
+ visit_routine_body<type::Method>(e);
+ }
+
+ /*---------------.
+ | Visit VarDec. |
+ `---------------*/
+
+ void TypeChecker::operator()(ast::VarDec& e)
+ {
+ // Signal that we are not directly inside a class' body, to avoid binding
+ // spurious members.
+ //
+ // For example:
+ // let
+ // class A =
+ // {
+ // var a := let var b := 0 in b end
+ // }
+ // var toto := new A
+ // in
+ // toto.a /* Valid */
+ // toto.b /* Invalid */
+ // end
+ bool saved_within_class_body = within_class_body_p_;
+ within_class_body_p_ = false;
+ super_type::operator()(e);
+ within_class_body_p_ = saved_within_class_body;
+
+ /* If we are directly inside a class declaration then E is an attribute:
+ record it into the CURRENT_ class. */
+ if (within_class_body_p_)
+ {
+ assertion(current_);
+
+ if (current_->attr_type(e.name_get()))
+ error(e, "attribute multiply defined", e.name_get());
+ else
+ current_->attr_add(&e);
+ }
+ }
+
+ /*-------------.
+ | Visit /Ty/. |
+ `-------------*/
+
+ // Don't handle members, as visit_dec_members is in charge of this task.
+ void TypeChecker::operator()(ast::ClassTy& e)
+ {
+ // FIXME DONE: Some code was deleted here (Create class).
+ /*
+ ** Moi sur le point de me faire chier dessus en code review parce que ma
+ ** variable est en français juste pour éviter le keyword `class'
+ */
+ auto classe = std::make_unique<type::Class>();
+
+ type_default(e, classe.get());
+
+ /* ------------------- *
+ * Superclass handling *
+ * ------------------- */
+
+ // FIXME DONE: Some code was deleted here (Set the type of the super class).
+
+ const type::Type* supertype;
+ if (e.super_get().def_get() == nullptr)
+ supertype = type(e.super_get());
+ else
+ supertype = type(e.super_get().def_get()->ty_get());
+
+ classe->super_set(dynamic_cast<const type::Class*>(&supertype->actual()));
+
+ // FIXME DONE: Some code was deleted here (Recursively update the list of subclasses of the super classes).
+ if (!classe->sound())
+ error(e, "infinite type inheritance recursion detected");
+
+ std::vector<const type::Class*> previous;
+ for (auto super = classe->super_get(); super != nullptr
+ && std::ranges::find(previous, super) == previous.end();
+ super = super->super_get())
+ {
+ super->subclass_add(classe.get());
+ previous.push_back(super);
+ }
+
+ /* à un moment je crois qu'il faut */
+ created_type_default(e, classe.release());
+ /* mais ça serait trop simple si je savais quand */
+ }
+
+ // Handle the members of a class.
+ void TypeChecker::visit_dec_members(ast::ClassTy& e)
+ {
+ assertion(!within_class_body_p_); // Should be false by the time we get here
+ const type::Type* type = nullptr;
+ // FIXME DONE: Some code was deleted here.
+ // là je crois faut get un type
+ type = e.type_get();
+
+ assertion(type);
+ auto class_type = dynamic_cast<const type::Class*>(type);
+ assertion(class_type);
+
+ type::Class* saved_class_type = current_;
+ within_class_body_p_ = true;
+ // Make the type writable, so that we can add references to the
+ // types of the members.
+ current_ = const_cast<type::Class*>(class_type);
+ e.chunks_get().accept(*this);
+
+ // Set back the status we had before we visited the members.
+ current_ = saved_class_type;
+ within_class_body_p_ = false;
+ }
+
+} // namespace object
diff --git a/tiger-compiler/src/object/type-checker.hh b/tiger-compiler/src/object/type-checker.hh
new file mode 100644
index 0000000..c965d25
--- /dev/null
+++ b/tiger-compiler/src/object/type-checker.hh
@@ -0,0 +1,108 @@
+/**
+ ** \file object/type-checker.hh
+ ** \brief Checking an ObjectTiger program in a Tiger program.
+ */
+
+#pragma once
+
+#include <type/class.hh>
+#include <type/type-checker.hh>
+#include <type/types.hh>
+
+namespace object
+{
+ /** \brief Perform type checking, allowing objects, and compute
+ ** the bindings of the object's methods and fields.
+ **
+ ** Inheritence is declared virtual to enable diamond inheritance with
+ ** the TypeChecker (src/combine/type-checker.hh), inheriting
+ ** from overload::TypeChecker and object::TypeChecker, both inheriting from
+ ** type::TypeChecker.
+ **/
+ class TypeChecker : virtual public type::TypeChecker
+ {
+ public:
+ /// Superclass.
+ using super_type = type::TypeChecker;
+ using super_type::operator();
+
+ /// Construction.
+ TypeChecker();
+
+ protected:
+ // ------------------------- //
+ // The core of the visitor. //
+ // ------------------------- //
+
+ // ---------------- //
+ // Visiting /Var/. //
+ // ---------------- //
+
+ void operator()(ast::FieldVar& e) override;
+ void operator()(ast::SimpleVar& e) override;
+
+ // --------------- //
+ // Visiting /Ty/. //
+ // --------------- //
+
+ void operator()(ast::NameTy& e) override;
+
+ // ---------------- //
+ // Visiting /Exp/. //
+ // ---------------- //
+
+ // Method exp.
+ void operator()(ast::IfExp& e) override;
+ void operator()(ast::OpExp& e) override;
+ void operator()(ast::ObjectExp& e) override;
+ void operator()(ast::MethodCallExp& e) override;
+
+ // ---------------- //
+ // Visiting /Dec/. //
+ // ---------------- //
+
+ /** Visit a chunk of type declarations.
+
+ This method is like type::TypeChecker's one, except that it
+ processes class declarations in three steps, instead of two
+ (visit the headers, then the bodies):
+
+ <ol>
+ <li>visit headers, as in type::TypeChecker;</li>
+ <li>visit bodies, which ignore all members of the class;</li>
+ <li>visit members, i.e., attributes and methods.</li>
+ </ol>
+
+ This way, a method of a class can use the type of this class
+ (e.g., return \a self), as that type has been created in the
+ second step, and the method is processed in the third. */
+ void operator()(ast::TypeChunk& e) override;
+
+ // Check a Method's declaration header.
+ void visit_dec_header(ast::MethodDec& e);
+ // Check a Method's declaration body.
+ void visit_dec_body(ast::MethodDec& e);
+
+ /// Visit a chunk of method declarations.
+ void operator()(ast::MethodChunk& e) override;
+
+ /// Visit a single Variable Declaration.
+ void operator()(ast::VarDec& e) override;
+
+ // --------------- //
+ // Visiting /Ty/. //
+ // --------------- //
+
+ // Visit a class definition \em without its members.
+ void operator()(ast::ClassTy& e) override;
+ // Visit the members of a class.
+ virtual void visit_dec_members(ast::ClassTy& e);
+
+ private:
+ /// Current visited class.
+ type::Class* current_ = nullptr;
+ /// Are we directly within an ast::ClassTy's body?
+ bool within_class_body_p_ = false;
+ };
+
+} // namespace object