From 967be9e750221ab2ab783f95df79bb26d290a45e Mon Sep 17 00:00:00 2001 From: Martial Simon Date: Mon, 15 Sep 2025 01:07:58 +0200 Subject: add: added projects --- tiger-compiler/src/object/binder.cc | 176 ++++ tiger-compiler/src/object/binder.hh | 98 +++ tiger-compiler/src/object/desugar-visitor.cc | 1127 ++++++++++++++++++++++++++ tiger-compiler/src/object/desugar-visitor.hh | 301 +++++++ tiger-compiler/src/object/fwd.hh | 16 + tiger-compiler/src/object/libobject.cc | 49 ++ tiger-compiler/src/object/libobject.hh | 82 ++ tiger-compiler/src/object/libobject.hxx | 48 ++ tiger-compiler/src/object/local.am | 33 + tiger-compiler/src/object/renamer.cc | 142 ++++ tiger-compiler/src/object/renamer.hh | 79 ++ tiger-compiler/src/object/tasks.cc | 52 ++ tiger-compiler/src/object/tasks.hh | 60 ++ tiger-compiler/src/object/type-checker.cc | 405 +++++++++ tiger-compiler/src/object/type-checker.hh | 108 +++ 15 files changed, 2776 insertions(+) create mode 100644 tiger-compiler/src/object/binder.cc create mode 100644 tiger-compiler/src/object/binder.hh create mode 100644 tiger-compiler/src/object/desugar-visitor.cc create mode 100644 tiger-compiler/src/object/desugar-visitor.hh create mode 100644 tiger-compiler/src/object/fwd.hh create mode 100644 tiger-compiler/src/object/libobject.cc create mode 100644 tiger-compiler/src/object/libobject.hh create mode 100644 tiger-compiler/src/object/libobject.hxx create mode 100644 tiger-compiler/src/object/local.am create mode 100644 tiger-compiler/src/object/renamer.cc create mode 100644 tiger-compiler/src/object/renamer.hh create mode 100644 tiger-compiler/src/object/tasks.cc create mode 100644 tiger-compiler/src/object/tasks.hh create mode 100644 tiger-compiler/src/object/type-checker.cc create mode 100644 tiger-compiler/src/object/type-checker.hh (limited to 'tiger-compiler/src/object') 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 +#include + +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(e); + } + + template void Binder::chunk_visit(ast::Chunk& e) + { + // Shorthand. + using chunk_type = ast::Chunk; + // FIXME: Some code was deleted here (Two passes: once on headers, then on bodies). + for (auto machala : e) + { + if (dynamic_cast(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& 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& 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(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 + +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 void chunk_visit(ast::Chunk& e); + + /// Check a Function declaration header. + template void visit_dec_header(D& e); + + /// Check a Function declaration body. + template 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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(&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(type)) + { + return "int"; + } + else if (dynamic_cast(type)) + { + return "string"; + } + else if (const auto named = dynamic_cast(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(result_); + if (chunk) + contents.emplace_back(chunk); + else + { + auto chunklist = dynamic_cast(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(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(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(contener_type); + auto init_class_type = dynamic_cast(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(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(given_type); + auto class_asked = dynamic_cast(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(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(&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(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(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(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 +#include +#include + +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; + + private: + /// \name Code generation. + /// \{ + + using pair_class_method = + std::pair; + using dispatch_list_type = std::set; + + /// \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; + + /// \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 dispatch_added_; + + /// Map / counter giving the correct current dispatch extensions for a method + misc::map 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 +#include +#include + +namespace object +{ + /// Names associated to class types. + using class_names_type = misc::map; +} // 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 +#include +#include +#include + +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 +#include +#include + +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 + 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 + A* raw_desugar(const A& tree, const class_names_type& class_names); + +} // namespace object + +#include 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 + +#include +#include +#include + +namespace object +{ + + /*------------------. + | Desugar objects. | + `------------------*/ + + template + A* raw_desugar(const A& tree, const class_names_type& class_names) + { + // Desugar. + ::object::DesugarVisitor desugar(class_names); + desugar(tree); + return dynamic_cast(desugar.result_get()); + } + + template + A* desugar(const A& tree, const class_names_type& class_names) + { + // Desugar. + A* desugared = raw_desugar(tree, class_names); + assertion(desugared); + std::unique_ptr 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 + +#include + +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(&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(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 + +#include +#include + +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 + +#include +#include +#include +#include +#define DEFINE_TASKS 1 +#include +#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; + + 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 +#include + +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 +#include +#include +#include + +#include +#include +#include + +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(&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(&e.thenclause_get()); + then_class) + { + auto else_class = dynamic_cast(&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(&e.left_get().type_get()->actual()); + const auto b_class = + dynamic_cast(&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(&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(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& params) { + check_types(e, "expected", *params.get<0>(), "actual", + *params.get<1>()); + }); + } + + type_default(e, + &dynamic_cast(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(&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(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(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(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_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(&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 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(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(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 +#include +#include + +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): + +
    +
  1. visit headers, as in type::TypeChecker;
  2. +
  3. visit bodies, which ignore all members of the class;
  4. +
  5. visit members, i.e., attributes and methods.
  6. +
+ + 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 -- cgit v1.2.3