aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--boot/evaluator.cc24
-rw-r--r--boot/name_analysis.cc21
-rw-r--r--boot/type_check.cc28
-rw-r--r--gcc/gcc/elna-builtins.cc4
-rw-r--r--gcc/gcc/elna-generic.cc108
-rw-r--r--gcc/gcc/elna-tree.cc2
-rw-r--r--gcc/gcc/elna1.cc4
-rw-r--r--include/elna/boot/dependency.h4
-rw-r--r--include/elna/boot/evaluator.h54
-rw-r--r--include/elna/boot/name_analysis.h4
-rw-r--r--include/elna/boot/result.h131
-rw-r--r--include/elna/boot/symbol.h26
-rw-r--r--include/elna/boot/type_check.h1
-rw-r--r--include/elna/gcc/elna-generic.h6
-rw-r--r--testsuite/fail_compilation/case_non_constant.elna16
-rw-r--r--testsuite/fail_compilation/case_type_mismatch.elna9
-rw-r--r--testsuite/runnable/case_else.elna14
-rw-r--r--testsuite/runnable/case_int.elna12
-rw-r--r--testsuite/runnable/case_multilabel.elna11
-rw-r--r--testsuite/runnable/case_record.elna17
20 files changed, 419 insertions, 77 deletions
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<constant_value> 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<ordered_map>{ std::move(aggregate) } };
}
else if (auto *array_constructor = subject.is_array_constructor())
{
+ std::vector<constant_value> 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::vector>{ 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<constant_value> {
+ return std::visit([](const auto& value) -> std::optional<constant_value> {
using T = std::decay_t<decltype(value)>;
if constexpr (std::is_same_v<T, bool>)
@@ -183,7 +190,7 @@ namespace elna::boot
}
if (subject.operation() == unary_operator::bitwise_negation)
{
- return std::visit([](auto value) -> std::optional<constant_value> {
+ return std::visit([](const auto& value) -> std::optional<constant_value> {
using T = std::decay_t<decltype(value)>;
if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>)
@@ -240,10 +247,11 @@ namespace elna::boot
}
template<typename T>
- static std::optional<constant_value> evaluate_operation(binary_operator operation, T lhs, T rhs)
+ static std::optional<constant_value> evaluate_operation(binary_operator operation, const T& lhs, const T& rhs)
{
if constexpr (std::is_same_v<T, std::nullptr_t>
- || std::is_same_v<T, compound_constant>)
+ || std::is_same_v<T, constant_aggregate<ordered_map>>
+ || std::is_same_v<T, constant_aggregate<std::vector>>)
{
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<std::string, field_origin>& names)
+ ordered_map<field_origin>& names)
{
auto record = resolve_underlying_type(composite_type).get<record_type>();
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<type_field> name_analysis_visitor::build_composite_type(
+ ordered_map<type> name_analysis_visitor::build_composite_type(
const std::vector<field_declaration>& fields,
- std::map<std::string, field_origin>& field_names,
+ ordered_map<field_origin>& field_names,
const type& aggregate)
{
- std::vector<type_field> result;
+ ordered_map<type> 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<std::string> 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<record_type>();
}
- std::map<std::string, field_origin> field_names;
+ ordered_map<field_origin> 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<slice_type>() && resolved_right.get<slice_type>())
+ || (resolved_left.get<record_type>() && resolved_right.get<record_type>());
+ }
+
bool type_analysis_visitor::check_unresolved_symbol(const std::shared_ptr<alias_type>& alias,
std::vector<std::string>& 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<type_mismatch_error>(
- declaration->initializer->position(),
+ add_error<type_mismatch_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<type_mismatch_error>(
- case_label->position(), condition_type, case_label->type_decoration);
+ add_error<binary_operation_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<slice_type>() && rhs_resolved.get<slice_type>())
- || (lhs_resolved.get<record_type>() && rhs_resolved.get<record_type>());
+ 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<boot::type_field>& fields, tree composite_type_node,
- std::shared_ptr<symbol_table> symbols)
+ tree build_composite_type(const boot::ordered_map<boot::type>& fields,
+ tree composite_type_node, std::shared_ptr<symbol_table> 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<boot::constant_aggregate<boot::ordered_map>>(constant_value))
+ {
+ return NULL_TREE; // Compound constants are not lowered to trees yet.
+ }
+ else if (std::holds_alternative<boot::constant_aggregate<std::vector>>(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<boot::slice_type>() != 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<boot::slice_type>() != 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<boot::record_type>() != nullptr
- || expression->lhs().type_decoration.get<boot::array_type>() != nullptr)
+ else if (left_type.get<boot::record_type>() != nullptr
+ || left_type.get<boot::array_type>() != 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 <cstdint>
#include <map>
+#include <memory>
#include <optional>
#include <string>
#include <variant>
@@ -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<template<typename, typename> typename C, template<typename> 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<ordered_map>,
+ constant_aggregate<std::vector>
>;
+ template<template<typename, typename> typename C, template<typename> typename Alloc>
+ class constant_aggregate
+ {
+ using Container = C<constant_value, Alloc<constant_value>>;
+ std::shared_ptr<Container> container;
+
+ public:
+ explicit constant_aggregate(const Container& value)
+ : container(std::make_shared<Container>(value))
+ {
+ }
+
+ explicit constant_aggregate(const Container&& value)
+ : container(std::make_shared<Container>(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<procedure_type, std::vector<std::string>> build_procedure(
procedure_type_expression& expression);
- std::vector<type_field> build_composite_type(const std::vector<field_declaration>& fields,
- std::map<std::string, field_origin>& field_names,
+ ordered_map<type> build_composite_type(const std::vector<field_declaration>& fields,
+ ordered_map<field_origin>& field_names,
const type& aggregate);
std::shared_ptr<variable_info> 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 <optional>
#include <utility>
#include <variant>
+#include <vector>
+#include <unordered_map>
+#include <ranges>
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<typename V, typename Alloc = std::allocator<V>>
+ class ordered_map
+ {
+ using vector_alloc = std::allocator_traits<Alloc>::template
+ rebind_alloc<std::pair<std::string, V>>;
+ using map_alloc = std::allocator_traits<Alloc>::template
+ rebind_alloc<std::pair<const std::string, std::size_t>>;
+
+ std::unordered_map<std::string, std::size_t,
+ std::hash<std::string>, std::equal_to<>, map_alloc> index_map;
+ std::vector<std::pair<std::string, V>, vector_alloc> payload;
+
+ public:
+ using key_type = std::string;
+ using mapped_type = V;
+ using value_type = std::pair<key_type, mapped_type>;
+ using iterator = std::vector<value_type>::iterator;
+ using const_iterator = std::vector<value_type>::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<iterator, bool> 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<iterator, bool> 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<type_field> fields;
+ ordered_map<type> 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_type>& alias,
std::vector<std::string>& 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<boot::expression *>& 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.