diff options
Diffstat (limited to 'boot')
| -rw-r--r-- | boot/dependency.cc | 3 | ||||
| -rw-r--r-- | boot/name_analysis.cc (renamed from boot/semantic.cc) | 488 | ||||
| -rw-r--r-- | boot/symbol.cc | 2 | ||||
| -rw-r--r-- | boot/type_check.cc | 573 |
4 files changed, 577 insertions, 489 deletions
diff --git a/boot/dependency.cc b/boot/dependency.cc index 84fd800..ada0bac 100644 --- a/boot/dependency.cc +++ b/boot/dependency.cc @@ -22,7 +22,8 @@ along with GCC; see the file COPYING3. If not see #include <string.h> #include "elna/boot/driver.h" -#include "elna/boot/semantic.h" +#include "elna/boot/name_analysis.h" +#include "elna/boot/type_check.h" #include "parser.hh" namespace elna::boot diff --git a/boot/semantic.cc b/boot/name_analysis.cc index c987cfd..cfc4e35 100644 --- a/boot/semantic.cc +++ b/boot/name_analysis.cc @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ -#include "elna/boot/semantic.h" +#include "elna/boot/name_analysis.h" #include <algorithm> @@ -61,175 +61,6 @@ namespace elna::boot return std::nullopt; } - type_mismatch_error::type_mismatch_error(const source_position position, - type expected, type actual) - : error(position), expected(expected), actual(actual) - { - } - - std::string type_mismatch_error::what() const - { - return "Expected type '" + expected.to_string() - + "', but got '" + actual.to_string() + "'"; - } - - constant_assignment_error::constant_assignment_error(const source_position position, - type assignee) - : error(position), assignee(assignee) - { - } - - std::string constant_assignment_error::what() const - { - return "Cannot assign to a value of type '" + assignee.to_string() - + "', because it is constant or contains constant members"; - } - - field_not_found_error::field_not_found_error(const identifier& field_name, - type composite_type) - : error(field_name.position()), field_name(field_name.name()), composite_type(composite_type) - { - } - - std::string field_not_found_error::what() const - { - type resolved = resolve_underlying_type(composite_type); - bool is_enum = resolved.get<enumeration_type>() != nullptr; - bool is_record = resolved.get<record_type>() != nullptr; - - if (is_enum || is_record) - { - std::string message = is_enum ? "Enumeration" : "Record"; - - if (auto alias = composite_type.get<alias_type>()) - { - message += " '" + alias->name + "'"; - } - message += " does not have a "; - message += is_enum ? "member" : "field"; - message += " named '" + field_name + "'"; - return message; - } - return "Type '" + composite_type.to_string() - + "' does not have a field named '" + field_name + "'"; - } - - duplicate_member_error::duplicate_member_error(const boot::identifier& member_name, - type aggregate, std::optional<source_position> original, - std::optional<std::string> base_name) - : error(member_name.position()), member_name(member_name.name()), aggregate(aggregate), - original(original), base_name(base_name) - { - } - - std::string duplicate_member_error::what() const - { - type resolved = resolve_underlying_type(aggregate); - bool is_enum = resolved.get<enumeration_type>() != nullptr; - std::string kind = is_enum ? "member" : "field"; - std::string message = is_enum ? "Enumeration" : "Record"; - - if (auto alias = aggregate.get<alias_type>()) - { - message += " '" + alias->name + "'"; - } - message += " already has a " + kind + " named '" + member_name + "'"; - - if (base_name.has_value()) - { - message += " (defined in base type '" + *base_name + "')"; - } - return message; - } - - std::optional<std::pair<std::string, source_position>> duplicate_member_error::note() const - { - if (original.has_value() && original->start().available()) - { - return std::make_pair("previously declared here", *original); - } - return std::nullopt; - } - - cyclic_declaration_error::cyclic_declaration_error(const std::vector<std::string>& cycle, - const source_position position) - : error(position), cycle(cycle) - { - } - - std::string cyclic_declaration_error::what() const - { - auto segment = std::cbegin(this->cycle); - std::string message = "Type declaration forms a cycle: " + *segment; - - ++segment; - for (; segment != std::cend(this->cycle); ++segment) - { - message += " -> " + *segment; - } - return message; - } - - return_error::return_error(const std::string& identifier, const source_position position, - type return_type) - : error(position), identifier(identifier), return_type(return_type) - { - } - - std::string return_error::what() const - { - if (!return_type.empty()) - { - return "Procedure '" + this->identifier - + "' does not return a value, but return expression has type '" - + return_type.to_string() + "'"; - } - return "Procedure '" + this->identifier - + "' is expected to return, but does not have a return statement"; - } - - base_type_error::base_type_error(type actual, const source_position position) - : error(position), actual(actual) - { - } - - std::string base_type_error::what() const - { - return "'" + actual.to_string() + "' is not a record type"; - } - - argument_count_error::argument_count_error(std::size_t expected, std::size_t actual, - const source_position position) - : error(position), expected(expected), actual(actual) - { - } - - std::string argument_count_error::what() const - { - if (actual > expected) - { - return "Too many arguments, expected " + std::to_string(expected) - + ", got " + std::to_string(actual); - } - else - { - return "Too few arguments, expected " + std::to_string(expected) - + ", got " + std::to_string(actual); - } - } - - unsupported_trait_type_error::unsupported_trait_type_error(const identifier& trait, - type actual) - : error(trait.position()), actual(actual), trait_name(trait.name()) - { - } - - std::string unsupported_trait_type_error::what() const - { - return "Type '" + actual.to_string() - + "' does not support trait '#" + trait_name + "'"; - } - // Members of a constant aggregate are constant themselves. static type qualify_member_type(const type& element, const type& aggregate) { @@ -244,323 +75,6 @@ namespace elna::boot } } - /* - * Whether the type itself is constant or has a constant member at any - * nesting level, so that values of this type cannot be reassigned as a - * whole. Pointers to constants do not make the type itself constant. - */ - static bool contains_constant_member(const type& checked) - { - auto referent = resolve_aliases(checked); - - if (referent.get<constant_type>() != nullptr) - { - return true; - } - else if (auto record = referent.get<record_type>()) - { - for (const type_field& field : record->fields) - { - if (contains_constant_member(field.second)) - { - return true; - } - } - return !record->base.empty() && contains_constant_member(record->base); - } - else if (auto array = referent.get<array_type>()) - { - return contains_constant_member(array->base); - } - return false; - } - - bool type_analysis_visitor::check_unresolved_symbol(std::shared_ptr<alias_type> alias, - std::vector<std::string>& alias_path) - { - if (std::find(std::cbegin(alias_path), std::cend(alias_path), alias->name) != std::cend(alias_path)) - { - return false; - } - alias_path.push_back(alias->name); - - if (auto another_alias = alias->reference.get<alias_type>()) - { - return check_unresolved_symbol(another_alias, alias_path); - } - return true; - } - - bool type_analysis_visitor::is_base_of(const std::shared_ptr<record_type>& base, - const std::shared_ptr<record_type>& derived) - { - if (derived != nullptr) - { - if (auto current_record = resolve_underlying_type(derived->base).get<record_type>()) - { - return current_record == base || is_base_of(base, current_record); - } - } - return false; - } - - bool type_analysis_visitor::is_assignable_from(const type& assignee, const type& assignment) - { - type resolved_assignee = resolve_underlying_type(assignee); - type resolved_assignment = resolve_underlying_type(assignment); - - if (resolved_assignee == resolved_assignment - || (is_primitive_type(resolved_assignee, "Pointer") && is_any_pointer_type(resolved_assignment)) - || (is_primitive_type(resolved_assignment, "Pointer") && is_any_pointer_type(resolved_assignee))) - { - return true; - } - std::shared_ptr<pointer_type> assignee_pointer = resolved_assignee.get<pointer_type>(); - std::shared_ptr<pointer_type> assignment_pointer = resolved_assignment.get<pointer_type>(); - - if (assignee_pointer == nullptr || assignment_pointer == nullptr) - { - return false; - } - auto assignee_pointee_const = resolve_aliases(assignee_pointer->base).get<constant_type>(); - auto assignment_pointee_const = resolve_aliases(assignment_pointer->base).get<constant_type>(); - - // Constness can be added at the first indirection level, but not removed. - if (assignee_pointee_const != nullptr - && assignee_pointee_const->unqualified == assignment_pointer->base) - { - return true; - } - if (assignment_pointee_const != nullptr && assignee_pointee_const == nullptr) - { - return false; - } - // A pointer to a record can be assigned to a pointer to its base type. - std::shared_ptr<record_type> assignee_record = - resolve_underlying_type(assignee_pointer->base).get<record_type>(); - - return assignee_record != nullptr - && is_base_of(assignee_record, resolve_underlying_type(assignment_pointer->base).get<record_type>()); - } - - type_analysis_visitor::type_analysis_visitor(symbol_bag bag) - : error_container(), bag(bag) - { - } - - void type_analysis_visitor::visit(procedure_declaration *declaration) - { - this->current_procedure = this->bag.lookup(declaration->identifier.name())->is_procedure(); - - if (declaration->body.has_value()) - { - this->bag.enter(this->current_procedure->scope); - } - walking_visitor::visit(declaration); - - if (declaration->body.has_value()) - { - if (declaration->body.value().return_expression != nullptr) - { - expression *return_expr = declaration->body.value().return_expression; - type return_type = this->current_procedure->symbol.return_type.proper_type; - - if (!return_type.empty()) - { - if (!is_assignable_from(return_type, return_expr->type_decoration)) - { - add_error<type_mismatch_error>( - return_expr->position(), return_type, return_expr->type_decoration); - } - } - else - { - add_error<return_error>(declaration->identifier.name(), - return_expr->position(), return_expr->type_decoration); - } - } - else if (declaration->heading().return_type.proper_type != nullptr) - { - add_error<return_error>(declaration->identifier.name(), declaration->position()); - } - this->bag.leave(); - } - this->current_procedure.reset(); - } - - void type_analysis_visitor::visit(unit *unit) - { - walking_visitor::visit(unit); - } - - void type_analysis_visitor::visit(assign_statement *statement) - { - walking_visitor::visit(statement); - - if (contains_constant_member(statement->lvalue().type_decoration)) - { - add_error<constant_assignment_error>(statement->position(), - statement->lvalue().type_decoration); - } - else if (!is_assignable_from(statement->lvalue().type_decoration, statement->rvalue().type_decoration)) - { - add_error<type_mismatch_error>(statement->position(), - statement->lvalue().type_decoration, statement->rvalue().type_decoration); - } - } - - void type_analysis_visitor::visit(variable_declaration *declaration) - { - walking_visitor::visit(declaration); - - if (declaration->initializer == nullptr) - { - return; - } - for (const identifier_definition& variable_identifier : declaration->identifiers) - { - auto variable_symbol = this->bag.lookup(variable_identifier.name())->is_variable(); - if (!is_assignable_from(variable_symbol->symbol, declaration->initializer->type_decoration)) - { - add_error<type_mismatch_error>( - declaration->initializer->position(), - variable_symbol->symbol, declaration->initializer->type_decoration); - } - } - } - - void type_analysis_visitor::visit(case_statement *statement) - { - walking_visitor::visit(statement); - type condition_type = resolve_underlying_type(statement->condition().type_decoration); - - for (const switch_case& case_block : statement->cases) - { - for (expression *const case_label : case_block.labels) - { - if (!is_assignable_from(condition_type, case_label->type_decoration)) - { - add_error<type_mismatch_error>( - case_label->position(), condition_type, case_label->type_decoration); - } - } - } - } - - void type_analysis_visitor::visit(type_declaration *declaration) - { - std::vector<std::string> alias_path; - auto unresolved_type = this->bag.lookup(declaration->identifier.name())->is_type()->symbol.get<alias_type>(); - - if (!check_unresolved_symbol(unresolved_type, alias_path)) - { - add_error<cyclic_declaration_error>(alias_path, declaration->position()); - } - else - { - walking_visitor::visit(declaration); - } - } - - void type_analysis_visitor::visit(record_type_expression *expression) - { - if (expression->base.has_value()) - { - type base_type = resolve_underlying_type(this->bag.lookup(expression->base.value().name())->is_type()->symbol); - if (base_type.get<record_type>() == nullptr) - { - add_error<base_type_error>(base_type, expression->position()); - } - } - walking_visitor::visit(expression); - } - - void type_analysis_visitor::visit(procedure_call *call) - { - call->callable().accept(this); - - if (auto procedure = call->callable().type_decoration.get<procedure_type>()) - { - std::vector<expression *>::const_iterator argument_iterator = std::cbegin(call->arguments); - std::vector<type>::const_iterator type_iterator = std::cbegin(procedure->parameters); - - while (argument_iterator != std::cend(call->arguments) - && type_iterator != std::cend(procedure->parameters)) - { - (*argument_iterator)->accept(this); - if (!is_assignable_from(*type_iterator, (*argument_iterator)->type_decoration)) - { - add_error<type_mismatch_error>( - (*argument_iterator)->position(), *type_iterator, - (*argument_iterator)->type_decoration); - } - ++argument_iterator; - ++type_iterator; - } - if (call->arguments.size() != procedure->parameters.size()) - { - add_error<argument_count_error>(procedure->parameters.size(), - call->arguments.size(), call->position()); - } - } - } - - void type_analysis_visitor::visit(record_constructor_expression *expression) - { - auto record = resolve_underlying_type(expression->type_decoration).get<record_type>(); - - if (record == nullptr) - { - add_error<type_mismatch_error>( - expression->position(), type(std::make_shared<record_type>()), - expression->type_decoration); - return; - } - for (const field_initializer& initializer : expression->field_initializers) - { - for (const type_field& field : record->fields) - { - if (field.first == initializer.name()) - { - if (!is_assignable_from(field.second, initializer.value().type_decoration)) - { - add_error<type_mismatch_error>( - initializer.value().position(), field.second, - initializer.value().type_decoration); - } - break; - } - } - } - } - - void type_analysis_visitor::visit(array_constructor_expression *expression) - { - auto array = resolve_underlying_type(expression->type_decoration).get<array_type>(); - - if (array == nullptr) - { - add_error<type_mismatch_error>( - expression->position(), type(std::make_shared<array_type>(type(), 0)), - expression->type_decoration); - return; - } - if (expression->elements.size() > array->size) - { - add_error<argument_count_error>(array->size, expression->elements.size(), - expression->position()); - return; - } - for (auto element : expression->elements) - { - if (!is_assignable_from(array->base, element->type_decoration)) - { - add_error<type_mismatch_error>( - element->position(), array->base, element->type_decoration); - } - } - } - name_analysis_visitor::name_analysis_visitor(symbol_bag bag) : error_container(), bag(bag) { diff --git a/boot/symbol.cc b/boot/symbol.cc index 9985489..bd6ec49 100644 --- a/boot/symbol.cc +++ b/boot/symbol.cc @@ -583,6 +583,6 @@ namespace elna::boot { return checked.get<pointer_type>() != nullptr || checked.get<procedure_type>() != nullptr - || is_primitive_type(checked, "Pointer"); + || is_primitive_type(resolve_underlying_type(checked), "Pointer"); } } diff --git a/boot/type_check.cc b/boot/type_check.cc new file mode 100644 index 0000000..3667007 --- /dev/null +++ b/boot/type_check.cc @@ -0,0 +1,573 @@ +/* Type analysis. + Copyright (C) 2025 Free Software Foundation, Inc. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "elna/boot/type_check.h" + +#include <algorithm> + +namespace elna::boot +{ + type_mismatch_error::type_mismatch_error(const source_position position, + type expected, type actual) + : error(position), expected(expected), actual(actual) + { + } + + std::string type_mismatch_error::what() const + { + return "Expected type '" + expected.to_string() + + "', but got '" + actual.to_string() + "'"; + } + + constant_assignment_error::constant_assignment_error(const source_position position, + type assignee) + : error(position), assignee(assignee) + { + } + + std::string constant_assignment_error::what() const + { + return "Cannot assign to a value of type '" + assignee.to_string() + + "', because it is constant or contains constant members"; + } + + field_not_found_error::field_not_found_error(const identifier& field_name, + type composite_type) + : error(field_name.position()), field_name(field_name.name()), composite_type(composite_type) + { + } + + std::string field_not_found_error::what() const + { + type resolved = resolve_underlying_type(composite_type); + bool is_enum = resolved.get<enumeration_type>() != nullptr; + bool is_record = resolved.get<record_type>() != nullptr; + + if (is_enum || is_record) + { + std::string message = is_enum ? "Enumeration" : "Record"; + + if (auto alias = composite_type.get<alias_type>()) + { + message += " '" + alias->name + "'"; + } + message += " does not have a "; + message += is_enum ? "member" : "field"; + message += " named '" + field_name + "'"; + return message; + } + return "Type '" + composite_type.to_string() + + "' does not have a field named '" + field_name + "'"; + } + + duplicate_member_error::duplicate_member_error(const boot::identifier& member_name, + type aggregate, std::optional<source_position> original, + std::optional<std::string> base_name) + : error(member_name.position()), member_name(member_name.name()), aggregate(aggregate), + original(original), base_name(base_name) + { + } + + std::string duplicate_member_error::what() const + { + type resolved = resolve_underlying_type(aggregate); + bool is_enum = resolved.get<enumeration_type>() != nullptr; + std::string kind = is_enum ? "member" : "field"; + std::string message = is_enum ? "Enumeration" : "Record"; + + if (auto alias = aggregate.get<alias_type>()) + { + message += " '" + alias->name + "'"; + } + message += " already has a " + kind + " named '" + member_name + "'"; + + if (base_name.has_value()) + { + message += " (defined in base type '" + *base_name + "')"; + } + return message; + } + + std::optional<std::pair<std::string, source_position>> duplicate_member_error::note() const + { + if (original.has_value() && original->start().available()) + { + return std::make_pair("previously declared here", *original); + } + return std::nullopt; + } + + cyclic_declaration_error::cyclic_declaration_error(const std::vector<std::string>& cycle, + const source_position position) + : error(position), cycle(cycle) + { + } + + std::string cyclic_declaration_error::what() const + { + auto segment = std::cbegin(this->cycle); + std::string message = "Type declaration forms a cycle: " + *segment; + + ++segment; + for (; segment != std::cend(this->cycle); ++segment) + { + message += " -> " + *segment; + } + return message; + } + + return_error::return_error(const std::string& identifier, const source_position position, + type return_type) + : error(position), identifier(identifier), return_type(return_type) + { + } + + std::string return_error::what() const + { + if (!return_type.empty()) + { + return "Procedure '" + this->identifier + + "' does not return a value, but return expression has type '" + + return_type.to_string() + "'"; + } + return "Procedure '" + this->identifier + + "' is expected to return, but does not have a return statement"; + } + + base_type_error::base_type_error(type actual, const source_position position) + : error(position), actual(actual) + { + } + + std::string base_type_error::what() const + { + return "'" + actual.to_string() + "' is not a record type"; + } + + argument_count_error::argument_count_error(std::size_t expected, std::size_t actual, + const source_position position) + : error(position), expected(expected), actual(actual) + { + } + + std::string argument_count_error::what() const + { + if (actual > expected) + { + return "Too many arguments, expected " + std::to_string(expected) + + ", got " + std::to_string(actual); + } + else + { + return "Too few arguments, expected " + std::to_string(expected) + + ", got " + std::to_string(actual); + } + } + + unsupported_trait_type_error::unsupported_trait_type_error(const identifier& trait, + type actual) + : error(trait.position()), actual(actual), trait_name(trait.name()) + { + } + + std::string unsupported_trait_type_error::what() const + { + return "Type '" + actual.to_string() + + "' does not support trait '#" + trait_name + "'"; + } + + /* + * Whether the type itself is constant or has a constant member at any + * nesting level, so that values of this type cannot be reassigned as a + * whole. Pointers to constants do not make the type itself constant. + */ + static bool contains_constant_member(const type& checked) + { + auto referent = resolve_aliases(checked); + + if (referent.get<constant_type>() != nullptr) + { + return true; + } + else if (auto record = referent.get<record_type>()) + { + for (const type_field& field : record->fields) + { + if (contains_constant_member(field.second)) + { + return true; + } + } + return !record->base.empty() && contains_constant_member(record->base); + } + else if (auto array = referent.get<array_type>()) + { + return contains_constant_member(array->base); + } + return false; + } + + bool type_analysis_visitor::check_unresolved_symbol(std::shared_ptr<alias_type> alias, + std::vector<std::string>& alias_path) + { + if (std::find(std::cbegin(alias_path), std::cend(alias_path), alias->name) != std::cend(alias_path)) + { + return false; + } + alias_path.push_back(alias->name); + + if (auto another_alias = alias->reference.get<alias_type>()) + { + return check_unresolved_symbol(another_alias, alias_path); + } + return true; + } + + /* + * Checks whether derived has base in its record parent chain. + * + * Base record type must not be null. Derived record type may be null. + */ + static bool is_base_of(const std::shared_ptr<record_type>& base, + const std::shared_ptr<record_type>& derived) + { + if (derived != nullptr) + { + if (auto current_record = resolve_underlying_type(derived->base).get<record_type>()) + { + return current_record == base || is_base_of(base, current_record); + } + } + return false; + } + + assign_check::verdict assign_check::guard_const_laundering() + { + if (!is_primitive_type(ctx.aliased_assignee, "Pointer")) + { + return verdict::pass; + } + if (auto ptr = ctx.aliased_assignment.get<pointer_type>(); + ptr != nullptr && resolve_aliases(ptr->base).get<constant_type>() != nullptr) + { + return verdict::reject; + } + if (auto const_assign = ctx.aliased_assignment.get<constant_type>(); + const_assign != nullptr && is_primitive_type(const_assign->unqualified, "Pointer")) + { + return verdict::reject; + } + return verdict::pass; + } + + assign_check::verdict assign_check::check_exact_match() + { + return ctx.resolved_assignee == ctx.resolved_assignment ? verdict::accept : verdict::pass; + } + + assign_check::verdict assign_check::check_pointer_hatch() + { + if (is_primitive_type(ctx.resolved_assignee, "Pointer") + && is_any_pointer_type(ctx.resolved_assignment)) + { + return verdict::accept; + } + if (is_primitive_type(ctx.resolved_assignment, "Pointer") + && is_any_pointer_type(ctx.resolved_assignee)) + { + return verdict::accept; + } + return verdict::pass; + } + + assign_check::verdict assign_check::check_pointer_conversion() + { + auto assignee_ptr = ctx.resolved_assignee.get<pointer_type>(); + auto assignment_ptr = ctx.resolved_assignment.get<pointer_type>(); + + if (assignee_ptr == nullptr || assignment_ptr == nullptr) + { + return verdict::reject; + } + auto assignee_pointee_const = resolve_aliases(assignee_ptr->base).get<constant_type>(); + auto assignment_pointee_const = resolve_aliases(assignment_ptr->base).get<constant_type>(); + + // Constness can be added at the first indirection level, but not removed. + if (assignee_pointee_const != nullptr + && assignee_pointee_const->unqualified == assignment_ptr->base) + { + return verdict::accept; + } + if (assignment_pointee_const != nullptr && assignee_pointee_const == nullptr) + { + return verdict::reject; + } + // A pointer to a record can be assigned to a pointer to its base type. + if (auto assignee_record = resolve_underlying_type(assignee_ptr->base).get<record_type>()) + { + return is_base_of(assignee_record, + resolve_underlying_type(assignment_ptr->base).get<record_type>()) + ? verdict::accept : verdict::pass; + } + return verdict::pass; + } + + bool assign_check::run() + { + for (auto handler : {&assign_check::guard_const_laundering, + &assign_check::check_exact_match, + &assign_check::check_pointer_hatch, + &assign_check::check_pointer_conversion}) + { + switch ((this->*handler)()) + { + case verdict::accept: return true; + case verdict::reject: return false; + case verdict::pass:; + } + } + return false; + } + + bool type_analysis_visitor::is_assignable_from(const type& assignee, const type& assignment) + { + return assign_check{ + resolve_aliases(assignee), + resolve_aliases(assignment), + resolve_underlying_type(assignee), + resolve_underlying_type(assignment) + }.run(); + } + + type_analysis_visitor::type_analysis_visitor(symbol_bag bag) + : error_container(), bag(bag) + { + } + + void type_analysis_visitor::visit(procedure_declaration *declaration) + { + this->current_procedure = this->bag.lookup(declaration->identifier.name())->is_procedure(); + + if (declaration->body.has_value()) + { + this->bag.enter(this->current_procedure->scope); + } + walking_visitor::visit(declaration); + + if (declaration->body.has_value()) + { + if (declaration->body.value().return_expression != nullptr) + { + expression *return_expr = declaration->body.value().return_expression; + type return_type = this->current_procedure->symbol.return_type.proper_type; + + if (!return_type.empty()) + { + if (!is_assignable_from(return_type, return_expr->type_decoration)) + { + add_error<type_mismatch_error>( + return_expr->position(), return_type, return_expr->type_decoration); + } + } + else + { + add_error<return_error>(declaration->identifier.name(), + return_expr->position(), return_expr->type_decoration); + } + } + else if (declaration->heading().return_type.proper_type != nullptr) + { + add_error<return_error>(declaration->identifier.name(), declaration->position()); + } + this->bag.leave(); + } + this->current_procedure.reset(); + } + + void type_analysis_visitor::visit(unit *unit) + { + walking_visitor::visit(unit); + } + + void type_analysis_visitor::visit(assign_statement *statement) + { + walking_visitor::visit(statement); + + if (contains_constant_member(statement->lvalue().type_decoration)) + { + add_error<constant_assignment_error>(statement->position(), + statement->lvalue().type_decoration); + } + else if (!is_assignable_from(statement->lvalue().type_decoration, statement->rvalue().type_decoration)) + { + add_error<type_mismatch_error>(statement->position(), + statement->lvalue().type_decoration, statement->rvalue().type_decoration); + } + } + + void type_analysis_visitor::visit(variable_declaration *declaration) + { + walking_visitor::visit(declaration); + + if (declaration->initializer == nullptr) + { + return; + } + for (const identifier_definition& variable_identifier : declaration->identifiers) + { + auto variable_symbol = this->bag.lookup(variable_identifier.name())->is_variable(); + if (!is_assignable_from(variable_symbol->symbol, declaration->initializer->type_decoration)) + { + add_error<type_mismatch_error>( + declaration->initializer->position(), + variable_symbol->symbol, declaration->initializer->type_decoration); + } + } + } + + void type_analysis_visitor::visit(case_statement *statement) + { + walking_visitor::visit(statement); + type condition_type = resolve_underlying_type(statement->condition().type_decoration); + + for (const switch_case& case_block : statement->cases) + { + for (expression *const case_label : case_block.labels) + { + if (!is_assignable_from(condition_type, case_label->type_decoration)) + { + add_error<type_mismatch_error>( + case_label->position(), condition_type, case_label->type_decoration); + } + } + } + } + + void type_analysis_visitor::visit(type_declaration *declaration) + { + std::vector<std::string> alias_path; + auto unresolved_type = this->bag.lookup(declaration->identifier.name())->is_type()->symbol.get<alias_type>(); + + if (!check_unresolved_symbol(unresolved_type, alias_path)) + { + add_error<cyclic_declaration_error>(alias_path, declaration->position()); + } + else + { + walking_visitor::visit(declaration); + } + } + + void type_analysis_visitor::visit(record_type_expression *expression) + { + if (expression->base.has_value()) + { + type base_type = resolve_underlying_type(this->bag.lookup(expression->base.value().name())->is_type()->symbol); + if (base_type.get<record_type>() == nullptr) + { + add_error<base_type_error>(base_type, expression->position()); + } + } + walking_visitor::visit(expression); + } + + void type_analysis_visitor::visit(procedure_call *call) + { + call->callable().accept(this); + + if (auto procedure = call->callable().type_decoration.get<procedure_type>()) + { + std::vector<expression *>::const_iterator argument_iterator = std::cbegin(call->arguments); + std::vector<type>::const_iterator type_iterator = std::cbegin(procedure->parameters); + + while (argument_iterator != std::cend(call->arguments) + && type_iterator != std::cend(procedure->parameters)) + { + (*argument_iterator)->accept(this); + if (!is_assignable_from(*type_iterator, (*argument_iterator)->type_decoration)) + { + add_error<type_mismatch_error>( + (*argument_iterator)->position(), *type_iterator, + (*argument_iterator)->type_decoration); + } + ++argument_iterator; + ++type_iterator; + } + if (call->arguments.size() != procedure->parameters.size()) + { + add_error<argument_count_error>(procedure->parameters.size(), + call->arguments.size(), call->position()); + } + } + } + + void type_analysis_visitor::visit(record_constructor_expression *expression) + { + auto record = resolve_underlying_type(expression->type_decoration).get<record_type>(); + + if (record == nullptr) + { + add_error<type_mismatch_error>( + expression->position(), type(std::make_shared<record_type>()), + expression->type_decoration); + return; + } + for (const field_initializer& initializer : expression->field_initializers) + { + for (const type_field& field : record->fields) + { + if (field.first == initializer.name()) + { + if (!is_assignable_from(field.second, initializer.value().type_decoration)) + { + add_error<type_mismatch_error>( + initializer.value().position(), field.second, + initializer.value().type_decoration); + } + break; + } + } + } + } + + void type_analysis_visitor::visit(array_constructor_expression *expression) + { + auto array = resolve_underlying_type(expression->type_decoration).get<array_type>(); + + if (array == nullptr) + { + add_error<type_mismatch_error>( + expression->position(), type(std::make_shared<array_type>(type(), 0)), + expression->type_decoration); + return; + } + if (expression->elements.size() > array->size) + { + add_error<argument_count_error>(array->size, expression->elements.size(), + expression->position()); + return; + } + for (auto element : expression->elements) + { + if (!is_assignable_from(array->base, element->type_decoration)) + { + add_error<type_mismatch_error>( + element->position(), array->base, element->type_decoration); + } + } + } +} |
