/** ** \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