diff options
Diffstat (limited to 'tiger-compiler/src/object/type-checker.cc')
| -rw-r--r-- | tiger-compiler/src/object/type-checker.cc | 405 |
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 |
