diff options
| author | Eugen Wissner <belka@caraus.de> | 2026-09-01 17:44:38 +0200 |
|---|---|---|
| committer | Eugen Wissner <belka@caraus.de> | 2026-09-01 17:44:38 +0200 |
| commit | 6eaec8726f716b2ab6b48c8cd3d7fee6aaf86977 (patch) | |
| tree | 3acd00ea423f9120d4d79e2646c042dd9f596122 | |
| parent | b3a7a5731285625aa9fc6a347841591757860edb (diff) | |
| download | elna-6eaec8726f716b2ab6b48c8cd3d7fee6aaf86977.tar.gz | |
Reject pointer arithmetic on opaque pointers
| -rw-r--r-- | boot/type_check.cc | 44 | ||||
| -rw-r--r-- | include/elna/boot/type_check.h | 1 | ||||
| -rw-r--r-- | testsuite/fail_compilation/opaque_pointer_arithmetic.elna | 9 | ||||
| -rw-r--r-- | testsuite/fail_compilation/opaque_slice_element.elna | 8 |
4 files changed, 59 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) 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. |
