From 1e0f89df48ba10cdde80bd623b84e20fb48b977d Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Fri, 24 Jul 2026 21:28:33 +0200 Subject: Accept any constant types in case labels --- boot/evaluator.cc | 24 ++-- boot/name_analysis.cc | 21 ++-- boot/type_check.cc | 28 +++-- gcc/gcc/elna-builtins.cc | 4 +- gcc/gcc/elna-generic.cc | 108 +++++++++++++---- gcc/gcc/elna-tree.cc | 2 +- gcc/gcc/elna1.cc | 4 +- include/elna/boot/dependency.h | 4 +- include/elna/boot/evaluator.h | 54 +++++++-- include/elna/boot/name_analysis.h | 4 +- include/elna/boot/result.h | 131 +++++++++++++++++++++ include/elna/boot/symbol.h | 26 +++- include/elna/boot/type_check.h | 1 + include/elna/gcc/elna-generic.h | 6 +- testsuite/fail_compilation/case_non_constant.elna | 16 +++ testsuite/fail_compilation/case_type_mismatch.elna | 9 ++ testsuite/runnable/case_else.elna | 14 +++ testsuite/runnable/case_int.elna | 12 ++ testsuite/runnable/case_multilabel.elna | 11 ++ testsuite/runnable/case_record.elna | 17 +++ 20 files changed, 419 insertions(+), 77 deletions(-) create mode 100644 testsuite/fail_compilation/case_non_constant.elna create mode 100644 testsuite/fail_compilation/case_type_mismatch.elna create mode 100644 testsuite/runnable/case_else.elna create mode 100644 testsuite/runnable/case_int.elna create mode 100644 testsuite/runnable/case_multilabel.elna create mode 100644 testsuite/runnable/case_record.elna diff --git a/boot/evaluator.cc b/boot/evaluator.cc index 4b37668..40a6533 100644 --- a/boot/evaluator.cc +++ b/boot/evaluator.cc @@ -61,25 +61,32 @@ namespace elna::boot } else if (auto *record_constructor = subject.is_record_constructor()) { + ordered_map aggregate; + for (const field_initializer& field_initializer : record_constructor->field_initializers) { - if (!evaluate(field_initializer.value())) + auto value = evaluate(field_initializer.value()); + if (!value) { return std::nullopt; } + aggregate.insert(field_initializer.name(), std::move(*value)); } - return constant_value{ compound_constant{} }; + return constant_value{ constant_aggregate{ std::move(aggregate) } }; } else if (auto *array_constructor = subject.is_array_constructor()) { + std::vector elements; for (expression *element : array_constructor->elements) { - if (!evaluate(*element)) + auto value = evaluate(*element); + if (!value) { return std::nullopt; } + elements.push_back(std::move(*value)); } - return constant_value{ compound_constant{} }; + return constant_value{ constant_aggregate{ std::move(elements) } }; } return std::nullopt; } @@ -171,7 +178,7 @@ namespace elna::boot } if (subject.operation() == unary_operator::logical_negation) { - return std::visit([](auto value) -> std::optional { + return std::visit([](const auto& value) -> std::optional { using T = std::decay_t; if constexpr (std::is_same_v) @@ -183,7 +190,7 @@ namespace elna::boot } if (subject.operation() == unary_operator::bitwise_negation) { - return std::visit([](auto value) -> std::optional { + return std::visit([](const auto& value) -> std::optional { using T = std::decay_t; if constexpr (std::is_integral_v && !std::is_same_v) @@ -240,10 +247,11 @@ namespace elna::boot } template - static std::optional evaluate_operation(binary_operator operation, T lhs, T rhs) + static std::optional evaluate_operation(binary_operator operation, const T& lhs, const T& rhs) { if constexpr (std::is_same_v - || std::is_same_v) + || std::is_same_v> + || std::is_same_v>) { switch (operation) { diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc index 1e224da..07f6d31 100644 --- a/boot/name_analysis.cc +++ b/boot/name_analysis.cc @@ -303,7 +303,7 @@ namespace elna::boot * Collects field names from a record type recursively, base first. */ static void collect_field_names(const type& composite_type, - std::map& names) + ordered_map& names) { auto record = resolve_underlying_type(composite_type).get(); if (record == nullptr) @@ -316,24 +316,25 @@ namespace elna::boot } for (auto& field : record->fields) { - names.insert({ field.first, field_origin{ .declaration = std::nullopt, .base_type = composite_type } }); + names.insert(field.first, field_origin{ .declaration = std::nullopt, .base_type = composite_type }); } } - std::vector name_analysis_visitor::build_composite_type( + ordered_map name_analysis_visitor::build_composite_type( const std::vector& fields, - std::map& field_names, + ordered_map& field_names, const type& aggregate) { - std::vector result; + ordered_map result; for (const auto& field : fields) { field.second->accept(this); for (const auto& field_name : field.first) { - auto existing = field_names.find(field_name.name()); - if (existing != field_names.end()) + auto [existing, inserted] = field_names.insert(field_name.name(), + field_origin{ .declaration = field.second->position(), .base_type = type() }); + if (!inserted) { std::optional base_name; @@ -351,9 +352,7 @@ namespace elna::boot } else { - field_names.insert({ field_name.name(), - field_origin{ .declaration = field.second->position(), .base_type = type() } }); - result.emplace_back(field_name.name(), this->current_type); + result.insert(field_name.name(), this->current_type); } } } @@ -394,7 +393,7 @@ namespace elna::boot result_type = std::make_shared(); } - std::map field_names; + ordered_map field_names; collect_field_names(result_type->base, field_names); result_type->fields = build_composite_type(expression->fields, field_names, type(result_type)); diff --git a/boot/type_check.cc b/boot/type_check.cc index 9bab537..c88a7d1 100644 --- a/boot/type_check.cc +++ b/boot/type_check.cc @@ -199,6 +199,18 @@ namespace elna::boot return false; } + 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() && resolved_right.get()) + || (resolved_left.get() && resolved_right.get()); + } + bool type_analysis_visitor::check_unresolved_symbol(const std::shared_ptr& alias, std::vector& alias_path) { @@ -462,8 +474,7 @@ namespace elna::boot auto variable_symbol = this->bag.lookup(variable_identifier.name())->is_variable(); if (!is_assignable_from(variable_symbol->symbol, declaration->initializer->type_decoration)) { - add_error( - declaration->initializer->position(), + add_error(declaration->initializer->position(), variable_symbol->symbol, declaration->initializer->type_decoration); } } @@ -478,10 +489,11 @@ namespace elna::boot { for (const expression *case_label : case_block.labels) { - if (!is_assignable_from(condition_type, case_label->type_decoration)) + if (!is_equality_compatible(condition_type, case_label->type_decoration)) { - add_error( - case_label->position(), condition_type, case_label->type_decoration); + add_error( + case_label->position(), condition_type, + case_label->type_decoration, binary_operator::equals); } } } @@ -836,11 +848,7 @@ namespace elna::boot break; case equals: case not_equals: - valid = lhs_resolved == rhs_resolved - || (is_primitive_type(lhs_resolved, "Pointer") && is_any_pointer_type(rhs_resolved)) - || (is_any_pointer_type(lhs_resolved) && is_primitive_type(rhs_resolved, "Pointer")) - || (lhs_resolved.get() && rhs_resolved.get()) - || (lhs_resolved.get() && rhs_resolved.get()); + valid = is_equality_compatible(lhs_resolved, rhs_resolved); break; case shift_left: case shift_right: diff --git a/gcc/gcc/elna-builtins.cc b/gcc/gcc/elna-builtins.cc index 57d2875..6ea8874 100644 --- a/gcc/gcc/elna-builtins.cc +++ b/gcc/gcc/elna-builtins.cc @@ -65,8 +65,8 @@ namespace elna::gcc return builtin_table; } - tree build_composite_type(const std::vector& fields, tree composite_type_node, - std::shared_ptr symbols) + tree build_composite_type(const boot::ordered_map& fields, + tree composite_type_node, std::shared_ptr symbols) { for (auto& field : fields) { diff --git a/gcc/gcc/elna-generic.cc b/gcc/gcc/elna-generic.cc index c462924..f77f371 100644 --- a/gcc/gcc/elna-generic.cc +++ b/gcc/gcc/elna-generic.cc @@ -429,6 +429,14 @@ namespace elna::gcc { return null_pointer_node; } + else if (std::holds_alternative>(constant_value)) + { + return NULL_TREE; // Compound constants are not lowered to trees yet. + } + else if (std::holds_alternative>(constant_value)) + { + return NULL_TREE; // Compound constants are not lowered to trees yet. + } return NULL_TREE; } @@ -498,26 +506,33 @@ namespace elna::gcc this->current_expression = build_constructor(slice_type, elms); } - tree generic_visitor::build_equality_operation(boot::binary_expression *expression, tree left, tree right) + tree generic_visitor::build_equality(boot::binary_expression *expression, tree left, tree right) { - location_t expression_location = get_location(&expression->position()); - tree_code equality_code, combination_code; + tree_code equality_code; if (expression->operation() == boot::binary_operator::equals) { equality_code = EQ_EXPR; - combination_code = TRUTH_ANDIF_EXPR; } else if (expression->operation() == boot::binary_operator::not_equals) { equality_code = NE_EXPR; - combination_code = TRUTH_ORIF_EXPR; } else { gcc_unreachable(); } - if (expression->lhs().type_decoration.get() != nullptr) + return build_equality_comparison(get_location(&expression->position()), left, right, + expression->lhs().type_decoration, equality_code); + } + + tree generic_visitor::build_equality_comparison(location_t loc, tree left, tree right, + const boot::type& left_type, tree_code equality_code) + { + tree_code combination_code = equality_code == EQ_EXPR + ? TRUTH_ANDIF_EXPR : TRUTH_ORIF_EXPR; + + if (left_type.get() != nullptr) { tree lhs_ptr_field = TYPE_FIELDS(TREE_TYPE(left)); tree lhs_length_field = TREE_CHAIN(lhs_ptr_field); @@ -543,22 +558,20 @@ namespace elna::gcc return build2(combination_code, elna_bool_type_node, length_equality, equals_zero); } - else if (expression->lhs().type_decoration.get() != nullptr - || expression->lhs().type_decoration.get() != nullptr) + else if (left_type.get() != nullptr + || left_type.get() != nullptr) { tree size = fold_convert(elna_word_type_node, size_in_bytes(TREE_TYPE(left))); - tree lhs_addr = build1(ADDR_EXPR, - build_pointer_type(TREE_TYPE(left)), left); - tree rhs_addr = build1(ADDR_EXPR, - build_pointer_type(TREE_TYPE(right)), right); + tree lhs_addr = build1(ADDR_EXPR, build_pointer_type(TREE_TYPE(left)), left); + tree rhs_addr = build1(ADDR_EXPR, build_pointer_type(TREE_TYPE(right)), right); tree memcmp_call = call_built_in(UNKNOWN_LOCATION, "__builtin_memcmp", integer_type_node, lhs_addr, rhs_addr, size); return build2(equality_code, elna_bool_type_node, memcmp_call, integer_zero_node); } else { - return build2_loc(expression_location, equality_code, elna_bool_type_node, left, right); + return build2_loc(loc, equality_code, elna_bool_type_node, left, right); } } @@ -574,9 +587,11 @@ namespace elna::gcc location_t expression_location = get_location(&expression->position()); + const bool is_pointer_arithmetic_operation + = expression->operation() == boot::binary_operator::sum + || expression->operation() == boot::binary_operator::subtraction; if ((POINTER_TYPE_P(left_type) || POINTER_TYPE_P(right_type)) - && (expression->operation() == boot::binary_operator::sum - || expression->operation() == boot::binary_operator::subtraction)) + && is_pointer_arithmetic_operation) { this->current_expression = do_pointer_arithmetic(expression->operation(), left, right, expression_location); @@ -655,7 +670,7 @@ namespace elna::gcc break; case equals: case not_equals: - this->current_expression = build_equality_operation(expression, left, right); + this->current_expression = build_equality(expression, left, right); break; case shift_left: this->current_expression = fold_build2_loc(expression_location, @@ -1247,16 +1262,21 @@ namespace elna::gcc { statement->condition().accept(this); tree condition_expression = this->current_expression; - tree unqualified_condition = get_qualified_type(TREE_TYPE(this->current_expression), TYPE_UNQUALIFIED); + tree unqualified_condition = get_qualified_type(TREE_TYPE(condition_expression), TYPE_UNQUALIFIED); - if (!INTEGRAL_TYPE_P(unqualified_condition)) + if (INTEGRAL_TYPE_P(unqualified_condition)) { - error_at(get_location(&statement->condition().position()), - "Case expressions can only be integral numbers, characters and enumerations, given '%s'", - print_type(unqualified_condition).c_str()); - this->current_expression = NULL_TREE; - return; + build_case_integral(statement, condition_expression); } + else + { + build_case_general(statement, condition_expression); + } + this->current_expression = NULL_TREE; + } + + void generic_visitor::build_case_integral(boot::case_statement *statement, tree condition_expression) + { tree end_label_declaration = create_artificial_label(get_location(&statement->position())); tree switch_statements = alloc_stmt_list(); @@ -1296,13 +1316,51 @@ namespace elna::gcc } tree switch_expression = build2(SWITCH_EXPR, TREE_TYPE(condition_expression), condition_expression, switch_statements); - append_statement(switch_expression); tree end_label_expression = build1(LABEL_EXPR, void_type_node, end_label_declaration); append_statement(end_label_expression); + } - this->current_expression = NULL_TREE; + void generic_visitor::build_case_general(boot::case_statement *statement, tree condition_expression) + { + tree condition_value = build1(SAVE_EXPR, TREE_TYPE(condition_expression), condition_expression); + + // Build from the bottom up: else branch first. + tree result = NULL_TREE; + if (statement->alternative != nullptr) + { + enter_scope(); + visit_statements(*statement->alternative); + result = leave_scope(); + } + for (const boot::switch_case& case_block : statement->cases | std::views::reverse) + { + tree case_condition = boolean_false_node; + for (boot::expression *const case_label : case_block.labels) + { + case_label->accept(this); + if (!assert_constant(get_location(&case_label->position()))) + { + continue; + } + tree label_comparison = build_equality_comparison(get_location(&case_label->position()), + condition_value, this->current_expression, + statement->condition().type_decoration, EQ_EXPR); + case_condition = build2(TRUTH_ORIF_EXPR, elna_bool_type_node, + case_condition, label_comparison); + } + + enter_scope(); + visit_statements(case_block.statements); + tree then_body = leave_scope(); + + result = build3(COND_EXPR, void_type_node, case_condition, then_body, result); + } + if (result != NULL_TREE) + { + append_statement(result); + } } bool generic_visitor::assert_constant(location_t expression_location) diff --git a/gcc/gcc/elna-tree.cc b/gcc/gcc/elna-tree.cc index 26936fe..c47bb94 100644 --- a/gcc/gcc/elna-tree.cc +++ b/gcc/gcc/elna-tree.cc @@ -252,7 +252,7 @@ namespace elna::gcc { return DECL_INITIAL(expression); } - else if (TREE_CODE_CLASS(code) == tcc_constant) + else if (TREE_CODE_CLASS(code) == tcc_constant || TREE_CONSTANT(expression)) { return expression; } diff --git a/gcc/gcc/elna1.cc b/gcc/gcc/elna1.cc index 94055e5..fde2adc 100644 --- a/gcc/gcc/elna1.cc +++ b/gcc/gcc/elna1.cc @@ -136,14 +136,14 @@ static elna::boot::dependency elna_parse_file(dependency_state& state, const cha std::filesystem::path sub_path = find_module(sub_tree)[0]; dependency_state::const_iterator cached_import = state.find(sub_path); - if (cached_import == state.cend()) + if (cached_import == std::cend(state)) { auto filename_pointer = ggc_strdup(sub_path.native().c_str()); elna_parse_file(state, filename_pointer); cached_import = state.find(sub_path); } - if (cached_import != state.cend()) + if (cached_import != std::cend(state)) { outcome_bag.add_import(cached_import->second); } diff --git a/include/elna/boot/dependency.h b/include/elna/boot/dependency.h index 24ded26..e6cfb36 100644 --- a/include/elna/boot/dependency.h +++ b/include/elna/boot/dependency.h @@ -79,12 +79,12 @@ namespace elna::boot return this->cache.end(); } - const_iterator cbegin() const + const_iterator begin() const { return this->cache.cbegin(); } - const_iterator cend() const + const_iterator end() const { return this->cache.cend(); } diff --git a/include/elna/boot/evaluator.h b/include/elna/boot/evaluator.h index b8a9956..015a2c7 100644 --- a/include/elna/boot/evaluator.h +++ b/include/elna/boot/evaluator.h @@ -19,6 +19,7 @@ along with GCC; see the file COPYING3. If not see #include #include +#include #include #include #include @@ -47,16 +48,14 @@ namespace elna::boot std::size_t bool_alignment{0}; }; - /** - * For aggregate values (arrays, records) signals that every sub-expression - * is constant without storing the full aggregate. - */ - struct compound_constant - { - }; + template typename C, template typename Alloc = std::allocator> + class constant_aggregate; /** * Typed constant value produced by the constant expression evaluator. + * + * Record constructors are stored as ordered_map keyed by field name, + * array constructors as a vector of elements. */ using constant_value = std::variant< std::int32_t, @@ -65,9 +64,48 @@ namespace elna::boot bool, unsigned char, std::nullptr_t, - compound_constant + constant_aggregate, + constant_aggregate >; + template typename C, template typename Alloc> + class constant_aggregate + { + using Container = C>; + std::shared_ptr container; + + public: + explicit constant_aggregate(const Container& value) + : container(std::make_shared(value)) + { + } + + explicit constant_aggregate(const Container&& value) + : container(std::make_shared(std::move(value))) + { + } + + constant_value& operator*() + { + return *this->container; + } + + constant_value& operator*() const + { + return *this->container; + } + + constant_value *operator->() + { + return this->container.get(); + } + + constant_value *operator->() const + { + return this->container.get(); + } + }; + /** * Called on-demand. */ diff --git a/include/elna/boot/name_analysis.h b/include/elna/boot/name_analysis.h index 68fe6da..a4f550d 100644 --- a/include/elna/boot/name_analysis.h +++ b/include/elna/boot/name_analysis.h @@ -130,8 +130,8 @@ namespace elna::boot std::pair> build_procedure( procedure_type_expression& expression); - std::vector build_composite_type(const std::vector& fields, - std::map& field_names, + ordered_map build_composite_type(const std::vector& fields, + ordered_map& field_names, const type& aggregate); std::shared_ptr register_variable(const std::string& name, const bool is_extern, const source_position position); diff --git a/include/elna/boot/result.h b/include/elna/boot/result.h index 328e44e..1f22baf 100644 --- a/include/elna/boot/result.h +++ b/include/elna/boot/result.h @@ -24,6 +24,9 @@ along with GCC; see the file COPYING3. If not see #include #include #include +#include +#include +#include namespace elna::boot { @@ -174,6 +177,134 @@ namespace elna::boot identifier m_identifier; bool m_exported{ false }; }; + + /** + * An associative container that contains key-value pairs with unique keys. + * Keys preserve the insertion order. + */ + template> + class ordered_map + { + using vector_alloc = std::allocator_traits::template + rebind_alloc>; + using map_alloc = std::allocator_traits::template + rebind_alloc>; + + std::unordered_map, std::equal_to<>, map_alloc> index_map; + std::vector, vector_alloc> payload; + + public: + using key_type = std::string; + using mapped_type = V; + using value_type = std::pair; + using iterator = std::vector::iterator; + using const_iterator = std::vector::const_iterator; + + /** + * Finds element with specific key. + * + * \param key Key value of the element to search for. + * \return An iterator to the requested element. If no such element is + * found, past-the-end (see #end()) iterator is returned. + */ + iterator find(const key_type& key) + { + auto search_result = this->index_map.find(key); + if (search_result == this->index_map.cend()) + { + return this->payload.end(); + } + else + { + return this->payload.at(search_result->second); + } + } + + /** + * \overload + */ + const_iterator find(const key_type& key) const + { + auto search_result = this->index_map.find(key); + if (search_result == this->index_map.cend()) + { + return this->payload.cend(); + } + else + { + return this->payload.at(search_result->second); + } + } + + /** + * Inserts elements. + * + * \param key Element key to insert. + * \param value Element value to insert. + * \return A pair consisting of an iterator to the inserted element (or + * to the element that prevented the insertion) and a \c bool + * value set to \c true if and only if the insertion took place. + */ + std::pair insert(const key_type& key, const mapped_type& value) + { + auto insert_result = this->index_map.emplace(key, this->payload.size()); + if (insert_result.second) + { + this->payload.emplace_back(key, value); + } + return { this->payload.begin() + insert_result.first->second, insert_result.second }; + } + + /** + * \overload + */ + std::pair insert(const key_type& key, mapped_type&& value) + { + auto insert_result = this->index_map.emplace(key, this->payload.size()); + if (insert_result.second) + { + this->payload.emplace_back(key, std::move(value)); + } + return { this->payload.begin() + insert_result.first->second, insert_result.second }; + } + + /** + * Returns an iterator to the beginning. + * + * \return Iterator to the first element. + */ + iterator begin() + { + return this->payload.begin(); + } + + /** + * \overload + */ + const_iterator begin() const + { + return this->payload.cbegin(); + } + + /** + * Returns an iterator to the end. + * + * \return Iterator to the element following the last element. + */ + iterator end() + { + return this->payload.end(); + } + + /** + * \overload + */ + const_iterator end() const + { + return this->payload.cend(); + } + }; } template<> diff --git a/include/elna/boot/symbol.h b/include/elna/boot/symbol.h index 4e97205..5b56904 100644 --- a/include/elna/boot/symbol.h +++ b/include/elna/boot/symbol.h @@ -132,7 +132,7 @@ namespace elna::boot struct record_type { - std::vector fields; + ordered_map fields; const type base; explicit record_type(type base = type()); @@ -201,21 +201,37 @@ namespace elna::boot { } + /** + * Returns an iterator to the beginning. + * + * \return Iterator to the first element. + */ iterator begin() { return this->entries.begin(); } - iterator end() + /** + * \overload + */ + const_iterator begin() const { - return this->entries.end(); + return this->entries.cbegin(); } - const_iterator begin() const + /** + * Returns an iterator to the end. + * + * \return Iterator to the element following the last element. + */ + iterator end() { - return this->entries.cbegin(); + return this->entries.end(); } + /** + * \overload + */ const_iterator end() const { return this->entries.cend(); diff --git a/include/elna/boot/type_check.h b/include/elna/boot/type_check.h index f95c72b..3a2b61d 100644 --- a/include/elna/boot/type_check.h +++ b/include/elna/boot/type_check.h @@ -225,6 +225,7 @@ namespace elna::boot * of type assignee. */ static bool is_assignable_from(const type& assignee, const type& assignment); + static bool is_equality_compatible(const type& left, const type& right); static bool check_unresolved_symbol(const std::shared_ptr& alias, std::vector& path); diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h index 1ac829f..727c646 100644 --- a/include/elna/gcc/elna-generic.h +++ b/include/elna/gcc/elna-generic.h @@ -51,7 +51,11 @@ namespace elna::gcc tree make_if_branch(boot::conditional_statements& branch, tree next, tree goto_append = NULL_TREE); - tree build_equality_operation(boot::binary_expression *expression, tree left, tree right); + tree build_equality(boot::binary_expression *expression, tree left, tree right); + tree build_equality_comparison(location_t loc, tree left, tree right, + const boot::type& left_type, tree_code equality_code); + void build_case_integral(boot::case_statement *statement, tree condition_expression); + void build_case_general(boot::case_statement *statement, tree condition_expression); void build_procedure_call(location_t call_location, tree procedure_address, const std::vector& arguments); bool build_builtin_procedures(boot::procedure_call *call); diff --git a/testsuite/fail_compilation/case_non_constant.elna b/testsuite/fail_compilation/case_non_constant.elna new file mode 100644 index 0000000..2f19df8 --- /dev/null +++ b/testsuite/fail_compilation/case_non_constant.elna @@ -0,0 +1,16 @@ +type + R = record + a: Int; + b: Int + end + +var + r: R + x: R + +begin + x := r; + case x of + r: (* @Error Expected a constant expression *) + end +end. diff --git a/testsuite/fail_compilation/case_type_mismatch.elna b/testsuite/fail_compilation/case_type_mismatch.elna new file mode 100644 index 0000000..9cdae38 --- /dev/null +++ b/testsuite/fail_compilation/case_type_mismatch.elna @@ -0,0 +1,9 @@ +var + x: Int + +begin + case x of + 1: (* ok *) + | true: (* @Error Invalid operands of type 'Int' and 'Bool' for operator = *) + end +end. diff --git a/testsuite/runnable/case_else.elna b/testsuite/runnable/case_else.elna new file mode 100644 index 0000000..dbec6df --- /dev/null +++ b/testsuite/runnable/case_else.elna @@ -0,0 +1,14 @@ +var + x: Int := 5 + matched: Bool := false + +begin + case x of + 1: assert(false) + | 2: assert(false) + | 3: assert(false) + else + matched := true + end; + assert(matched) +end. diff --git a/testsuite/runnable/case_int.elna b/testsuite/runnable/case_int.elna new file mode 100644 index 0000000..3b770a7 --- /dev/null +++ b/testsuite/runnable/case_int.elna @@ -0,0 +1,12 @@ +var + x: Int := 2 + +begin + case x of + 1: assert(false) + | 2: assert(true) + | 3: assert(false) + else + assert(false) + end +end. diff --git a/testsuite/runnable/case_multilabel.elna b/testsuite/runnable/case_multilabel.elna new file mode 100644 index 0000000..be647d4 --- /dev/null +++ b/testsuite/runnable/case_multilabel.elna @@ -0,0 +1,11 @@ +var + x: Int := 3 + +begin + case x of + 1, 2, 3: assert(true) + | 4, 5: assert(false) + else + assert(false) + end +end. diff --git a/testsuite/runnable/case_record.elna b/testsuite/runnable/case_record.elna new file mode 100644 index 0000000..4f65a5b --- /dev/null +++ b/testsuite/runnable/case_record.elna @@ -0,0 +1,17 @@ +type + R = record + a: Int; + b: Int + end + +var + r: R := R{a: 1, b: 2} + +begin + case r of + R{a: 0, b: 0}: assert(false) + | R{a: 1, b: 2}: assert(true) + else + assert(false) + end +end. -- cgit v1.2.3