aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2026-08-31 17:18:39 +0200
committerEugen Wissner <belka@caraus.de>2026-08-31 17:18:39 +0200
commit0f5e9f00b07606871a6289060fa904555d41ae7d (patch)
tree2699d0c10e5e1756bec8f6093796c7379ff95822
parentd905e48d6af0445c7941cd2e638461db2f4d9ee8 (diff)
downloadelna-0f5e9f00b07606871a6289060fa904555d41ae7d.tar.gz
Reject comparison of uncompatible types
-rw-r--r--boot/type_check.cc18
-rw-r--r--gcc/gcc/elna-builtins.cc19
-rw-r--r--gcc/gcc/elna-generic.cc72
-rw-r--r--gcc/gcc/elna-module-loader.cc8
-rw-r--r--gcc/gcc/elna-tree.cc39
-rw-r--r--include/elna/boot/dependency.h16
-rw-r--r--include/elna/boot/result.h4
-rw-r--r--include/elna/gcc/elna-builtins.h2
-rw-r--r--include/elna/gcc/elna-generic.h2
-rw-r--r--include/elna/gcc/elna-module-loader.h5
-rw-r--r--include/elna/gcc/elna-tree.h2
-rw-r--r--testsuite/fail_compilation/assign_void_call.elna9
-rw-r--r--testsuite/fail_compilation/compare_unrelated_records.elna17
-rw-r--r--testsuite/fail_compilation/proc_type_without_parameters.elna9
-rw-r--r--testsuite/fail_compilation/record-base-cycle-indirect.elna13
-rw-r--r--testsuite/runnable/case_constant_label.elna13
-rw-r--r--testsuite/runnable/exported_variable/helper.elna4
-rw-r--r--testsuite/runnable/exported_variable/sut.elna5
-rw-r--r--testsuite/runnable/generic_pointer_arithmetic.elna13
19 files changed, 174 insertions, 96 deletions
diff --git a/boot/type_check.cc b/boot/type_check.cc
index 5b0547b..068d958 100644
--- a/boot/type_check.cc
+++ b/boot/type_check.cc
@@ -127,6 +127,11 @@ namespace elna::boot
if constexpr (std::is_same_v<T, expected_type>)
{
+ if (this->actual.empty())
+ {
+ return "Expected type '" + payload.value.to_string()
+ + "', but the expression has no value";
+ }
return "Expected type '" + payload.value.to_string()
+ "', but got '" + this->actual.to_string() + "'";
}
@@ -381,14 +386,7 @@ namespace elna::boot
bool type_analysis_visitor::is_equality_compatible(const type& left, const type& right)
{
- auto resolved_left = resolve_underlying_type(left);
- auto resolved_right = resolve_underlying_type(right);
-
- return resolved_left == resolved_right
- || (is_primitive_type(resolved_left, "Pointer") && is_any_pointer_type(resolved_right))
- || (is_any_pointer_type(resolved_left) && is_primitive_type(resolved_right, "Pointer"))
- || (resolved_left.get<slice_type>() && resolved_right.get<slice_type>())
- || (resolved_left.get<record_type>() && resolved_right.get<record_type>());
+ return is_assignable_from(left, right) || is_assignable_from(right, left);
}
void type_analysis_visitor::visit_and_validate_condition(expression& condition)
@@ -465,7 +463,7 @@ namespace elna::boot
if (assignee_ptr == nullptr || assignment_ptr == nullptr)
{
- return verdict::reject;
+ return verdict::pass;
}
auto assignee_pointee_const = resolve_aliases(assignee_ptr->base).get<constant_type>();
auto assignment_pointee_const = resolve_aliases(assignment_ptr->base).get<constant_type>();
@@ -678,7 +676,7 @@ namespace elna::boot
found.value(), type_requirement_error::kind::zero_sized);
}
}
- if (declaration->initializer == nullptr || has_errors())
+ if (declaration->initializer == nullptr)
{
return;
}
diff --git a/gcc/gcc/elna-builtins.cc b/gcc/gcc/elna-builtins.cc
index 18dcc52..23315e5 100644
--- a/gcc/gcc/elna-builtins.cc
+++ b/gcc/gcc/elna-builtins.cc
@@ -16,6 +16,7 @@ along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "elna/gcc/elna-builtins.h"
+#include "elna/gcc/elna-diagnostic.h"
#include "elna/gcc/elna1.h"
#include "stor-layout.h"
#include "stringpool.h"
@@ -304,7 +305,11 @@ namespace elna::gcc
const std::shared_ptr<symbol_table>& symbols)
{
tree declaration_type = gcc::build_procedure_type(info.symbol, symbols);
- tree fndecl = build_fn_decl(name.c_str(), declaration_type);
+ const location_t location = info.position.has_value()
+ ? get_location(&info.position.value())
+ : UNKNOWN_LOCATION;
+ tree fndecl = build_decl(location, FUNCTION_DECL,
+ get_identifier(name.c_str()), declaration_type);
symbols->enter(name, fndecl);
if (info.symbol.return_type.no_return)
@@ -330,8 +335,11 @@ namespace elna::gcc
function_args_iter_next(&parameter_type);
}
DECL_ARGUMENTS(fndecl) = argument_chain;
+ // Elna has no exceptions.
+ TREE_NOTHROW(fndecl) = 1;
DECL_EXTERNAL(fndecl) = static_cast<unsigned>(info.is_extern());
- TREE_PUBLIC(fndecl) = static_cast<unsigned>(info.exported);
+ // An extern procedure has C linkage whether or not Elna re-exports the name.
+ TREE_PUBLIC(fndecl) = static_cast<unsigned>(info.exported || info.is_extern());
}
tree declare_variable(const std::string& name, const boot::variable_info& info,
@@ -361,7 +369,7 @@ namespace elna::gcc
}
void rewrite_symbol_table(const std::shared_ptr<boot::symbol_table>& info_table,
- const std::shared_ptr<symbol_table>& symbols)
+ const std::shared_ptr<symbol_table>& symbols, const bool imported)
{
for (auto& [symbol_name, symbol_info] : *info_table)
{
@@ -371,7 +379,10 @@ namespace elna::gcc
}
else if (auto variable_info = symbol_info->is_variable())
{
- declare_variable(symbol_name, *variable_info, symbols);
+ tree declaration_tree = declare_variable(symbol_name, *variable_info, symbols);
+
+ DECL_EXTERNAL(declaration_tree) =
+ static_cast<unsigned>(imported || variable_info->is_extern);
}
else if (auto procedure_info = symbol_info->is_procedure())
{
diff --git a/gcc/gcc/elna-generic.cc b/gcc/gcc/elna-generic.cc
index e2d50b4..fb68136 100644
--- a/gcc/gcc/elna-generic.cc
+++ b/gcc/gcc/elna-generic.cc
@@ -176,8 +176,7 @@ namespace elna::gcc
for (const boot::field_initializer& initializer : expression->field_initializers)
{
- tree field_decl = find_field_by_name(get_location(&expression->position()),
- record_type, initializer.name());
+ tree field_decl = find_field_by_name(record_type, initializer.name());
initializer.value().accept(this);
// NOLINTNEXTLINE(misc-const-correctness)
CONSTRUCTOR_APPEND_ELT(tree_arguments, field_decl, this->current_expression);
@@ -474,8 +473,6 @@ namespace elna::gcc
DECL_INITIAL(fndecl) = BIND_EXPR_BLOCK(mapping);
DECL_SAVED_TREE(fndecl) = mapping;
- DECL_PRESERVE_P(fndecl) = 1;
-
dump_function(TDI_original, fndecl);
pop_cfun();
@@ -720,8 +717,7 @@ namespace elna::gcc
if (lang_hooks.decls.global_bindings_p())
{
- TREE_STATIC(declaration_tree) =
- static_cast<unsigned>(!variable_identifier.exported() && !declaration->is_extern);
+ TREE_STATIC(declaration_tree) = static_cast<unsigned>(!declaration->is_extern);
varpool_node::get_create(declaration_tree);
varpool_node::finalize_decl(declaration_tree);
}
@@ -741,14 +737,9 @@ namespace elna::gcc
{
tree symbol = this->symbols->lookup(expression->name);
- if (symbol == NULL_TREE)
- {
- this->current_expression = error_mark_node;
- }
- else
- {
- this->current_expression = symbol;
- }
+ // Name analysis rejects an undeclared name.
+ gcc_assert(symbol != NULL_TREE);
+ this->current_expression = symbol;
}
void generic_visitor::visit(boot::array_access_expression *expression)
@@ -813,15 +804,12 @@ namespace elna::gcc
}
else
{
- tree field_declaration = find_field_by_name(expression_location,
+ tree field_declaration = find_field_by_name(
TREE_TYPE(this->current_expression), expression->field().name());
- if (field_declaration != error_mark_node)
- {
- this->current_expression = build3_loc(expression_location, COMPONENT_REF,
- TREE_TYPE(field_declaration), this->current_expression,
- field_declaration, NULL_TREE);
- }
+ this->current_expression = build3_loc(expression_location, COMPONENT_REF,
+ TREE_TYPE(field_declaration), this->current_expression,
+ field_declaration, NULL_TREE);
}
}
@@ -866,10 +854,7 @@ namespace elna::gcc
}
result = make_if_branch(statement->branch(), result);
- if (result != error_mark_node)
- {
- append_statement(result);
- }
+ append_statement(result);
this->current_expression = NULL_TREE;
}
@@ -1010,16 +995,13 @@ namespace elna::gcc
}
result = make_if_branch(statement->branch(), result, goto_check);
- if (result != error_mark_node)
- {
- tree check_label_expr = build1_loc(prerequisite_location, LABEL_EXPR,
- void_type_node, while_check_label);
- tree end_label_expr = build1(LABEL_EXPR, void_type_node, while_end_label);
+ tree check_label_expr = build1_loc(prerequisite_location, LABEL_EXPR,
+ void_type_node, while_check_label);
+ tree end_label_expr = build1(LABEL_EXPR, void_type_node, while_end_label);
- append_statement(check_label_expr);
- append_statement(result);
- append_statement(end_label_expr);
- }
+ append_statement(check_label_expr);
+ append_statement(result);
+ append_statement(end_label_expr);
this->current_expression = NULL_TREE;
}
@@ -1077,7 +1059,7 @@ namespace elna::gcc
case_label->accept(this);
const location_t case_location = get_location(&case_label->position());
- assert_constant(case_location);
+ assert_constant();
tree case_label_declaration = create_artificial_label(case_location);
tree case_expression = build_case_label(this->current_expression, NULL_TREE, case_label_declaration);
@@ -1130,10 +1112,7 @@ namespace elna::gcc
for (boot::expression *const case_label : case_block.labels)
{
case_label->accept(this);
- if (!assert_constant(get_location(&case_label->position())))
- {
- continue;
- }
+ assert_constant();
tree label_comparison = build_equality_comparison(get_location(&case_label->position()),
condition_value, this->current_expression,
statement->condition().type_decoration, EQ_EXPR);
@@ -1153,19 +1132,12 @@ namespace elna::gcc
}
}
- bool generic_visitor::assert_constant(location_t expression_location)
+ void generic_visitor::assert_constant()
{
tree constant_expression = extract_constant(this->current_expression);
- if (constant_expression == NULL_TREE)
- {
- error_at(expression_location, "Expected a constant expression");
- this->current_expression = error_mark_node;
- }
- else
- {
- this->current_expression = constant_expression;
- }
- return this->current_expression != error_mark_node;
+ // Type checking rejects a case label that is not a constant expression.
+ gcc_assert(constant_expression != NULL_TREE);
+ this->current_expression = constant_expression;
}
}
diff --git a/gcc/gcc/elna-module-loader.cc b/gcc/gcc/elna-module-loader.cc
index 8827a1e..5852bbe 100644
--- a/gcc/gcc/elna-module-loader.cc
+++ b/gcc/gcc/elna-module-loader.cc
@@ -78,9 +78,9 @@ namespace elna::gcc
}
void module_loader::finalize(const std::filesystem::path&,
- const std::shared_ptr<boot::symbol_table>& module_scope) const
+ const std::shared_ptr<boot::symbol_table>& module_scope, const bool imported) const
{
- rewrite_symbol_table(module_scope, this->symbols);
+ rewrite_symbol_table(module_scope, this->symbols, imported);
}
void compile_files(const char *const *filenames, unsigned int count)
@@ -99,9 +99,9 @@ namespace elna::gcc
report_errors(diagnostics);
}
- if (result.value != nullptr)
+ if (result.value != nullptr && result.errors.empty())
{
- linemap_add(line_table, LC_ENTER, 0, key.native().c_str(), 1);
+ linemap_add(line_table, LC_ENTER, 0, ggc_strdup(key.native().c_str()), 1);
generic_visitor visitor{ state.custom, state.find(key)->second, target };
result.value->accept(&visitor);
linemap_add(line_table, LC_LEAVE, 0, nullptr, 0);
diff --git a/gcc/gcc/elna-tree.cc b/gcc/gcc/elna-tree.cc
index cddb407..7aa4012 100644
--- a/gcc/gcc/elna-tree.cc
+++ b/gcc/gcc/elna-tree.cc
@@ -97,6 +97,14 @@ namespace elna::gcc
return field_declaration;
}
+ // The generic Pointer has no pointee size; it advances byte by byte.
+ static tree pointer_stride(tree pointer_type, tree offset_type)
+ {
+ return pointer_type == elna_pointer_type_node
+ ? size_one_node
+ : fold_convert(offset_type, size_in_bytes(TREE_TYPE(pointer_type)));
+ }
+
tree do_pointer_arithmetic(boot::binary_operator binary_operator,
tree left, tree right, location_t operation_location)
{
@@ -124,9 +132,7 @@ namespace elna::gcc
{
return error_mark_node;
}
- tree size_exp = pointer_type == elna_pointer_type_node
- ? size_one_node
- : fold_convert(TREE_TYPE(offset), size_in_bytes(TREE_TYPE(TREE_TYPE(pointer))));
+ tree size_exp = pointer_stride(pointer_type, TREE_TYPE(offset));
offset = fold_build2(MULT_EXPR, TREE_TYPE(offset), offset, size_exp);
offset = fold_convert(size_type_node, offset);
@@ -139,7 +145,7 @@ namespace elna::gcc
{
tree pointer_type = left_type;
tree offset_type = right_type;
- tree size_exp = fold_convert(offset_type, size_in_bytes(TREE_TYPE(pointer_type)));
+ tree size_exp = pointer_stride(pointer_type, offset_type);
tree convert_expression = fold_build2(MULT_EXPR, offset_type, right, size_exp);
convert_expression = fold_convert(size_type_node, convert_expression);
@@ -156,13 +162,11 @@ namespace elna::gcc
gcc_unreachable();
}
- tree find_field_by_name(location_t, tree type, const std::string& field_name)
+ tree find_field_by_name(tree type, const std::string& field_name)
{
- // NOLINTNEXTLINE(readability-simplify-boolean-expr)
- if (type == error_mark_node || !RECORD_OR_UNION_TYPE_P(type))
- {
- return error_mark_node;
- }
+ // Name analysis rejects a field the record does not have.
+ gcc_assert(RECORD_OR_UNION_TYPE_P(type));
+
for (tree field = TYPE_FIELDS(type); field != NULL_TREE; field = TREE_CHAIN(field))
{
if (field_name == IDENTIFIER_POINTER(DECL_NAME(field)))
@@ -170,7 +174,7 @@ namespace elna::gcc
return field;
}
}
- return error_mark_node;
+ gcc_unreachable();
}
tree build_static_array_type(tree type, const std::uint64_t size)
@@ -233,8 +237,13 @@ namespace elna::gcc
tree extract_constant(tree expression)
{
const int code = TREE_CODE(expression);
+ // A mutable variable can have a constant initializer too, so readonly is
+ // what marks one whose value is known at compile time.
+ const bool constant_variable = code == VAR_DECL && TREE_READONLY(expression)
+ && DECL_INITIAL(expression) != NULL_TREE
+ && TREE_CONSTANT(DECL_INITIAL(expression));
- if (code == CONST_DECL)
+ if (code == CONST_DECL || constant_variable)
{
return DECL_INITIAL(expression);
}
@@ -333,11 +342,7 @@ namespace elna::gcc
for (const auto& [field_name, field_value] : fields)
{
- tree field_decl = find_field_by_name(UNKNOWN_LOCATION, type, field_name);
- if (field_decl == error_mark_node)
- {
- return NULL_TREE;
- }
+ tree field_decl = find_field_by_name(type, field_name);
tree value_tree = constant_to_tree(field_value, symbols, TREE_TYPE(field_decl));
if (value_tree == NULL_TREE)
{
diff --git a/include/elna/boot/dependency.h b/include/elna/boot/dependency.h
index 18df64c..9bc1adf 100644
--- a/include/elna/boot/dependency.h
+++ b/include/elna/boot/dependency.h
@@ -174,7 +174,7 @@ namespace elna::boot
{
{ loader.resolve(relative, position, key) } -> std::same_as<std::filesystem::path>;
{ loader.read(key) } -> std::same_as<read_result>;
- loader.finalize(key, module_scope);
+ loader.finalize(key, module_scope, true);
};
/**
@@ -217,6 +217,9 @@ namespace elna::boot
* their canonical paths to detect circular imports.
* \param loader Host-side module loader.
* \param target Target machine information.
+ * \param imported Whether \p key is being compiled because another module
+ * imports it. False for the module the caller asked for; set when
+ * recursing into an import, and passed on to \c finalize.
*
* \return Abstract syntax tree of the module itself and the
* diagnostics of this module and all its imports, grouped by
@@ -224,7 +227,7 @@ namespace elna::boot
*/
template<module_loader Loader>
dependency compile(const std::filesystem::path& key, Loader& loader,
- const target_info& target)
+ const target_info& target, const bool imported = false)
{
auto cached = this->cache.find(key);
if (cached != this->cache.cend())
@@ -257,7 +260,7 @@ namespace elna::boot
module_path.stem().string()));
continue;
}
- dependency sub = this->compile(module_path, loader, target);
+ dependency sub = this->compile(module_path, loader, target, true);
for (auto& diagnostics : sub.errors)
{
result.append(std::move(diagnostics.module), std::move(diagnostics.errors()));
@@ -273,12 +276,11 @@ namespace elna::boot
result.append(key, std::move(analysis.errors));
}
this->cache.insert({ key, analysis.value });
- loader.finalize(key, analysis.value.leave());
- this->in_progress.erase(key);
- if (failed)
+ if (!failed)
{
- tree.reset();
+ loader.finalize(key, analysis.value.leave(), imported);
}
+ this->in_progress.erase(key);
result.value = std::move(tree);
return result;
}
diff --git a/include/elna/boot/result.h b/include/elna/boot/result.h
index eacc072..d37484a 100644
--- a/include/elna/boot/result.h
+++ b/include/elna/boot/result.h
@@ -263,6 +263,10 @@ namespace elna::boot
template<has_to_string T>
std::string join(const std::vector<T>& identifiers, std::string_view delimiter = ", ")
{
+ if (identifiers.empty())
+ {
+ return std::string();
+ }
return std::accumulate(std::next(identifiers.begin()), identifiers.end(),
identifiers.front().to_string(),
[delimiter](const std::string& accumulator, const T& next) -> std::string {
diff --git a/include/elna/gcc/elna-builtins.h b/include/elna/gcc/elna-builtins.h
index 75b409d..10a2732 100644
--- a/include/elna/gcc/elna-builtins.h
+++ b/include/elna/gcc/elna-builtins.h
@@ -33,7 +33,7 @@ namespace elna::gcc
std::shared_ptr<symbol_table> builtin_symbol_table();
void rewrite_symbol_table(const std::shared_ptr<boot::symbol_table>& info_table,
- const std::shared_ptr<symbol_table>& symbols);
+ const std::shared_ptr<symbol_table>& symbols, bool imported);
tree handle_symbol(const std::string& symbol_name,
const std::shared_ptr<boot::alias_type>& reference,
const std::shared_ptr<symbol_table>& symbols);
diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h
index fd54ba5..c0c23b1 100644
--- a/include/elna/gcc/elna-generic.h
+++ b/include/elna/gcc/elna-generic.h
@@ -62,7 +62,7 @@ namespace elna::gcc
void build_assert_builtin(location_t call_location, const std::vector<boot::expression *>& arguments);
void visit_statements(const std::vector<boot::statement *>& statements);
- bool assert_constant(location_t expression_location);
+ void assert_constant();
tree declare_local_variable(const boot::identifier& name,
const boot::variable_info& info, tree initial_value);
diff --git a/include/elna/gcc/elna-module-loader.h b/include/elna/gcc/elna-module-loader.h
index 2b46fc5..5f5aab6 100644
--- a/include/elna/gcc/elna-module-loader.h
+++ b/include/elna/gcc/elna-module-loader.h
@@ -78,9 +78,12 @@ namespace elna::gcc
*
* \param key Resolved module path.
* \param module_scope Analyzed module scope.
+ * \param imported Whether the module is being registered because another
+ * module imports it, rather than being the translation unit itself.
+ * Its variables are then declarations instead of definitions.
*/
void finalize(const std::filesystem::path& key,
- const std::shared_ptr<boot::symbol_table>& module_scope) const;
+ const std::shared_ptr<boot::symbol_table>& module_scope, bool imported) const;
};
/**
diff --git a/include/elna/gcc/elna-tree.h b/include/elna/gcc/elna-tree.h
index f201e64..a3a9106 100644
--- a/include/elna/gcc/elna-tree.h
+++ b/include/elna/gcc/elna-tree.h
@@ -55,7 +55,7 @@ namespace elna::gcc
tree do_pointer_arithmetic(boot::binary_operator binary_operator,
tree left, tree right, location_t operation_location);
tree build_field(location_t location, tree record_type, const std::string& name, tree type);
- tree find_field_by_name(location_t expression_location, tree type, const std::string& field_name);
+ tree find_field_by_name(tree type, const std::string& field_name);
tree build_static_array_type(tree type, const std::uint64_t size);
tree build_slice(tree slice_type, tree ptr, tree length);
tree build_enumeration_type(const std::vector<std::string>& members);
diff --git a/testsuite/fail_compilation/assign_void_call.elna b/testsuite/fail_compilation/assign_void_call.elna
new file mode 100644
index 0000000..d18b1c8
--- /dev/null
+++ b/testsuite/fail_compilation/assign_void_call.elna
@@ -0,0 +1,9 @@
+var
+ x: Int
+
+proc v()
+return
+
+begin
+ x := v() (* @Error Expected type 'Int', but the expression has no value *)
+end.
diff --git a/testsuite/fail_compilation/compare_unrelated_records.elna b/testsuite/fail_compilation/compare_unrelated_records.elna
new file mode 100644
index 0000000..71eaeda
--- /dev/null
+++ b/testsuite/fail_compilation/compare_unrelated_records.elna
@@ -0,0 +1,17 @@
+type
+ A = record
+ x: Int
+ end
+ B = record
+ y: Int;
+ z: Int
+ end
+
+var
+ a: A
+ b: B
+ ok: Bool
+
+begin
+ ok := b = a (* @Error Invalid operands of type 'B' and 'A' for operator = *)
+end.
diff --git a/testsuite/fail_compilation/proc_type_without_parameters.elna b/testsuite/fail_compilation/proc_type_without_parameters.elna
new file mode 100644
index 0000000..92514f6
--- /dev/null
+++ b/testsuite/fail_compilation/proc_type_without_parameters.elna
@@ -0,0 +1,9 @@
+var
+ x: Int
+
+proc f()
+return
+
+begin
+ x := f (* @Error Expected type 'Int', but got 'proc\(\)' *)
+end.
diff --git a/testsuite/fail_compilation/record-base-cycle-indirect.elna b/testsuite/fail_compilation/record-base-cycle-indirect.elna
new file mode 100644
index 0000000..412e6ea
--- /dev/null
+++ b/testsuite/fail_compilation/record-base-cycle-indirect.elna
@@ -0,0 +1,13 @@
+type
+ A = record(B)
+ x: Int
+ end
+ B = record(A) (* @Error Type declaration forms a cycle: A -> B -> A *)
+ y: Int
+ end
+
+var
+ v: A
+
+begin
+end.
diff --git a/testsuite/runnable/case_constant_label.elna b/testsuite/runnable/case_constant_label.elna
new file mode 100644
index 0000000..5d55e9f
--- /dev/null
+++ b/testsuite/runnable/case_constant_label.elna
@@ -0,0 +1,13 @@
+var
+ base: const Int := 2
+ limit: const Int := base
+ x: Int := 2
+ matched: Bool := false
+
+begin
+ case x of
+ 1: assert(false)
+ | limit: matched := true
+ end;
+ assert(matched)
+end.
diff --git a/testsuite/runnable/exported_variable/helper.elna b/testsuite/runnable/exported_variable/helper.elna
new file mode 100644
index 0000000..23924be
--- /dev/null
+++ b/testsuite/runnable/exported_variable/helper.elna
@@ -0,0 +1,4 @@
+var
+ Shared*: Int := 42
+
+end.
diff --git a/testsuite/runnable/exported_variable/sut.elna b/testsuite/runnable/exported_variable/sut.elna
new file mode 100644
index 0000000..52fb291
--- /dev/null
+++ b/testsuite/runnable/exported_variable/sut.elna
@@ -0,0 +1,5 @@
+import helper
+
+begin
+ assert(Shared = 42)
+end.
diff --git a/testsuite/runnable/generic_pointer_arithmetic.elna b/testsuite/runnable/generic_pointer_arithmetic.elna
new file mode 100644
index 0000000..c737c7d
--- /dev/null
+++ b/testsuite/runnable/generic_pointer_arithmetic.elna
@@ -0,0 +1,13 @@
+var
+ base: [4]Int
+ element: ^Int
+ first: Pointer
+ second: Pointer
+
+begin
+ element := base.ptr;
+ first := element;
+ second := first + 1;
+ assert(cast(second : Word) - cast(first : Word) = 1u);
+ assert(second - 1 = first)
+end.