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 ++++++++++++++++++++-- include/elna/boot/type_check.h | 1 + .../opaque_pointer_arithmetic.elna | 9 +++++ .../fail_compilation/opaque_slice_element.elna | 8 ++++ 4 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 testsuite/fail_compilation/opaque_pointer_arithmetic.elna create mode 100644 testsuite/fail_compilation/opaque_slice_element.elna 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) diff --git a/include/elna/boot/type_check.h b/include/elna/boot/type_check.h index e2f5cc9..fea7e49 100644 --- a/include/elna/boot/type_check.h +++ b/include/elna/boot/type_check.h @@ -93,6 +93,7 @@ namespace elna::boot opaque_field, opaque_element, opaque_cast, + opaque_arithmetic, zero_sized, not_addressable }; diff --git a/testsuite/fail_compilation/opaque_pointer_arithmetic.elna b/testsuite/fail_compilation/opaque_pointer_arithmetic.elna new file mode 100644 index 0000000..307aed8 --- /dev/null +++ b/testsuite/fail_compilation/opaque_pointer_arithmetic.elna @@ -0,0 +1,9 @@ +type + Handle = extern + +var + p: ^Handle + +begin + p := p + 1 (* @Error Opaque type 'Handle' cannot be used as an element type in pointer arithmetic *) +end. diff --git a/testsuite/fail_compilation/opaque_slice_element.elna b/testsuite/fail_compilation/opaque_slice_element.elna new file mode 100644 index 0000000..0f28dae --- /dev/null +++ b/testsuite/fail_compilation/opaque_slice_element.elna @@ -0,0 +1,8 @@ +type + Handle = extern + +var + s: []Handle (* @Error Opaque type 'Handle' cannot be used to declare a variable *) + +begin +end. -- cgit v1.2.3