aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2026-07-17 10:18:58 +0200
committerEugen Wissner <belka@caraus.de>2026-07-17 10:18:58 +0200
commita0e1740227535adc3151c02eb102c4f96b4f9731 (patch)
tree0e29cd3719370eca8c74fb67480f3bfc1a207106
parentd0a666507a6f11b65170c257fd483613f462f3f2 (diff)
downloadelna-a0e1740227535adc3151c02eb102c4f96b4f9731.tar.gz
Implement type constness
-rw-r--r--boot/ast.cc101
-rw-r--r--boot/parser.yy28
-rw-r--r--boot/semantic.cc338
-rw-r--r--boot/symbol.cc91
-rw-r--r--gcc/gcc/elna-builtins.cc4
-rw-r--r--gcc/gcc/elna-generic.cc51
-rw-r--r--gcc/testlib/elna-dg.exp45
-rw-r--r--include/elna/boot/ast.h47
-rw-r--r--include/elna/boot/semantic.h42
-rw-r--r--include/elna/boot/symbol.h72
-rw-r--r--include/elna/gcc/elna-generic.h1
-rw-r--r--source/lexer.elna3
-rw-r--r--testsuite/compilable/float_literals.elna10
-rw-r--r--testsuite/compilable/pointer_const_conversion.elna7
-rw-r--r--testsuite/compilable/take_const_address.elna7
-rw-r--r--testsuite/fail_compilation/assign_const_array_pointer.elna9
-rw-r--r--testsuite/fail_compilation/assign_const_member.elna14
-rw-r--r--testsuite/fail_compilation/assign_const_primitive.elna8
-rw-r--r--testsuite/fail_compilation/assign_const_record_pointer.elna17
-rw-r--r--testsuite/fail_compilation/assign_deep_const_pointer.elna9
-rw-r--r--testsuite/fail_compilation/assign_element_of_const.elna8
-rw-r--r--testsuite/fail_compilation/assign_member_of_const.elna13
-rw-r--r--testsuite/fail_compilation/local_const_exported.elna6
-rw-r--r--testsuite/runnable/const_copy.elna12
-rw-r--r--testsuite/runnable/const_initialization.elna10
25 files changed, 558 insertions, 395 deletions
diff --git a/boot/ast.cc b/boot/ast.cc
index 37e89ac..3454cce 100644
--- a/boot/ast.cc
+++ b/boot/ast.cc
@@ -29,6 +29,11 @@ namespace elna::boot
__builtin_unreachable();
}
+ void empty_visitor::visit(constant_type_expression *)
+ {
+ __builtin_unreachable();
+ }
+
void empty_visitor::visit(type_declaration *)
{
__builtin_unreachable();
@@ -54,11 +59,6 @@ namespace elna::boot
__builtin_unreachable();
}
- void empty_visitor::visit(constant_declaration *)
- {
- __builtin_unreachable();
- }
-
void empty_visitor::visit(procedure_declaration *)
{
__builtin_unreachable();
@@ -210,10 +210,6 @@ namespace elna::boot
}
if (declaration->body.has_value())
{
- for (constant_declaration *const constant : declaration->body.value().constants)
- {
- constant->accept(this);
- }
for (variable_declaration *const variable : declaration->body.value().variables)
{
variable->accept(this);
@@ -336,10 +332,6 @@ namespace elna::boot
{
variable->accept(this);
}
- for (constant_declaration *const constant : unit->constants)
- {
- constant->accept(this);
- }
for (procedure_declaration *const procedure : unit->procedures)
{
procedure->accept(this);
@@ -364,17 +356,17 @@ namespace elna::boot
}
}
- void walking_visitor::visit(constant_declaration *declaration)
+ void walking_visitor::visit(array_type_expression *expression)
{
- declaration->initializer().accept(this);
+ expression->base().accept(this);
}
- void walking_visitor::visit(array_type_expression *expression)
+ void walking_visitor::visit(pointer_type_expression *expression)
{
expression->base().accept(this);
}
- void walking_visitor::visit(pointer_type_expression *expression)
+ void walking_visitor::visit(constant_type_expression *expression)
{
expression->base().accept(this);
}
@@ -557,6 +549,11 @@ namespace elna::boot
return nullptr;
}
+ constant_type_expression *type_expression::is_constant()
+ {
+ return nullptr;
+ }
+
record_type_expression *type_expression::is_record()
{
return nullptr;
@@ -624,6 +621,32 @@ namespace elna::boot
return *m_base;
}
+ constant_type_expression::constant_type_expression(const source_position position,
+ type_expression *base)
+ : node(position), m_base(base)
+ {
+ }
+
+ constant_type_expression::~constant_type_expression()
+ {
+ delete m_base;
+ }
+
+ void constant_type_expression::accept(parser_visitor *visitor)
+ {
+ visitor->visit(this);
+ }
+
+ constant_type_expression *constant_type_expression::is_constant()
+ {
+ return this;
+ }
+
+ type_expression& constant_type_expression::base()
+ {
+ return *m_base;
+ }
+
record_type_expression::record_type_expression(const source_position position,
std::vector<field_declaration>&& fields)
: node(position), fields(std::move(fields))
@@ -753,27 +776,6 @@ namespace elna::boot
{
}
- constant_declaration::constant_declaration(const source_position position, identifier_definition identifier,
- expression *initializer)
- : declaration(position, identifier), m_initializer(initializer)
- {
- }
-
- void constant_declaration::accept(parser_visitor *visitor)
- {
- visitor->visit(this);
- }
-
- expression& constant_declaration::initializer()
- {
- return *m_initializer;
- }
-
- constant_declaration::~constant_declaration()
- {
- delete m_initializer;
- }
-
procedure_type_expression::procedure_type_expression(const source_position position,
std::vector<field_declaration>&& parameters, return_t return_type)
: node(position), return_type(return_type), parameters(std::move(parameters))
@@ -863,17 +865,15 @@ namespace elna::boot
return *m_underlying_type;
}
- procedure_body::procedure_body(std::vector<constant_declaration *>&& constants,
- std::vector<variable_declaration *>&& variables, std::vector<statement *>&& entry_point,
- expression *return_expr)
- : constants(std::move(constants)), variables(std::move(variables)),
- entry_point(std::move(entry_point)), return_expression(return_expr)
+ procedure_body::procedure_body(std::vector<variable_declaration *>&& variables,
+ std::vector<statement *>&& entry_point, expression *return_expression)
+ : variables(std::move(variables)),
+ entry_point(std::move(entry_point)), return_expression(return_expression)
{
}
procedure_body::procedure_body(procedure_body&& that)
- : constants(std::move(const_cast<std::vector<constant_declaration *>&>(that.constants))),
- variables(std::move(const_cast<std::vector<variable_declaration *>&>(that.variables))),
+ : variables(std::move(const_cast<std::vector<variable_declaration *>&>(that.variables))),
entry_point(std::move(const_cast<std::vector<statement *>&>(that.entry_point))),
return_expression(that.return_expression)
{
@@ -889,27 +889,22 @@ namespace elna::boot
{
delete variable;
}
- for (constant_declaration *constant : this->constants)
- {
- delete constant;
- }
}
unit::unit(const source_position position)
- : node(position), procedure_body(std::vector<constant_declaration *>{},
- std::vector<variable_declaration *>{}, std::vector<statement *>{}, nullptr)
+ : node(position),
+ procedure_body(std::vector<variable_declaration *>{}, std::vector<statement *>{}, nullptr)
{
}
unit::unit(const source_position position,
std::vector<import_declaration *>&& imports,
- std::vector<constant_declaration *>&& constants,
std::vector<type_declaration *>&& types,
std::vector<variable_declaration *>&& variables,
std::vector<procedure_declaration *>&& procedures,
std::vector<statement *>&& entry_point)
: node(position),
- procedure_body(std::move(constants), std::move(variables), std::move(entry_point)),
+ procedure_body(std::move(variables), std::move(entry_point)),
imports(std::move(imports)), types(std::move(types)), procedures(std::move(procedures))
{
}
diff --git a/boot/parser.yy b/boot/parser.yy
index c34c905..1dd42a8 100644
--- a/boot/parser.yy
+++ b/boot/parser.yy
@@ -129,8 +129,6 @@ along with GCC; see the file COPYING3. If not see
%type <std::vector<elna::boot::expression *>> case_labels;
%type <elna::boot::switch_case> switch_case;
%type <std::vector<elna::boot::switch_case>> switch_cases;
-%type <elna::boot::constant_declaration *> constant_declaration;
-%type <std::vector<elna::boot::constant_declaration *>> constant_part constant_declarations;
%type <elna::boot::variable_declaration *> variable_declaration;
%type <std::vector<elna::boot::variable_declaration *>> variable_declarations variable_part;
%type <elna::boot::type_expression *> type_expression;
@@ -166,14 +164,14 @@ along with GCC; see the file COPYING3. If not see
%type <std::vector<elna::boot::import_declaration *>> import_declarations import_part;
%%
program:
- import_part constant_part type_part variable_part procedure_part statement_part "end" "."
+ import_part type_part variable_part procedure_part statement_part "end" "."
{
- boot::unit *tree = new boot::unit(boot::make_position(@$), $1, $2, $3, $4, $5, $6);
+ boot::unit *tree = new boot::unit(boot::make_position(@$), $1, $2, $3, $4, $5);
driver.tree.reset(tree);
}
procedure_body:
- constant_part variable_part statement_part procedure_return
- { $$ = std::make_unique<boot::procedure_body>($1, $2, $3, $4); }
+ variable_part statement_part procedure_return
+ { $$ = std::make_unique<boot::procedure_body>($1, $2, $3); }
statement_part:
/* no statements */ {}
@@ -444,6 +442,10 @@ type_expression:
{
$$ = new boot::array_type_expression(boot::make_position(@$), $4, $2);
}
+ | "const" type_expression
+ {
+ $$ = new boot::constant_type_expression(boot::make_position(@$), $2);
+ }
| "^" type_expression
{
$$ = new boot::pointer_type_expression(boot::make_position(@$), $2);
@@ -501,20 +503,6 @@ variable_declarations:
variable_part:
/* no variable declarations */ {}
| "var" variable_declarations { $$ = $2; }
-constant_declaration: identifier_definition ":=" expression
- {
- $$ = new boot::constant_declaration(boot::make_position(@$), std::move(*$1), $3);
- }
-constant_declarations:
- constant_declaration constant_declarations
- {
- $$ = $2;
- $$.insert(std::cbegin($$), $1);
- }
- | /* no constant definitions */ {}
-constant_part:
- /* no constant definitions */ {}
- | "const" constant_declarations { $$ = $2; }
import_declaration:
IDENTIFIER "." import_declaration
{
diff --git a/boot/semantic.cc b/boot/semantic.cc
index 9137053..c987cfd 100644
--- a/boot/semantic.cc
+++ b/boot/semantic.cc
@@ -73,6 +73,18 @@ namespace elna::boot
+ "', but got '" + actual.to_string() + "'";
}
+ constant_assignment_error::constant_assignment_error(const source_position position,
+ type assignee)
+ : error(position), assignee(assignee)
+ {
+ }
+
+ std::string constant_assignment_error::what() const
+ {
+ return "Cannot assign to a value of type '" + assignee.to_string()
+ + "', 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(composite_type)
@@ -81,7 +93,7 @@ namespace elna::boot
std::string field_not_found_error::what() const
{
- type resolved = inner_aliased_type(composite_type);
+ type resolved = resolve_underlying_type(composite_type);
bool is_enum = resolved.get<enumeration_type>() != nullptr;
bool is_record = resolved.get<record_type>() != nullptr;
@@ -112,7 +124,7 @@ namespace elna::boot
std::string duplicate_member_error::what() const
{
- type resolved = inner_aliased_type(aggregate);
+ type resolved = resolve_underlying_type(aggregate);
bool is_enum = resolved.get<enumeration_type>() != nullptr;
std::string kind = is_enum ? "member" : "field";
std::string message = is_enum ? "Enumeration" : "Record";
@@ -208,7 +220,7 @@ namespace elna::boot
unsupported_trait_type_error::unsupported_trait_type_error(const identifier& trait,
type actual)
- : error(trait.position()), trait_name(trait.name()), actual(actual)
+ : error(trait.position()), actual(actual), trait_name(trait.name())
{
}
@@ -218,6 +230,119 @@ namespace elna::boot
+ "' 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)
+ {
+ if (resolve_aliases(aggregate).get<constant_type>() != nullptr
+ && resolve_aliases(element).get<constant_type>() == nullptr)
+ {
+ return type(std::make_shared<constant_type>(element));
+ }
+ else
+ {
+ return element;
+ }
+ }
+
+ /*
+ * Whether the type itself is constant or has a constant member at any
+ * nesting level, so that values of this type cannot be reassigned as a
+ * whole. Pointers to constants do not make the type itself constant.
+ */
+ static bool contains_constant_member(const type& checked)
+ {
+ auto referent = resolve_aliases(checked);
+
+ if (referent.get<constant_type>() != nullptr)
+ {
+ return true;
+ }
+ else if (auto record = referent.get<record_type>())
+ {
+ for (const type_field& field : record->fields)
+ {
+ if (contains_constant_member(field.second))
+ {
+ return true;
+ }
+ }
+ return !record->base.empty() && contains_constant_member(record->base);
+ }
+ else if (auto array = referent.get<array_type>())
+ {
+ return contains_constant_member(array->base);
+ }
+ return false;
+ }
+
+ bool type_analysis_visitor::check_unresolved_symbol(std::shared_ptr<alias_type> alias,
+ std::vector<std::string>& alias_path)
+ {
+ if (std::find(std::cbegin(alias_path), std::cend(alias_path), alias->name) != std::cend(alias_path))
+ {
+ return false;
+ }
+ alias_path.push_back(alias->name);
+
+ if (auto another_alias = alias->reference.get<alias_type>())
+ {
+ return check_unresolved_symbol(another_alias, alias_path);
+ }
+ return true;
+ }
+
+ bool type_analysis_visitor::is_base_of(const std::shared_ptr<record_type>& base,
+ const std::shared_ptr<record_type>& derived)
+ {
+ if (derived != nullptr)
+ {
+ if (auto current_record = resolve_underlying_type(derived->base).get<record_type>())
+ {
+ return current_record == base || is_base_of(base, current_record);
+ }
+ }
+ return false;
+ }
+
+ bool type_analysis_visitor::is_assignable_from(const type& assignee, const type& assignment)
+ {
+ type resolved_assignee = resolve_underlying_type(assignee);
+ type resolved_assignment = resolve_underlying_type(assignment);
+
+ if (resolved_assignee == resolved_assignment
+ || (is_primitive_type(resolved_assignee, "Pointer") && is_any_pointer_type(resolved_assignment))
+ || (is_primitive_type(resolved_assignment, "Pointer") && is_any_pointer_type(resolved_assignee)))
+ {
+ return true;
+ }
+ std::shared_ptr<pointer_type> assignee_pointer = resolved_assignee.get<pointer_type>();
+ std::shared_ptr<pointer_type> assignment_pointer = resolved_assignment.get<pointer_type>();
+
+ if (assignee_pointer == nullptr || assignment_pointer == nullptr)
+ {
+ return false;
+ }
+ auto assignee_pointee_const = resolve_aliases(assignee_pointer->base).get<constant_type>();
+ auto assignment_pointee_const = resolve_aliases(assignment_pointer->base).get<constant_type>();
+
+ // Constness can be added at the first indirection level, but not removed.
+ if (assignee_pointee_const != nullptr
+ && assignee_pointee_const->unqualified == assignment_pointer->base)
+ {
+ return true;
+ }
+ if (assignment_pointee_const != nullptr && assignee_pointee_const == nullptr)
+ {
+ return false;
+ }
+ // A pointer to a record can be assigned to a pointer to its base type.
+ std::shared_ptr<record_type> assignee_record =
+ resolve_underlying_type(assignee_pointer->base).get<record_type>();
+
+ return assignee_record != nullptr
+ && is_base_of(assignee_record, resolve_underlying_type(assignment_pointer->base).get<record_type>());
+ }
+
type_analysis_visitor::type_analysis_visitor(symbol_bag bag)
: error_container(), bag(bag)
{
@@ -272,7 +397,12 @@ namespace elna::boot
{
walking_visitor::visit(statement);
- if (!is_assignable_from(statement->lvalue().type_decoration, statement->rvalue().type_decoration))
+ if (contains_constant_member(statement->lvalue().type_decoration))
+ {
+ add_error<constant_assignment_error>(statement->position(),
+ statement->lvalue().type_decoration);
+ }
+ else if (!is_assignable_from(statement->lvalue().type_decoration, statement->rvalue().type_decoration))
{
add_error<type_mismatch_error>(statement->position(),
statement->lvalue().type_decoration, statement->rvalue().type_decoration);
@@ -302,7 +432,7 @@ namespace elna::boot
void type_analysis_visitor::visit(case_statement *statement)
{
walking_visitor::visit(statement);
- type condition_type = inner_aliased_type(statement->condition().type_decoration);
+ type condition_type = resolve_underlying_type(statement->condition().type_decoration);
for (const switch_case& case_block : statement->cases)
{
@@ -317,22 +447,6 @@ namespace elna::boot
}
}
- bool type_analysis_visitor::check_unresolved_symbol(std::shared_ptr<alias_type> alias,
- std::vector<std::string>& alias_path)
- {
- if (std::find(std::cbegin(alias_path), std::cend(alias_path), alias->name) != std::cend(alias_path))
- {
- return false;
- }
- alias_path.push_back(alias->name);
-
- if (auto another_alias = alias->reference.get<alias_type>())
- {
- return check_unresolved_symbol(another_alias, alias_path);
- }
- return true;
- }
-
void type_analysis_visitor::visit(type_declaration *declaration)
{
std::vector<std::string> alias_path;
@@ -352,7 +466,7 @@ namespace elna::boot
{
if (expression->base.has_value())
{
- type base_type = inner_aliased_type(this->bag.lookup(expression->base.value().name())->is_type()->symbol);
+ type base_type = resolve_underlying_type(this->bag.lookup(expression->base.value().name())->is_type()->symbol);
if (base_type.get<record_type>() == nullptr)
{
add_error<base_type_error>(base_type, expression->position());
@@ -393,7 +507,7 @@ namespace elna::boot
void type_analysis_visitor::visit(record_constructor_expression *expression)
{
- auto record = inner_aliased_type(expression->type_decoration).get<record_type>();
+ auto record = resolve_underlying_type(expression->type_decoration).get<record_type>();
if (record == nullptr)
{
@@ -422,7 +536,7 @@ namespace elna::boot
void type_analysis_visitor::visit(array_constructor_expression *expression)
{
- auto array = inner_aliased_type(expression->type_decoration).get<array_type>();
+ auto array = resolve_underlying_type(expression->type_decoration).get<array_type>();
if (array == nullptr)
{
@@ -490,49 +604,9 @@ namespace elna::boot
return this->bag.lookup(name)->is_type()->symbol;
}
- type name_analysis_visitor::type_of_constant(const constant_info::variant& value)
- {
- return std::visit([this](auto&& arg) -> type {
- using T = std::decay_t<decltype(arg)>;
-
- if constexpr (std::is_same_v<T, std::int32_t>)
- {
- return lookup_primitive_type("Int");
- }
- else if constexpr (std::is_same_v<T, std::uint32_t>)
- {
- return lookup_primitive_type("Word");
- }
- else if constexpr (std::is_same_v<T, double>)
- {
- return lookup_primitive_type("Float");
- }
- else if constexpr (std::is_same_v<T, bool>)
- {
- return lookup_primitive_type("Bool");
- }
- else if constexpr (std::is_same_v<T, unsigned char>)
- {
- return lookup_primitive_type("Char");
- }
- else if constexpr (std::is_same_v<T, std::nullptr_t>)
- {
- return lookup_primitive_type("Pointer");
- }
- else if constexpr (std::is_same_v<T, std::string>)
- {
- return lookup_primitive_type("String");
- }
- else
- {
- static_assert(!sizeof(T*), "Unhandled constant type");
- }
- }, value);
- }
-
type name_analysis_visitor::lookup_field(const type& composite_type, const std::string& field_name)
{
- type resolved_type = inner_aliased_type(composite_type);
+ type resolved_type = resolve_underlying_type(composite_type);
if (auto record = resolved_type.get<record_type>())
{
@@ -579,6 +653,12 @@ namespace elna::boot
this->current_type = type(std::make_shared<pointer_type>(this->current_type));
}
+ void name_analysis_visitor::visit(constant_type_expression *expression)
+ {
+ walking_visitor::visit(expression);
+ this->current_type = type(std::make_shared<constant_type>(this->current_type));
+ }
+
void name_analysis_visitor::visit(array_type_expression *expression)
{
walking_visitor::visit(expression);
@@ -593,7 +673,7 @@ namespace elna::boot
static void collect_field_names(const type& composite_type,
std::map<std::string, field_origin>& names)
{
- auto record = inner_aliased_type(composite_type).get<record_type>();
+ auto record = resolve_underlying_type(composite_type).get<record_type>();
if (record == nullptr)
{
return;
@@ -803,15 +883,6 @@ namespace elna::boot
}
}
- void name_analysis_visitor::visit(constant_declaration *declaration)
- {
- walking_visitor::visit(declaration);
- auto constant_symbol = std::make_shared<constant_info>(this->current_literal);
-
- constant_symbol->exported = declaration->identifier.exported();
- constant_symbol->position.emplace(declaration->position());
- this->bag.enter(declaration->identifier.name(), constant_symbol);
- }
void name_analysis_visitor::visit(procedure_declaration *declaration)
{
@@ -833,10 +904,6 @@ namespace elna::boot
++name_iterator;
++type_iterator;
}
- for (constant_declaration *const constant : declaration->body.value().constants)
- {
- constant->accept(this);
- }
for (variable_declaration *const variable : declaration->body.value().variables)
{
variable->accept(this);
@@ -887,10 +954,6 @@ namespace elna::boot
{
variable->accept(this);
}
- for (constant_declaration *const constant : unit->constants)
- {
- constant->accept(this);
- }
for (procedure_declaration *const procedure : unit->procedures)
{
procedure->accept(this);
@@ -932,7 +995,7 @@ namespace elna::boot
if (!trait->type_decoration.empty())
{
- type resolved = inner_aliased_type(trait->type_decoration);
+ type resolved = resolve_underlying_type(trait->type_decoration);
bool is_enum = resolved.get<enumeration_type>() != nullptr;
bool is_integral = false;
@@ -1005,7 +1068,7 @@ namespace elna::boot
void name_analysis_visitor::visit(array_access_expression *expression)
{
walking_visitor::visit(expression);
- auto resolved_base= inner_aliased_type(expression->base().type_decoration);
+ auto resolved_base = resolve_underlying_type(expression->base().type_decoration);
if (auto array = resolved_base.get<array_type>())
{
@@ -1015,6 +1078,13 @@ namespace elna::boot
{
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())
+ {
+ expression->type_decoration = qualify_member_type(expression->type_decoration,
+ expression->base().type_decoration);
+ }
}
void name_analysis_visitor::visit(field_access_expression *expression)
@@ -1029,7 +1099,11 @@ namespace elna::boot
}
if (expression->type_decoration.empty())
{
- add_error<field_not_found_error>(expression->field(),
+ add_error<field_not_found_error>(expression->field(), expression->base().type_decoration);
+ }
+ else
+ {
+ expression->type_decoration = qualify_member_type(expression->type_decoration,
expression->base().type_decoration);
}
}
@@ -1038,7 +1112,7 @@ namespace elna::boot
{
walking_visitor::visit(expression);
- if (auto pointer = inner_aliased_type(expression->base().type_decoration).get<pointer_type>())
+ if (auto pointer = resolve_underlying_type(expression->base().type_decoration).get<pointer_type>())
{
expression->type_decoration = pointer->base;
}
@@ -1068,10 +1142,6 @@ namespace elna::boot
{
expression->type_decoration = variable_symbol->symbol;
}
- else if (auto constant_symbol = from_symbol_table->is_constant())
- {
- expression->type_decoration = type_of_constant(constant_symbol->symbol);
- }
else if (auto procedure_symbol = from_symbol_table->is_procedure())
{
expression->type_decoration = type(std::make_shared<procedure_type>(procedure_symbol->symbol));
@@ -1086,43 +1156,36 @@ namespace elna::boot
void name_analysis_visitor::visit(literal<std::int32_t> *literal)
{
- this->current_literal = literal->value;
literal->type_decoration = lookup_primitive_type("Int");
}
void name_analysis_visitor::visit(literal<std::uint32_t> *literal)
{
- this->current_literal = literal->value;
literal->type_decoration = lookup_primitive_type("Word");
}
void name_analysis_visitor::visit(literal<double> *literal)
{
- this->current_literal = literal->value;
literal->type_decoration = lookup_primitive_type("Float");
}
void name_analysis_visitor::visit(literal<bool> *literal)
{
- this->current_literal = literal->value;
literal->type_decoration = lookup_primitive_type("Bool");
}
void name_analysis_visitor::visit(literal<unsigned char> *literal)
{
- this->current_literal = literal->value;
literal->type_decoration = lookup_primitive_type("Char");
}
void name_analysis_visitor::visit(literal<std::nullptr_t> *literal)
{
- this->current_literal = literal->value;
literal->type_decoration = lookup_primitive_type("Pointer");
}
void name_analysis_visitor::visit(literal<std::string> *literal)
{
- this->current_literal = literal->value;
literal->type_decoration = lookup_primitive_type("String");
}
@@ -1168,10 +1231,6 @@ namespace elna::boot
{
return;
}
- for (constant_declaration *const constant : declaration->body.value().constants)
- {
- constant->accept(this);
- }
for (variable_declaration *const variable : declaration->body.value().variables)
{
variable->accept(this);
@@ -1189,79 +1248,4 @@ namespace elna::boot
}
}
}
-
- void declaration_visitor::visit(constant_declaration *declaration)
- {
- if (declaration->identifier.exported())
- {
- add_error<declaration_error>(declaration_error::kind::local_export,
- declaration->identifier.id());
- }
- }
-
- bool is_base_of(std::shared_ptr<record_type> base, std::shared_ptr<record_type> derived)
- {
- type current = derived->base;
-
- while (!current.empty())
- {
- auto current_record = inner_aliased_type(current).get<record_type>();
- if (current_record == nullptr)
- {
- return false;
- }
- if (current_record == base)
- {
- return true;
- }
- current = current_record->base;
- }
- return false;
- }
-
- bool are_compatible_pointers(type lhs_type, type rhs_type)
- {
- std::shared_ptr<primitive_type> lhs_primitive = lhs_type.get<primitive_type>();
- std::shared_ptr<primitive_type> rhs_primitive = rhs_type.get<primitive_type>();
-
- bool lhs_is_primitive_pointer = lhs_primitive != nullptr && lhs_primitive->identifier == "Pointer";
- bool rhs_is_primitive_pointer = rhs_primitive != nullptr && rhs_primitive->identifier == "Pointer";
-
- return (lhs_is_primitive_pointer && rhs_type.get<pointer_type>() != nullptr)
- || (rhs_is_primitive_pointer && lhs_type.get<pointer_type>() != nullptr)
- || (lhs_is_primitive_pointer && rhs_type.get<procedure_type>() != nullptr)
- || (rhs_is_primitive_pointer && lhs_type.get<procedure_type>() != nullptr)
- || (lhs_is_primitive_pointer && rhs_is_primitive_pointer);
- }
-
- bool is_assignable_from(const type& assignee, const type& assignment)
- {
- type resolved_assignee = inner_aliased_type(assignee);
- type resolved_assignment = inner_aliased_type(assignment);
-
- if (resolved_assignee == resolved_assignment)
- {
- return true;
- }
- // If the types do not match, only some pointers can be assigned.
- if (are_compatible_pointers(resolved_assignee, resolved_assignment))
- {
- return true;
- }
- std::shared_ptr<pointer_type> assignee_pointer = resolved_assignee.get<pointer_type>();
- std::shared_ptr<pointer_type> assignment_pointer = resolved_assignment.get<pointer_type>();
-
- if (assignee_pointer == nullptr || assignment_pointer == nullptr)
- {
- return false;
- }
- // A pointer to a record can be assigned to a pointer to its base type.
- type assignee_pointee = inner_aliased_type(assignee_pointer->base);
- type assignment_pointee = inner_aliased_type(assignment_pointer->base);
- std::shared_ptr<record_type> assignee_record = assignee_pointee.get<record_type>();
- std::shared_ptr<record_type> assignment_record = assignment_pointee.get<record_type>();
-
- return assignee_record != nullptr && assignment_record != nullptr
- && is_base_of(assignee_record, assignment_record);
- }
}
diff --git a/boot/symbol.cc b/boot/symbol.cc
index a3a36c1..9985489 100644
--- a/boot/symbol.cc
+++ b/boot/symbol.cc
@@ -43,6 +43,11 @@ namespace elna::boot
{
}
+ type::type(std::shared_ptr<constant_type> constant)
+ : tag(type_tag::constant), constant(constant)
+ {
+ }
+
type::type(std::shared_ptr<array_type> array)
: tag(type_tag::array), array(array)
{
@@ -76,6 +81,9 @@ namespace elna::boot
case type_tag::pointer:
new (&pointer) std::shared_ptr<pointer_type>(other.pointer);
break;
+ case type_tag::constant:
+ new (&constant) std::shared_ptr<constant_type>(other.constant);
+ break;
case type_tag::array:
new (&array) std::shared_ptr<array_type>(other.array);
break;
@@ -112,6 +120,9 @@ namespace elna::boot
case type_tag::pointer:
new (&pointer) std::shared_ptr<pointer_type>(std::move(other.pointer));
break;
+ case type_tag::constant:
+ new (&constant) std::shared_ptr<constant_type>(std::move(other.constant));
+ break;
case type_tag::array:
new (&array) std::shared_ptr<array_type>(std::move(other.array));
break;
@@ -164,6 +175,9 @@ namespace elna::boot
case type_tag::pointer:
this->pointer.~shared_ptr<pointer_type>();
break;
+ case type_tag::constant:
+ this->constant.~shared_ptr<constant_type>();
+ break;
case type_tag::array:
this->array.~shared_ptr<array_type>();
break;
@@ -207,6 +221,12 @@ namespace elna::boot
}
template<>
+ std::shared_ptr<constant_type> type::get<constant_type>() const
+ {
+ return tag == type_tag::constant ? this->constant : nullptr;
+ }
+
+ template<>
std::shared_ptr<procedure_type> type::get<procedure_type>() const
{
return tag == type_tag::procedure ? this->procedure : nullptr;
@@ -225,8 +245,8 @@ namespace elna::boot
bool type::operator==(const type& other) const
{
- type resolved_this = inner_aliased_type(*this);
- type resolved_that = inner_aliased_type(other);
+ type resolved_this = resolve_aliases(*this);
+ type resolved_that = resolve_aliases(other);
if (auto left_record = resolved_this.get<record_type>())
{
@@ -248,6 +268,12 @@ namespace elna::boot
return right_pointer != nullptr && left_pointer->base == right_pointer->base;
}
+ if (auto left_const = resolved_this.get<constant_type>())
+ {
+ auto right_const = resolved_that.get<constant_type>();
+
+ return right_const != nullptr && left_const->unqualified == right_const->unqualified;
+ }
if (auto left_array = resolved_this.get<array_type>())
{
auto right_array = resolved_that.get<array_type>();
@@ -282,6 +308,8 @@ namespace elna::boot
return primitive->identifier;
case type_tag::pointer:
return "^" + pointer->base.to_string();
+ case type_tag::constant:
+ return "const " + constant->unqualified.to_string();
case type_tag::array:
return "[" + std::to_string(array->size) + "]" + array->base.to_string();
case type_tag::record:
@@ -328,6 +356,11 @@ namespace elna::boot
{
}
+ constant_type::constant_type(type unqualified)
+ : unqualified(unqualified)
+ {
+ }
+
array_type::array_type(type base, std::uint64_t size)
: base(base), size(size)
{
@@ -367,11 +400,6 @@ namespace elna::boot
return nullptr;
}
- std::shared_ptr<constant_info> info::is_constant()
- {
- return nullptr;
- }
-
std::shared_ptr<variable_info> info::is_variable()
{
return nullptr;
@@ -403,16 +431,6 @@ namespace elna::boot
return this->scope == nullptr;
}
- constant_info::constant_info(const variant& symbol)
- : symbol(symbol)
- {
- }
-
- std::shared_ptr<constant_info> constant_info::is_constant()
- {
- return std::static_pointer_cast<constant_info>(shared_from_this());
- }
-
variable_info::variable_info(const type symbol, bool is_extern)
: symbol(symbol), is_extern(is_extern)
{
@@ -524,20 +542,47 @@ namespace elna::boot
return m_exported;
}
- type inner_aliased_type(std::shared_ptr<alias_type> alias)
+ type resolve_underlying_type(std::shared_ptr<alias_type> alias)
{
- return inner_aliased_type(alias->reference);
+ return resolve_underlying_type(alias->reference);
}
- type inner_aliased_type(const type& alias)
+ type resolve_underlying_type(const type& alias)
{
- if (auto aliased = alias.get<alias_type>())
+ type resolved_alias = resolve_aliases(alias);
+
+ if (auto qualified = resolved_alias.get<constant_type>())
{
- return inner_aliased_type(aliased);
+ return resolve_underlying_type(qualified->unqualified);
}
else
{
- return alias;
+ return resolved_alias;
+ }
+ }
+
+ type resolve_aliases(const type& checked)
+ {
+ if (auto alias = checked.get<alias_type>())
+ {
+ return resolve_aliases(alias->reference);
+ }
+ return checked;
+ }
+
+ bool is_primitive_type(const type& checked, const std::string& name)
+ {
+ if (auto primitive_checked = checked.get<primitive_type>())
+ {
+ return primitive_checked->identifier == name;
}
+ return false;
+ }
+
+ bool is_any_pointer_type(const type& checked)
+ {
+ return checked.get<pointer_type>() != nullptr
+ || checked.get<procedure_type>() != nullptr
+ || is_primitive_type(checked, "Pointer");
}
}
diff --git a/gcc/gcc/elna-builtins.cc b/gcc/gcc/elna-builtins.cc
index 9e331cf..3c424f8 100644
--- a/gcc/gcc/elna-builtins.cc
+++ b/gcc/gcc/elna-builtins.cc
@@ -160,6 +160,10 @@ namespace elna::gcc
return build_pointer_type(procedure);
}
+ else if (auto reference = type.get<boot::constant_type>())
+ {
+ return get_inner_alias(reference->unqualified, symbols);
+ }
else if (auto reference = type.get<boot::alias_type>())
{
return TREE_TYPE(handle_symbol(reference->name, reference, symbols));
diff --git a/gcc/gcc/elna-generic.cc b/gcc/gcc/elna-generic.cc
index 4067bb6..b849715 100644
--- a/gcc/gcc/elna-generic.cc
+++ b/gcc/gcc/elna-generic.cc
@@ -228,10 +228,6 @@ namespace elna::gcc
{
declaration->accept(this);
}
- for (boot::constant_declaration *const constant : unit->constants)
- {
- constant->accept(this);
- }
for (boot::variable_declaration *const variable : unit->variables)
{
variable->accept(this);
@@ -309,10 +305,6 @@ namespace elna::gcc
{
this->symbols->enter(IDENTIFIER_POINTER(DECL_NAME(argument_chain)), argument_chain);
}
- for (boot::constant_declaration *const constant : declaration->body.value().constants)
- {
- constant->accept(this);
- }
for (boot::variable_declaration *const variable : declaration->body.value().variables)
{
variable->accept(this);
@@ -704,41 +696,6 @@ namespace elna::gcc
}
}
- void generic_visitor::visit(boot::constant_declaration *declaration)
- {
- location_t definition_location = get_location(&declaration->position());
- declaration->initializer().accept(this);
-
- if (assert_constant(definition_location))
- {
- this->current_expression = fold_init(this->current_expression);
- }
- else
- {
- this->current_expression = NULL_TREE;
- return;
- }
- tree definition_tree = build_decl(definition_location, CONST_DECL,
- get_identifier(declaration->identifier.name().c_str()), TREE_TYPE(this->current_expression));
- auto result = this->symbols->enter(declaration->identifier.name(), definition_tree);
-
- if (result)
- {
- DECL_INITIAL(definition_tree) = this->current_expression;
- TREE_CONSTANT(definition_tree) = 1;
- TREE_READONLY(definition_tree) = 1;
- TREE_PUBLIC(definition_tree) = declaration->identifier.exported();
-
- if (!lang_hooks.decls.global_bindings_p())
- {
- auto declaration_statement = build1_loc(definition_location, DECL_EXPR,
- void_type_node, definition_tree);
- append_statement(declaration_statement);
- }
- }
- this->current_expression = NULL_TREE;
- }
-
void generic_visitor::visit(boot::variable_declaration *declaration)
{
for (const auto& variable_identifier : declaration->identifiers)
@@ -762,6 +719,14 @@ namespace elna::gcc
{
DECL_INITIAL(declaration_tree) = elna_pointer_nil_node;
}
+ {
+ auto variable_symbol = this->bag.lookup(variable_identifier.name())->is_variable();
+
+ if (variable_symbol != nullptr && variable_symbol->symbol.get<boot::constant_type>() != nullptr)
+ {
+ TREE_READONLY(declaration_tree) = 1;
+ }
+ }
this->current_expression = NULL_TREE;
if (lang_hooks.decls.global_bindings_p())
diff --git a/gcc/testlib/elna-dg.exp b/gcc/testlib/elna-dg.exp
index ddebf58..5142884 100644
--- a/gcc/testlib/elna-dg.exp
+++ b/gcc/testlib/elna-dg.exp
@@ -35,6 +35,47 @@ proc elna-dg-prune { system text } {
# Utility routines.
#
+# Escapes a directive argument so that it reaches dejagnu intact.
+#
+# dg.exp evaluates each extracted directive as a Tcl script
+# ("catch $op"), which performs one round of backslash, command and
+# variable substitution inside the double-quoted argument. Quoting the
+# affected characters here means the argument dejagnu receives is
+# exactly what the test author wrote, so @Error messages keep their
+# regular expression semantics: "\[2\]" matches literal brackets and
+# "[2]" stays a character class.
+#
+proc elna-escape-directive { text } {
+ return [string map [list \
+ "\\" "\\\\" \
+ "\[" "\\\[" \
+ "\]" "\\\]" \
+ "\$" "\\\$" \
+ "\"" "\\\""] $text]
+}
+
+#
+# Replaces an elna-native directive in the line with its dg-* equivalent,
+# escaping the directive argument for dejagnu's Tcl evaluation.
+#
+# line_var - name of the variable holding the line in the caller.
+# directive - elna directive name, e.g. @Error.
+# dg_name - dejagnu directive name, e.g. dg-error.
+#
+proc elna-convert-directive { line_var directive dg_name } {
+ upvar 1 $line_var line
+
+ if { [regexp -indices "\\(\\*\\s*$directive\\s+(.*?)\\s*\\*\\)" $line whole argument] } {
+ set escaped [elna-escape-directive \
+ [string range $line [lindex $argument 0] [lindex $argument 1]]]
+ set prefix [string range $line 0 [expr {[lindex $whole 0] - 1}]]
+ set suffix [string range $line [expr {[lindex $whole 1] + 1}] end]
+
+ set line "${prefix}(* { $dg_name \"$escaped\" } *)$suffix"
+ }
+}
+
+#
# This procedure copies a test file from the source tree to the
# build directory while rewriting elna-native directives to dejagnu
# dg-* equivalents.
@@ -60,8 +101,8 @@ proc elna-convert-test { base test } {
# Reads the source test file line by line and replace directives with
# Elna equivalents using a regular expression.
while { [gets $fin line] >= 0 } {
- regsub -all {\(\*\s*@Error\s+(.*?)\s*\*\)} $line {(* { dg-error "\1" } *)} line
- regsub -all {\(\*\s*@Flags\s+(.*?)\s*\*\)} $line {(* { dg-additional-options "\1" } *)} line
+ elna-convert-directive line @Error dg-error
+ elna-convert-directive line @Flags dg-additional-options
puts $fout $line
}
diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h
index b6aad70..42e7666 100644
--- a/include/elna/boot/ast.h
+++ b/include/elna/boot/ast.h
@@ -55,7 +55,6 @@ namespace elna::boot
};
class variable_declaration;
- class constant_declaration;
class procedure_declaration;
class type_declaration;
class procedure_call;
@@ -74,6 +73,7 @@ namespace elna::boot
class type_expression;
class array_type_expression;
class pointer_type_expression;
+ class constant_type_expression;
class record_type_expression;
class procedure_type_expression;
class enumeration_type_expression;
@@ -94,7 +94,6 @@ namespace elna::boot
struct parser_visitor
{
virtual void visit(variable_declaration *) = 0;
- virtual void visit(constant_declaration *) = 0;
virtual void visit(procedure_declaration *) = 0;
virtual void visit(type_declaration *) = 0;
virtual void visit(procedure_call *) = 0;
@@ -114,6 +113,7 @@ namespace elna::boot
virtual void visit(unary_expression *) = 0;
virtual void visit(array_type_expression *) = 0;
virtual void visit(pointer_type_expression *) = 0;
+ virtual void visit(constant_type_expression *) = 0;
virtual void visit(record_type_expression *) = 0;
virtual void visit(procedure_type_expression *) = 0;
virtual void visit(enumeration_type_expression *) = 0;
@@ -138,13 +138,13 @@ namespace elna::boot
public:
[[noreturn]] virtual void visit(array_type_expression *) override;
[[noreturn]] virtual void visit(pointer_type_expression *) override;
+ [[noreturn]] virtual void visit(constant_type_expression *) override;
[[noreturn]] virtual void visit(type_declaration *) override;
[[noreturn]] virtual void visit(record_type_expression *) override;
[[noreturn]] virtual void visit(procedure_type_expression *) override;
[[noreturn]] virtual void visit(enumeration_type_expression *) override;
[[noreturn]] virtual void visit(variable_declaration *) override;
- [[noreturn]] virtual void visit(constant_declaration *) override;
[[noreturn]] virtual void visit(procedure_declaration *) override;
[[noreturn]] virtual void visit(assign_statement *) override;
[[noreturn]] virtual void visit(if_statement *) override;
@@ -182,13 +182,13 @@ namespace elna::boot
public:
virtual void visit(array_type_expression *) override;
virtual void visit(pointer_type_expression *) override;
+ virtual void visit(constant_type_expression *) override;
virtual void visit(type_declaration *) override;
virtual void visit(record_type_expression *) override;
virtual void visit(procedure_type_expression *) override;
virtual void visit(enumeration_type_expression *) override;
virtual void visit(variable_declaration *) override;
- virtual void visit(constant_declaration *) override;
virtual void visit(procedure_declaration *) override;
virtual void visit(assign_statement *) override;
virtual void visit(if_statement *) override;
@@ -280,6 +280,7 @@ namespace elna::boot
virtual named_expression *is_named();
virtual array_type_expression *is_array();
virtual pointer_type_expression *is_pointer();
+ virtual constant_type_expression *is_constant();
virtual record_type_expression *is_record();
virtual procedure_type_expression *is_procedure();
virtual enumeration_type_expression *is_enumeration();
@@ -316,6 +317,20 @@ namespace elna::boot
type_expression& base();
};
+ class constant_type_expression : public type_expression
+ {
+ type_expression *m_base;
+
+ public:
+ constant_type_expression(const source_position position, type_expression *base);
+ ~constant_type_expression() override;
+
+ void accept(parser_visitor *visitor) override;
+ constant_type_expression *is_constant() override;
+
+ type_expression& base();
+ };
+
using field_declaration = std::pair<std::vector<identifier>, std::shared_ptr<type_expression>>;
class record_type_expression : public type_expression
@@ -430,23 +445,6 @@ namespace elna::boot
};
/**
- * Constant definition.
- */
- class constant_declaration : public declaration
- {
- expression *m_initializer;
-
- public:
- constant_declaration(const source_position position, identifier_definition identifier,
- expression *initializer);
- void accept(parser_visitor *visitor) override;
-
- expression& initializer();
-
- virtual ~constant_declaration() override;
- };
-
- /**
* Procedure type.
*/
class procedure_type_expression : public type_expression
@@ -467,15 +465,13 @@ namespace elna::boot
struct procedure_body
{
- const std::vector<constant_declaration *> constants;
const std::vector<variable_declaration *> variables;
const std::vector<statement *> entry_point;
expression *const return_expression{ nullptr };
- procedure_body(std::vector<constant_declaration *>&& constants,
- std::vector<variable_declaration *>&& variables,
+ procedure_body(std::vector<variable_declaration *>&& variables,
std::vector<statement *>&& entry_point,
- expression *return_expr = nullptr);
+ expression *return_expression = nullptr);
procedure_body(const procedure_body&) = delete;
procedure_body(procedure_body&& that);
@@ -773,7 +769,6 @@ namespace elna::boot
unit(const source_position position);
unit(const source_position position,
std::vector<import_declaration *>&& imports,
- std::vector<constant_declaration *>&& constants,
std::vector<type_declaration *>&& types,
std::vector<variable_declaration *>&& variables,
std::vector<procedure_declaration *>&& procedures,
diff --git a/include/elna/boot/semantic.h b/include/elna/boot/semantic.h
index 0f8f6c7..3d2de10 100644
--- a/include/elna/boot/semantic.h
+++ b/include/elna/boot/semantic.h
@@ -85,6 +85,19 @@ namespace elna::boot
};
/**
+ * Attempted to assign a value whose type contains constant members.
+ */
+ class constant_assignment_error : public error
+ {
+ type assignee;
+
+ public:
+ constant_assignment_error(const source_position position, type assignee);
+
+ std::string what() const override;
+ };
+
+ /**
* Attempted to access a field that does not exist on the given type.
*/
class field_not_found_error : public error
@@ -199,8 +212,20 @@ namespace elna::boot
symbol_bag bag;
std::shared_ptr<procedure_info> current_procedure;
- bool check_unresolved_symbol(std::shared_ptr<alias_type> alias,
+ /*
+ * Whether an expression of type assignment can be assigned to a variable
+ * of type assignee.
+ */
+ static bool is_assignable_from(const type& assignee, const type& assignment);
+ /*
+ * Checks whether derived has base in its parent chain.
+ * base should not be null, derived can be null.
+ */
+ static bool is_base_of(const std::shared_ptr<record_type>& base,
+ const std::shared_ptr<record_type>& derived);
+ static bool check_unresolved_symbol(std::shared_ptr<alias_type> alias,
std::vector<std::string>& path);
+
public:
explicit type_analysis_visitor(symbol_bag bag);
@@ -231,7 +256,6 @@ namespace elna::boot
class name_analysis_visitor final : public walking_visitor, public error_container
{
type current_type;
- constant_info::variant current_literal;
symbol_bag bag;
@@ -244,7 +268,6 @@ namespace elna::boot
const bool is_extern, const source_position position);
type lookup_primitive_type(const std::string& name);
- type type_of_constant(const constant_info::variant& value);
type lookup_field(const type& composite_type, const std::string& field_name);
public:
@@ -252,6 +275,7 @@ namespace elna::boot
void visit(array_type_expression *expression) override;
void visit(pointer_type_expression *expression) override;
+ void visit(constant_type_expression *expression) override;
void visit(type_declaration *declaration) override;
void visit(record_type_expression *expression) override;
void visit(record_constructor_expression *expression) override;
@@ -260,7 +284,6 @@ namespace elna::boot
void visit(enumeration_type_expression *expression) override;
void visit(variable_declaration *declaration) override;
- void visit(constant_declaration *declaration) override;
void visit(procedure_declaration *declaration) override;
void visit(procedure_call *call) override;
@@ -297,16 +320,5 @@ namespace elna::boot
void visit(type_declaration *declaration) override;
void visit(procedure_declaration *declaration) override;
void visit(variable_declaration *declaration) override;
- void visit(constant_declaration *declaration) override;
};
-
- /**
- * Whether an expression of type \a assignment can be assigned to a variable
- * of type \a assignee.
- *
- * \param assignee Assignee.
- * \param assignment Assignment.
- * \return Whether the assignment can be performed.
- */
- bool is_assignable_from(const type& assignee, const type& assignment);
}
diff --git a/include/elna/boot/symbol.h b/include/elna/boot/symbol.h
index ec5a051..6884dd7 100644
--- a/include/elna/boot/symbol.h
+++ b/include/elna/boot/symbol.h
@@ -32,6 +32,7 @@ namespace elna::boot
class primitive_type;
class record_type;
class pointer_type;
+ class constant_type;
class array_type;
class procedure_type;
class enumeration_type;
@@ -45,6 +46,7 @@ namespace elna::boot
primitive,
record,
pointer,
+ constant,
array,
procedure,
enumeration
@@ -56,6 +58,7 @@ namespace elna::boot
std::shared_ptr<primitive_type> primitive;
std::shared_ptr<record_type> record;
std::shared_ptr<pointer_type> pointer;
+ std::shared_ptr<constant_type> constant;
std::shared_ptr<array_type> array;
std::shared_ptr<procedure_type> procedure;
std::shared_ptr<enumeration_type> enumeration;
@@ -70,6 +73,7 @@ namespace elna::boot
explicit type(std::shared_ptr<primitive_type> primitive);
explicit type(std::shared_ptr<record_type> record);
explicit type(std::shared_ptr<pointer_type> pointer);
+ explicit type(std::shared_ptr<constant_type> constant);
explicit type(std::shared_ptr<array_type> array);
explicit type(std::shared_ptr<procedure_type> procedure);
explicit type(std::shared_ptr<enumeration_type> enumeration);
@@ -112,6 +116,13 @@ namespace elna::boot
explicit pointer_type(type base);
};
+ struct constant_type
+ {
+ const type unqualified;
+
+ explicit constant_type(type unqualified);
+ };
+
struct array_type
{
const type base;
@@ -156,7 +167,6 @@ namespace elna::boot
class type_info;
class procedure_info;
- class constant_info;
class variable_info;
class info : public std::enable_shared_from_this<info>
@@ -169,7 +179,6 @@ namespace elna::boot
virtual std::shared_ptr<type_info> is_type();
virtual std::shared_ptr<procedure_info> is_procedure();
- virtual std::shared_ptr<constant_info> is_constant();
virtual std::shared_ptr<variable_info> is_variable();
};
@@ -332,18 +341,6 @@ namespace elna::boot
bool is_extern() const;
};
- class constant_info : public info
- {
- public:
- using variant = typename
- std::variant<std::int32_t, std::uint32_t, double, bool, unsigned char, std::nullptr_t, std::string>;
-
- const variant symbol;
-
- explicit constant_info(const variant& symbol);
- std::shared_ptr<constant_info> is_constant() override;
- };
-
/**
* Variable symbol information.
*/
@@ -467,13 +464,46 @@ namespace elna::boot
};
/**
- * If a type is a declared name for another type, look up recursively
- * the declared type. If the given type is not an alias, then this function
- * returns its argument.
+ * Resolves alias chains and type qualifiers until the underlying type is
+ * reached. If the given type is neither an alias nor qualified, it is
+ * returned as is.
+ *
+ * \param alias The type to resolve.
+ * \return The underlying, unqualified type.
+ */
+ type resolve_underlying_type(const type& alias);
+
+ /**
+ * \overload
+ */
+ type resolve_underlying_type(std::shared_ptr<alias_type> alias);
+
+ /**
+ * Resolves an alias chain until the underlying type or a qualified type is
+ * reached.
+ *
+ * \param checked The type to resolve.
+ * \return The first non-alias type in the chain.
+ */
+ type resolve_aliases(const type& checked);
+
+ /**
+ * Checks whether the given type is the built-in type \a name.
+ *
+ * \param checked The type to check.
+ * \param name The name of the primitive type to compare against.
+ * \return Whether the type is the primitive type with the given name.
+ */
+ bool is_primitive_type(const type& checked, const std::string& name);
+
+ /**
+ * Checks whether the given type is a pointer type.
+ *
+ * A pointer type is considered to be a variable pointer, a procedure
+ * pointer, or a built-in Pointer.
*
- * \param alias The type to lookup the innermost declaration for.
- * \return Innermost type declaration.
+ * \param checked The type to check.
+ * \return Whether the type is a pointer type.
*/
- type inner_aliased_type(const type& alias);
- type inner_aliased_type(std::shared_ptr<alias_type> alias);
+ bool is_any_pointer_type(const type& checked);
}
diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h
index e0f20f3..a1689be 100644
--- a/include/elna/gcc/elna-generic.h
+++ b/include/elna/gcc/elna-generic.h
@@ -76,7 +76,6 @@ namespace elna::gcc
void visit(boot::literal<std::string> *string) override;
void visit(boot::binary_expression *expression) override;
void visit(boot::unary_expression *expression) override;
- void visit(boot::constant_declaration *declaration) override;
void visit(boot::variable_declaration *declaration) override;
void visit(boot::named_expression *expression) override;
void visit(boot::array_access_expression *expression) override;
diff --git a/source/lexer.elna b/source/lexer.elna
index 63ca3d3..c221f07 100644
--- a/source/lexer.elna
+++ b/source/lexer.elna
@@ -4,9 +4,6 @@
import cstdio, common, cstdlib
-const
- CHUNK_SIZE := 85536u
-
type
ElnaLexerState = (
start,
diff --git a/testsuite/compilable/float_literals.elna b/testsuite/compilable/float_literals.elna
index ed23989..77af6b1 100644
--- a/testsuite/compilable/float_literals.elna
+++ b/testsuite/compilable/float_literals.elna
@@ -1,9 +1,9 @@
proc f()
-const
- x := 3.14
- y := 1e10
- z := 4.567e8
- t := 2.5E-3
+var
+ x: const Float := 3.14
+ y: const Float := 1e10
+ z: const Float := 4.567e8
+ t: const Float := 2.5E-3
return
end.
diff --git a/testsuite/compilable/pointer_const_conversion.elna b/testsuite/compilable/pointer_const_conversion.elna
new file mode 100644
index 0000000..fd8e097
--- /dev/null
+++ b/testsuite/compilable/pointer_const_conversion.elna
@@ -0,0 +1,7 @@
+var
+ x: Int
+ p: ^const Int
+
+begin
+ p := @x
+end.
diff --git a/testsuite/compilable/take_const_address.elna b/testsuite/compilable/take_const_address.elna
new file mode 100644
index 0000000..54a2992
--- /dev/null
+++ b/testsuite/compilable/take_const_address.elna
@@ -0,0 +1,7 @@
+var
+ x: ^const Int
+ y: const Int
+
+begin
+ x := @y
+end.
diff --git a/testsuite/fail_compilation/assign_const_array_pointer.elna b/testsuite/fail_compilation/assign_const_array_pointer.elna
new file mode 100644
index 0000000..32dc66d
--- /dev/null
+++ b/testsuite/fail_compilation/assign_const_array_pointer.elna
@@ -0,0 +1,9 @@
+proc f()
+var
+ p: ^[2]Int
+ q: ^const [2]Int
+begin
+ p := q (* @Error Expected type '\^\[2\]Int', but got '\^const \[2\]Int' *)
+return
+
+end.
diff --git a/testsuite/fail_compilation/assign_const_member.elna b/testsuite/fail_compilation/assign_const_member.elna
new file mode 100644
index 0000000..8aecb01
--- /dev/null
+++ b/testsuite/fail_compilation/assign_const_member.elna
@@ -0,0 +1,14 @@
+type
+ R = record
+ x: const Int
+ end
+
+proc f()
+var
+ r1: R := R{ x: 5 }
+ r2: R := R{ x: 5 }
+begin
+ r1 := r2 (* @Error Cannot assign to a value of type 'R', because it is constant or contains constant members *)
+return
+
+end.
diff --git a/testsuite/fail_compilation/assign_const_primitive.elna b/testsuite/fail_compilation/assign_const_primitive.elna
new file mode 100644
index 0000000..34b095b
--- /dev/null
+++ b/testsuite/fail_compilation/assign_const_primitive.elna
@@ -0,0 +1,8 @@
+proc f()
+var
+ x: const Int
+begin
+ x := 5 (* @Error Cannot assign to a value of type 'const Int', because it is constant or contains constant members *)
+return
+
+end.
diff --git a/testsuite/fail_compilation/assign_const_record_pointer.elna b/testsuite/fail_compilation/assign_const_record_pointer.elna
new file mode 100644
index 0000000..5e6f3df
--- /dev/null
+++ b/testsuite/fail_compilation/assign_const_record_pointer.elna
@@ -0,0 +1,17 @@
+type
+ B = record
+ x: Int
+ end
+ D = record(B)
+ y: Int
+ end
+
+proc f()
+var
+ b: ^B
+ d: ^const D
+begin
+ b := d (* @Error Expected type '\^B', but got '\^const D' *)
+return
+
+end.
diff --git a/testsuite/fail_compilation/assign_deep_const_pointer.elna b/testsuite/fail_compilation/assign_deep_const_pointer.elna
new file mode 100644
index 0000000..7ed3840
--- /dev/null
+++ b/testsuite/fail_compilation/assign_deep_const_pointer.elna
@@ -0,0 +1,9 @@
+proc f()
+var
+ p: ^^const Int
+ q: ^^Int
+begin
+ p := q (* @Error Expected type '\^\^const Int', but got '\^\^Int' *)
+return
+
+end.
diff --git a/testsuite/fail_compilation/assign_element_of_const.elna b/testsuite/fail_compilation/assign_element_of_const.elna
new file mode 100644
index 0000000..9afd520
--- /dev/null
+++ b/testsuite/fail_compilation/assign_element_of_const.elna
@@ -0,0 +1,8 @@
+proc f()
+var
+ a: const [2]Int
+begin
+ a[1] := 6 (* @Error Cannot assign to a value of type 'const Int', because it is constant or contains constant members *)
+return
+
+end.
diff --git a/testsuite/fail_compilation/assign_member_of_const.elna b/testsuite/fail_compilation/assign_member_of_const.elna
new file mode 100644
index 0000000..fc945bb
--- /dev/null
+++ b/testsuite/fail_compilation/assign_member_of_const.elna
@@ -0,0 +1,13 @@
+type
+ R = record
+ x: Int
+ end
+
+proc f()
+var
+ r: const R := R{ x: 5 }
+begin
+ r.x := 6 (* @Error Cannot assign to a value of type 'const Int', because it is constant or contains constant members *)
+return
+
+end.
diff --git a/testsuite/fail_compilation/local_const_exported.elna b/testsuite/fail_compilation/local_const_exported.elna
deleted file mode 100644
index 1c6c728..0000000
--- a/testsuite/fail_compilation/local_const_exported.elna
+++ /dev/null
@@ -1,6 +0,0 @@
-proc test_local_export()
-const
- c* := 42 (* @Error Local symbol 'c' cannot be exported *)
-return
-
-end.
diff --git a/testsuite/runnable/const_copy.elna b/testsuite/runnable/const_copy.elna
new file mode 100644
index 0000000..5982149
--- /dev/null
+++ b/testsuite/runnable/const_copy.elna
@@ -0,0 +1,12 @@
+proc f()
+var
+ x: const Int := 5
+ y: Int
+begin
+ y := x;
+ assert(y = 5)
+return
+
+begin
+ f()
+end.
diff --git a/testsuite/runnable/const_initialization.elna b/testsuite/runnable/const_initialization.elna
new file mode 100644
index 0000000..7f922ff
--- /dev/null
+++ b/testsuite/runnable/const_initialization.elna
@@ -0,0 +1,10 @@
+proc f()
+var
+ x: const Int := 5
+begin
+ assert(x = 5)
+return
+
+begin
+ f()
+end.