From 6eaec8726f716b2ab6b48c8cd3d7fee6aaf86977 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Tue, 1 Sep 2026 17:44:38 +0200 Subject: Reject pointer arithmetic on opaque pointers --- boot/type_check.cc | 44 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) (limited to 'boot') 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 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()) + { + return find_opaque_type(slice->base); + } else if (auto procedure = referent.get()) { 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 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()) + { + 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(expression->position(), expression->lhs().type_decoration, binary_error); } + else if (auto opaque = find_opaque_arithmetic(operation, lhs_resolved, rhs_resolved)) + { + add_error(expression->position(), + opaque.value(), type_requirement_error::kind::opaque_arithmetic); + } } void type_analysis_visitor::visit(traits_expression *trait) -- cgit v1.2.3