diff options
| author | Eugen Wissner <belka@caraus.de> | 2026-09-11 09:11:47 +0200 |
|---|---|---|
| committer | Eugen Wissner <belka@caraus.de> | 2026-09-11 09:11:47 +0200 |
| commit | bd2b80d6ac582b9f0bcd96c86d4b79bfdb64eca1 (patch) | |
| tree | a835a5cfe5a9cf48720b9255261e065fd7978849 | |
| parent | b27872d5c5bfecae74195771f5c1f4e73385b6d6 (diff) | |
| download | elna-bd2b80d6ac582b9f0bcd96c86d4b79bfdb64eca1.tar.gz | |
Implement aligned attribute
| -rw-r--r-- | boot/ast.cc | 69 | ||||
| -rw-r--r-- | boot/evaluator.cc | 14 | ||||
| -rw-r--r-- | boot/materialization.cc | 48 | ||||
| -rw-r--r-- | boot/name_analysis.cc | 119 | ||||
| -rw-r--r-- | boot/parser.yy | 76 | ||||
| -rw-r--r-- | boot/result.cc | 33 | ||||
| -rw-r--r-- | boot/symbol.cc | 9 | ||||
| -rw-r--r-- | boot/type_check.cc | 28 | ||||
| -rw-r--r-- | gcc/gcc/elna-builtins.cc | 13 | ||||
| -rw-r--r-- | gcc/gcc/elna-generic.cc | 13 | ||||
| -rw-r--r-- | include/elna/boot/ast.h | 55 | ||||
| -rw-r--r-- | include/elna/boot/materialization.h | 13 | ||||
| -rw-r--r-- | include/elna/boot/name_analysis.h | 40 | ||||
| -rw-r--r-- | include/elna/boot/result.h | 23 | ||||
| -rw-r--r-- | include/elna/boot/symbol.h | 21 | ||||
| -rw-r--r-- | testsuite/fail_compilation/aligned_attribute.elna | 20 | ||||
| -rw-r--r-- | testsuite/fail_compilation/local_var_exported.elna | 2 | ||||
| -rw-r--r-- | testsuite/runnable/aligned_attribute.elna | 41 |
18 files changed, 505 insertions, 132 deletions
diff --git a/boot/ast.cc b/boot/ast.cc index 9dbcf7f..08d3a78 100644 --- a/boot/ast.cc +++ b/boot/ast.cc @@ -17,6 +17,7 @@ along with GCC; see the file COPYING3. If not see #include "elna/boot/ast.h" +#include <algorithm> #include <utility> namespace elna::boot @@ -588,6 +589,74 @@ namespace elna::boot return nullptr; } + attribute::attribute(identifier&& name, std::vector<expression *> arguments) + : m_name(std::move(name)), m_arguments(std::move(arguments)) + { + } + + attribute::attribute(attribute&& that) noexcept + : m_name(std::move(that.m_name)), m_arguments(std::move(that.m_arguments)) + { + } + + attribute::~attribute() + { + for (const expression *argument : this->arguments()) + { + delete argument; + } + } + + attribute& attribute::operator=(attribute&& that) noexcept + { + std::swap(this->m_name, that.m_name); + std::swap(this->m_arguments, that.m_arguments); + + return *this; + } + + const identifier& attribute::name() const + { + return this->m_name; + } + + const std::vector<expression *>& attribute::arguments() const + { + return this->m_arguments; + } + + identifier_definition::identifier_definition(const std::string& name, + const source_position& position, const bool exported, std::vector<attribute>&& attributes) + : attributes(std::move(attributes)), m_identifier(name, position), m_exported(exported) + { + } + + const std::string& identifier_definition::name() const + { + return this->m_identifier.name(); + } + + const identifier& identifier_definition::id() const + { + return this->m_identifier; + } + + bool identifier_definition::exported() const + { + return this->m_exported; + } + + std::vector<identifier> extract_identifiers(const std::vector<identifier_definition>& identifiers) + { + std::vector<identifier> result; + result.reserve(identifiers.size()); + + std::ranges::transform(identifiers, std::back_inserter(result), + [](const auto& identifier) { return identifier.id(); }); + + return result; + } + named_expression *type_expression::is_named() { return nullptr; diff --git a/boot/evaluator.cc b/boot/evaluator.cc index 0a6ebe5..0085c62 100644 --- a/boot/evaluator.cc +++ b/boot/evaluator.cc @@ -139,20 +139,26 @@ namespace elna::boot { for (auto const& field : current_record->fields) { - auto props = get_type_properties(field.second, target); + auto props = get_type_properties(field.second.field_type, target); if (!props.has_value()) { return std::nullopt; } - size = (size + props->alignment - 1) & ~(props->alignment - 1); + // #aligned replaces the field's own alignment outright, so that + // a smaller value packs it. + const std::size_t field_alignment = field.second.alignment.value_or(props->alignment); + + size = (size + field_alignment - 1) & ~(field_alignment - 1); size += props->size; - alignment = std::max(alignment, props->alignment); - current_offset = (current_offset + props->alignment - 1) & ~(props->alignment - 1); + alignment = std::max(alignment, field_alignment); + current_offset = (current_offset + field_alignment - 1) & ~(field_alignment - 1); result_map.insert(field.first, current_offset); current_offset += props->size; } } + alignment = subject->alignment.value_or(alignment); + return record_properties{ .offset_map = std::move(result_map), .size = (size + alignment - 1) & ~(alignment - 1), diff --git a/boot/materialization.cc b/boot/materialization.cc index 4b58ac2..5b200d5 100644 --- a/boot/materialization.cc +++ b/boot/materialization.cc @@ -33,6 +33,10 @@ namespace elna::boot return "Integer literal overflows"; case real_overflow: return "Real literal overflows"; + case local_export: + return "Local symbols cannot be exported"; + case field_export: + return "Record fields cannot be exported"; } __builtin_unreachable(); } @@ -42,6 +46,43 @@ namespace elna::boot { } + void materialization_visitor::visit(procedure_declaration *declaration) + { + this->in_global_scope = false; + walking_visitor::visit(declaration); + this->in_global_scope = true; + } + + void materialization_visitor::visit(variable_declaration *declaration) + { + walking_visitor::visit(declaration); + for (const auto& variable_identifier : declaration->identifiers) + { + if (!this->in_global_scope && variable_identifier.exported()) + { + add_error<materialization_error>(variable_identifier.id().position(), + materialization_error::kind::local_export); + } + } + } + + void materialization_visitor::visit(record_type_expression *expression) + { + for (const auto& [field_identifiers, field_type] : expression->fields) + { + field_type->accept(this); + + for (const auto& field_identifier : field_identifiers) + { + if (field_identifier.exported()) + { + add_error<materialization_error>(field_identifier.id().position(), + materialization_error::kind::field_export); + } + } + } + } + void materialization_visitor::visit(literal<integer_literal> *literal) { switch (literal->wants_signed) @@ -96,4 +137,11 @@ namespace elna::boot materialization_error::kind::real_overflow); } } + + void materialization_visitor::visit_entry_point(unit *unit) + { + this->in_global_scope = false; + walking_visitor::visit_entry_point(unit); + this->in_global_scope = true; + } } diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc index a979a37..caff18b 100644 --- a/boot/name_analysis.cc +++ b/boot/name_analysis.cc @@ -50,8 +50,6 @@ namespace elna::boot return "Symbol '" + this->name + "' not declared"; case not_a_type: return "'" + this->name + "' is not a type"; - case local_export: - return "Local symbol '" + this->name + "' cannot be exported"; default: __builtin_unreachable(); } @@ -59,6 +57,34 @@ namespace elna::boot }, this->payload); } + attribute_error::attribute_error(const source_position position, const std::string& name, + kind attribute_kind) + : diagnostic(position), name(name), m_kind(attribute_kind) + { + } + + std::string attribute_error::what() const + { + switch (this->m_kind) + { + using enum kind; + case unknown: + return "Attribute '#" + this->name + "' not declared"; + case duplicate: + return "Attribute '#" + this->name + "' specified more than once"; + case unsupported: + return "Attribute '#" + this->name + "' is not supported in this position"; + case argument_count: + return "Attribute '#" + this->name + "' takes exactly one argument"; + case not_constant: + return "Attribute '#" + this->name + "' requires a constant expression"; + case invalid_alignment: + return "Alignment must be a power of two"; + default: + __builtin_unreachable(); + } + } + std::optional<diagnostic_note> symbol_declaration_error::note() const { if (std::holds_alternative<redefinition>(this->payload)) @@ -306,6 +332,7 @@ namespace elna::boot for (const auto& parameter_name : parameter_names) { + evaluate_alignment(parameter_name.attributes, false); result_type.first.parameters.push_back(parameter_type); result_type.second.push_back(parameter_name.name()); } @@ -313,19 +340,71 @@ namespace elna::boot return result_type; } - ordered_map<type> resolving_visitor::build_composite_type(const std::vector<field_declaration>& fields, - ordered_map<field_origin>& field_names, const type& aggregate) + std::optional<std::size_t> resolving_visitor::evaluate_alignment( + const std::vector<attribute>& attributes, const bool supported) + { + std::optional<std::size_t> result; + + for (const attribute& written : attributes) + { + if (written.name().name() != "aligned") + { + add_error<attribute_error>(written.name().position(), written.name().name(), + attribute_error::kind::unknown); + } + else if (!supported) + { + add_error<attribute_error>(written.name().position(), written.name().name(), + attribute_error::kind::unsupported); + } + else if (written.arguments().size() != 1) + { + add_error<attribute_error>(written.name().position(), written.name().name(), + attribute_error::kind::argument_count); + } + else if (result.has_value()) + { + add_error<attribute_error>(written.name().position(), written.name().name(), + attribute_error::kind::duplicate); + } + else if (auto value = this->constant_evaluator.evaluate_index(*written.arguments().front())) + { + if (value.value() == 0 || (value.value() & (value.value() - 1)) != 0) + { + add_error<attribute_error>(written.arguments().front()->position(), + written.name().name(), attribute_error::kind::invalid_alignment); + } + else + { + result = value; + } + } + else + { + add_error<attribute_error>(written.arguments().front()->position(), + written.name().name(), attribute_error::kind::not_constant); + } + } + return result; + } + + ordered_map<field_info> resolving_visitor::build_composite_type( + const std::vector<field_declaration>& fields, + ordered_map<field_origin>& field_names, const type& aggregate, const bool supported) { - ordered_map<type> result; + ordered_map<field_info> result; for (const auto& field : fields) { const type field_type = resolve_type(*field.second); - for (const auto& field_name : field.first) + for (const identifier_definition& field_name : field.first) { + const std::optional<std::size_t> alignment = + evaluate_alignment(field_name.attributes, supported); auto [existing, inserted] = field_names.insert(field_name.name(), field_origin{ .position = field.second->position(), .base_type = type() }); + if (!inserted) { std::optional<std::string> base_name; @@ -338,12 +417,12 @@ namespace elna::boot base_name = alias->name; } } - add_error<member_error>(field_name.position(), field_name.name(), aggregate, + add_error<member_error>(field_name.id().position(), field_name.name(), aggregate, member_error::duplicate{ .original = existing->second.position, .base = base_name }); } else { - result.insert(field_name.name(), field_type); + result.insert(field_name.name(), field_info(field_type, alignment)); } } } @@ -432,16 +511,18 @@ namespace elna::boot } else if (!declaration->is_extern && resolve_aliases(variable_type).get<constant_type>() != nullptr) { - auto position_span = source_position(declaration->identifiers.front().id().position().start(), + auto position_span = source_position( + declaration->identifiers.front().id().position().start(), declaration->identifiers.back().id().position().end()); add_error<declaration_format_error>(position_span, declaration_format_error::not_initialized{ extract_identifiers(declaration->identifiers) }); } - for (const identifier_definition& variable_identifier : declaration->identifiers) + for (const auto& variable_identifier : declaration->identifiers) { auto variable_symbol = register_variable(variable_identifier.name(), variable_type, declaration->position(), declaration->is_extern); variable_symbol->exported = variable_identifier.exported(); + variable_symbol->alignment = evaluate_alignment(variable_identifier.attributes, true); variable_symbol->value = computed; } } @@ -609,6 +690,7 @@ namespace elna::boot 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)); + result_type->alignment = evaluate_alignment(expression->attributes, true); this->current_type = type(result_type); } @@ -1216,10 +1298,10 @@ namespace elna::boot { return false; } - for (const auto& [field_name, field_type] : link->fields) + for (const auto& [field_name, field] : link->fields) { alias_path.resize(saved_path); - if (!find_alias_cycle(being_resolved, field_type, alias_path, in_record)) + if (!find_alias_cycle(being_resolved, field.field_type, alias_path, in_record)) { return false; } @@ -1407,19 +1489,6 @@ namespace elna::boot __builtin_unreachable(); } - void name_analysis_visitor::visit(variable_declaration *declaration) - { - resolving_visitor::visit(declaration); - for (const identifier_definition& variable_identifier : declaration->identifiers) - { - if (variable_identifier.exported()) - { - add_error<symbol_declaration_error>(variable_identifier.id().position(), - variable_identifier.id().name(), symbol_declaration_error::kind::local_export); - } - } - } - void name_analysis_visitor::visit(procedure_declaration *declaration) { if (declaration->body.has_value()) diff --git a/boot/parser.yy b/boot/parser.yy index 00bdae8..21ff827 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -88,8 +88,7 @@ along with GCC; see the file COPYING3. If not see %token <std::string> STRING %token <bool> BOOLEAN %token LEFT_PAREN "(" RIGHT_PAREN ")" LEFT_SQUARE "[" RIGHT_SQUARE "]" -%token GENERIC_OPEN "#[" -%token LEFT_BRACE "{" RIGHT_BRACE "}" + LEFT_BRACE "{" RIGHT_BRACE "}" GENERIC_OPEN "#[" %token ASSIGNMENT ":=" EXCLAMATION "!" ARROW "->" AT "@" HAT "^" @@ -149,14 +148,16 @@ along with GCC; see the file COPYING3. If not see %type <std::vector<elna::boot::type_declaration *>> type_declarations type_part; %type <std::unique_ptr<elna::boot::procedure_body>> procedure_body; %type <elna::boot::field_declaration> field_declaration; -%type <std::vector<elna::boot::field_declaration>> optional_fields required_fields; +%type <std::vector<elna::boot::field_declaration>> field_declarations; %type <std::unique_ptr<elna::boot::field_initializer>> field_initializer; %type <std::vector<elna::boot::field_initializer>> field_initializers; %type <std::vector<elna::boot::conditional_statements *>> elsif_then_statements elsif_do_statements; %type <std::vector<elna::boot::statement *> *> else_statements; %type <std::unique_ptr<elna::boot::identifier>> identifier with_counter; -%type <std::unique_ptr<elna::boot::identifier_definition>> identifier_definition; +%type <std::unique_ptr<elna::boot::identifier_definition>> identifier_definition attributed_identifier; %type <std::vector<elna::boot::identifier_definition>> identifier_definitions; +%type <std::unique_ptr<elna::boot::attribute>> attribute; +%type <std::vector<elna::boot::attribute>> attributes; %type <std::vector<std::string>> import_declaration; %type <std::vector<elna::boot::identifier>> required_identifiers optional_identifiers generic_parameters; %type <elna::boot::named_expression *> name_reference; @@ -188,18 +189,43 @@ identifier: identifier_definition: IDENTIFIER "*" { $$ = std::make_unique<boot::identifier_definition>($1, boot::make_position(@1), true); } | IDENTIFIER { $$ = std::make_unique<boot::identifier_definition>($1, boot::make_position(@1), false); } +attribute: + TRAIT "(" required_expressions ")" + { + $$ = std::make_unique<boot::attribute>(boot::identifier($1, boot::make_position(@1)), $3); + } +attributes: + %empty {} + | attribute attributes + { + $$ = $2; + $$.emplace($$.cbegin(), std::move(*$1)); + } +attributed_identifier: + attributes identifier_definition + { + $$ = $2; + $$->attributes = $1; + + // An empty rule is located where the previous token ended, which + // would push the declaration's position back over it. + if ($$->attributes.empty()) + { + @$ = @2; + } + } identifier_definitions: - identifier_definition "," identifier_definitions + attributed_identifier "," identifier_definitions { $$ = $3; $$.emplace($$.cbegin(), std::move(*$1)); } - | identifier_definition { $$.emplace_back(std::move(*$1)); } + | attributed_identifier { $$.emplace_back(std::move(*$1)); } return_declaration: %empty {} | "->" "!" { $$ = boot::procedure_type_expression::return_t(std::monostate{}); } | "->" type_expression { $$ = boot::procedure_type_expression::return_t($2); } -procedure_heading: "(" optional_fields ")" return_declaration +procedure_heading: "(" field_declarations ")" return_declaration { $$ = std::make_unique<boot::procedure_type_expression>(boot::make_position(@$), $2, $4); } procedure_declaration: "proc" identifier_definition generic_parameters procedure_heading procedure_body @@ -555,16 +581,15 @@ statements: } | statement { $$.push_back($1); } field_declaration: - required_identifiers ":" type_expression { $$ = std::make_pair($1, std::shared_ptr<boot::type_expression>($3)); } -required_fields: - field_declaration ";" required_fields + identifier_definitions ":" type_expression + { $$ = std::make_pair($1, std::shared_ptr<boot::type_expression>($3)); } +field_declarations: + field_declaration ";" field_declarations { $$ = $3; $$.emplace($$.cbegin(), $1); } | field_declaration { $$.emplace_back($1); } -optional_fields: - required_fields { $$ = $1; } | %empty {} field_initializer: identifier ":" expression { $$ = std::make_unique<boot::field_initializer>(std::move(*$1), $3); } @@ -591,18 +616,35 @@ type_expression: { $$ = new boot::pointer_type_expression(boot::make_position(@$), $2); } - | "record" optional_fields "end" + | attributes "record" field_declarations "end" { - $$ = new boot::record_type_expression(boot::make_position(@$), $2); + std::vector<boot::attribute> written = $1; + + if (written.empty()) + { + @$ = @2 + @4; + } + auto *record = new boot::record_type_expression(boot::make_position(@$), $3); + + record->attributes = std::move(written); + $$ = record; } - | "record" "(" name_reference ")" optional_fields "end" + | attributes "record" "(" name_reference ")" field_declarations "end" { - boot::named_expression *reference = $3; + std::vector<boot::attribute> written = $1; - $$ = new boot::record_type_expression(boot::make_position(@$), $5, + if (written.empty()) + { + @$ = @2 + @7; + } + boot::named_expression *reference = $4; + auto *record = new boot::record_type_expression(boot::make_position(@$), $6, boot::identifier(reference->name, reference->position()), std::move(reference->arguments)); + + record->attributes = std::move(written); delete reference; + $$ = record; } | "proc" procedure_heading { diff --git a/boot/result.cc b/boot/result.cc index 635a121..c8ea267 100644 --- a/boot/result.cc +++ b/boot/result.cc @@ -17,7 +17,6 @@ along with GCC; see the file COPYING3. If not see #include "elna/boot/result.h" -#include <algorithm> #include <charconv> #include <cmath> #include <cstring> @@ -115,27 +114,6 @@ namespace elna::boot return this->m_name == that; } - identifier_definition::identifier_definition(const std::string& name, - const source_position& position, const bool exported) - : m_identifier(name, position), m_exported(exported) - { - } - - const std::string& identifier_definition::name() const - { - return this->m_identifier.name(); - } - - const identifier& identifier_definition::id() const - { - return this->m_identifier; - } - - bool identifier_definition::exported() const - { - return this->m_exported; - } - std::optional<diagnostic_note> previous_declaration_note( const std::optional<source_position>& original, std::string_view label, const std::filesystem::path& file) @@ -159,17 +137,6 @@ namespace elna::boot return diagnostic_note{ .message = join(identifiers), .position = position_span, .file = {} }; } - std::vector<identifier> extract_identifiers(const std::vector<identifier_definition>& identifiers) - { - std::vector<identifier> result; - result.reserve(identifiers.size()); - - std::ranges::transform(identifiers, std::back_inserter(result), - [](const auto& identifier) { return identifier.id(); }); - - return result; - } - integer_literal::integer_literal(bool is_signed, std::size_t size) : m_signed(is_signed), m_size(size) { diff --git a/boot/symbol.cc b/boot/symbol.cc index 9092866..0814217 100644 --- a/boot/symbol.cc +++ b/boot/symbol.cc @@ -253,6 +253,11 @@ namespace elna::boot { } + field_info::field_info(type field_type, std::optional<std::size_t> alignment) + : field_type(std::move(field_type)), alignment(alignment) + { + } + record_type::record_type(type base) : base(std::move(base)) { @@ -780,11 +785,11 @@ namespace elna::boot if (auto record = erase_generic(resolved_type).get<record_type>()) { - for (const auto& [name, field_type] : record->fields) + for (const auto& [name, field] : record->fields) { if (name == field_name) { - return substitute_from(resolved_type, field_type); + return substitute_from(resolved_type, field.field_type); } } } diff --git a/boot/type_check.cc b/boot/type_check.cc index 5078da1..8847693 100644 --- a/boot/type_check.cc +++ b/boot/type_check.cc @@ -344,9 +344,9 @@ namespace elna::boot } else if (auto record = referent.get<record_type>()) { - for (const auto& [field_name, field_type] : record->fields) + for (const auto& [field_name, field] : record->fields) { - if (contains_constant_member(field_type)) + if (contains_constant_member(field.field_type)) { return true; } @@ -378,9 +378,9 @@ namespace elna::boot if (auto record = referent.get<record_type>()) { - for (const auto& [field_name, field_type] : record->fields) + for (const auto& [field_name, field] : record->fields) { - if (auto opaque = find_opaque_type(field_type)) + if (auto opaque = find_opaque_type(field.field_type)) { return opaque; } @@ -462,9 +462,9 @@ namespace elna::boot } if (auto record = resolve_aliases(checked).get<record_type>()) { - for (const auto& [field_name, field_type] : record->fields) + for (const auto& [field_name, field] : record->fields) { - if (auto found = find_zero_sized(field_type, target)) + if (auto found = find_zero_sized(field.field_type, target)) { return found; } @@ -804,7 +804,7 @@ namespace elna::boot { walking_visitor::visit(declaration); - for (const identifier_definition& variable_identifier : declaration->identifiers) + for (const auto& variable_identifier : declaration->identifiers) { auto variable_symbol = this->bag.lookup(variable_identifier.name())->is_variable(); @@ -823,7 +823,7 @@ namespace elna::boot { return; } - for (const identifier_definition& variable_identifier : declaration->identifiers) + for (const auto& variable_identifier : declaration->identifiers) { auto variable_symbol = this->bag.lookup(variable_identifier.name())->is_variable(); if (!is_assignable_from(variable_symbol->symbol, declaration->initializer->type_decoration)) @@ -945,17 +945,17 @@ namespace elna::boot if (auto record = referent.get<record_type>()) { - for (const auto& [field_name, field_type] : record->fields) + for (const auto& [field_name, field] : record->fields) { - if (auto opaque = find_opaque_type(field_type)) + if (auto opaque = find_opaque_type(field.field_type)) { add_error<type_requirement_error>(declaration->position(), opaque.value(), type_requirement_error::kind::opaque_field); } - else if (has_zero_size(field_type, this->target)) + else if (has_zero_size(field.field_type, this->target)) { add_error<type_requirement_error>(declaration->position(), - field_type, type_requirement_error::kind::zero_sized); + field.field_type, type_requirement_error::kind::zero_sized); } } } @@ -1060,9 +1060,9 @@ namespace elna::boot } if (auto record = subject.get<record_type>()) { - for (const auto& [field_name, field_type] : record->fields) + for (const auto& [field_name, field] : record->fields) { - if (occurs_in(parameter, field_type)) + if (occurs_in(parameter, field.field_type)) { return true; } diff --git a/gcc/gcc/elna-builtins.cc b/gcc/gcc/elna-builtins.cc index 7d2f8f0..2dc1147 100644 --- a/gcc/gcc/elna-builtins.cc +++ b/gcc/gcc/elna-builtins.cc @@ -116,14 +116,21 @@ namespace elna::gcc return builtin_table; } - static tree build_composite_type(const boot::ordered_map<boot::type>& fields, + static tree build_composite_type(const boot::ordered_map<boot::field_info>& fields, tree composite_type_node, const std::shared_ptr<symbol_table>& symbols) { for (const auto& field : fields) { - tree rewritten_field = get_inner_alias(field.second, symbols); + tree rewritten_field = get_inner_alias(field.second.field_type, symbols); tree field_declaration = build_field(UNKNOWN_LOCATION, composite_type_node, field.first, rewritten_field); + + if (field.second.alignment.has_value()) + { + SET_DECL_ALIGN(field_declaration, field.second.alignment.value() * BITS_PER_UNIT); + DECL_USER_ALIGN(field_declaration) = 1; + DECL_PACKED(field_declaration) = 1; + } TYPE_FIELDS(composite_type_node) = chainon(TYPE_FIELDS(composite_type_node), field_declaration); } return composite_type_node; @@ -148,6 +155,8 @@ namespace elna::gcc } TYPE_SIZE_UNIT(record_type) = size_int(record_layout.value().size); TYPE_SIZE(record_type) = bitsize_int(record_layout.value().size * BITS_PER_UNIT); + SET_TYPE_ALIGN(record_type, record_layout.value().alignment * BITS_PER_UNIT); + TYPE_USER_ALIGN(record_type) = static_cast<unsigned>(subject->alignment.has_value()); return true; } diff --git a/gcc/gcc/elna-generic.cc b/gcc/gcc/elna-generic.cc index b67e91c..8ab6762 100644 --- a/gcc/gcc/elna-generic.cc +++ b/gcc/gcc/elna-generic.cc @@ -744,13 +744,20 @@ namespace elna::gcc for (const auto& variable_identifier : declaration->identifiers) { const location_t declaration_location = get_location(&declaration->position()); - tree declaration_tree = this->symbols->lookup(variable_identifier.name()); + const std::string& variable_name = variable_identifier.name(); + tree declaration_tree = this->symbols->lookup(variable_name); - auto variable_symbol = this->bag.lookup(variable_identifier.name())->is_variable(); + auto variable_symbol = this->bag.lookup(variable_name)->is_variable(); if (declaration_tree == NULL_TREE) { - declaration_tree = declare_variable(variable_identifier.name(), *variable_symbol, this->symbols); + declaration_tree = declare_variable(variable_name, *variable_symbol, this->symbols); + } + if (variable_symbol->alignment.has_value()) + { + SET_DECL_ALIGN(declaration_tree, + variable_symbol->alignment.value() * BITS_PER_UNIT); + DECL_USER_ALIGN(declaration_tree) = 1; } if (variable_symbol->value.has_value()) { diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h index f697b44..76fb94e 100644 --- a/include/elna/boot/ast.h +++ b/include/elna/boot/ast.h @@ -342,6 +342,57 @@ namespace elna::boot }; /** + * Attribute applied to a declaration. + */ + struct attribute + { + attribute(identifier&& name, std::vector<expression *> arguments); + attribute(const attribute&) = delete; + attribute(attribute&& that) noexcept; + ~attribute(); + + attribute& operator=(const attribute& that) = delete; + attribute& operator=(attribute&& that) noexcept; + + const identifier& name() const; + const std::vector<expression *>& arguments() const; + + private: + identifier m_name; + std::vector<expression *> m_arguments; + }; + + /** + * A declared name together with some attributes. + */ + struct identifier_definition + { + identifier_definition(const std::string& name, const source_position& position, + const bool exported, std::vector<attribute>&& attributes = std::vector<attribute>{}); + + const std::string& name() const; + const identifier& id() const; + bool exported() const; + /** + * \return Attributes written in front of the identifier. + */ + std::vector<attribute> attributes; + + private: + identifier m_identifier; + bool m_exported{ false }; + }; + + /** + * Extracts identifiers (name and position) from identifier definitions and + * returns them in an allocated vector. + * + * \param identifiers Identifier definitions. + * \return Extracted identifiers. + */ + std::vector<identifier> extract_identifiers(const std::vector<identifier_definition>& identifiers); + + /** * Symbol definition. */ class declaration : public node @@ -438,7 +489,7 @@ namespace elna::boot void accept(parser_visitor *visitor) override; }; - using field_declaration = std::pair<std::vector<identifier>, std::shared_ptr<type_expression>>; + using field_declaration = std::pair<std::vector<identifier_definition>, std::shared_ptr<type_expression>>; class record_type_expression : public type_expression { @@ -450,6 +501,8 @@ namespace elna::boot * absent. */ std::vector<type_expression *> base_arguments; + /// Attributes written in front of the \c record keyword. + std::vector<attribute> attributes; record_type_expression(const source_position position, std::vector<field_declaration>&& fields); diff --git a/include/elna/boot/materialization.h b/include/elna/boot/materialization.h index f3c720e..ef1b70b 100644 --- a/include/elna/boot/materialization.h +++ b/include/elna/boot/materialization.h @@ -27,7 +27,9 @@ namespace elna::boot enum class kind { integer_overflow, - real_overflow + real_overflow, + local_export, + field_export }; materialization_error(const source_position position, kind error_kind); @@ -41,11 +43,20 @@ namespace elna::boot class materialization_visitor final : public walking_visitor, public diagnostic_container { const target_info& target; + bool in_global_scope{ true }; public: explicit materialization_visitor(const target_info& target); + void visit(procedure_declaration *declaration) override; + void visit(variable_declaration *declaration) override; + + void visit(record_type_expression *expression) override; + void visit(literal<integer_literal> *literal) override; void visit(literal<float_literal> *literal) override; + + protected: + void visit_entry_point(unit *unit) override; }; } diff --git a/include/elna/boot/name_analysis.h b/include/elna/boot/name_analysis.h index 46a3e2d..f2f2813 100644 --- a/include/elna/boot/name_analysis.h +++ b/include/elna/boot/name_analysis.h @@ -43,8 +43,7 @@ namespace elna::boot undeclared_type, undeclared_trait, undeclared_symbol, - not_a_type, - local_export + not_a_type }; struct redefinition { @@ -68,6 +67,31 @@ namespace elna::boot }; /** + * Error applying an attribute. + */ + class attribute_error final : public diagnostic + { + public: + enum class kind + { + unknown, + duplicate, + unsupported, + argument_count, + not_constant, + invalid_alignment + }; + + attribute_error(const source_position position, const std::string& name, kind attribute_kind); + + std::string what() const override; + + private: + std::string name; + kind m_kind; + }; + + /** * Cyclic type declaration. */ class cyclic_declaration_error final : public diagnostic @@ -179,8 +203,15 @@ namespace elna::boot const procedure_type_expression::return_t& return_type); std::pair<procedure_type, std::vector<std::string>> build_procedure( procedure_type_expression& expression); - ordered_map<type> build_composite_type(const std::vector<field_declaration>& fields, - ordered_map<field_origin>& field_names, const type& aggregate); + ordered_map<field_info> build_composite_type(const std::vector<field_declaration>& fields, + ordered_map<field_origin>& field_names, const type& aggregate, + bool supported = true); + /* + * Reads the alignment out of the attributes written in front of a + * declaration, reporting the ones that do not belong there. + */ + std::optional<std::size_t> evaluate_alignment(const std::vector<attribute>& attributes, + bool supported); /* * Resolves a type expression, reporting names that denote something * other than a type. @@ -294,7 +325,6 @@ namespace elna::boot void visit(unit *unit) override; [[noreturn]] void visit(type_declaration *) override; - void visit(variable_declaration *declaration) override; void visit(procedure_declaration *declaration) override; void visit(for_statement *statement) override; diff --git a/include/elna/boot/result.h b/include/elna/boot/result.h index d37484a..6defcb8 100644 --- a/include/elna/boot/result.h +++ b/include/elna/boot/result.h @@ -202,20 +202,6 @@ namespace elna::boot source_position m_position; }; - struct identifier_definition - { - identifier_definition(const std::string& name, const source_position& position, - const bool exported); - - const std::string& name() const; - const identifier& id() const; - bool exported() const; - - private: - identifier m_identifier; - bool m_exported{ false }; - }; - /** * Creates an error note pointing to a previous declaration. * @@ -243,15 +229,6 @@ namespace elna::boot }; /** - * Extracts identifiers (name and position) from identifier definitions and - * returns them in an allocated vector. - * - * \param identifiers Identifier definitions. - * \return Extracted identifiers. - */ - std::vector<identifier> extract_identifiers(const std::vector<identifier_definition>& identifiers); - - /** * Joins an array of string-convertable objects (with a .t_string() method) * into a delimiter separated list. * diff --git a/include/elna/boot/symbol.h b/include/elna/boot/symbol.h index 8646f48..e9c9a0c 100644 --- a/include/elna/boot/symbol.h +++ b/include/elna/boot/symbol.h @@ -172,10 +172,25 @@ namespace elna::boot primitive_type(const std::string& identifier, const type_properties& properties); }; + /** + * A record field: its type and whatever its attributes decided about it. + */ + struct field_info + { + type field_type; + /// Alignment written with \c #aligned, empty when the field takes its + /// type's own alignment. + std::optional<std::size_t> alignment; + + field_info(type field_type = type(), std::optional<std::size_t> alignment = std::nullopt); + }; + struct record_type { - ordered_map<type> fields; + ordered_map<field_info> fields; const type base; + /// Alignment written with \c #aligned on the record itself. + std::optional<std::size_t> alignment; explicit record_type(type base = type()); }; @@ -465,6 +480,10 @@ namespace elna::boot /// Evaluated constant value, set by the constant folder. std::optional<constant_value> value; + /// Alignment written with \c #aligned, empty when the variable takes + /// its type's own alignment. + std::optional<std::size_t> alignment; + /** * Constructs a variable symbol information. * diff --git a/testsuite/fail_compilation/aligned_attribute.elna b/testsuite/fail_compilation/aligned_attribute.elna new file mode 100644 index 0000000..3b5a56e --- /dev/null +++ b/testsuite/fail_compilation/aligned_attribute.elna @@ -0,0 +1,20 @@ +type + Uneven = record + #aligned(3) x: Word8 (* @Error Alignment must be a power of two *) + end + Twice = record + #aligned(4) #aligned(8) y: Word8 (* @Error Attribute '#aligned' specified more than once *) + end + Unknown = record + #packed(1) z: Word8 (* @Error Attribute '#packed' not declared *) + end + +proc takes(#aligned(8) p: Int) (* @Error Attribute '#aligned' is not supported in this position *) +begin +return + +program() +begin +return 0u8 + +end. diff --git a/testsuite/fail_compilation/local_var_exported.elna b/testsuite/fail_compilation/local_var_exported.elna index 5323e5e..307d34f 100644 --- a/testsuite/fail_compilation/local_var_exported.elna +++ b/testsuite/fail_compilation/local_var_exported.elna @@ -1,6 +1,6 @@ proc test_local_export() var - v* : Int (* @Error Local symbol 'v' cannot be exported *) + v* : Int (* @Error Local symbols cannot be exported *) return end. diff --git a/testsuite/runnable/aligned_attribute.elna b/testsuite/runnable/aligned_attribute.elna new file mode 100644 index 0000000..330847e --- /dev/null +++ b/testsuite/runnable/aligned_attribute.elna @@ -0,0 +1,41 @@ +type + Plain = record + a: Word8; + b: Word8 + end + + Spread = record + a, #aligned(8) b: Word8 + end + + Packed = record + a: Word8; + #aligned(1) wide: Word + end + + Over = #aligned(16) record + a: Word8 + end + +var + #aligned(32) tuned: Word8 + spread: Spread + first: ^Word8 + second: ^Word8 + +program() +begin + assert(#size(Plain) = 2u); + assert(#size(Spread) = 16u); + assert(#offset(Spread, a) = 0u); + assert(#offset(Spread, b) = 8u); + assert(#size(Packed) = 9u); + assert(#offset(Packed, wide) = 1u); + assert(#size(Over) = 16u); + assert(#alignment(Over) = 16u); + first := @spread.a; + second := @spread.b; + assert(cast(second: Word) - cast(first: Word) = 8u) +return 0u8 + +end. |
