aboutsummaryrefslogtreecommitdiff
path: root/boot/type_check.cc
diff options
context:
space:
mode:
Diffstat (limited to 'boot/type_check.cc')
-rw-r--r--boot/type_check.cc44
1 files changed, 41 insertions, 3 deletions
diff --git a/boot/type_check.cc b/boot/type_check.cc
index 74c8176..4ceab18 100644
--- a/boot/type_check.cc
+++ b/boot/type_check.cc
@@ -218,6 +218,9 @@ namespace elna::boot
case opaque_cast:
return "Opaque type '" + this->actual.to_string()
+ "' cannot be used as a cast target type";
+ case opaque_arithmetic:
+ return "Opaque type '" + this->actual.to_string()
+ + "' cannot be used as an element type in pointer arithmetic";
case zero_sized:
return "Zero-sized type '" + this->actual.to_string()
+ "' cannot be declared";
@@ -289,9 +292,10 @@ namespace elna::boot
/*
* Finds the first opaque type in a value position, following aliases,
- * qualifiers, arrays, records and procedures but not pointers or slices.
- * Pointers and slices don't store their base type by value, so an opaque
- * behind them is only reachable as a transient value.
+ * qualifiers, arrays, slices, records and procedures but not pointers.
+ * A pointer doesn't store its base type by value, so an opaque behind it
+ * is only reachable as a transient value. A slice does: indexing, slicing
+ * and taking an element's address all scale by the element size.
*/
static std::optional<type> find_opaque_type(const type& checked)
{
@@ -316,6 +320,10 @@ namespace elna::boot
{
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)
@@ -332,6 +340,31 @@ namespace elna::boot
return std::nullopt;
}
+ /*
+ * Scaling an offset needs the pointee size, which an opaque type doesn't
+ * have. Only a pointer_type scales; the generic Pointer and procedure
+ * values advance byte-wise.
+ */
+ static std::optional<type> find_opaque_arithmetic(binary_operator operation,
+ const type& lhs, const type& rhs)
+ {
+ if (operation != binary_operator::sum && operation != binary_operator::subtraction)
+ {
+ return std::nullopt;
+ }
+ const bool lhs_integral = is_integral_type(lhs);
+
+ if (lhs_integral == is_integral_type(rhs))
+ {
+ return std::nullopt;
+ }
+ if (auto pointer = (lhs_integral ? rhs : lhs).get<pointer_type>())
+ {
+ return find_opaque_type(pointer->base);
+ }
+ return std::nullopt;
+ }
+
// Checks whether the type has zero size.
static bool has_zero_size(const type& checked, const target_info& target)
{
@@ -1213,6 +1246,11 @@ namespace elna::boot
};
add_error<type_mismatch_error>(expression->position(), expression->lhs().type_decoration, binary_error);
}
+ else if (auto opaque = find_opaque_arithmetic(operation, lhs_resolved, rhs_resolved))
+ {
+ add_error<type_requirement_error>(expression->position(),
+ opaque.value(), type_requirement_error::kind::opaque_arithmetic);
+ }
}
void type_analysis_visitor::visit(traits_expression *trait)