diff options
Diffstat (limited to 'boot/type_check.cc')
| -rw-r--r-- | boot/type_check.cc | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/boot/type_check.cc b/boot/type_check.cc index 2d82579..e810e30 100644 --- a/boot/type_check.cc +++ b/boot/type_check.cc @@ -16,6 +16,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #include "elna/boot/type_check.h" +#include "elna/boot/evaluator.h" #include <algorithm> #include <utility> @@ -195,6 +196,27 @@ namespace elna::boot case dereference_of_non_pointer: return "Type '" + this->actual.to_string() + "' cannot be dereferenced, it is not a pointer"; + case opaque_variable: + return "Opaque type '" + this->actual.to_string() + + "' cannot be used to declare a variable"; + case opaque_parameter: + return "Opaque type '" + this->actual.to_string() + + "' cannot be used as a procedure parameter"; + case opaque_return: + return "Opaque type '" + this->actual.to_string() + + "' cannot be used as a return type"; + case opaque_field: + return "Opaque type '" + this->actual.to_string() + + "' cannot be used as a record field type"; + case opaque_element: + return "Opaque type '" + this->actual.to_string() + + "' cannot be used as an array or slice element type"; + case opaque_cast: + return "Opaque type '" + this->actual.to_string() + + "' cannot be used as a cast target type"; + case zero_sized: + return "Zero-sized type '" + this->actual.to_string() + + "' cannot be declared"; default: __builtin_unreachable(); } @@ -277,6 +299,82 @@ namespace elna::boot return false; } + /* + * Finds the first opaque type in a value position, following aliases, + * qualifiers, arrays, slices, records and procedures but not pointers. + */ + static std::optional<type> find_opaque_type(const type& checked) + { + if (resolve_underlying_type(checked).get<extern_type>() != nullptr) + { + return checked; + } + const type referent = resolve_aliases(checked); + + if (auto record = referent.get<record_type>()) + { + for (const auto& [field_name, field_type] : record->fields) + { + if (auto opaque = find_opaque_type(field_type)) + { + return opaque; + } + } + return !record->base.empty() ? find_opaque_type(record->base) : std::nullopt; + } + else if (auto array = referent.get<array_type>()) + { + return find_opaque_type(array->base); + } + else if (auto slice = referent.get<slice_type>()) + { + return find_opaque_type(slice->base); + } + else if (auto procedure = referent.get<procedure_type>()) + { + for (const type& parameter : procedure->parameters) + { + if (auto opaque = find_opaque_type(parameter)) + { + return opaque; + } + } + return !procedure->return_type.proper_type.empty() + ? find_opaque_type(procedure->return_type.proper_type) + : std::nullopt; + } + return std::nullopt; + } + + // Checks whether the type has zero size. + static bool has_zero_size(const type& checked, const target_info& target) + { + auto properties = get_type_properties(resolve_underlying_type(checked), target); + + return properties.has_value() && properties->size == 0; + } + + // Finds the first zero-sized type: the type itself or a record field. + // Bases, pointers, arrays and slices are not entered. + static std::optional<type> find_zero_sized(const type& checked, const target_info& target) + { + if (has_zero_size(checked, target)) + { + return checked; + } + if (auto record = resolve_aliases(checked).get<record_type>()) + { + for (const auto& [field_name, field_type] : record->fields) + { + if (auto found = find_zero_sized(field_type, target)) + { + return found; + } + } + } + return std::nullopt; + } + bool type_analysis_visitor::is_equality_compatible(const type& left, const type& right) { auto resolved_left = resolve_underlying_type(left); @@ -479,6 +577,39 @@ namespace elna::boot } walking_visitor::visit(declaration); + std::size_t parameter_index = 0; + + for (const type& parameter : this->current_procedure->symbol.parameters) + { + if (auto opaque = find_opaque_type(parameter)) + { + add_error<type_requirement_error>( + declaration->heading().parameters.at(parameter_index).second->position(), + opaque.value(), type_requirement_error::kind::opaque_parameter); + } + else if (auto found = find_zero_sized(parameter, this->target)) + { + add_error<type_requirement_error>( + declaration->heading().parameters.at(parameter_index).second->position(), + found.value(), type_requirement_error::kind::zero_sized); + } + ++parameter_index; + } + if (declaration->heading().return_type.proper_type != nullptr) + { + if (auto opaque = find_opaque_type(this->current_procedure->symbol.return_type.proper_type)) + { + add_error<type_requirement_error>( + declaration->heading().return_type.proper_type->position(), + opaque.value(), type_requirement_error::kind::opaque_return); + } + else if (auto found = find_zero_sized(this->current_procedure->symbol.return_type.proper_type, this->target)) + { + add_error<type_requirement_error>( + declaration->heading().return_type.proper_type->position(), + found.value(), type_requirement_error::kind::zero_sized); + } + } if (declaration->body.has_value()) { if (declaration->body.value().return_expression != nullptr) @@ -538,6 +669,21 @@ namespace elna::boot { walking_visitor::visit(declaration); + for (const identifier_definition& variable_identifier : declaration->identifiers) + { + auto variable_symbol = this->bag.lookup(variable_identifier.name())->is_variable(); + + if (auto opaque = find_opaque_type(variable_symbol->symbol)) + { + add_error<type_requirement_error>(variable_identifier.id().position(), + opaque.value(), type_requirement_error::kind::opaque_variable); + } + else if (auto found = find_zero_sized(variable_symbol->symbol, this->target)) + { + add_error<type_requirement_error>(variable_identifier.id().position(), + found.value(), type_requirement_error::kind::zero_sized); + } + } if (declaration->initializer == nullptr || has_errors()) { return; @@ -661,6 +807,70 @@ namespace elna::boot else { walking_visitor::visit(declaration); + const type referent = resolve_aliases(unresolved_type->referent); + + if (auto record = referent.get<record_type>()) + { + for (const auto& [field_name, field_type] : record->fields) + { + if (auto opaque = find_opaque_type(field_type)) + { + add_error<type_requirement_error>(declaration->position(), + opaque.value(), type_requirement_error::kind::opaque_field); + } + else if (has_zero_size(field_type, this->target)) + { + add_error<type_requirement_error>(declaration->position(), + field_type, type_requirement_error::kind::zero_sized); + } + } + } + else if (auto array = referent.get<array_type>()) + { + if (auto opaque = find_opaque_type(array->base)) + { + add_error<type_requirement_error>(declaration->position(), + opaque.value(), type_requirement_error::kind::opaque_element); + } + } + else if (auto slice = referent.get<slice_type>()) + { + if (auto opaque = find_opaque_type(slice->base)) + { + add_error<type_requirement_error>(declaration->position(), + opaque.value(), type_requirement_error::kind::opaque_element); + } + } + else if (auto procedure = referent.get<procedure_type>()) + { + for (const type& parameter : procedure->parameters) + { + if (auto opaque = find_opaque_type(parameter)) + { + add_error<type_requirement_error>(declaration->position(), + opaque.value(), type_requirement_error::kind::opaque_parameter); + } + else if (has_zero_size(parameter, this->target)) + { + add_error<type_requirement_error>(declaration->position(), + parameter, type_requirement_error::kind::zero_sized); + } + } + if (!procedure->return_type.proper_type.empty()) + { + if (auto opaque = find_opaque_type(procedure->return_type.proper_type)) + { + add_error<type_requirement_error>(declaration->position(), + opaque.value(), type_requirement_error::kind::opaque_return); + } + else if (has_zero_size(procedure->return_type.proper_type, this->target)) + { + add_error<type_requirement_error>(declaration->position(), + procedure->return_type.proper_type, + type_requirement_error::kind::zero_sized); + } + } + } } } @@ -886,6 +1096,12 @@ namespace elna::boot { walking_visitor::visit(expression); + if (auto opaque = find_opaque_type(expression->type_decoration)) + { + add_error<type_requirement_error>(expression->position(), + opaque.value(), type_requirement_error::kind::opaque_cast); + return; + } auto source = resolve_underlying_type(expression->value().type_decoration); auto target = resolve_underlying_type(expression->type_decoration); @@ -1009,6 +1225,11 @@ namespace elna::boot argument_count_error::kind::trait, trait->name.name(), 1, trait->arguments.size()); } + else if (find_opaque_type(trait->types.front())) + { + add_error<trait_error>(trait->name.position(), trait->name.name(), + trait_error::unsupported_type{ trait->types.front() }); + } } else if ((trait->name == "min" || trait->name == "max") && !trait->type_decoration.empty()) { @@ -1039,6 +1260,11 @@ namespace elna::boot argument_count_error::kind::trait, trait->name.name(), 2, trait->arguments.size()); } + else if (find_opaque_type(trait->types.front())) + { + add_error<trait_error>(trait->name.position(), trait->name.name(), + trait_error::unsupported_type{ trait->types.front() }); + } else if (trait->arguments.at(1)->is_named() == nullptr) { add_error<trait_error>(trait->arguments.at(1)->position(), trait->name.name(), |
