aboutsummaryrefslogtreecommitdiff
path: root/boot/type_check.cc
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2026-09-24 00:39:51 +0200
committerEugen Wissner <belka@caraus.de>2026-09-24 00:39:51 +0200
commitf7b035a505e86ad916a43ab1dc1a662a6066b011 (patch)
treea54369ae8ad56352d148c4583ab568f7d4869d30 /boot/type_check.cc
parent74e75e41fe12f19e5365a3152ffafa1a29376240 (diff)
downloadelna-f7b035a505e86ad916a43ab1dc1a662a6066b011.tar.gz
Move module heading to the top
Diffstat (limited to 'boot/type_check.cc')
-rw-r--r--boot/type_check.cc197
1 files changed, 100 insertions, 97 deletions
diff --git a/boot/type_check.cc b/boot/type_check.cc
index 5d5bc59..7be236d 100644
--- a/boot/type_check.cc
+++ b/boot/type_check.cc
@@ -44,7 +44,7 @@ namespace elna::boot
__builtin_unreachable();
}
- static std::string print_binary_operator(const binary_operator operation)
+ static std::string_view print_binary_operator(const binary_operator operation)
{
switch (operation)
{
@@ -215,7 +215,7 @@ namespace elna::boot
{
return "Invalid operands of type '" + this->actual.to_string()
+ "' and '" + payload.right.to_string()
- + "' for operator " + print_binary_operator(payload.operation);
+ + "' for operator " + std::string(print_binary_operator(payload.operation));
}
else if constexpr (std::is_same_v<T, invalid_cast>)
{
@@ -345,13 +345,13 @@ namespace elna::boot
static bool contains_constant_member(const type& checked)
{
- auto referent = resolve_aliases(checked);
+ const type referent = resolve_aliases(checked);
if (referent.get<constant_type>() != nullptr)
{
return true;
}
- else if (auto record = referent.get<record_type>())
+ else if (const auto record = referent.get<record_type>())
{
for (const auto& [field_name, field] : record->fields)
{
@@ -363,7 +363,7 @@ namespace elna::boot
return !record->base.empty()
&& contains_constant_member(resolve_underlying_type(record->base));
}
- else if (auto array = referent.get<array_type>())
+ else if (const auto array = referent.get<array_type>())
{
return contains_constant_member(array->base);
}
@@ -385,30 +385,30 @@ namespace elna::boot
}
const type referent = resolve_aliases(checked);
- if (auto record = referent.get<record_type>())
+ if (const auto record = referent.get<record_type>())
{
for (const auto& [field_name, field] : record->fields)
{
- if (auto opaque = find_opaque_type(field.field_type))
+ if (const auto opaque = find_opaque_type(field.field_type))
{
return opaque;
}
}
return !record->base.empty() ? find_opaque_type(record->base) : std::nullopt;
}
- else if (auto array = referent.get<array_type>())
+ else if (const auto array = referent.get<array_type>())
{
return find_opaque_type(array->base);
}
- else if (auto slice = referent.get<slice_type>())
+ else if (const auto slice = referent.get<slice_type>())
{
return find_opaque_type(slice->base);
}
- else if (auto procedure = referent.get<procedure_type>())
+ else if (const auto procedure = referent.get<procedure_type>())
{
for (const type& parameter : procedure->parameters)
{
- if (auto opaque = find_opaque_type(parameter))
+ if (const auto opaque = find_opaque_type(parameter))
{
return opaque;
}
@@ -446,7 +446,7 @@ namespace elna::boot
{
return std::nullopt;
}
- if (auto pointer = (lhs_integral ? rhs : lhs).get<pointer_type>())
+ if (const auto pointer = (lhs_integral ? rhs : lhs).get<pointer_type>())
{
return find_opaque_type(pointer->base);
}
@@ -456,7 +456,8 @@ namespace elna::boot
// 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);
+ const std::optional<type_properties> properties =
+ get_type_properties(resolve_underlying_type(checked), target);
return properties.has_value() && properties->size == 0;
}
@@ -469,7 +470,7 @@ namespace elna::boot
{
return checked;
}
- if (auto record = resolve_aliases(checked).get<record_type>())
+ if (const auto record = resolve_aliases(checked).get<record_type>())
{
for (const auto& [field_name, field] : record->fields)
{
@@ -520,23 +521,18 @@ namespace elna::boot
}
}
- /*
- * Checks whether derived has base in its record parent chain.
- *
- * Base record type must not be null. Derived record type may be null.
- */
assign_check::verdict assign_check::guard_const_laundering() const
{
if (!is_primitive_type(ctx.aliased_assignee, "Pointer"))
{
return verdict::pass;
}
- if (auto ptr = ctx.aliased_assignment.get<pointer_type>();
+ if (const 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>();
+ if (const auto const_assign = ctx.aliased_assignment.get<constant_type>();
const_assign != nullptr && is_primitive_type(const_assign->unqualified, "Pointer"))
{
return verdict::reject;
@@ -566,15 +562,16 @@ namespace elna::boot
assign_check::verdict assign_check::check_pointer_conversion() const
{
- auto assignee_ptr = ctx.resolved_assignee.get<pointer_type>();
- auto assignment_ptr = ctx.resolved_assignment.get<pointer_type>();
+ const std::shared_ptr<pointer_type> assignee_ptr = ctx.resolved_assignee.get<pointer_type>();
+ const std::shared_ptr<pointer_type> assignment_ptr = ctx.resolved_assignment.get<pointer_type>();
if (assignee_ptr == nullptr || assignment_ptr == nullptr)
{
return verdict::pass;
}
- auto assignee_pointee_const = resolve_aliases(assignee_ptr->base).get<constant_type>();
- auto assignment_pointee_const = resolve_aliases(assignment_ptr->base).get<constant_type>();
+ const std::shared_ptr<constant_type> assignee_pointee_const =
+ resolve_aliases(assignee_ptr->base).get<constant_type>();
+ const std::shared_ptr<constant_type> 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
@@ -599,15 +596,17 @@ namespace elna::boot
assign_check::verdict assign_check::check_slice_conversion() const
{
- auto assignee_slice = ctx.resolved_assignee.get<slice_type>();
- auto assignment_slice = ctx.resolved_assignment.get<slice_type>();
+ const std::shared_ptr<slice_type> assignee_slice = ctx.resolved_assignee.get<slice_type>();
+ const std::shared_ptr<slice_type> assignment_slice = ctx.resolved_assignment.get<slice_type>();
if (assignee_slice == nullptr || assignment_slice == nullptr)
{
return verdict::pass;
}
- auto assignee_element_const = resolve_aliases(assignee_slice->base).get<constant_type>();
- auto assignment_element_const = resolve_aliases(assignment_slice->base).get<constant_type>();
+ const std::shared_ptr<constant_type> assignee_element_const =
+ resolve_aliases(assignee_slice->base).get<constant_type>();
+ const std::shared_ptr<constant_type> assignment_element_const =
+ resolve_aliases(assignment_slice->base).get<constant_type>();
// Const can be added to the element type, but not removed.
if (assignee_element_const != nullptr
@@ -624,16 +623,18 @@ namespace elna::boot
assign_check::verdict assign_check::check_array_conversion() const
{
- auto assignee_array = ctx.resolved_assignee.get<array_type>();
- auto assignment_array = ctx.resolved_assignment.get<array_type>();
+ const std::shared_ptr<array_type> assignee_array = ctx.resolved_assignee.get<array_type>();
+ const std::shared_ptr<array_type> assignment_array = ctx.resolved_assignment.get<array_type>();
if (assignee_array == nullptr || assignment_array == nullptr
|| assignee_array->size != assignment_array->size)
{
return verdict::pass;
}
- auto assignee_element_const = resolve_aliases(assignee_array->base).get<constant_type>();
- auto assignment_element_const = resolve_aliases(assignment_array->base).get<constant_type>();
+ const std::shared_ptr<constant_type> assignee_element_const =
+ resolve_aliases(assignee_array->base).get<constant_type>();
+ const std::shared_ptr<constant_type> assignment_element_const =
+ resolve_aliases(assignment_array->base).get<constant_type>();
// An array is copied by value, so const can be added to the element type,
// but not removed. Writing to such an array is rejected on the lvalue.
@@ -651,7 +652,7 @@ namespace elna::boot
bool assign_check::run()
{
- auto const assign_handlers = {
+ const std::initializer_list assign_handlers = {
&assign_check::guard_const_laundering,
&assign_check::check_exact_match,
&assign_check::check_slice_conversion,
@@ -659,7 +660,7 @@ namespace elna::boot
&assign_check::check_pointer_hatch,
&assign_check::check_pointer_conversion
};
- for (auto handler : assign_handlers)
+ for (const auto handler : assign_handlers)
{
switch ((this->*handler)())
{
@@ -804,7 +805,6 @@ namespace elna::boot
this->current_procedure = this->bag.lookup("")->is_procedure();
this->bag.enter(this->current_procedure->scope);
walking_visitor::visit_entry_point(unit);
- check_return(unit->entry_point.value(), unit->entry_position.value(), "");
this->bag.leave();
this->current_procedure.reset();
}
@@ -815,14 +815,15 @@ namespace elna::boot
for (const auto& variable_identifier : declaration->identifiers)
{
- auto variable_symbol = this->bag.lookup(variable_identifier.name())->is_variable();
+ const std::shared_ptr<variable_info> variable_symbol =
+ this->bag.lookup(variable_identifier.name())->is_variable();
- if (auto opaque = find_opaque_type(variable_symbol->symbol))
+ if (const 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))
+ else if (const 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);
@@ -834,7 +835,9 @@ namespace elna::boot
}
for (const auto& variable_identifier : declaration->identifiers)
{
- auto variable_symbol = this->bag.lookup(variable_identifier.name())->is_variable();
+ const std::shared_ptr<variable_info> 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(),
@@ -869,7 +872,7 @@ namespace elna::boot
void type_analysis_visitor::visit(for_statement *statement)
{
statement->range().accept(this);
- auto resolved_range = resolve_underlying_type(statement->range().type_decoration);
+ const type resolved_range = resolve_underlying_type(statement->range().type_decoration);
if (!get_range_base_type(resolved_range))
{
@@ -923,9 +926,10 @@ namespace elna::boot
void type_analysis_visitor::visit(type_declaration *declaration)
{
walking_visitor::visit(declaration);
- auto unresolved_type = this->bag.lookup(declaration->identifier.name())->is_type()->symbol.get<alias_type>();
+ const std::shared_ptr<alias_type> unresolved_type =
+ this->bag.lookup(declaration->identifier.name())->is_type()->symbol.get<alias_type>();
- if (auto generic = unresolved_type->referent.get<generic_type>())
+ if (const auto generic = unresolved_type->referent.get<generic_type>())
{
check_parameters_used(declaration->position(), declaration->identifier.name(),
generic->parameters, generic->referent);
@@ -933,11 +937,11 @@ namespace elna::boot
}
const type referent = resolve_aliases(unresolved_type->referent);
- if (auto record = referent.get<record_type>())
+ if (const auto record = referent.get<record_type>())
{
for (const auto& [field_name, field] : record->fields)
{
- if (auto opaque = find_opaque_type(field.field_type))
+ if (const auto opaque = find_opaque_type(field.field_type))
{
add_error<type_requirement_error>(declaration->position(),
opaque.value(), type_requirement_error::kind::opaque_field);
@@ -949,27 +953,27 @@ namespace elna::boot
}
}
}
- else if (auto array = referent.get<array_type>())
+ else if (const auto array = referent.get<array_type>())
{
- if (auto opaque = find_opaque_type(array->base))
+ if (const 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>())
+ else if (const auto slice = referent.get<slice_type>())
{
- if (auto opaque = find_opaque_type(slice->base))
+ if (const 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>())
+ else if (const auto procedure = referent.get<procedure_type>())
{
for (const type& parameter : procedure->parameters)
{
- if (auto opaque = find_opaque_type(parameter))
+ if (const auto opaque = find_opaque_type(parameter))
{
add_error<type_requirement_error>(declaration->position(),
opaque.value(), type_requirement_error::kind::opaque_parameter);
@@ -982,7 +986,7 @@ namespace elna::boot
}
if (!procedure->return_type.proper_type.empty())
{
- if (auto opaque = find_opaque_type(procedure->return_type.proper_type))
+ if (const 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);
@@ -1001,7 +1005,8 @@ namespace elna::boot
{
if (expression->base.has_value())
{
- auto const base_symbol = this->bag.lookup(expression->base.value().name());
+ const std::shared_ptr<info> base_symbol = this->bag.lookup(expression->base.value().name());
+
if (base_symbol == nullptr || base_symbol->is_type() == nullptr)
{
add_error<type_requirement_error>(expression->position(),
@@ -1009,7 +1014,7 @@ namespace elna::boot
}
else
{
- type const base_type = erase_generic(base_symbol->is_type()->symbol);
+ const type base_type = erase_generic(base_symbol->is_type()->symbol);
if (base_type.get<record_type>() == nullptr)
{
add_error<type_requirement_error>(expression->position(),
@@ -1032,23 +1037,23 @@ namespace elna::boot
{
return true;
}
- if (auto pointer = subject.get<pointer_type>())
+ else if (const auto pointer = subject.get<pointer_type>())
{
return occurs_in(parameter, pointer->base);
}
- if (auto slice = subject.get<slice_type>())
+ else if (const auto slice = subject.get<slice_type>())
{
return occurs_in(parameter, slice->base);
}
- if (auto array = subject.get<array_type>())
+ else if (const auto array = subject.get<array_type>())
{
return occurs_in(parameter, array->base);
}
- if (auto qualified = subject.get<constant_type>())
+ else if (const auto qualified = subject.get<constant_type>())
{
return occurs_in(parameter, qualified->unqualified);
}
- if (auto record = subject.get<record_type>())
+ else if (const auto record = subject.get<record_type>())
{
for (const auto& [field_name, field] : record->fields)
{
@@ -1059,7 +1064,7 @@ namespace elna::boot
}
return !record->base.empty() && occurs_in(parameter, record->base);
}
- if (auto procedure = subject.get<procedure_type>())
+ else if (const auto procedure = subject.get<procedure_type>())
{
for (const type& parameter_type : procedure->parameters)
{
@@ -1071,7 +1076,7 @@ namespace elna::boot
return !procedure->return_type.proper_type.empty()
&& occurs_in(parameter, procedure->return_type.proper_type);
}
- if (auto instance = subject.get<instantiated_type>())
+ else if (const auto instance = subject.get<instantiated_type>())
{
for (const type& argument : instance->arguments)
{
@@ -1112,7 +1117,7 @@ namespace elna::boot
const type left = resolve_aliases(declared);
const type right = resolve_aliases(actual);
- if (auto parameter = left.get<parameter_type>())
+ if (const auto parameter = left.get<parameter_type>())
{
for (std::size_t i = 0; i < parameters.size(); ++i)
{
@@ -1125,40 +1130,40 @@ namespace elna::boot
}
return true;
}
- if (auto left_pointer = left.get<pointer_type>())
+ else if (const auto left_pointer = left.get<pointer_type>())
{
- auto right_pointer = right.get<pointer_type>();
+ const std::shared_ptr<pointer_type> right_pointer = right.get<pointer_type>();
return right_pointer != nullptr
&& bind_arguments(parameters, bindings, left_pointer->base, right_pointer->base);
}
- if (auto left_slice = left.get<slice_type>())
+ else if (const auto left_slice = left.get<slice_type>())
{
- auto right_slice = right.get<slice_type>();
+ const std::shared_ptr<slice_type> right_slice = right.get<slice_type>();
return right_slice != nullptr
&& bind_arguments(parameters, bindings, left_slice->base, right_slice->base);
}
- if (auto left_array = left.get<array_type>())
+ else if (const auto left_array = left.get<array_type>())
{
- auto right_array = right.get<array_type>();
+ const std::shared_ptr<array_type> right_array = right.get<array_type>();
return right_array != nullptr
&& bind_arguments(parameters, bindings, left_array->base, right_array->base);
}
- if (auto left_constant = left.get<constant_type>())
+ else if (const auto left_constant = left.get<constant_type>())
{
- auto right_constant = right.get<constant_type>();
+ const std::shared_ptr<constant_type> right_constant = right.get<constant_type>();
return right_constant != nullptr
- && bind_arguments(parameters, bindings,
- left_constant->unqualified, right_constant->unqualified);
+ && bind_arguments(parameters, bindings, left_constant->unqualified, right_constant->unqualified);
}
- if (auto left_instance = left.get<instantiated_type>())
+ else if (const auto left_instance = left.get<instantiated_type>())
{
for (type candidate = right; !candidate.empty(); candidate = base_of(candidate))
{
- auto right_instance = resolve_aliases(candidate).get<instantiated_type>();
+ const std::shared_ptr<instantiated_type> right_instance =
+ resolve_aliases(candidate).get<instantiated_type>();
if (right_instance == nullptr
|| !(left_instance->generic == right_instance->generic)
@@ -1178,9 +1183,9 @@ namespace elna::boot
}
return false;
}
- if (auto left_procedure = left.get<procedure_type>())
+ else if (const auto left_procedure = left.get<procedure_type>())
{
- auto right_procedure = right.get<procedure_type>();
+ const std::shared_ptr<procedure_type> right_procedure = right.get<procedure_type>();
if (right_procedure == nullptr
|| left_procedure->parameters.size() != right_procedure->parameters.size())
@@ -1281,7 +1286,7 @@ namespace elna::boot
}
const type denoted = expression->type_decoration;
- if (auto instance = denoted.get<instantiated_type>())
+ if (const auto instance = denoted.get<instantiated_type>())
{
std::shared_ptr<generic_type> const generic = unwrap_generic(instance->generic);
@@ -1404,7 +1409,7 @@ namespace elna::boot
{
// Inference reads the argument types, so they are decorated
// before the parameter types they will be checked against exist.
- for (expression *argument : call->arguments)
+ for (expression *const argument : call->arguments)
{
argument->accept(this);
}
@@ -1414,13 +1419,13 @@ namespace elna::boot
// Name analysis decorated the call with the declared return
// type, which still mentions the parameters inference has
// just bound.
- if (auto substituted = callable_type.get<procedure_type>())
+ if (const auto substituted = callable_type.get<procedure_type>())
{
call->type_decoration = substituted->return_type.proper_type;
}
}
}
- if (auto procedure = callable_type.get<procedure_type>())
+ if (const auto procedure = callable_type.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);
@@ -1455,7 +1460,7 @@ namespace elna::boot
if (call->arguments.size() < procedure->parameters.size()
|| (!procedure->variadic && call->arguments.size() > procedure->parameters.size()))
{
- if (auto *procedure_name = call->callable().is_named())
+ if (const auto *procedure_name = call->callable().is_named())
{
add_error<argument_count_error>(call->position(),
argument_count_error::kind::call, procedure_name->name,
@@ -1480,7 +1485,7 @@ namespace elna::boot
void type_analysis_visitor::visit(record_constructor_expression *expression)
{
- auto record = erase_generic(expression->type_decoration).get<record_type>();
+ const std::shared_ptr<record_type> record = erase_generic(expression->type_decoration).get<record_type>();
if (record == nullptr)
{
@@ -1507,7 +1512,8 @@ namespace elna::boot
void type_analysis_visitor::visit(array_constructor_expression *expression)
{
- auto array = resolve_underlying_type(expression->type_decoration).get<array_type>();
+ const std::shared_ptr<array_type> array =
+ resolve_underlying_type(expression->type_decoration).get<array_type>();
if (array == nullptr)
{
@@ -1518,7 +1524,7 @@ namespace elna::boot
}
// The first element of the array literal determines the array type and
// does not have to be checked.
- for (auto *element : expression->elements | std::views::drop(1))
+ for (const auto *element : expression->elements | std::views::drop(1))
{
if (!is_assignable_from(array->base, element->type_decoration))
{
@@ -1548,7 +1554,7 @@ namespace elna::boot
{
walking_visitor::visit(expression);
- auto resolved_base = resolve_underlying_type(expression->base().type_decoration);
+ const type resolved_base = resolve_underlying_type(expression->base().type_decoration);
if (resolved_base.get<array_type>() == nullptr
&& resolved_base.get<slice_type>() == nullptr)
{
@@ -1566,7 +1572,7 @@ namespace elna::boot
{
walking_visitor::visit(expression);
- auto operation = expression->operation();
+ const unary_operator operation = expression->operation();
type const resolved = resolve_underlying_type(expression->operand().type_decoration);
if (operation == unary_operator::plus || operation == unary_operator::minus)
@@ -1595,14 +1601,11 @@ namespace elna::boot
type_mismatch_error::unary{ .operation = operation });
}
}
- else if (operation == unary_operator::reference)
+ else if (operation == unary_operator::reference && !is_addressable(expression->operand()))
{
- if (!is_addressable(expression->operand()))
- {
- add_error<type_requirement_error>(expression->position(),
- expression->operand().type_decoration,
- type_requirement_error::kind::not_addressable);
- }
+ add_error<type_requirement_error>(expression->position(),
+ expression->operand().type_decoration,
+ type_requirement_error::kind::not_addressable);
}
}
@@ -1634,8 +1637,8 @@ namespace elna::boot
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);
+ const type source = resolve_underlying_type(expression->value().type_decoration);
+ const type target = resolve_underlying_type(expression->type_decoration);
if (source != target // const cast.
&& (!is_scalar_type(source) || !is_scalar_type(target))
@@ -1651,7 +1654,7 @@ namespace elna::boot
{
walking_visitor::visit(expression);
- auto operation = expression->operation();
+ const binary_operator operation = expression->operation();
type const lhs_resolved = resolve_underlying_type(expression->lhs().type_decoration);
type const rhs_resolved = resolve_underlying_type(expression->rhs().type_decoration);
bool valid = false;
@@ -1804,7 +1807,7 @@ namespace elna::boot
add_error<trait_error>(trait->name.position(), trait->name.name(),
trait_error::unsupported_type{ trait->types.front() });
}
- else if (auto *field = trait->arguments.at(1)->is_named())
+ else if (const auto *field = trait->arguments.at(1)->is_named())
{
if (erase_generic(trait->types.front()).get<record_type>() == nullptr)
{