aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--boot/ast.cc75
-rw-r--r--boot/semantic.cc524
-rw-r--r--boot/symbol.cc68
-rw-r--r--gcc/gcc/elna-generic.cc119
-rw-r--r--include/elna/boot/ast.h6
-rw-r--r--include/elna/boot/result.h5
-rw-r--r--include/elna/boot/semantic.h79
-rw-r--r--include/elna/boot/symbol.h7
-rw-r--r--source/lexer.elna8
-rw-r--r--testsuite/compilable/assign_record_to_base.elna16
-rw-r--r--testsuite/compilable/pointer_cast.elna8
11 files changed, 714 insertions, 201 deletions
diff --git a/boot/ast.cc b/boot/ast.cc
index 4270a3b..d0d19b7 100644
--- a/boot/ast.cc
+++ b/boot/ast.cc
@@ -19,184 +19,179 @@ along with GCC; see the file COPYING3. If not see
namespace elna::boot
{
- void empty_visitor::not_implemented()
- {
- __builtin_unreachable();
- }
-
void empty_visitor::visit(array_type_expression *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(pointer_type_expression *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(type_declaration *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(record_type_expression *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(union_type_expression *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(procedure_type_expression *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(enumeration_type_expression *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(variable_declaration *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(constant_declaration *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(procedure_declaration *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(assign_statement *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(if_statement *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(import_declaration *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(while_statement *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(return_statement *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(defer_statement *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(empty_statement *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(case_statement *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(procedure_call *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(unit *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(cast_expression *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(traits_expression *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(binary_expression *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(unary_expression *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(named_expression *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(array_access_expression *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(field_access_expression *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(dereference_expression *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(literal<std::int32_t> *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(literal<std::uint32_t> *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(literal<double> *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(literal<bool> *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(literal<unsigned char> *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(literal<std::nullptr_t> *)
{
- not_implemented();
+ __builtin_unreachable();
}
void empty_visitor::visit(literal<std::string> *)
{
- not_implemented();
+ __builtin_unreachable();
}
void walking_visitor::visit(import_declaration *)
diff --git a/boot/semantic.cc b/boot/semantic.cc
index 42b7b3c..df6e70e 100644
--- a/boot/semantic.cc
+++ b/boot/semantic.cc
@@ -18,28 +18,46 @@ along with GCC; see the file COPYING3. If not see
#include "elna/boot/semantic.h"
#include <algorithm>
-#include <set>
namespace elna::boot
{
- undeclared_error::undeclared_error(const std::string& identifier, const struct position position)
- : error(position), identifier(identifier)
+ declaration_error::declaration_error(const kind error_kind,
+ const std::string& identifier, const struct position position)
+ : error(position), identifier(identifier), error_kind(error_kind)
{
}
- std::string undeclared_error::what() const
+ std::string declaration_error::what() const
{
- return "Type '" + identifier + "' not declared";
+ switch (this->error_kind)
+ {
+ case kind::undeclared:
+ return "Type '" + identifier + "' not declared";
+ case kind::already:
+ return "Symbol '" + identifier + "' has been already declared";
+ default:
+ __builtin_unreachable();
+ }
}
- already_declared_error::already_declared_error(const std::string& identifier, const struct position position)
- : error(position), identifier(identifier)
+ type_expectation_error::type_expectation_error(const kind error_kind, const struct position position)
+ : error(position), error_kind(error_kind)
{
}
- std::string already_declared_error::what() const
+ std::string type_expectation_error::what() const
{
- return "Symbol '" + identifier + "' has been already declared";
+ switch (this->error_kind)
+ {
+ case kind::assignment:
+ return "Assinging an incompatible type";
+ case kind::result:
+ return "Procedure return type and type of the return expression are not compatible";
+ case kind::argument:
+ return "Argument type does not match parameter";
+ default:
+ __builtin_unreachable();
+ }
}
field_duplication_error::field_duplication_error(const std::string& field_name, const struct position position)
@@ -91,6 +109,26 @@ namespace elna::boot
return "Record base type '" + this->base + "' is not a record.";
}
+ argument_count_error::argument_count_error(std::size_t expected, std::size_t actual,
+ const struct position position)
+ : error(position), expected(expected), actual(actual)
+ {
+ }
+
+ std::string argument_count_error::what() const
+ {
+ if (actual > expected)
+ {
+ return "Too many arguments, expected " + std::to_string(expected)
+ + ", got " + std::to_string(actual);
+ }
+ else
+ {
+ return "Too few arguments, expected " + std::to_string(expected)
+ + ", got " + std::to_string(actual);
+ }
+ }
+
type_analysis_visitor::type_analysis_visitor(symbol_bag bag)
: error_container(), bag(bag)
{
@@ -98,24 +136,101 @@ namespace elna::boot
void type_analysis_visitor::visit(procedure_declaration *declaration)
{
+ this->current_procedure = this->bag.lookup(declaration->identifier.name)->is_procedure();
this->returns = false;
+
+ if (declaration->body.has_value())
+ {
+ this->bag.enter(this->current_procedure->scope);
+ }
walking_visitor::visit(declaration);
if (declaration->body.has_value())
{
+ this->bag.leave();
if (!this->returns && declaration->heading().return_type.proper_type != nullptr)
{
add_error<return_error>(declaration->identifier.name, declaration->position());
}
}
+ this->current_procedure.reset();
}
void type_analysis_visitor::visit(return_statement *statement)
{
walking_visitor::visit(statement);
+ type return_type;
+
+ if (this->current_procedure == nullptr)
+ {
+ // In the main function.
+ return_type = this->bag.lookup("Int")->is_type()->symbol;
+ }
+ else
+ {
+ return_type = this->current_procedure->symbol.return_type.proper_type;
+ }
+ if (!return_type.empty())
+ {
+ if (!is_assignable_from(return_type, statement->return_expression().type_decoration))
+ {
+ add_error<type_expectation_error>(type_expectation_error::kind::result, statement->position());
+ }
+ }
+ else
+ {
+ add_error<type_expectation_error>(type_expectation_error::kind::result, statement->position());
+ }
this->returns = true;
}
+ void type_analysis_visitor::visit(assign_statement *statement)
+ {
+ walking_visitor::visit(statement);
+
+ if (!is_assignable_from(statement->lvalue().type_decoration, statement->rvalue().type_decoration))
+ {
+ add_error<type_expectation_error>(type_expectation_error::kind::assignment, statement->position());
+ }
+ }
+
+ void type_analysis_visitor::visit(variable_declaration *declaration)
+ {
+ walking_visitor::visit(declaration);
+
+ if (declaration->initializer == nullptr)
+ {
+ return;
+ }
+ for (const identifier_definition& 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))
+ {
+ add_error<type_expectation_error>(type_expectation_error::kind::assignment,
+ declaration->initializer->position());
+ }
+ }
+ }
+
+ void type_analysis_visitor::visit(case_statement *statement)
+ {
+ walking_visitor::visit(statement);
+ type condition_type = inner_aliased_type(statement->condition().type_decoration);
+
+ for (const switch_case& case_block : statement->cases)
+ {
+ for (expression *const case_label : case_block.labels)
+ {
+ if (!is_assignable_from(condition_type, case_label->type_decoration))
+ {
+ add_error<type_expectation_error>(type_expectation_error::kind::assignment,
+ case_label->position());
+ }
+ }
+ }
+ }
+
bool type_analysis_visitor::check_unresolved_symbol(std::shared_ptr<alias_type> alias,
std::vector<std::string>& alias_path)
{
@@ -160,6 +275,78 @@ namespace elna::boot
walking_visitor::visit(expression);
}
+ std::size_t type_analysis_visitor::match_record_fields(
+ const std::shared_ptr<record_type>& record,
+ std::vector<expression *>::const_iterator& argument_it,
+ const std::vector<expression *>::const_iterator& argument_end)
+ {
+ std::size_t expected = 0;
+
+ if (!record->base.empty())
+ {
+ if (auto base = inner_aliased_type(record->base).get<record_type>())
+ {
+ expected += match_record_fields(base, argument_it, argument_end);
+ }
+ }
+ for (auto& field : record->fields)
+ {
+ ++expected;
+ if (argument_it != argument_end)
+ {
+ (*argument_it)->accept(this);
+ if (!is_assignable_from(field.second, (*argument_it)->type_decoration))
+ {
+ add_error<type_expectation_error>(type_expectation_error::kind::argument,
+ (*argument_it)->position());
+ }
+ ++argument_it;
+ }
+ }
+ return expected;
+ }
+
+ void type_analysis_visitor::visit(procedure_call *call)
+ {
+ call->callable().accept(this);
+
+ if (auto procedure = call->callable().type_decoration.get<procedure_type>())
+ {
+ std::vector<expression *>::const_iterator argument_iterator = std::cbegin(call->arguments);
+ std::vector<type>::const_iterator type_iterator = std::cbegin(procedure->parameters);
+
+ while (argument_iterator != std::cend(call->arguments)
+ && type_iterator != std::cend(procedure->parameters))
+ {
+ (*argument_iterator)->accept(this);
+ if (!is_assignable_from(*type_iterator, (*argument_iterator)->type_decoration))
+ {
+ add_error<type_expectation_error>(type_expectation_error::kind::argument,
+ (*argument_iterator)->position());
+ }
+ ++argument_iterator;
+ ++type_iterator;
+ }
+ if (call->arguments.size() != procedure->parameters.size())
+ {
+ add_error<argument_count_error>(procedure->parameters.size(),
+ call->arguments.size(), call->position());
+ }
+ }
+ else if (auto callable_record = inner_aliased_type(call->type_decoration).get<record_type>())
+ {
+ auto argument_it = std::cbegin(call->arguments);
+ auto argument_end = std::cend(call->arguments);
+
+ std::size_t expected = match_record_fields(callable_record, argument_it, argument_end);
+
+ if (call->arguments.size() != expected)
+ {
+ add_error<argument_count_error>(expected, call->arguments.size(), call->position());
+ }
+ }
+ }
+
name_analysis_visitor::name_analysis_visitor(symbol_bag bag)
: error_container(), bag(bag)
{
@@ -198,6 +385,93 @@ namespace elna::boot
return result_type;
}
+ type name_analysis_visitor::lookup_primitive_type(const std::string& name)
+ {
+ 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);
+
+ if (auto record = resolved_type.get<record_type>())
+ {
+ for (auto& field : record->fields)
+ {
+ if (field.first == field_name)
+ {
+ return field.second;
+ }
+ }
+ if (!record->base.empty())
+ {
+ return lookup_field(record->base, field_name);
+ }
+ }
+ else if (auto primitive = resolved_type.get<primitive_type>(); primitive != nullptr && primitive->identifier == "String")
+ {
+ if (field_name == "length")
+ {
+ return lookup_primitive_type("Word");
+ }
+ else if (field_name == "ptr")
+ {
+ return type(std::make_shared<pointer_type>(lookup_primitive_type("Char")));
+ }
+ }
+ else if (auto composite = resolved_type.get<union_type>())
+ {
+ for (auto& field : composite->fields)
+ {
+ if (field.first == field_name)
+ {
+ return field.second;
+ }
+ }
+ }
+ return type();
+ }
+
void name_analysis_visitor::visit(type_declaration *declaration)
{
walking_visitor::visit(declaration);
@@ -222,23 +496,42 @@ namespace elna::boot
this->current_type = type(result_type);
}
- std::vector<type_field> name_analysis_visitor::build_composite_type(const std::vector<field_declaration>& fields)
+ /**
+ * Collects field names from a record type recursively, base first.
+ */
+ static void collect_field_names(const type& composite_type, std::set<std::string>& names)
+ {
+ auto record = inner_aliased_type(composite_type).get<record_type>();
+ if (record == nullptr)
+ {
+ return;
+ }
+ if (!record->base.empty())
+ {
+ collect_field_names(record->base, names);
+ }
+ for (auto& field : record->fields)
+ {
+ names.insert(field.first);
+ }
+ }
+
+ std::vector<type_field> name_analysis_visitor::build_composite_type(
+ const std::vector<field_declaration>& fields, std::set<std::string>& field_names)
{
std::vector<type_field> result;
- std::set<std::string> field_names;
for (auto& field : fields)
{
field.second->accept(this);
for (auto& field_name : field.first)
{
- if (field_names.find(field_name) != field_names.cend())
+ if (!field_names.insert(field_name).second)
{
add_error<field_duplication_error>(field_name, field.second->position());
}
else
{
- field_names.insert(field_name);
result.push_back(std::make_pair(field_name, this->current_type));
}
}
@@ -270,7 +563,8 @@ namespace elna::boot
}
else
{
- add_error<undeclared_error>(expression->base.value(), expression->position());
+ add_error<declaration_error>(declaration_error::kind::undeclared,
+ expression->base.value(), expression->position());
this->current_type = type();
return;
}
@@ -279,7 +573,10 @@ namespace elna::boot
{
result_type = std::make_shared<record_type>();
}
- result_type->fields = build_composite_type(expression->fields);
+
+ std::set<std::string> field_names;
+ collect_field_names(result_type->base, field_names);
+ result_type->fields = build_composite_type(expression->fields, field_names);
this->current_type = type(result_type);
}
@@ -288,7 +585,8 @@ namespace elna::boot
{
auto result_type = std::make_shared<union_type>();
- result_type->fields = build_composite_type(expression->fields);
+ std::set<std::string> field_names;
+ result_type->fields = build_composite_type(expression->fields, field_names);
this->current_type = type(result_type);
}
@@ -315,7 +613,7 @@ namespace elna::boot
if (!this->bag.enter(name, variable_symbol))
{
- add_error<already_declared_error>(name, position);
+ add_error<declaration_error>(declaration_error::kind::already, name, position);
}
return variable_symbol;
}
@@ -385,11 +683,14 @@ namespace elna::boot
void name_analysis_visitor::visit(procedure_call *call)
{
- auto name_expression = call->callable().is_named();
-
- if (name_expression == nullptr || name_expression->name != "assert")
+ call->callable().accept(this);
+ if (auto procedure = call->callable().type_decoration.get<procedure_type>())
+ {
+ call->type_decoration = procedure->return_type.proper_type;
+ }
+ else if (call->callable().is_named() != nullptr)
{
- call->callable().accept(this);
+ call->type_decoration = this->current_type;
}
for (expression *const argument : call->arguments)
{
@@ -418,10 +719,10 @@ namespace elna::boot
if (unit->entry_point.has_value())
{
this->bag.enter();
- auto variable_type = this->bag.lookup("Int")->is_type()->symbol;
+ auto variable_type = lookup_primitive_type("Int");
this->bag.enter("count", std::make_shared<variable_info>(variable_type, false));
- variable_type = this->bag.lookup("Char")->is_type()->symbol;
+ variable_type = lookup_primitive_type("Char");
variable_type = type(std::make_shared<pointer_type>(variable_type));
variable_type = type(std::make_shared<pointer_type>(variable_type));
this->bag.enter("parameters", std::make_shared<variable_info>(variable_type, false));
@@ -441,16 +742,99 @@ namespace elna::boot
trait->parameters.front()->accept(this);
trait->types.push_back(this->current_type);
}
+
+ if (trait->name == "size" || trait->name == "alignment" || trait->name == "offset")
+ {
+ trait->type_decoration = lookup_primitive_type("Word");
+ }
+ else if (trait->name == "min" || trait->name == "max")
+ {
+ trait->type_decoration = trait->types.empty() ? type() : trait->types.front();
+ }
+ }
+
+ void name_analysis_visitor::visit(binary_expression *expression)
+ {
+ walking_visitor::visit(expression);
+
+ switch (expression->operation())
+ {
+ case binary_operator::equals:
+ case binary_operator::not_equals:
+ case binary_operator::less:
+ case binary_operator::greater:
+ case binary_operator::less_equal:
+ case binary_operator::greater_equal:
+ expression->type_decoration = lookup_primitive_type("Bool");
+ break;
+ default:
+ expression->type_decoration = expression->lhs().type_decoration;
+ break;
+ }
+ }
+
+ void name_analysis_visitor::visit(unary_expression *expression)
+ {
+ walking_visitor::visit(expression);
+
+ if (expression->operation() == unary_operator::reference)
+ {
+ expression->type_decoration = this->current_type
+ = type(std::make_shared<pointer_type>(expression->operand().type_decoration));
+ }
+ else
+ {
+ expression->type_decoration = expression->operand().type_decoration;
+ }
+ }
+
+ void name_analysis_visitor::visit(array_access_expression *expression)
+ {
+ walking_visitor::visit(expression);
+ auto resolved_base= inner_aliased_type(expression->base().type_decoration);
+
+ if (auto array = resolved_base.get<array_type>())
+ {
+ expression->type_decoration = array->base;
+ }
+ else if (resolved_base == lookup_primitive_type("String"))
+ {
+ expression->type_decoration = lookup_primitive_type("Char");
+ }
+ }
+
+ void name_analysis_visitor::visit(field_access_expression *expression)
+ {
+ walking_visitor::visit(expression);
+ expression->type_decoration = lookup_field(expression->base().type_decoration, expression->field());
+ auto is_designator = expression->base().is_designator();
+
+ if (expression->type_decoration.empty() && is_designator != nullptr && is_designator->is_named() != nullptr)
+ {
+ expression->type_decoration = this->current_type;
+ }
+ }
+
+ void name_analysis_visitor::visit(dereference_expression *expression)
+ {
+ walking_visitor::visit(expression);
+
+ if (auto pointer = inner_aliased_type(expression->base().type_decoration).get<pointer_type>())
+ {
+ expression->type_decoration = pointer->base;
+ }
}
void name_analysis_visitor::visit(cast_expression *expression)
{
walking_visitor::visit(expression);
- expression->expression_type = this->current_type;
+ expression->type_decoration = this->current_type;
}
void name_analysis_visitor::visit(named_expression *expression)
{
+ this->current_type = type();
+
if (auto unresolved_alias = this->bag.declared(expression->name))
{
this->current_type = type(unresolved_alias);
@@ -461,47 +845,66 @@ namespace elna::boot
{
this->current_type = type_symbol->symbol;
}
+ else if (auto variable_symbol = from_symbol_table->is_variable())
+ {
+ 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));
+ }
}
else
{
- add_error<undeclared_error>(expression->name, expression->position());
- this->current_type = type();
+ add_error<declaration_error>(declaration_error::kind::undeclared,
+ expression->name, expression->position());
}
}
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");
}
declaration_visitor::declaration_visitor()
@@ -531,7 +934,74 @@ namespace elna::boot
if (!this->unresolved.insert({ type_identifier, std::make_shared<alias_type>(type_identifier) }).second)
{
- add_error<already_declared_error>(declaration->identifier.name, declaration->position());
+ add_error<declaration_error>(declaration_error::kind::already,
+ declaration->identifier.name, declaration->position());
+ }
+ }
+
+ 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 512cf04..b75cdc1 100644
--- a/boot/symbol.cc
+++ b/boot/symbol.cc
@@ -157,11 +157,6 @@ namespace elna::boot
return *this;
}
- bool type::operator==(const std::nullptr_t&)
- {
- return empty();
- }
-
type::~type()
{
switch (tag)
@@ -243,6 +238,57 @@ namespace elna::boot
return tag == type_tag::enumeration ? this->enumeration : nullptr;
}
+ bool type::operator==(const std::nullptr_t&) const
+ {
+ return empty();
+ }
+
+ bool type::operator==(const type& other) const
+ {
+ type resolved_this = inner_aliased_type(*this);
+ type resolved_that = inner_aliased_type(other);
+
+ if (auto left_record = resolved_this.get<record_type>())
+ {
+ return left_record == resolved_that.get<record_type>();
+ }
+ if (auto left_primitive = resolved_this.get<primitive_type>())
+ {
+ auto right_primitive = resolved_that.get<primitive_type>();
+
+ return right_primitive != nullptr && left_primitive->identifier == right_primitive->identifier;
+ }
+ if (auto left_union = resolved_this.get<union_type>())
+ {
+ return left_union == resolved_that.get<union_type>();
+ }
+ if (auto left_enumeration = resolved_this.get<enumeration_type>())
+ {
+ return left_enumeration == resolved_that.get<enumeration_type>();
+ }
+ if (auto left_pointer = resolved_this.get<pointer_type>())
+ {
+ auto right_pointer = resolved_that.get<pointer_type>();
+
+ return right_pointer != nullptr && left_pointer->base == right_pointer->base;
+ }
+ if (auto left_array = resolved_this.get<array_type>())
+ {
+ auto right_array = resolved_that.get<array_type>();
+
+ return right_array != nullptr && left_array->size == right_array->size
+ && left_array->base == right_array->base;
+ }
+ if (auto left_procedure = resolved_this.get<procedure_type>())
+ {
+ auto right_procedure = resolved_that.get<procedure_type>();
+
+ return right_procedure != nullptr && left_procedure->return_type == right_procedure->return_type
+ && left_procedure->parameters == right_procedure->parameters;
+ }
+ return resolved_this.empty() && resolved_that.empty();
+ }
+
bool type::empty() const
{
return tag == type_tag::empty;
@@ -360,11 +406,19 @@ namespace elna::boot
result->enter("Int", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Int"))));
result->enter("Word", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Word"))));
result->enter("Char", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Char"))));
- result->enter("Bool", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Bool"))));
result->enter("Pointer", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Pointer"))));
result->enter("Float", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Float"))));
result->enter("String", std::make_shared<type_info>(type(std::make_shared<primitive_type>("String"))));
+ type boolean = type(std::make_shared<primitive_type>("Bool"));
+ result->enter("Bool", std::make_shared<type_info>(boolean));
+
+ procedure_type assert_symbol{ procedure_type::return_t() };
+ assert_symbol.parameters.push_back(boolean);
+ std::shared_ptr<procedure_info> assert_info = std::make_shared<procedure_info>(assert_symbol,
+ std::vector<std::string>{ "condition" });
+ result->enter("assert", assert_info);
+
return result;
}
@@ -435,7 +489,7 @@ namespace elna::boot
return inner_aliased_type(alias->reference);
}
- type inner_aliased_type(type alias)
+ type inner_aliased_type(const type& alias)
{
if (auto aliased = alias.get<alias_type>())
{
diff --git a/gcc/gcc/elna-generic.cc b/gcc/gcc/elna-generic.cc
index 9c8990b..497bcc6 100644
--- a/gcc/gcc/elna-generic.cc
+++ b/gcc/gcc/elna-generic.cc
@@ -46,86 +46,29 @@ namespace elna::gcc
vec<tree, va_gc> *argument_trees = nullptr;
tree symbol_type = TREE_TYPE(TREE_TYPE(procedure_address));
- tree current_parameter = TYPE_ARG_TYPES(symbol_type);
-
vec_alloc(argument_trees, arguments.size());
for (boot::expression *const argument : arguments)
{
- location_t argument_location = get_location(&argument->position());
- if (VOID_TYPE_P(TREE_VALUE(current_parameter)))
- {
- error_at(argument_location, "Too many arguments, expected %i, got %lu",
- list_length(TYPE_ARG_TYPES(symbol_type)) - 1, arguments.size());
- this->current_expression = error_mark_node;
- break;
- }
argument->accept(this);
this->current_expression = prepare_rvalue(this->current_expression);
- if (!is_assignable_from(TREE_VALUE(current_parameter), this->current_expression))
- {
- error_at(argument_location,
- "Cannot assign value of type '%s' to variable of type '%s'",
- print_type(TREE_TYPE(this->current_expression)).c_str(),
- print_type(TREE_VALUE(current_parameter)).c_str());
- this->current_expression = error_mark_node;
- }
- current_parameter = TREE_CHAIN(current_parameter);
argument_trees->quick_push(this->current_expression);
}
- tree stmt = fold_build_call_array_loc(call_location, TREE_TYPE(symbol_type),
+ this->current_expression = fold_build_call_array_loc(call_location, TREE_TYPE(symbol_type),
procedure_address, vec_safe_length(argument_trees), vec_safe_address(argument_trees));
-
- if (!VOID_TYPE_P(TREE_VALUE(current_parameter)))
- {
- error_at(call_location, "Too few arguments, expected %i, got %lu",
- list_length(TYPE_ARG_TYPES(symbol_type)) - 1, arguments.size());
- this->current_expression = error_mark_node;
- }
- else
- {
- this->current_expression = stmt;
- }
}
- void generic_visitor::build_record_call(location_t call_location,
- tree symbol, const std::vector<boot::expression *>& arguments)
+ void generic_visitor::build_record_call(location_t, tree symbol,
+ const std::vector<boot::expression *>& arguments)
{
vec<constructor_elt, va_gc> *tree_arguments = nullptr;
tree record_fields = TYPE_FIELDS(symbol);
for (boot::expression *const argument : arguments)
{
- location_t argument_location = get_location(&argument->position());
-
- if (is_void_type(record_fields))
- {
- error_at(argument_location, "Too many arguments, expected %i, got %lu",
- list_length(TYPE_FIELDS(symbol)), arguments.size());
- this->current_expression = error_mark_node;
- break;
- }
argument->accept(this);
- tree unqualified_field = get_qualified_type(TREE_TYPE(record_fields), TYPE_UNQUALIFIED);
- if (!is_assignable_from(unqualified_field, this->current_expression))
- {
- error_at(argument_location,
- "Cannot assign value of type '%s' to variable of type '%s'",
- print_type(TREE_TYPE(this->current_expression)).c_str(),
- print_type(TREE_TYPE(record_fields)).c_str());
- this->current_expression = error_mark_node;
- }
CONSTRUCTOR_APPEND_ELT(tree_arguments, record_fields, this->current_expression);
record_fields = TREE_CHAIN(record_fields);
}
- if (!is_void_type(record_fields))
- {
- error_at(call_location, "Too few arguments, expected %i, got %lu",
- list_length(TYPE_FIELDS(symbol)), arguments.size());
- this->current_expression = error_mark_node;
- }
- else
- {
- this->current_expression = build_constructor(symbol, tree_arguments);
- }
+ this->current_expression = build_constructor(symbol, tree_arguments);
}
void generic_visitor::build_assert_builtin(location_t call_location,
@@ -217,7 +160,7 @@ namespace elna::gcc
void generic_visitor::visit(boot::cast_expression *expression)
{
- tree cast_target = get_inner_alias(expression->expression_type, this->symbols->scope());
+ tree cast_target = get_inner_alias(expression->type_decoration, this->symbols->scope());
expression->value().accept(this);
tree cast_source = TREE_TYPE(this->current_expression);
@@ -746,16 +689,7 @@ namespace elna::gcc
if (declaration->initializer != nullptr)
{
declaration->initializer->accept(this);
- if (is_assignable_from(TREE_TYPE(declaration_tree), this->current_expression))
- {
- DECL_INITIAL(declaration_tree) = this->current_expression;
- }
- else
- {
- error_at(declaration_location, "Cannot initialize variable of type '%s' with a value of type '%s'",
- print_type(TREE_TYPE(declaration_tree)).c_str(),
- print_type(TREE_TYPE(this->current_expression)).c_str());
- }
+ DECL_INITIAL(declaration_tree) = this->current_expression;
}
else if (!declaration->is_extern && POINTER_TYPE_P(TREE_TYPE(declaration_tree)))
{
@@ -1027,18 +961,12 @@ namespace elna::gcc
error_at(statement_location, "Cannot modify a constant expression of type '%s'",
print_type(TREE_TYPE(lvalue)).c_str());
}
- else if (is_assignable_from(TREE_TYPE(lvalue), rvalue))
+ else
{
tree assignment = build2_loc(statement_location, MODIFY_EXPR, void_type_node, lvalue, rvalue);
append_statement(assignment);
}
- else
- {
- error_at(statement_location, "Cannot assign value of type '%s' to variable of type '%s'",
- print_type(TREE_TYPE(rvalue)).c_str(),
- print_type(TREE_TYPE(lvalue)).c_str());
- }
this->current_expression = NULL_TREE;
}
@@ -1143,7 +1071,6 @@ namespace elna::gcc
boot::expression *return_expression = &statement->return_expression();
location_t statement_position = get_location(&statement->position());
tree set_result{ NULL_TREE };
- tree return_type = TREE_TYPE(TREE_TYPE(current_function_decl));
if (TREE_THIS_VOLATILE(current_function_decl) == 1)
{
@@ -1157,26 +1084,9 @@ namespace elna::gcc
set_result = build2(INIT_EXPR, void_type_node, DECL_RESULT(current_function_decl),
this->current_expression);
}
- if (return_type == void_type_node && set_result != NULL_TREE)
- {
- error_at(statement_position, "Proper procedure is not allowed to return a value");
- }
- else if (return_type != void_type_node && set_result == NULL_TREE)
- {
- error_at(statement_position, "Procedure is expected to return a value of type '%s'",
- print_type(return_type).c_str());
- }
- else if (return_type != void_type_node && !is_assignable_from(return_type, this->current_expression))
- {
- error_at(statement_position, "Cannot return '%s' from a procedure returning '%s'",
- print_type(return_type).c_str(),
- print_type(TREE_TYPE(this->current_expression)).c_str());
- }
- else
- {
- tree return_stmt = build1_loc(statement_position, RETURN_EXPR, void_type_node, set_result);
- append_statement(return_stmt);
- }
+ tree return_stmt = build1_loc(statement_position, RETURN_EXPR, void_type_node, set_result);
+ append_statement(return_stmt);
+
this->current_expression = NULL_TREE;
}
@@ -1215,14 +1125,7 @@ namespace elna::gcc
case_label->accept(this);
location_t case_location = get_location(&case_label->position());
- if (assert_constant(case_location)
- && !is_assignable_from(unqualified_condition, this->current_expression))
- {
- error_at(case_location, "Case type '%s' does not match the expression type '%s'",
- print_type(TREE_TYPE(this->current_expression)).c_str(),
- print_type(unqualified_condition).c_str());
- this->current_expression = error_mark_node;
- }
+ assert_constant(case_location);
tree case_label_declaration = create_artificial_label(case_location);
tree case_expression = build_case_label(this->current_expression, NULL_TREE, case_label_declaration);
diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h
index 1c27166..17a0dc1 100644
--- a/include/elna/boot/ast.h
+++ b/include/elna/boot/ast.h
@@ -135,8 +135,6 @@ namespace elna::boot
*/
class empty_visitor : public parser_visitor
{
- [[noreturn]] void not_implemented();
-
public:
[[noreturn]] virtual void visit(array_type_expression *) override;
[[noreturn]] virtual void visit(pointer_type_expression *) override;
@@ -250,6 +248,8 @@ namespace elna::boot
class expression : public virtual node
{
public:
+ type type_decoration;
+
virtual cast_expression *is_cast();
virtual traits_expression *is_traits();
virtual binary_expression *is_binary();
@@ -501,8 +501,6 @@ namespace elna::boot
expression *m_value;
public:
- type expression_type;
-
cast_expression(const struct position position, type_expression *target, expression *value);
void accept(parser_visitor *visitor) override;
cast_expression *is_cast() override;
diff --git a/include/elna/boot/result.h b/include/elna/boot/result.h
index 3a84229..1b04bfb 100644
--- a/include/elna/boot/result.h
+++ b/include/elna/boot/result.h
@@ -100,6 +100,11 @@ namespace elna::boot
{
}
+ bool operator==(const return_declaration& other) const
+ {
+ return this->proper_type == other.proper_type && this->no_return == other.no_return;
+ }
+
T proper_type{};
bool no_return{ false };
};
diff --git a/include/elna/boot/semantic.h b/include/elna/boot/semantic.h
index 9d79b14..e351258 100644
--- a/include/elna/boot/semantic.h
+++ b/include/elna/boot/semantic.h
@@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see
#include <string>
#include <unordered_map>
#include <memory>
+#include <set>
#include <deque>
#include "elna/boot/ast.h"
@@ -29,29 +30,47 @@ along with GCC; see the file COPYING3. If not see
namespace elna::boot
{
/**
- * Undeclared identifier.
+ * Error declaring a symbol.
*/
- class undeclared_error : public error
+ class declaration_error : public error
{
const std::string identifier;
public:
- undeclared_error(const std::string& identifier, const struct position position);
+ enum class kind
+ {
+ undeclared,
+ already
+ };
+
+ declaration_error(const kind error_kind,
+ const std::string& identifier, const struct position position);
std::string what() const override;
+
+ private:
+ const kind error_kind;
};
/**
* A symbol was already declared in this scope.
*/
- class already_declared_error : public error
+ class type_expectation_error : public error
{
- const std::string identifier;
-
public:
- already_declared_error(const std::string& identifier, const struct position position);
+ enum class kind
+ {
+ assignment,
+ result,
+ argument
+ };
+
+ type_expectation_error(const kind error_kind, const struct position position);
std::string what() const override;
+
+ private:
+ const kind error_kind;
};
/**
@@ -107,23 +126,47 @@ namespace elna::boot
};
/**
+ * Argument count in a procedure or record call doesn't match
+ * the expected number of parameters or fields.
+ */
+ class argument_count_error : public error
+ {
+ const std::size_t expected;
+ const std::size_t actual;
+
+ public:
+ argument_count_error(std::size_t expected, std::size_t actual,
+ const struct position position);
+
+ std::string what() const override;
+ };
+
+ /**
* Checks types.
*/
class type_analysis_visitor final : public walking_visitor, public error_container
{
bool returns;
symbol_bag bag;
+ std::shared_ptr<procedure_info> current_procedure;
bool check_unresolved_symbol(std::shared_ptr<alias_type> alias,
std::vector<std::string>& path);
+ std::size_t match_record_fields(const std::shared_ptr<record_type>& record,
+ std::vector<expression *>::const_iterator& argument_it,
+ const std::vector<expression *>::const_iterator& argument_end);
public:
explicit type_analysis_visitor(symbol_bag bag);
void visit(procedure_declaration *declaration) override;
void visit(return_statement *statement) override;
+ void visit(assign_statement *statement) override;
+ void visit(variable_declaration *declaration) override;
void visit(type_declaration *declaration) override;
void visit(record_type_expression *expression) override;
+ void visit(procedure_call *call) override;
+ void visit(case_statement *statement) override;
};
/**
@@ -138,10 +181,15 @@ namespace elna::boot
std::pair<procedure_type, std::vector<std::string>> build_procedure(
procedure_type_expression& expression);
- std::vector<type_field> build_composite_type(const std::vector<field_declaration>& fields);
+ std::vector<type_field> build_composite_type(const std::vector<field_declaration>& fields,
+ std::set<std::string>& known_names);
std::shared_ptr<variable_info> register_variable(const std::string& name,
const bool is_extern, const struct 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:
name_analysis_visitor(symbol_bag bag);
@@ -161,7 +209,12 @@ namespace elna::boot
void visit(unit *unit) override;
void visit(cast_expression *expression) override;
void visit(traits_expression *trait) override;
+ void visit(binary_expression *expression) override;
+ void visit(unary_expression *expression) override;
void visit(named_expression *expression) override;
+ void visit(array_access_expression *expression) override;
+ void visit(field_access_expression *expression) override;
+ void visit(dereference_expression *expression) override;
void visit(literal<std::int32_t> *literal) override;
void visit(literal<std::uint32_t> *literal) override;
void visit(literal<double> *literal) override;
@@ -185,4 +238,14 @@ namespace elna::boot
void visit(unit *unit) override;
void visit(type_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 6258760..85532db 100644
--- a/include/elna/boot/symbol.h
+++ b/include/elna/boot/symbol.h
@@ -84,13 +84,14 @@ namespace elna::boot
type(type&& other);
type& operator=(type&& other);
- bool operator==(const std::nullptr_t&);
-
~type();
template<typename T>
std::shared_ptr<T> get() const;
+ bool operator==(const std::nullptr_t&) const;
+ bool operator==(const type& other) const;
+
bool empty() const;
};
@@ -466,6 +467,6 @@ namespace elna::boot
* \param alias The type to lookup the innermost declaration for.
* \return Innermost type declaration.
*/
- type inner_aliased_type(type alias);
+ type inner_aliased_type(const type& alias);
type inner_aliased_type(std::shared_ptr<alias_type> alias);
}
diff --git a/source/lexer.elna b/source/lexer.elna
index 9008139..bb8c8ef 100644
--- a/source/lexer.elna
+++ b/source/lexer.elna
@@ -408,7 +408,7 @@ begin
text_length := cast(lexer^.current.iterator - lexer^.start.iterator + 1: Word);
token^.value.stringKind := String(cast(malloc(text_length): ^Char), text_length);
- memcpy(cast(token^.value.stringKind.ptr: Pointer), cast(lexer^.start.iterator: Pointer), text_length);
+ memcpy(token^.value.stringKind.ptr, lexer^.start.iterator, text_length);
token^.kind := LexerKind.character
end;
@@ -416,7 +416,7 @@ begin
text_length := cast(lexer^.current.iterator - lexer^.start.iterator + 1: Word);
token^.value.stringKind := String(cast(malloc(text_length): ^Char), text_length);
- memcpy(cast(token^.value.stringKind.ptr: Pointer), cast(lexer^.start.iterator: Pointer), text_length);
+ memcpy(token^.value.stringKind.ptr, lexer^.start.iterator, text_length);
token^.kind := LexerKind.string
end;
@@ -429,7 +429,7 @@ begin
token^.kind := LexerKind.identifier;
token^.value.identifierKind[1] := cast(lexer^.current.iterator - lexer^.start.iterator: Char);
- memcpy(cast(@token^.value.identifierKind[2]: Pointer), cast(lexer^.start.iterator: Pointer), cast(token^.value.identifierKind[1]: Word));
+ memcpy(@token^.value.identifierKind[2], lexer^.start.iterator, cast(token^.value.identifierKind[1]: Word));
if compare_keyword("program", lexer^.start, lexer^.current.iterator) then
token^.kind := LexerKind._program
@@ -579,7 +579,7 @@ begin
integer_length := cast(lexer^.current.iterator - lexer^.start.iterator: Word);
memset(cast(token^.value.identifierKind.ptr: Pointer), 0, #size(Identifier));
- memcpy(cast(@token^.value.identifierKind[1]: Pointer), cast(lexer^.start.iterator: Pointer), integer_length);
+ memcpy(@token^.value.identifierKind[1], lexer^.start.iterator, integer_length);
token^.value.identifierKind[cast(token^.value.identifierKind[1]: Int) + 2] := '\0';
token^.value.integerKind := atoi(@token^.value.identifierKind[2])
diff --git a/testsuite/compilable/assign_record_to_base.elna b/testsuite/compilable/assign_record_to_base.elna
new file mode 100644
index 0000000..7583c70
--- /dev/null
+++ b/testsuite/compilable/assign_record_to_base.elna
@@ -0,0 +1,16 @@
+type
+ B = record
+ x: Int
+ end
+ R = record(B)
+ y: Int
+ end
+
+var
+ x: ^B := nil
+ y: ^R := nil
+
+begin
+ x := y;
+ return 0
+end.
diff --git a/testsuite/compilable/pointer_cast.elna b/testsuite/compilable/pointer_cast.elna
new file mode 100644
index 0000000..d10fb23
--- /dev/null
+++ b/testsuite/compilable/pointer_cast.elna
@@ -0,0 +1,8 @@
+var
+ c: ^Char := nil
+ p: Pointer := nil
+
+begin
+ p := c;
+ c := p
+end.