aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2026-09-11 09:11:47 +0200
committerEugen Wissner <belka@caraus.de>2026-09-11 09:11:47 +0200
commitbd2b80d6ac582b9f0bcd96c86d4b79bfdb64eca1 (patch)
treea835a5cfe5a9cf48720b9255261e065fd7978849
parentb27872d5c5bfecae74195771f5c1f4e73385b6d6 (diff)
downloadelna-bd2b80d6ac582b9f0bcd96c86d4b79bfdb64eca1.tar.gz
Implement aligned attribute
-rw-r--r--boot/ast.cc69
-rw-r--r--boot/evaluator.cc14
-rw-r--r--boot/materialization.cc48
-rw-r--r--boot/name_analysis.cc119
-rw-r--r--boot/parser.yy76
-rw-r--r--boot/result.cc33
-rw-r--r--boot/symbol.cc9
-rw-r--r--boot/type_check.cc28
-rw-r--r--gcc/gcc/elna-builtins.cc13
-rw-r--r--gcc/gcc/elna-generic.cc13
-rw-r--r--include/elna/boot/ast.h55
-rw-r--r--include/elna/boot/materialization.h13
-rw-r--r--include/elna/boot/name_analysis.h40
-rw-r--r--include/elna/boot/result.h23
-rw-r--r--include/elna/boot/symbol.h21
-rw-r--r--testsuite/fail_compilation/aligned_attribute.elna20
-rw-r--r--testsuite/fail_compilation/local_var_exported.elna2
-rw-r--r--testsuite/runnable/aligned_attribute.elna41
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.