summaryrefslogtreecommitdiff
path: root/tiger-compiler/src/object/type-checker.cc
diff options
context:
space:
mode:
Diffstat (limited to 'tiger-compiler/src/object/type-checker.cc')
-rw-r--r--tiger-compiler/src/object/type-checker.cc405
1 files changed, 405 insertions, 0 deletions
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