diff options
| author | Eugen Wissner <belka@caraus.de> | 2026-07-21 21:27:03 +0200 |
|---|---|---|
| committer | Eugen Wissner <belka@caraus.de> | 2026-07-21 21:27:03 +0200 |
| commit | 5cc2947143f2a6ebd4ef1ae4d25ab956b87de808 (patch) | |
| tree | 0d4489b298da3be04ed4bce117f616468146a0f8 | |
| parent | 44d6e8a27294e5ca7300ab11900011b73d9336d4 (diff) | |
| download | elna-5cc2947143f2a6ebd4ef1ae4d25ab956b87de808.tar.gz | |
| -rw-r--r-- | boot/ast.cc | 38 | ||||
| -rw-r--r-- | boot/evaluator.cc | 42 | ||||
| -rw-r--r-- | boot/name_analysis.cc | 135 | ||||
| -rw-r--r-- | boot/symbol.cc | 2 | ||||
| -rw-r--r-- | boot/type_check.cc | 254 | ||||
| -rw-r--r-- | gcc/gcc/elna-builtins.cc | 20 | ||||
| -rw-r--r-- | gcc/gcc/elna-diagnostic.cc | 4 | ||||
| -rw-r--r-- | gcc/gcc/elna-generic.cc | 388 | ||||
| -rw-r--r-- | gcc/gcc/elna-tree.cc | 36 | ||||
| -rw-r--r-- | include/elna/boot/ast.h | 20 | ||||
| -rw-r--r-- | include/elna/boot/name_analysis.h | 53 | ||||
| -rw-r--r-- | include/elna/boot/type_check.h | 65 | ||||
| -rw-r--r-- | include/elna/gcc/elna-generic.h | 8 | ||||
| -rw-r--r-- | include/elna/gcc/elna-tree.h | 18 | ||||
| -rw-r--r-- | include/elna/gcc/elna1.h | 7 | ||||
| -rw-r--r-- | rakelib/gcc.rake | 7 | ||||
| -rw-r--r-- | source/common.elna | 4 | ||||
| -rw-r--r-- | source/cstdio.elna | 14 | ||||
| -rw-r--r-- | source/cstring.elna | 14 | ||||
| -rw-r--r-- | source/lexer.elna | 10 | ||||
| -rw-r--r-- | source/main.elna | 18 | ||||
| -rw-r--r-- | testsuite/runnable/record_extension.elna | 2 | ||||
| -rw-r--r-- | testsuite/runnable/return_aggregate.elna | 4 | ||||
| -rw-r--r-- | testsuite/runnable/slice_array.elna | 11 | ||||
| -rw-r--r-- | testsuite/runnable/slice_pointer.elna | 11 | ||||
| -rw-r--r-- | testsuite/runnable/slice_slice.elna | 13 |
26 files changed, 649 insertions, 549 deletions
diff --git a/boot/ast.cc b/boot/ast.cc index 3727aba..ec5b27d 100644 --- a/boot/ast.cc +++ b/boot/ast.cc @@ -438,8 +438,8 @@ namespace elna::boot void walking_visitor::visit(slicing_expression *expression) { expression->base().accept(this); - expression->offset().accept(this); - expression->length().accept(this); + expression->start().accept(this); + expression->end().accept(this); } void walking_visitor::visit(traits_expression *trait) @@ -1070,8 +1070,8 @@ namespace elna::boot } slicing_expression::slicing_expression(const source_position position, - expression *base, expression *offset, expression *length) - : node(position), m_base(base), m_offset(offset), m_length(length) + expression *base, expression *start, expression *end) + : node(position), m_base(base), m_start(start), m_end(end) { } @@ -1088,8 +1088,8 @@ namespace elna::boot slicing_expression::~slicing_expression() { delete m_base; - delete m_offset; - delete m_length; + delete m_start; + delete m_end; } expression& slicing_expression::base() @@ -1097,14 +1097,14 @@ namespace elna::boot return *this->m_base; } - expression& slicing_expression::offset() + expression& slicing_expression::start() { - return *this->m_offset; + return *this->m_start; } - expression& slicing_expression::length() + expression& slicing_expression::end() { - return *this->m_length; + return *this->m_end; } named_expression::named_expression(const source_position position, const std::string& name) @@ -1242,6 +1242,11 @@ namespace elna::boot return m_operator; } + void binary_expression::operation(binary_operator operation) + { + this->m_operator = operation; + } + binary_expression::~binary_expression() { delete m_lhs; @@ -1274,6 +1279,11 @@ namespace elna::boot return this->m_operator; } + void unary_expression::operation(unary_operator operation) + { + this->m_operator = operation; + } + unary_expression::~unary_expression() { delete m_operand; @@ -1540,10 +1550,16 @@ namespace elna::boot case greater_equal: return ">="; case conjunction: - return "and"; + case logical_conjunction: + case bitwise_conjunction: + return "&"; case disjunction: + case logical_disjunction: + case bitwise_disjunction: return "or"; case exclusive_disjunction: + case logical_exclusive_disjunction: + case bitwise_exclusive_disjunction: return "xor"; case shift_left: return "<<"; diff --git a/boot/evaluator.cc b/boot/evaluator.cc index 990f358..77a56ad 100644 --- a/boot/evaluator.cc +++ b/boot/evaluator.cc @@ -169,7 +169,7 @@ namespace elna::boot return std::nullopt; }, operand.value()); } - if (subject.operation() == unary_operator::negation) + if (subject.operation() == unary_operator::logical_negation) { return std::visit([](auto value) -> std::optional<constant_value> { using T = std::decay_t<decltype(value)>; @@ -178,7 +178,15 @@ namespace elna::boot { return constant_value{ !value }; } - else if constexpr (std::is_integral_v<T>) + return std::nullopt; + }, operand.value()); + } + if (subject.operation() == unary_operator::bitwise_negation) + { + return std::visit([](auto value) -> std::optional<constant_value> { + using T = std::decay_t<decltype(value)>; + + if constexpr (std::is_integral_v<T>) { return constant_value{ ~value }; } @@ -299,23 +307,44 @@ namespace elna::boot } return std::nullopt; case disjunction: - if constexpr (std::is_integral_v<T>) + case bitwise_disjunction: + if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>) { return constant_value{ lhs | rhs }; } return std::nullopt; case conjunction: - if constexpr (std::is_integral_v<T>) + case bitwise_conjunction: + if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>) { return constant_value{ lhs & rhs }; } return std::nullopt; case exclusive_disjunction: - if constexpr (std::is_integral_v<T>) + case bitwise_exclusive_disjunction: + if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>) { return constant_value{ lhs ^ rhs }; } return std::nullopt; + case logical_conjunction: + if constexpr (std::is_same_v<T, bool>) + { + return constant_value{ lhs && rhs }; + } + return std::nullopt; + case logical_disjunction: + if constexpr (std::is_same_v<T, bool>) + { + return constant_value{ lhs || rhs }; + } + return std::nullopt; + case logical_exclusive_disjunction: + if constexpr (std::is_same_v<T, bool>) + { + return constant_value{ lhs != rhs }; + } + return std::nullopt; case shift_left: if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>) { @@ -420,8 +449,7 @@ namespace elna::boot { return target.bool_size; } - else if (is_primitive_type(resolved, "String") - || resolved.get<slice_type>() != nullptr) + else if (resolved.get<slice_type>() != nullptr) { return target.pointer_size + target.word_size; } diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc index a2cacb2..907f318 100644 --- a/boot/name_analysis.cc +++ b/boot/name_analysis.cc @@ -17,7 +17,6 @@ along with GCC; see the file COPYING3. If not see #include "elna/boot/name_analysis.h" -#include <algorithm> #include <utility> namespace elna::boot @@ -81,6 +80,84 @@ namespace elna::boot return "Duplicate 'const' qualifier is not allowed"; } + field_not_found_error::field_not_found_error(const identifier& field_name, + type composite_type) + : error(field_name.position()), field_name(field_name.name()), composite_type(std::move(composite_type)) + { + } + + std::string field_not_found_error::what() const + { + type const resolved = resolve_underlying_type(composite_type); + bool const is_enum = resolved.get<enumeration_type>() != nullptr; + bool const is_record = resolved.get<record_type>() != nullptr; + + if (is_enum || is_record) + { + std::string message = is_enum ? "Enumeration" : "Record"; + + if (auto alias = composite_type.get<alias_type>()) + { + message += " '" + alias->name + "'"; + } + message += " does not have a "; + message += is_enum ? "member" : "field"; + message += " named '" + field_name + "'"; + return message; + } + return "Type '" + composite_type.to_string() + + "' does not have a field named '" + field_name + "'"; + } + + duplicate_member_error::duplicate_member_error(const boot::identifier& member_name, + type aggregate, std::optional<source_position> original, + std::optional<std::string> base_name) + : error(member_name.position()), member_name(member_name.name()), aggregate(std::move(aggregate)), + original(original), base_name(std::move(base_name)) + { + } + + std::string duplicate_member_error::what() const + { + type const resolved = resolve_underlying_type(aggregate); + bool const is_enum = resolved.get<enumeration_type>() != nullptr; + std::string const kind = is_enum ? "member" : "field"; + std::string message = is_enum ? "Enumeration" : "Record"; + + if (auto alias = aggregate.get<alias_type>()) + { + message += " '" + alias->name + "'"; + } + message += " already has a " + kind + " named '" + member_name + "'"; + + if (base_name.has_value()) + { + message += " (defined in base type '" + *base_name + "')"; + } + return message; + } + + std::optional<std::pair<std::string, source_position>> duplicate_member_error::note() const + { + if (original.has_value() && original->start().available()) + { + return std::make_pair("previously declared here", *original); + } + return std::nullopt; + } + + unsupported_trait_type_error::unsupported_trait_type_error(const identifier& trait, + type actual) + : error(trait.position()), actual(std::move(actual)), trait_name(trait.name()) + { + } + + std::string unsupported_trait_type_error::what() const + { + return "Type '" + actual.to_string() + + "' does not support trait '#" + trait_name + "'"; + } + // Members of a constant aggregate are constant themselves. static type qualify_member_type(const type& element, const type& aggregate) { @@ -133,6 +210,20 @@ namespace elna::boot return result_type; } + std::optional<type> name_analysis_visitor::lookup_pointer_like_field( + const std::string& field_name, const type& element_type) + { + if (field_name == "length") + { + return lookup_primitive_type("Word"); + } + if (field_name == "ptr") + { + return type(std::make_shared<pointer_type>(element_type)); + } + return std::nullopt; + } + type name_analysis_visitor::lookup_primitive_type(const std::string& name) { return this->bag.lookup(name)->is_type()->symbol; @@ -156,26 +247,18 @@ namespace elna::boot return lookup_field(record->base, field_name); } } - else if (auto slice = resolved_type.get<slice_type>()) + else if (auto array = resolved_type.get<array_type>()) { - if (field_name == "length") - { - return lookup_primitive_type("Word"); - } - else if (field_name == "ptr") + if (auto field = lookup_pointer_like_field(field_name, array->base)) { - return type(std::make_shared<pointer_type>(slice->base)); + return field.value(); } } - else if (auto primitive = resolved_type.get<primitive_type>(); primitive != nullptr && primitive->identifier == "String") + else if (auto slice = resolved_type.get<slice_type>()) { - if (field_name == "length") + if (auto field = lookup_pointer_like_field(field_name, slice->base)) { - return lookup_primitive_type("Word"); - } - else if (field_name == "ptr") - { - return type(std::make_shared<pointer_type>(lookup_primitive_type("Char"))); + return field.value(); } } return type(); @@ -303,17 +386,6 @@ namespace elna::boot } else { - type actual; - - if (auto var = base_symbol->is_variable()) - { - actual = var->symbol; - } - else if (auto proc = base_symbol->is_procedure()) - { - actual = type(std::make_shared<procedure_type>(proc->symbol)); - } - add_error<base_type_error>(actual, expression->position()); this->current_type = type(); return; } @@ -513,10 +585,6 @@ for (const auto& member : expression->members) { call->type_decoration = procedure->return_type.proper_type; } - else if (call->callable().is_named() != nullptr) - { - call->type_decoration = this->current_type; - } for (expression *const argument : call->arguments) { argument->accept(this); @@ -653,10 +721,6 @@ for (const auto& member : expression->members) { expression->type_decoration = slice->base; } - else if (resolved_base == lookup_primitive_type("String")) - { - expression->type_decoration = lookup_primitive_type("Char"); - } // Elements of a constant array are constant themselves since a static // array is a holistic type. if (!expression->type_decoration.empty()) @@ -765,7 +829,8 @@ for (const auto& member : expression->members) void name_analysis_visitor::visit(literal<std::string> *literal) { - literal->type_decoration = lookup_primitive_type("String"); + literal->type_decoration = type(std::make_shared<slice_type>( + type(std::make_shared<constant_type>(lookup_primitive_type("Char"))))); } declaration_visitor::declaration_visitor() diff --git a/boot/symbol.cc b/boot/symbol.cc index 738e4e1..da948fd 100644 --- a/boot/symbol.cc +++ b/boot/symbol.cc @@ -329,8 +329,6 @@ namespace elna::boot result->enter("Char", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Char")))); result->enter("Pointer", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Pointer")))); result->enter("Float", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Float")))); - result->enter("String", std::make_shared<type_info>(type(std::make_shared<primitive_type>("String")))); - type const boolean = type(std::make_shared<primitive_type>("Bool")); result->enter("Bool", std::make_shared<type_info>(boolean)); diff --git a/boot/type_check.cc b/boot/type_check.cc index 5d0c7a6..7989925 100644 --- a/boot/type_check.cc +++ b/boot/type_check.cc @@ -56,72 +56,6 @@ namespace elna::boot + "', because it is constant or contains constant members"; } - field_not_found_error::field_not_found_error(const identifier& field_name, - type composite_type) - : error(field_name.position()), field_name(field_name.name()), composite_type(std::move(composite_type)) - { - } - - std::string field_not_found_error::what() const - { - type const resolved = resolve_underlying_type(composite_type); - bool const is_enum = resolved.get<enumeration_type>() != nullptr; - bool const is_record = resolved.get<record_type>() != nullptr; - - if (is_enum || is_record) - { - std::string message = is_enum ? "Enumeration" : "Record"; - - if (auto alias = composite_type.get<alias_type>()) - { - message += " '" + alias->name + "'"; - } - message += " does not have a "; - message += is_enum ? "member" : "field"; - message += " named '" + field_name + "'"; - return message; - } - return "Type '" + composite_type.to_string() - + "' does not have a field named '" + field_name + "'"; - } - - duplicate_member_error::duplicate_member_error(const boot::identifier& member_name, - type aggregate, std::optional<source_position> original, - std::optional<std::string> base_name) - : error(member_name.position()), member_name(member_name.name()), aggregate(std::move(aggregate)), - original(original), base_name(std::move(base_name)) - { - } - - std::string duplicate_member_error::what() const - { - type const resolved = resolve_underlying_type(aggregate); - bool const is_enum = resolved.get<enumeration_type>() != nullptr; - std::string const kind = is_enum ? "member" : "field"; - std::string message = is_enum ? "Enumeration" : "Record"; - - if (auto alias = aggregate.get<alias_type>()) - { - message += " '" + alias->name + "'"; - } - message += " already has a " + kind + " named '" + member_name + "'"; - - if (base_name.has_value()) - { - message += " (defined in base type '" + *base_name + "')"; - } - return message; - } - - std::optional<std::pair<std::string, source_position>> duplicate_member_error::note() const - { - if (original.has_value() && original->start().available()) - { - return std::make_pair("previously declared here", *original); - } - return std::nullopt; - } - cyclic_declaration_error::cyclic_declaration_error(const std::vector<std::string>& cycle, const source_position position) : error(position), cycle(cycle) @@ -189,18 +123,6 @@ namespace elna::boot } } - unsupported_trait_type_error::unsupported_trait_type_error(const identifier& trait, - type actual) - : error(trait.position()), actual(std::move(actual)), trait_name(trait.name()) - { - } - - std::string unsupported_trait_type_error::what() const - { - return "Type '" + actual.to_string() - + "' does not support trait '#" + trait_name + "'"; - } - unary_operation_error::unary_operation_error(const source_position position, type actual, unary_operator operation) : error(position), actual(std::move(actual)), op(operation) @@ -215,6 +137,8 @@ namespace elna::boot case reference: return '@'; case negation: + case bitwise_negation: + case logical_negation: return '~'; case minus: return '-'; @@ -368,10 +292,36 @@ namespace elna::boot return verdict::pass; } + assign_check::verdict assign_check::check_slice_conversion() const + { + auto assignee_slice = ctx.resolved_assignee.get<slice_type>(); + auto assignment_slice = ctx.resolved_assignment.get<slice_type>(); + + if (assignee_slice == nullptr || assignment_slice == nullptr) + { + return verdict::pass; + } + auto assignee_element_const = resolve_aliases(assignee_slice->base).get<constant_type>(); + auto assignment_element_const = resolve_aliases(assignment_slice->base).get<constant_type>(); + + // Const can be added to the element type, but not removed. + if (assignee_element_const != nullptr + && assignee_element_const->unqualified == assignment_slice->base) + { + return verdict::accept; + } + if (assignment_element_const != nullptr && assignee_element_const == nullptr) + { + return verdict::reject; + } + return verdict::pass; + } + bool assign_check::run() { for (auto handler : {&assign_check::guard_const_laundering, &assign_check::check_exact_match, + &assign_check::check_slice_conversion, &assign_check::check_pointer_hatch, &assign_check::check_pointer_conversion}) { @@ -534,10 +484,18 @@ namespace elna::boot { if (expression->base.has_value()) { - type const base_type = resolve_underlying_type(this->bag.lookup(expression->base.value().name())->is_type()->symbol); - if (base_type.get<record_type>() == nullptr) + auto const base_symbol = this->bag.lookup(expression->base.value().name()); + if (base_symbol == nullptr || base_symbol->is_type() == nullptr) { - add_error<base_type_error>(base_type, expression->position()); + add_error<base_type_error>(type(), expression->position()); + } + else + { + type const base_type = resolve_underlying_type(base_symbol->is_type()->symbol); + if (base_type.get<record_type>() == nullptr) + { + add_error<base_type_error>(base_type, expression->position()); + } } } walking_visitor::visit(expression); @@ -571,6 +529,12 @@ namespace elna::boot call->arguments.size(), call->position()); } } + else if (!call->callable().type_decoration.empty()) + { + add_error<type_mismatch_error>(call->position(), + type(std::make_shared<procedure_type>()), + call->callable().type_decoration); + } } void type_analysis_visitor::visit(record_constructor_expression *expression) @@ -660,12 +624,136 @@ namespace elna::boot } else if (operation == unary_operator::negation) { - if (!is_primitive_type(resolved, "Bool") - && !is_integral_type(resolved)) + if (is_primitive_type(resolved, "Bool")) + { + expression->operation(unary_operator::logical_negation); + } + else if (is_integral_type(resolved)) + { + expression->operation(unary_operator::bitwise_negation); + } + else { add_error<unary_operation_error>(expression->position(), expression->operand().type_decoration, operation); } } + else if (operation == unary_operator::reference) + { + auto *designator = expression->operand().is_designator(); + if (designator == nullptr || designator->is_slicing() != nullptr) + { + add_error<unary_operation_error>(expression->position(), + expression->operand().type_decoration, operation); + } + } + } + + binary_operation_error::binary_operation_error(const source_position position, + type left, type right, binary_operator operation) + : error(position), left(std::move(left)), right(std::move(right)), op(operation) + { + } + + std::string binary_operation_error::what() const + { + return "Invalid operands of type '" + left.to_string() + + "' and '" + right.to_string() + + "' for operator " + print_binary_operator(op); + } + + void type_analysis_visitor::visit(binary_expression *expression) + { + walking_visitor::visit(expression); + + auto operation = expression->operation(); + type const lhs_resolved = resolve_underlying_type(expression->lhs().type_decoration); + type const rhs_resolved = resolve_underlying_type(expression->rhs().type_decoration); + bool valid = false; + + switch (operation) + { + using enum binary_operator; + case sum: + valid = (is_any_pointer_type(lhs_resolved) && is_integral_type(rhs_resolved)) + || (is_integral_type(lhs_resolved) && is_any_pointer_type(rhs_resolved)) + || (is_numeric_type(lhs_resolved) && lhs_resolved == rhs_resolved); + break; + case subtraction: + valid = (is_any_pointer_type(lhs_resolved) && is_integral_type(rhs_resolved)) + || (is_any_pointer_type(lhs_resolved) && is_any_pointer_type(rhs_resolved)) + || (is_numeric_type(lhs_resolved) && lhs_resolved == rhs_resolved); + break; + case division: + case remainder: + case multiplication: + valid = is_numeric_type(lhs_resolved) && lhs_resolved == rhs_resolved; + break; + case less: + case greater: + case less_equal: + case greater_equal: + valid = (is_numeric_type(lhs_resolved) && lhs_resolved == rhs_resolved) + || (is_any_pointer_type(lhs_resolved) && is_any_pointer_type(rhs_resolved)); + break; + case conjunction: + case disjunction: + case exclusive_disjunction: + if (is_primitive_type(lhs_resolved, "Bool") && lhs_resolved == rhs_resolved) + { + switch (operation) + { + case conjunction: + expression->operation(logical_conjunction); + break; + case disjunction: + expression->operation(logical_disjunction); + break; + case exclusive_disjunction: + expression->operation(logical_exclusive_disjunction); + break; + default: + break; + } + valid = true; + } + else if (is_integral_type(lhs_resolved) && lhs_resolved == rhs_resolved) + { + switch (operation) + { + case conjunction: + expression->operation(bitwise_conjunction); + break; + case disjunction: + expression->operation(bitwise_disjunction); + break; + case exclusive_disjunction: + expression->operation(bitwise_exclusive_disjunction); + break; + default: + break; + } + valid = true; + } + 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>()); + break; + case shift_left: + case shift_right: + valid = is_integral_type(lhs_resolved) + && is_primitive_type(rhs_resolved, "Word"); + break; + } + if (!valid) + { + add_error<binary_operation_error>(expression->position(), + expression->lhs().type_decoration, expression->rhs().type_decoration, operation); + } } } diff --git a/gcc/gcc/elna-builtins.cc b/gcc/gcc/elna-builtins.cc index 141b34f..77ffb1c 100644 --- a/gcc/gcc/elna-builtins.cc +++ b/gcc/gcc/elna-builtins.cc @@ -38,17 +38,6 @@ namespace elna::gcc elna_bool_false_node = boolean_false_node; elna_pointer_nil_node = null_pointer_node; - - elna_string_type_node = make_node(RECORD_TYPE); - tree string_ptr_type = build_pointer_type(elna_char_type_node); - - elna_string_length_field_node = build_field(UNKNOWN_LOCATION, - elna_string_type_node, "length", build_qualified_type(elna_word_type_node, TYPE_QUAL_CONST)); - elna_string_ptr_field_node = build_field(UNKNOWN_LOCATION, - elna_string_type_node, "ptr", build_qualified_type(string_ptr_type, TYPE_QUAL_CONST)); - - TYPE_FIELDS(elna_string_type_node) = chainon(elna_string_ptr_field_node, elna_string_length_field_node); - layout_type(elna_string_type_node); } static @@ -73,10 +62,6 @@ namespace elna::gcc declare_builtin_type(builtin_table, "Pointer", elna_pointer_type_node); declare_builtin_type(builtin_table, "Float", elna_float_type_node); - tree string_declaration = declare_builtin_type(builtin_table, "String", elna_string_type_node); - TYPE_NAME(elna_string_type_node) = DECL_NAME(string_declaration); - TYPE_STUB_DECL(elna_string_type_node) = string_declaration; - return builtin_table; } @@ -165,7 +150,7 @@ namespace elna::gcc TYPE_FIELDS(slice_record) = chainon(ptr_field, length_field); layout_type(slice_record); - return slice_record; + return type_hash_canon(type_hash_canon_hash(slice_record), slice_record); } else if (auto reference = type.get<boot::procedure_type>()) { @@ -175,7 +160,8 @@ namespace elna::gcc } else if (auto reference = type.get<boot::constant_type>()) { - return get_inner_alias(reference->unqualified, symbols); + tree unqualified = get_inner_alias(reference->unqualified, symbols); + return build_qualified_type(unqualified, TYPE_QUAL_CONST); } else if (auto reference = type.get<boot::alias_type>()) { diff --git a/gcc/gcc/elna-diagnostic.cc b/gcc/gcc/elna-diagnostic.cc index 3769ee4..630db0c 100644 --- a/gcc/gcc/elna-diagnostic.cc +++ b/gcc/gcc/elna-diagnostic.cc @@ -108,10 +108,6 @@ namespace elna::gcc { return "Char"; } - else if (unqualified_type == elna_string_type_node) - { - return "String"; - } else if (is_void_type(unqualified_type)) // For procedures without a return type. { return "()"; diff --git a/gcc/gcc/elna-generic.cc b/gcc/gcc/elna-generic.cc index ea209df..c2c59aa 100644 --- a/gcc/gcc/elna-generic.cc +++ b/gcc/gcc/elna-generic.cc @@ -126,18 +126,7 @@ namespace elna::gcc ? this->current_expression : TREE_TYPE(this->current_expression); - if (TREE_CODE(expression_type) == RECORD_TYPE - && TYPE_NAME(expression_type) == get_identifier("String")) - { - vec<constructor_elt, va_gc> *elms = nullptr; - - call->arguments.at(0)->accept(this); - CONSTRUCTOR_APPEND_ELT(elms, elna_string_ptr_field_node, this->current_expression); - call->arguments.at(1)->accept(this); - CONSTRUCTOR_APPEND_ELT(elms, elna_string_length_field_node, this->current_expression); - this->current_expression = build_constructor(elna_string_type_node, elms); - } - else if (TREE_CODE(expression_type) == FUNCTION_TYPE) + if (TREE_CODE(expression_type) == FUNCTION_TYPE) { this->current_expression = build1(ADDR_EXPR, build_pointer_type(expression_type), this->current_expression); @@ -230,28 +219,31 @@ namespace elna::gcc tree base = this->current_expression; tree base_type = TREE_TYPE(base); - expression->offset().accept(this); - tree offset = fold_convert(elna_word_type_node, this->current_expression); - offset = build2(MINUS_EXPR, elna_word_type_node, offset, size_one_node); + expression->start().accept(this); + tree start_index = fold_convert(elna_word_type_node, this->current_expression); + tree zero_based_offset = build2(MINUS_EXPR, elna_word_type_node, + start_index, size_one_node); tree slice_ptr; if (TREE_CODE(base_type) == ARRAY_TYPE) { + // Elna arrays are 1-indexed in TYPE_DOMAIN. Pass the raw + // (1-based) start index — GCC handles the 1→0 conversion + // via the domain lower bound. No -1 adjustment needed here. slice_ptr = build4_loc(location, ARRAY_REF, TREE_TYPE(base_type), - base, offset, size_one_node, NULL_TREE); + base, start_index, size_one_node, NULL_TREE); slice_ptr = build1(ADDR_EXPR, build_pointer_type(TREE_TYPE(base_type)), slice_ptr); } else { tree ptr = build3_loc(location, COMPONENT_REF, TREE_TYPE(ptr_field), base, ptr_field, NULL_TREE); - slice_ptr = do_pointer_arithmetic(boot::binary_operator::sum, ptr, offset, location); + slice_ptr = do_pointer_arithmetic(boot::binary_operator::sum, + ptr, zero_based_offset, location); } - expression->length().accept(this); + expression->end().accept(this); tree end_index = fold_convert(elna_word_type_node, this->current_expression); - expression->offset().accept(this); - tree start_index = fold_convert(elna_word_type_node, this->current_expression); tree slice_length = build2(MINUS_EXPR, elna_word_type_node, end_index, start_index); slice_length = build2(PLUS_EXPR, elna_word_type_node, slice_length, size_one_node); @@ -288,10 +280,7 @@ namespace elna::gcc DECL_CONTEXT(resdecl) = fndecl; DECL_RESULT(fndecl) = resdecl; - push_struct_function(fndecl, false); - DECL_STRUCT_FUNCTION(fndecl)->language = ggc_cleared_alloc<language_function>(); - - enter_scope(); + begin_function(fndecl); tree parameter_type = TYPE_ARG_TYPES(declaration_type); for (const char *argument_name : std::array<const char *, 2>{ "count", "parameters" }) @@ -311,18 +300,9 @@ namespace elna::gcc tree return_stmt = build1(RETURN_EXPR, void_type_node, set_result); append_statement(return_stmt); this->current_expression = NULL_TREE; - tree mapping = leave_scope(); - - BLOCK_SUPERCONTEXT(BIND_EXPR_BLOCK(mapping)) = fndecl; - DECL_INITIAL(fndecl) = BIND_EXPR_BLOCK(mapping); - DECL_SAVED_TREE(fndecl) = mapping; DECL_EXTERNAL(fndecl) = 0; - DECL_PRESERVE_P(fndecl) = 1; - - pop_cfun(); - gimplify_function_tree(fndecl); - cgraph_node::finalize_function(fndecl, true); + finish_function(fndecl); } } @@ -334,10 +314,7 @@ namespace elna::gcc { return; } - push_struct_function(fndecl, false); - DECL_STRUCT_FUNCTION(fndecl)->language = ggc_cleared_alloc<language_function>(); - - enter_scope(); + begin_function(fndecl); this->bag.enter(this->bag.lookup(declaration->identifier.name())->is_procedure()->scope); tree argument_chain = DECL_ARGUMENTS(fndecl); @@ -369,18 +346,8 @@ namespace elna::gcc this->current_expression = NULL_TREE; } - tree mapping = leave_scope(); this->bag.leave(); - - BLOCK_SUPERCONTEXT(BIND_EXPR_BLOCK(mapping)) = fndecl; - DECL_INITIAL(fndecl) = BIND_EXPR_BLOCK(mapping); - DECL_SAVED_TREE(fndecl) = mapping; - - DECL_PRESERVE_P(fndecl) = 1; - - pop_cfun(); - gimplify_function_tree(fndecl); - cgraph_node::finalize_function(fndecl, true); + finish_function(fndecl); } void generic_visitor::enter_scope() @@ -416,14 +383,72 @@ namespace elna::gcc return bind_expr; } + void generic_visitor::begin_function(tree fndecl) + { + push_struct_function(fndecl, false); + DECL_STRUCT_FUNCTION(fndecl)->language = ggc_cleared_alloc<language_function>(); + enter_scope(); + } + + void generic_visitor::finish_function(tree fndecl) + { + tree mapping = leave_scope(); + + BLOCK_SUPERCONTEXT(BIND_EXPR_BLOCK(mapping)) = fndecl; + DECL_INITIAL(fndecl) = BIND_EXPR_BLOCK(mapping); + DECL_SAVED_TREE(fndecl) = mapping; + + DECL_PRESERVE_P(fndecl) = 1; + + pop_cfun(); + gimplify_function_tree(fndecl); + cgraph_node::finalize_function(fndecl, true); + } + + static tree constant_to_tree(const boot::constant_value& constant_value) + { + if (std::holds_alternative<std::int32_t>(constant_value)) + { + return build_int_cst(elna_int_type_node, std::get<std::int32_t>(constant_value)); + } + else if (std::holds_alternative<std::uint32_t>(constant_value)) + { + return build_int_cstu(elna_word_type_node, std::get<std::uint32_t>(constant_value)); + } + + else if (std::holds_alternative<double>(constant_value)) + { + auto real_value = std::get<double>(constant_value); + REAL_VALUE_TYPE real; + HOST_WIDE_INT target_bits[(sizeof(double) + sizeof(HOST_WIDE_INT) - 1) + / sizeof(HOST_WIDE_INT)]; + std::memcpy(target_bits, &real_value, sizeof(real_value)); + real_from_target(&real, target_bits, REAL_MODE_FORMAT(TYPE_MODE(elna_float_type_node))); + return build_real(elna_float_type_node, real); + } + else if (std::holds_alternative<bool>(constant_value)) + { + return std::get<bool>(constant_value) ? boolean_true_node : boolean_false_node; + } + else if (std::holds_alternative<unsigned char>(constant_value)) + { + return build_int_cstu(elna_char_type_node, std::get<unsigned char>(constant_value)); + } + else if (std::holds_alternative<std::nullptr_t>(constant_value)) + { + return elna_pointer_nil_node; + } + return NULL_TREE; + } + void generic_visitor::visit(boot::literal<std::int32_t> *literal) { - this->current_expression = build_int_cst(elna_int_type_node, literal->value); + this->current_expression = constant_to_tree(boot::constant_value{ literal->value }); } void generic_visitor::visit(boot::literal<std::uint32_t> *literal) { - this->current_expression = build_int_cstu(elna_word_type_node, literal->value); + this->current_expression = constant_to_tree(boot::constant_value{ literal->value }); } void generic_visitor::visit(boot::literal<double> *literal) @@ -443,97 +468,43 @@ namespace elna::gcc void generic_visitor::visit(boot::literal<bool> *boolean) { - this->current_expression = boolean->value ? boolean_true_node : boolean_false_node; + this->current_expression = constant_to_tree(boot::constant_value{ boolean->value }); } void generic_visitor::visit(boot::literal<unsigned char> *character) { - this->current_expression = build_int_cstu(elna_char_type_node, character->value); + this->current_expression = constant_to_tree(boot::constant_value{ character->value }); } void generic_visitor::visit(boot::literal<std::nullptr_t> *) { - this->current_expression = elna_pointer_nil_node; + this->current_expression = constant_to_tree(boot::constant_value{ std::nullptr_t{} }); } void generic_visitor::visit(boot::literal<std::string> *string) { tree index_constant = build_int_cstu(elna_word_type_node, string->value.size()); - tree string_type = build_array_type(elna_char_type_node, build_index_type(index_constant)); + tree char_array_type = build_array_type(elna_char_type_node, build_index_type(index_constant)); tree string_literal = build_string(string->value.size(), string->value.c_str()); - TREE_TYPE(string_literal) = string_type; + TREE_TYPE(string_literal) = char_array_type; TREE_CONSTANT(string_literal) = 1; TREE_READONLY(string_literal) = 1; TREE_STATIC(string_literal) = 1; - string_type = TREE_TYPE(elna_string_ptr_field_node); + tree slice_type = get_inner_alias(string->type_decoration, this->symbols); + tree ptr_field = TYPE_FIELDS(slice_type); + + tree ptr_type = TREE_TYPE(ptr_field); string_literal = build4(ARRAY_REF, elna_char_type_node, string_literal, integer_zero_node, NULL_TREE, NULL_TREE); - string_literal = build1(ADDR_EXPR, string_type, string_literal); + string_literal = build1(ADDR_EXPR, ptr_type, string_literal); vec<constructor_elt, va_gc> *elms = nullptr; - CONSTRUCTOR_APPEND_ELT(elms, elna_string_ptr_field_node, string_literal); - CONSTRUCTOR_APPEND_ELT(elms, elna_string_length_field_node, index_constant); - - this->current_expression = build_constructor(elna_string_type_node, elms); - } - - tree generic_visitor::build_arithmetic_operation(boot::binary_expression *expression, - tree_code operator_code, tree left, tree right) - { - return build_binary_operation(is_numeric_type(TREE_TYPE(left)), - expression, operator_code, left, right, TREE_TYPE(left)); - } - - tree generic_visitor::build_comparison_operation(boot::binary_expression *expression, - tree_code operator_code, tree left, tree right) - { - return build_binary_operation(is_numeric_type(TREE_TYPE(left)) || POINTER_TYPE_P(TREE_TYPE(left)), - expression, operator_code, left, right, elna_bool_type_node); - } + CONSTRUCTOR_APPEND_ELT(elms, ptr_field, string_literal); + CONSTRUCTOR_APPEND_ELT(elms, TREE_CHAIN(ptr_field), index_constant); - tree generic_visitor::build_bit_logic_operation(boot::binary_expression *expression, tree left, tree right) - { - location_t expression_location = get_location(&expression->position()); - tree left_type = TREE_TYPE(left); - tree right_type = TREE_TYPE(right); - tree_code logical_code, bit_code; - - if (expression->operation() == boot::binary_operator::conjunction) - { - bit_code = BIT_AND_EXPR; - logical_code = TRUTH_ANDIF_EXPR; - } - else if (expression->operation() == boot::binary_operator::disjunction) - { - bit_code = BIT_IOR_EXPR; - logical_code = TRUTH_ORIF_EXPR; - } - else if (expression->operation() == boot::binary_operator::exclusive_disjunction) - { - bit_code = BIT_XOR_EXPR; - logical_code = TRUTH_XOR_EXPR; - } - else - { - gcc_unreachable(); - } - if (left_type == elna_bool_type_node) - { - return build2_loc(expression_location, logical_code, elna_bool_type_node, left, right); - } - else if (is_integral_type(left_type)) - { - return build2_loc(expression_location, bit_code, left_type, left, right); - } - else - { - error_at(expression_location, "Invalid operands of type '%s' and '%s' for operator %s", - print_type(left_type).c_str(), print_type(right_type).c_str(), - boot::print_binary_operator(expression->operation())); - return error_mark_node; - } + this->current_expression = build_constructor(slice_type, elms); } tree generic_visitor::build_equality_operation(boot::binary_expression *expression, tree left, tree right) @@ -555,26 +526,7 @@ namespace elna::gcc { gcc_unreachable(); } - if (TREE_TYPE(left) == elna_string_type_node) - { - tree lhs_length = build3(COMPONENT_REF, TREE_TYPE(elna_string_length_field_node), - left, elna_string_length_field_node, NULL_TREE); - tree lhs_ptr = build3(COMPONENT_REF, TREE_TYPE(elna_string_ptr_field_node), - left, elna_string_ptr_field_node, NULL_TREE); - - tree rhs_length = build3(COMPONENT_REF, TREE_TYPE(elna_string_length_field_node), - right, elna_string_length_field_node, NULL_TREE); - tree rhs_ptr = build3(COMPONENT_REF, TREE_TYPE(elna_string_ptr_field_node), - right, elna_string_ptr_field_node, NULL_TREE); - - tree length_equality = build2(equality_code, elna_bool_type_node, lhs_length, rhs_length); - tree memcmp_call = call_built_in(UNKNOWN_LOCATION, "__builtin_memcmp", integer_type_node, - lhs_ptr, rhs_ptr, lhs_length); - tree equals_zero = build2(equality_code, elna_bool_type_node, memcmp_call, integer_zero_node); - - return build2(combination_code, elna_bool_type_node, length_equality, equals_zero); - } - else if (expression->lhs().type_decoration.get<boot::slice_type>() != nullptr) + if (expression->lhs().type_decoration.get<boot::slice_type>() != nullptr) { tree lhs_ptr_field = TYPE_FIELDS(TREE_TYPE(left)); tree lhs_length_field = TREE_CHAIN(lhs_ptr_field); @@ -600,7 +552,8 @@ namespace elna::gcc return build2(combination_code, elna_bool_type_node, length_equality, equals_zero); } - else if (TREE_CODE(TREE_TYPE(left)) == RECORD_TYPE) + else if (expression->lhs().type_decoration.get<boot::record_type>() != nullptr + || expression->lhs().type_decoration.get<boot::array_type>() != nullptr) { tree size = fold_convert(elna_word_type_node, size_in_bytes(TREE_TYPE(left))); @@ -636,84 +589,90 @@ namespace elna::gcc { this->current_expression = do_pointer_arithmetic(expression->operation(), left, right, expression_location); - if (this->current_expression == error_mark_node) - { - error_at(expression_location, - "invalid operation %s on a pointer and an integral type", - boot::print_binary_operator(expression->operation())); - } - else if (TREE_TYPE(this->current_expression) == ssizetype) + if (TREE_TYPE(this->current_expression) == ssizetype) { this->current_expression = fold_convert(elna_int_type_node, this->current_expression); } return; } - if (left_type != right_type - && !are_compatible_pointers(left_type, right) - && !are_compatible_pointers(right_type, left) - && !(is_integral_type(left_type) && right_type == elna_word_type_node)) - { - error_at(expression_location, - "invalid operands of type '%s' and '%s' for operator %s", - print_type(left_type).c_str(), print_type(right_type).c_str(), - boot::print_binary_operator(expression->operation())); - this->current_expression = error_mark_node; - return; - } switch (expression->operation()) { using enum boot::binary_operator; case sum: - this->current_expression = build_arithmetic_operation(expression, PLUS_EXPR, left, right); + this->current_expression = fold_build2_loc(expression_location, + PLUS_EXPR, left_type, left, right); break; case subtraction: - this->current_expression = build_arithmetic_operation(expression, MINUS_EXPR, left, right); + this->current_expression = fold_build2_loc(expression_location, + MINUS_EXPR, left_type, left, right); break; case division: - this->current_expression = build_arithmetic_operation(expression, TRUNC_DIV_EXPR, left, right); + this->current_expression = fold_build2_loc(expression_location, + TRUNC_DIV_EXPR, left_type, left, right); break; case remainder: - this->current_expression = build_arithmetic_operation(expression, TRUNC_MOD_EXPR, left, right); + this->current_expression = fold_build2_loc(expression_location, + TRUNC_MOD_EXPR, left_type, left, right); break; case multiplication: - this->current_expression = build_arithmetic_operation(expression, MULT_EXPR, left, right); + this->current_expression = fold_build2_loc(expression_location, + MULT_EXPR, left_type, left, right); break; case less: - this->current_expression = build_comparison_operation(expression, LT_EXPR, left, right); + this->current_expression = fold_build2_loc(expression_location, + LT_EXPR, elna_bool_type_node, left, right); break; case greater: - this->current_expression = build_comparison_operation(expression, GT_EXPR, left, right); + this->current_expression = fold_build2_loc(expression_location, + GT_EXPR, elna_bool_type_node, left, right); break; case less_equal: - this->current_expression = build_comparison_operation(expression, LE_EXPR, left, right); + this->current_expression = fold_build2_loc(expression_location, + LE_EXPR, elna_bool_type_node, left, right); break; case greater_equal: - this->current_expression = build_comparison_operation(expression, GE_EXPR, left, right); + this->current_expression = fold_build2_loc(expression_location, + GE_EXPR, elna_bool_type_node, left, right); break; case conjunction: - this->current_expression = build_bit_logic_operation(expression, left, right); - break; case disjunction: - this->current_expression = build_bit_logic_operation(expression, left, right); - break; case exclusive_disjunction: - this->current_expression = build_bit_logic_operation(expression, left, right); + gcc_unreachable(); + case logical_conjunction: + this->current_expression = build2_loc(expression_location, + TRUTH_ANDIF_EXPR, elna_bool_type_node, left, right); break; - case equals: - this->current_expression = build_equality_operation(expression, left, right); + case logical_disjunction: + this->current_expression = build2_loc(expression_location, + TRUTH_ORIF_EXPR, elna_bool_type_node, left, right); break; + case logical_exclusive_disjunction: + this->current_expression = build2_loc(expression_location, + TRUTH_XOR_EXPR, elna_bool_type_node, left, right); + break; + case bitwise_conjunction: + this->current_expression = build2_loc(expression_location, + BIT_AND_EXPR, left_type, left, right); + break; + case bitwise_disjunction: + this->current_expression = build2_loc(expression_location, + BIT_IOR_EXPR, left_type, left, right); + break; + case bitwise_exclusive_disjunction: + this->current_expression = build2_loc(expression_location, + BIT_XOR_EXPR, left_type, left, right); + break; + case equals: case not_equals: this->current_expression = build_equality_operation(expression, left, right); break; case shift_left: - this->current_expression = build_binary_operation( - is_numeric_type(left_type) && right_type == elna_word_type_node, - expression, LSHIFT_EXPR, left, right, left_type); + this->current_expression = fold_build2_loc(expression_location, + LSHIFT_EXPR, left_type, left, right); break; case shift_right: - this->current_expression = build_binary_operation( - is_numeric_type(left_type) && right_type == elna_word_type_node, - expression, RSHIFT_EXPR, left, right, left_type); + this->current_expression = fold_build2_loc(expression_location, + RSHIFT_EXPR, left_type, left, right); break; } } @@ -734,22 +693,14 @@ namespace elna::gcc build_pointer_type(TREE_TYPE(this->current_expression))); TREE_NO_TRAMPOLINE(this->current_expression) = 1; break; - case negation: - { - tree operand_type = TREE_TYPE(this->current_expression); - - if (operand_type == elna_bool_type_node) - { - this->current_expression = build1_loc(location, TRUTH_NOT_EXPR, - boolean_type_node, this->current_expression); - } - else - { - this->current_expression = build1_loc(location, BIT_NOT_EXPR, - operand_type, this->current_expression); - } + case bitwise_negation: + this->current_expression = build1_loc(location, BIT_NOT_EXPR, + TREE_TYPE(this->current_expression), this->current_expression); + break; + case logical_negation: + this->current_expression = build1_loc(location, TRUTH_NOT_EXPR, + boolean_type_node, this->current_expression); break; - } case minus: { tree operand_type = TREE_TYPE(this->current_expression); @@ -761,45 +712,11 @@ namespace elna::gcc case plus: // Identity: operand already in current_expression. break; + default: + gcc_unreachable(); } } - static tree constant_to_tree(const boot::constant_value& constant_value) - { - if (std::holds_alternative<std::int32_t>(constant_value)) - { - return build_int_cst(elna_int_type_node, std::get<std::int32_t>(constant_value)); - } - else if (std::holds_alternative<std::uint32_t>(constant_value)) - { - return build_int_cst(elna_word_type_node, std::get<std::uint32_t>(constant_value)); - } - - else if (std::holds_alternative<double>(constant_value)) - { - auto real_value = std::get<double>(constant_value); - REAL_VALUE_TYPE real; - HOST_WIDE_INT target_bits[(sizeof(double) + sizeof(HOST_WIDE_INT) - 1) - / sizeof(HOST_WIDE_INT)]; - std::memcpy(target_bits, &real_value, sizeof(real_value)); - real_from_target(&real, target_bits, REAL_MODE_FORMAT(TYPE_MODE(elna_float_type_node))); - return build_real(elna_float_type_node, real); - } - else if (std::holds_alternative<bool>(constant_value)) - { - return std::get<bool>(constant_value) ? boolean_true_node : boolean_false_node; - } - else if (std::holds_alternative<unsigned char>(constant_value)) - { - return build_int_cst(elna_char_type_node, std::get<unsigned char>(constant_value)); - } - else if (std::holds_alternative<std::nullptr_t>(constant_value)) - { - return elna_pointer_nil_node; - } - return NULL_TREE; - } - void generic_visitor::visit(boot::variable_declaration *declaration) { for (const auto& variable_identifier : declaration->identifiers) @@ -865,7 +782,7 @@ namespace elna::gcc DECL_CONTEXT(declaration_tree) = current_function_decl; f_names = chainon(f_names, declaration_tree); - auto declaration_statement = build1_loc(declaration_location, DECL_EXPR, + auto *declaration_statement = build1_loc(declaration_location, DECL_EXPR, void_type_node, declaration_tree); append_statement(declaration_statement); } @@ -909,12 +826,9 @@ namespace elna::gcc this->current_expression = build4_loc(location, ARRAY_REF, element_type, designator, offset, size_one_node, NULL_TREE); } - else if (TREE_TYPE(designator) == elna_string_type_node - || expression->base().type_decoration.get<boot::slice_type>() != nullptr) + else if (expression->base().type_decoration.get<boot::slice_type>() != nullptr) { - tree ptr_field = (TREE_TYPE(designator) == elna_string_type_node) - ? elna_string_ptr_field_node - : TYPE_FIELDS(TREE_TYPE(designator)); + tree ptr_field = TYPE_FIELDS(TREE_TYPE(designator)); offset = build2(MINUS_EXPR, elna_word_type_node, offset, size_one_node); tree slice_ptr = build3_loc(location, COMPONENT_REF, TREE_TYPE(ptr_field), designator, ptr_field, NULL_TREE); diff --git a/gcc/gcc/elna-tree.cc b/gcc/gcc/elna-tree.cc index 0227433..ab7363f 100644 --- a/gcc/gcc/elna-tree.cc +++ b/gcc/gcc/elna-tree.cc @@ -53,15 +53,6 @@ namespace elna::gcc return INTEGRAL_TYPE_P(type) || POINTER_TYPE_P(type) || TREE_CODE(type) == REAL_TYPE; } - bool are_compatible_pointers(tree lhs_type, tree rhs) - { - gcc_assert(TYPE_P(lhs_type)); - tree rhs_type = TREE_TYPE(rhs); - - return (POINTER_TYPE_P(lhs_type) && rhs == elna_pointer_nil_node) - || (POINTER_TYPE_P(lhs_type) && lhs_type == rhs_type); - } - tree prepare_rvalue(tree rvalue) { if (DECL_P(rvalue) && TREE_CODE(TREE_TYPE(rvalue)) == FUNCTION_TYPE) @@ -74,12 +65,6 @@ namespace elna::gcc } } - bool is_assignable_from(tree assignee, tree assignment) - { - return get_qualified_type(TREE_TYPE(assignment), TYPE_UNQUALIFIED) == assignee - || are_compatible_pointers(assignee, assignment); - } - void append_statement(tree statement_tree) { if (!vec_safe_is_empty(f_binding_level->defers)) @@ -187,27 +172,6 @@ namespace elna::gcc gcc_unreachable(); } - tree build_binary_operation(bool condition, boot::binary_expression *expression, - tree_code operator_code, tree left, tree right, tree target_type) - { - location_t expression_location = get_location(&expression->position()); - tree left_type = get_qualified_type(TREE_TYPE(left), TYPE_UNQUALIFIED); - tree right_type = get_qualified_type(TREE_TYPE(right), TYPE_UNQUALIFIED); - - if (condition) - { - return fold_build2_loc(expression_location, operator_code, target_type, left, right); - } - else - { - error_at(expression_location, - "invalid operands of type '%s' and '%s' for operator %s", - print_type(left_type).c_str(), print_type(right_type).c_str(), - boot::print_binary_operator(expression->operation())); - return error_mark_node; - } - } - tree find_field_by_name(location_t expression_location, tree type, const std::string& field_name) { if (type == error_mark_node) diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h index 9cc1419..6080155 100644 --- a/include/elna/boot/ast.h +++ b/include/elna/boot/ast.h @@ -43,6 +43,12 @@ namespace elna::boot disjunction, conjunction, exclusive_disjunction, + logical_conjunction, + logical_disjunction, + logical_exclusive_disjunction, + bitwise_conjunction, + bitwise_disjunction, + bitwise_exclusive_disjunction, shift_left, shift_right }; @@ -51,6 +57,8 @@ namespace elna::boot { reference, negation, + bitwise_negation, + logical_negation, minus, plus }; @@ -638,18 +646,18 @@ namespace elna::boot class slicing_expression : public designator_expression { expression *m_base; - expression *m_offset; - expression *m_length; + expression *m_start; + expression *m_end; public: slicing_expression(const source_position position, - expression *base, expression *offset, expression *length); + expression *base, expression *start, expression *end); void accept(parser_visitor *visitor) override; slicing_expression *is_slicing() override; expression& base(); - expression& offset(); - expression& length(); + expression& start(); + expression& end(); ~slicing_expression() override; }; @@ -882,6 +890,7 @@ namespace elna::boot expression& lhs(); expression& rhs(); binary_operator operation() const; + void operation(binary_operator operation); ~binary_expression() override; }; @@ -900,6 +909,7 @@ namespace elna::boot expression& operand(); unary_operator operation() const; + void operation(unary_operator operation); ~unary_expression() override; }; diff --git a/include/elna/boot/name_analysis.h b/include/elna/boot/name_analysis.h index bb66c7a..5afe102 100644 --- a/include/elna/boot/name_analysis.h +++ b/include/elna/boot/name_analysis.h @@ -20,11 +20,11 @@ along with GCC; see the file COPYING3. If not see #include <string> #include <memory> #include <map> +#include <optional> #include "elna/boot/ast.h" #include "elna/boot/result.h" #include "elna/boot/symbol.h" -#include "elna/boot/type_check.h" namespace elna::boot { @@ -93,6 +93,55 @@ namespace elna::boot }; /** + * Access to a field that does not exist in the given type. + */ + class field_not_found_error : public error + { + std::string field_name; + type composite_type; + + public: + field_not_found_error(const identifier& field_name, + type composite_type); + + std::string what() const override; + }; + + /** + * Field with the same name is already declared in this type. + */ + class duplicate_member_error : public error + { + std::string member_name; + type aggregate; + std::optional<source_position> original; + std::optional<std::string> base_name; + + public: + duplicate_member_error(const boot::identifier& member_name, + type aggregate, std::optional<source_position> original = std::nullopt, + std::optional<std::string> base_name = std::nullopt); + + std::string what() const override; + + std::optional<std::pair<std::string, source_position>> note() const override; + }; + + /** + * Trait is not applicable to the given type. + */ + class unsupported_trait_type_error : public error + { + type actual; + std::string trait_name; + + public: + unsupported_trait_type_error(const identifier& trait, type actual); + + std::string what() const override; + }; + + /** * Origin of a field in a composite type. */ struct field_origin @@ -120,6 +169,8 @@ namespace elna::boot type lookup_primitive_type(const std::string& name); type lookup_field(const type& composite_type, const std::string& field_name); + std::optional<type> lookup_pointer_like_field(const std::string& field_name, + const type& element_type); public: name_analysis_visitor(symbol_bag bag); diff --git a/include/elna/boot/type_check.h b/include/elna/boot/type_check.h index 32da10c..1680077 100644 --- a/include/elna/boot/type_check.h +++ b/include/elna/boot/type_check.h @@ -59,38 +59,6 @@ namespace elna::boot /** * Attempted to access a field that does not exist on the given type. */ - class field_not_found_error : public error - { - std::string field_name; - type composite_type; - - public: - field_not_found_error(const identifier& field_name, - type composite_type); - - std::string what() const override; - }; - - /** - * Field with the same name is already declared in this type. - */ - class duplicate_member_error : public error - { - std::string member_name; - type aggregate; - std::optional<source_position> original; - std::optional<std::string> base_name; - - public: - duplicate_member_error(const boot::identifier& member_name, - type aggregate, std::optional<source_position> original = std::nullopt, - std::optional<std::string> base_name = std::nullopt); - - std::string what() const override; - - std::optional<std::pair<std::string, source_position>> note() const override; - }; - /** * Cyclic type declaration. */ @@ -133,8 +101,8 @@ namespace elna::boot }; /** - * Argument count in a procedure or record call doesn't match - * the expected number of parameters or fields. + * Argument count in a procedure call or array constructor doesn't match + * the expected number of parameters or elements. */ class argument_count_error : public error { @@ -152,17 +120,6 @@ namespace elna::boot * Type passed to a trait like #min or #max does not support * the trait. */ - class unsupported_trait_type_error : public error - { - type actual; - std::string trait_name; - - public: - unsupported_trait_type_error(const identifier& trait, type actual); - - std::string what() const override; - }; - /** * A module-level variable initializer must be a constant expression. */ @@ -192,6 +149,22 @@ namespace elna::boot }; /** + * A binary operator is applied to unsupported types. + */ + class binary_operation_error : public error + { + type left; + type right; + binary_operator op; + + public: + binary_operation_error(const source_position position, + type left, type right, binary_operator operation); + + std::string what() const override; + }; + + /** * Chain of responsibility for type compatibility checks. * * Populate \c ctx with pre-resolved types, then call \c run(). @@ -220,6 +193,7 @@ namespace elna::boot verdict check_exact_match() const; verdict check_pointer_hatch() const; verdict check_pointer_conversion() const; + verdict check_slice_conversion() const; bool run(); }; @@ -261,5 +235,6 @@ namespace elna::boot void visit(array_constructor_expression *expression) override; void visit(slicing_expression *expression) override; void visit(unary_expression *expression) override; + void visit(binary_expression *expression) override; }; } diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h index 1d98675..04b91fb 100644 --- a/include/elna/gcc/elna-generic.h +++ b/include/elna/gcc/elna-generic.h @@ -45,14 +45,12 @@ namespace elna::gcc void enter_scope(); tree leave_scope(); + void begin_function(tree fndecl); + void finish_function(tree fndecl); + tree make_if_branch(boot::conditional_statements& branch, tree next, tree goto_append = NULL_TREE); - tree build_arithmetic_operation(boot::binary_expression *expression, - tree_code operator_code, tree left, tree right); - tree build_comparison_operation(boot::binary_expression *expression, - tree_code operator_code, tree left, tree right); - tree build_bit_logic_operation(boot::binary_expression *expression, tree left, tree right); tree build_equality_operation(boot::binary_expression *expression, tree left, tree right); void build_procedure_call(location_t call_location, tree procedure_address, const std::vector<boot::expression *>& arguments); diff --git a/include/elna/gcc/elna-tree.h b/include/elna/gcc/elna-tree.h index 556d52b..d826f25 100644 --- a/include/elna/gcc/elna-tree.h +++ b/include/elna/gcc/elna-tree.h @@ -47,13 +47,6 @@ namespace elna::gcc bool is_castable_type(tree type); /** - * \param lhs Left hand value. - * \param rhs Right hand value. - * \return Whether rhs can be assigned to lhs. - */ - bool are_compatible_pointers(tree lhs_type, tree rhs); - - /** * Prepares a value to be bound to a variable or parameter. * * If rvalue is a procedure declaration, builds a procedure pointer. @@ -63,23 +56,12 @@ namespace elna::gcc */ tree prepare_rvalue(tree rvalue); - /** - * \param assignee Assignee. - * \param assignee Assignment. - * \return Whether an expression assignment can be assigned to a variable of type assignee. - */ - bool is_assignable_from(tree assignee, tree assignment); - void append_statement(tree statement_tree); void defer(tree statement_tree); tree chain_defer(); tree do_pointer_arithmetic(boot::binary_operator binary_operator, tree left, tree right, location_t expression_location); - tree build_binary_operation(bool condition, boot::binary_expression *expression, - tree_code operator_code, tree left, tree right, tree target_type); - tree build_arithmetic_operation(boot::binary_expression *expression, - tree_code operator_code, tree left, tree right); 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 build_static_array_type(tree type, const std::uint64_t size); diff --git a/include/elna/gcc/elna1.h b/include/elna/gcc/elna1.h index 3f48af5..b05cdf2 100644 --- a/include/elna/gcc/elna1.h +++ b/include/elna/gcc/elna1.h @@ -25,12 +25,9 @@ enum elna_tree_index ELNA_TI_BOOL_TYPE, ELNA_TI_POINTER_TYPE, ELNA_TI_FLOAT_TYPE, - ELNA_TI_STRING_TYPE, ELNA_TI_BOOL_TRUE, ELNA_TI_BOOL_FALSE, ELNA_TI_POINTER_NIL, - ELNA_TI_STRING_PTR_FIELD, - ELNA_TI_STRING_LENGTH_FIELD, ELNA_TI_MAX }; @@ -44,13 +41,9 @@ extern std::vector<std::string> elna_include_dirs; #define elna_bool_type_node elna_global_trees[ELNA_TI_BOOL_TYPE] #define elna_pointer_type_node elna_global_trees[ELNA_TI_POINTER_TYPE] #define elna_float_type_node elna_global_trees[ELNA_TI_FLOAT_TYPE] -#define elna_string_type_node elna_global_trees[ELNA_TI_STRING_TYPE] #define elna_bool_true_node elna_global_trees[ELNA_TI_BOOL_TRUE] #define elna_bool_false_node elna_global_trees[ELNA_TI_BOOL_FALSE] #define elna_pointer_nil_node elna_global_trees[ELNA_TI_POINTER_NIL] -#define elna_string_ptr_field_node elna_global_trees[ELNA_TI_STRING_PTR_FIELD] -#define elna_string_length_field_node elna_global_trees[ELNA_TI_STRING_LENGTH_FIELD] - /* Language-dependent contents of a type. */ struct GTY (()) lang_type { diff --git a/rakelib/gcc.rake b/rakelib/gcc.rake index ce02bb6..abdeb6d 100644 --- a/rakelib/gcc.rake +++ b/rakelib/gcc.rake @@ -147,7 +147,11 @@ namespace :gcc do desc 'Run tests' task :check do + log_file = Pathname.new(HOST_GCC) + 'gcc/testsuite/elna/elna.log' + sh 'make', 'check-elna', chdir: File.join(HOST_GCC, 'gcc') + + fail "\nSee #{log_file}." if log_file.file? and log_file.read =~ /^(FAIL|XPASS|UNRESOLVED|ERROR):/ end desc 'Run clang-tidy' @@ -165,6 +169,9 @@ namespace :gcc do task :doc do sh 'make', 'html', chdir: File.join(HOST_GCC, 'gcc') end + + desc 'Run GCC linters and tests' + task test: %w[gcc:tidy gcc:check] end desc 'Build the bootstrap compiler' diff --git a/source/common.elna b/source/common.elna index b6f85f1..1bdfa1f 100644 --- a/source/common.elna +++ b/source/common.elna @@ -17,7 +17,7 @@ type proc write*(fd: Int; buf: Pointer; Word: Int): Int extern -proc write_s*(value: String) +proc write_s*(value: []const Char) begin (* fwrite(cast(value.ptr: Pointer), value.length, 1u, stdout) *) write(1, cast(value.ptr: Pointer), cast(value.length: Int)) @@ -78,7 +78,7 @@ begin return nil (* Returns true or false depending whether two strings are equal. *) -proc string_compare*(lhs_pointer: ^Char; lhs_length: Word; rhs_pointer: String): Bool +proc string_compare*(lhs_pointer: ^Char; lhs_length: Word; rhs_pointer: []const Char): Bool var result: Bool begin diff --git a/source/cstdio.elna b/source/cstdio.elna index 15c467e..f44a972 100644 --- a/source/cstdio.elna +++ b/source/cstdio.elna @@ -10,7 +10,7 @@ var stdout*: ^FILE := extern stderr*: ^FILE := extern -proc fopen*(pathname: ^Char; mode: ^Char): ^FILE +proc fopen*(pathname, mode: ^const Char): ^FILE extern proc fclose*(stream: ^FILE): Int @@ -31,28 +31,28 @@ extern proc fread*(ptr: Pointer; size: Word; nmemb: Word; stream: ^FILE): Word extern -proc fwrite*(ptr: Pointer; size: Word; nitems: Word; stream: ^FILE): Word +proc fwrite*(ptr: const Pointer; size: Word; nitems: Word; stream: ^FILE): Word extern proc fputc*(c: Int; stream: ^FILE): Word extern -proc perror*(s: ^Char) +proc perror*(s: ^const Char) extern -proc puts*(s: ^Char): Int +proc puts*(s: ^const Char): Int extern proc putchar*(c: Int): Int extern -proc sprintf*(str: Pointer; format: ^Char; number: Word): Int +proc sprintf*(str: Pointer; format: ^const Char; number: Word): Int extern -proc fprintf*(stream: Pointer; format: ^Char; number: Word): Int +proc fprintf*(stream: Pointer; format: ^const Char; number: Word): Int extern -proc fdopen*(fildes: Int; mode: ^Char): Pointer +proc fdopen*(fildes: Int; mode: ^const Char): Pointer extern end. diff --git a/source/cstring.elna b/source/cstring.elna index e21056a..0b4bd90 100644 --- a/source/cstring.elna +++ b/source/cstring.elna @@ -5,25 +5,25 @@ proc memset*(ptr: Pointer; c: Int; n: Word): ^Char extern -proc memcpy*(dst, src: Pointer; n: Word) +proc memcpy*(dst: Pointer; src: const Pointer; n: Word) extern -proc memcmp*(s1, s2: Pointer; n: Word): Int +proc memcmp*(s1, s2: const Pointer; n: Word): Int extern -proc strcmp*(s1: ^Char; s2: ^Char): Int +proc strcmp*(s1: ^const Char; s2: ^const Char): Int extern -proc strncmp*(s1: ^Char; s2: ^Char; n: Word): Int +proc strncmp*(s1: ^const Char; s2: ^const Char; n: Word): Int extern -proc strncpy*(dst: ^Char; src: ^Char; dsize: Word): ^Char +proc strncpy*(dst: ^Char; src: ^const Char; dsize: Word): ^Char extern -proc strcpy*(dst: ^Char; src: ^Char): ^Char +proc strcpy*(dst: ^Char; src: ^const Char): ^Char extern -proc strlen*(ptr: ^Char): Word +proc strlen*(ptr: ^const Char): Word extern end. diff --git a/source/lexer.elna b/source/lexer.elna index 9e1db21..fa79917 100644 --- a/source/lexer.elna +++ b/source/lexer.elna @@ -28,7 +28,7 @@ type ) ElnaLexerToken* = record kind: ElnaLexerKind; - start: String; (* DEPRECATED *) + start: []const Char; (* DEPRECATED *) position: ElnaPosition end ElnaLexerBooleanToken* = record(ElnaLexerToken) @@ -38,7 +38,7 @@ type value: Char end ElnaLexerStringToken* = record(ElnaLexerToken) - value: String + value: []const Char end ElnaLexerIntegerToken* = record(ElnaLexerToken) value: Int @@ -540,7 +540,7 @@ begin result_length := cast(position_end - position_start: Word); result := elna_lexer_token_create(ElnaLexerKind.identifier, position); - result^.start := String(position_start, result_length); + result^.start := position_start[1 to result_length]; if position_start^ = '#' then result^.kind := ElnaLexerKind.trait @@ -601,7 +601,7 @@ begin elsif delimiter = '"' then result := elna_lexer_token_create(ElnaLexerKind.string, position) end; - result^.start := String(start_position, cast(end_position - start_position: Word)) + result^.start := start_position[1 to cast(end_position - start_position: Word)] return result proc elna_lexer_classify_integer(start_position, end_position: ^Char; position: ^ElnaPosition): ^ElnaLexerToken @@ -609,7 +609,7 @@ var result: ^ElnaLexerToken begin result := elna_lexer_token_create(ElnaLexerKind.integer, position); - result^.start := String(start_position, cast(end_position - start_position: Word)) + result^.start := start_position[1 to cast(end_position - start_position: Word)] return result proc elna_lexer_classify_finalize(start_position: ^Char; position: ^ElnaPosition): ^ElnaLexerToken diff --git a/source/main.elna b/source/main.elna index 4a23898..c127ba4 100644 --- a/source/main.elna +++ b/source/main.elna @@ -40,19 +40,13 @@ var proc reallocarray(ptr: Pointer; n: Word; size: Word): Pointer return realloc(ptr, n * size) -proc substring(string: String; start: Word; count: Word): String -return String(string.ptr + start, count) - -proc open_substring(string: String; start: Word): String -return substring(string, start, string.length - start) - -proc string_dup(origin: String): String +proc string_dup(origin: []const Char): []const Char var copy: ^Char begin copy := malloc(origin.length); strncpy(copy, origin.ptr, origin.length) -return String(copy, origin.length) +return copy[1 to origin.length] proc string_buffer_new(): StringBuffer var @@ -78,11 +72,11 @@ begin buffer^.size := buffer^.size - count return -proc string_buffer_clear(buffer: ^StringBuffer): String +proc string_buffer_clear(buffer: ^StringBuffer): []const Char var - result: String + result: []const Char begin - result := String(cast(buffer^.data: ^Char), buffer^.size); + result := cast(buffer^.data: ^Char)[1 to buffer^.size]; buffer^.size := 0u return result @@ -300,7 +294,7 @@ begin return (* Categorize an identifier. *) -proc lexer_categorize(token_content: String): ^ElnaLexerToken +proc lexer_categorize(token_content: []const Char): ^ElnaLexerToken var current_token: ^ElnaLexerToken begin diff --git a/testsuite/runnable/record_extension.elna b/testsuite/runnable/record_extension.elna index 1aae5b3..47ac908 100644 --- a/testsuite/runnable/record_extension.elna +++ b/testsuite/runnable/record_extension.elna @@ -3,7 +3,7 @@ type kind: Word end ElnaLexerStringToken = record(ElnaLexerToken) - value: String + value: []const Char end var diff --git a/testsuite/runnable/return_aggregate.elna b/testsuite/runnable/return_aggregate.elna index e162498..90c1677 100644 --- a/testsuite/runnable/return_aggregate.elna +++ b/testsuite/runnable/return_aggregate.elna @@ -5,8 +5,8 @@ type end proc f(): R -return R{a: 1, b: 2} +return R{ a: 1, b: 2 } begin - assert(f() = R{a: 1, b: 2}) + assert(f() = R{ a: 1, b: 2 }) end. diff --git a/testsuite/runnable/slice_array.elna b/testsuite/runnable/slice_array.elna new file mode 100644 index 0000000..af0a270 --- /dev/null +++ b/testsuite/runnable/slice_array.elna @@ -0,0 +1,11 @@ +var + array: [3]Int := [3]Int{ 2, 4, 6 } + slice: []Int + +begin + slice := array[1 to 3]; + + assert(slice.length = 3u); + assert(slice[1] = 2); + assert(slice[3] = 6) +end. diff --git a/testsuite/runnable/slice_pointer.elna b/testsuite/runnable/slice_pointer.elna new file mode 100644 index 0000000..d053f51 --- /dev/null +++ b/testsuite/runnable/slice_pointer.elna @@ -0,0 +1,11 @@ +var + array: [3]Int := [3]Int{ 2, 4, 6 } + slice: []Int + +begin + slice := array.ptr[1 to 3]; + + assert(slice.length = 3u); + assert(slice[1] = 2); + assert(slice[3] = 6) +end. diff --git a/testsuite/runnable/slice_slice.elna b/testsuite/runnable/slice_slice.elna new file mode 100644 index 0000000..d6f309f --- /dev/null +++ b/testsuite/runnable/slice_slice.elna @@ -0,0 +1,13 @@ +var + array: [5]Int := [5]Int{ 2, 4, 6, 8, 10 } + slice1, slice2: []Int + +begin + slice1 := array.ptr[1 to array.length]; + slice2 := slice1[2 to 4]; + + assert(slice2.length = 3u); + assert(slice2[1] = 4); + assert(slice2[2] = 6); + assert(slice2[3] = 8) +end. |
