aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2026-09-01 17:44:38 +0200
committerEugen Wissner <belka@caraus.de>2026-09-01 17:44:38 +0200
commit6eaec8726f716b2ab6b48c8cd3d7fee6aaf86977 (patch)
tree3acd00ea423f9120d4d79e2646c042dd9f596122
parentb3a7a5731285625aa9fc6a347841591757860edb (diff)
downloadelna-6eaec8726f716b2ab6b48c8cd3d7fee6aaf86977.tar.gz
Reject pointer arithmetic on opaque pointers
-rw-r--r--boot/type_check.cc44
-rw-r--r--include/elna/boot/type_check.h1
-rw-r--r--testsuite/fail_compilation/opaque_pointer_arithmetic.elna9
-rw-r--r--testsuite/fail_compilation/opaque_slice_element.elna8
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.