aboutsummaryrefslogtreecommitdiff
path: root/boot
diff options
context:
space:
mode:
Diffstat (limited to 'boot')
-rw-r--r--boot/ast.cc38
-rw-r--r--boot/evaluator.cc42
-rw-r--r--boot/name_analysis.cc135
-rw-r--r--boot/symbol.cc2
-rw-r--r--boot/type_check.cc254
5 files changed, 333 insertions, 138 deletions
diff --git a/boot/ast.cc b/boot/ast.cc
index 3727aba..ec5b27d 100644
--- a/boot/ast.cc
+++ b/boot/ast.cc
@@ -438,8 +438,8 @@ namespace elna::boot
void walking_visitor::visit(slicing_expression *expression)
{
expression->base().accept(this);
- expression->offset().accept(this);
- expression->length().accept(this);
+ expression->start().accept(this);
+ expression->end().accept(this);
}
void walking_visitor::visit(traits_expression *trait)
@@ -1070,8 +1070,8 @@ namespace elna::boot
}
slicing_expression::slicing_expression(const source_position position,
- expression *base, expression *offset, expression *length)
- : node(position), m_base(base), m_offset(offset), m_length(length)
+ expression *base, expression *start, expression *end)
+ : node(position), m_base(base), m_start(start), m_end(end)
{
}
@@ -1088,8 +1088,8 @@ namespace elna::boot
slicing_expression::~slicing_expression()
{
delete m_base;
- delete m_offset;
- delete m_length;
+ delete m_start;
+ delete m_end;
}
expression& slicing_expression::base()
@@ -1097,14 +1097,14 @@ namespace elna::boot
return *this->m_base;
}
- expression& slicing_expression::offset()
+ expression& slicing_expression::start()
{
- return *this->m_offset;
+ return *this->m_start;
}
- expression& slicing_expression::length()
+ expression& slicing_expression::end()
{
- return *this->m_length;
+ return *this->m_end;
}
named_expression::named_expression(const source_position position, const std::string& name)
@@ -1242,6 +1242,11 @@ namespace elna::boot
return m_operator;
}
+ void binary_expression::operation(binary_operator operation)
+ {
+ this->m_operator = operation;
+ }
+
binary_expression::~binary_expression()
{
delete m_lhs;
@@ -1274,6 +1279,11 @@ namespace elna::boot
return this->m_operator;
}
+ void unary_expression::operation(unary_operator operation)
+ {
+ this->m_operator = operation;
+ }
+
unary_expression::~unary_expression()
{
delete m_operand;
@@ -1540,10 +1550,16 @@ namespace elna::boot
case greater_equal:
return ">=";
case conjunction:
- return "and";
+ case logical_conjunction:
+ case bitwise_conjunction:
+ return "&";
case disjunction:
+ case logical_disjunction:
+ case bitwise_disjunction:
return "or";
case exclusive_disjunction:
+ case logical_exclusive_disjunction:
+ case bitwise_exclusive_disjunction:
return "xor";
case shift_left:
return "<<";
diff --git a/boot/evaluator.cc b/boot/evaluator.cc
index 990f358..77a56ad 100644
--- a/boot/evaluator.cc
+++ b/boot/evaluator.cc
@@ -169,7 +169,7 @@ namespace elna::boot
return std::nullopt;
}, operand.value());
}
- if (subject.operation() == unary_operator::negation)
+ if (subject.operation() == unary_operator::logical_negation)
{
return std::visit([](auto value) -> std::optional<constant_value> {
using T = std::decay_t<decltype(value)>;
@@ -178,7 +178,15 @@ namespace elna::boot
{
return constant_value{ !value };
}
- else if constexpr (std::is_integral_v<T>)
+ return std::nullopt;
+ }, operand.value());
+ }
+ if (subject.operation() == unary_operator::bitwise_negation)
+ {
+ return std::visit([](auto value) -> std::optional<constant_value> {
+ using T = std::decay_t<decltype(value)>;
+
+ if constexpr (std::is_integral_v<T>)
{
return constant_value{ ~value };
}
@@ -299,23 +307,44 @@ namespace elna::boot
}
return std::nullopt;
case disjunction:
- if constexpr (std::is_integral_v<T>)
+ case bitwise_disjunction:
+ if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>)
{
return constant_value{ lhs | rhs };
}
return std::nullopt;
case conjunction:
- if constexpr (std::is_integral_v<T>)
+ case bitwise_conjunction:
+ if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>)
{
return constant_value{ lhs & rhs };
}
return std::nullopt;
case exclusive_disjunction:
- if constexpr (std::is_integral_v<T>)
+ case bitwise_exclusive_disjunction:
+ if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>)
{
return constant_value{ lhs ^ rhs };
}
return std::nullopt;
+ case logical_conjunction:
+ if constexpr (std::is_same_v<T, bool>)
+ {
+ return constant_value{ lhs && rhs };
+ }
+ return std::nullopt;
+ case logical_disjunction:
+ if constexpr (std::is_same_v<T, bool>)
+ {
+ return constant_value{ lhs || rhs };
+ }
+ return std::nullopt;
+ case logical_exclusive_disjunction:
+ if constexpr (std::is_same_v<T, bool>)
+ {
+ return constant_value{ lhs != rhs };
+ }
+ return std::nullopt;
case shift_left:
if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool>)
{
@@ -420,8 +449,7 @@ namespace elna::boot
{
return target.bool_size;
}
- else if (is_primitive_type(resolved, "String")
- || resolved.get<slice_type>() != nullptr)
+ else if (resolved.get<slice_type>() != nullptr)
{
return target.pointer_size + target.word_size;
}
diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc
index a2cacb2..907f318 100644
--- a/boot/name_analysis.cc
+++ b/boot/name_analysis.cc
@@ -17,7 +17,6 @@ along with GCC; see the file COPYING3. If not see
#include "elna/boot/name_analysis.h"
-#include <algorithm>
#include <utility>
namespace elna::boot
@@ -81,6 +80,84 @@ namespace elna::boot
return "Duplicate 'const' qualifier is not allowed";
}
+ field_not_found_error::field_not_found_error(const identifier& field_name,
+ type composite_type)
+ : error(field_name.position()), field_name(field_name.name()), composite_type(std::move(composite_type))
+ {
+ }
+
+ std::string field_not_found_error::what() const
+ {
+ type const resolved = resolve_underlying_type(composite_type);
+ bool const is_enum = resolved.get<enumeration_type>() != nullptr;
+ bool const is_record = resolved.get<record_type>() != nullptr;
+
+ if (is_enum || is_record)
+ {
+ std::string message = is_enum ? "Enumeration" : "Record";
+
+ if (auto alias = composite_type.get<alias_type>())
+ {
+ message += " '" + alias->name + "'";
+ }
+ message += " does not have a ";
+ message += is_enum ? "member" : "field";
+ message += " named '" + field_name + "'";
+ return message;
+ }
+ return "Type '" + composite_type.to_string()
+ + "' does not have a field named '" + field_name + "'";
+ }
+
+ duplicate_member_error::duplicate_member_error(const boot::identifier& member_name,
+ type aggregate, std::optional<source_position> original,
+ std::optional<std::string> base_name)
+ : error(member_name.position()), member_name(member_name.name()), aggregate(std::move(aggregate)),
+ original(original), base_name(std::move(base_name))
+ {
+ }
+
+ std::string duplicate_member_error::what() const
+ {
+ type const resolved = resolve_underlying_type(aggregate);
+ bool const is_enum = resolved.get<enumeration_type>() != nullptr;
+ std::string const kind = is_enum ? "member" : "field";
+ std::string message = is_enum ? "Enumeration" : "Record";
+
+ if (auto alias = aggregate.get<alias_type>())
+ {
+ message += " '" + alias->name + "'";
+ }
+ message += " already has a " + kind + " named '" + member_name + "'";
+
+ if (base_name.has_value())
+ {
+ message += " (defined in base type '" + *base_name + "')";
+ }
+ return message;
+ }
+
+ std::optional<std::pair<std::string, source_position>> duplicate_member_error::note() const
+ {
+ if (original.has_value() && original->start().available())
+ {
+ return std::make_pair("previously declared here", *original);
+ }
+ return std::nullopt;
+ }
+
+ unsupported_trait_type_error::unsupported_trait_type_error(const identifier& trait,
+ type actual)
+ : error(trait.position()), actual(std::move(actual)), trait_name(trait.name())
+ {
+ }
+
+ std::string unsupported_trait_type_error::what() const
+ {
+ return "Type '" + actual.to_string()
+ + "' does not support trait '#" + trait_name + "'";
+ }
+
// Members of a constant aggregate are constant themselves.
static type qualify_member_type(const type& element, const type& aggregate)
{
@@ -133,6 +210,20 @@ namespace elna::boot
return result_type;
}
+ std::optional<type> name_analysis_visitor::lookup_pointer_like_field(
+ const std::string& field_name, const type& element_type)
+ {
+ if (field_name == "length")
+ {
+ return lookup_primitive_type("Word");
+ }
+ if (field_name == "ptr")
+ {
+ return type(std::make_shared<pointer_type>(element_type));
+ }
+ return std::nullopt;
+ }
+
type name_analysis_visitor::lookup_primitive_type(const std::string& name)
{
return this->bag.lookup(name)->is_type()->symbol;
@@ -156,26 +247,18 @@ namespace elna::boot
return lookup_field(record->base, field_name);
}
}
- else if (auto slice = resolved_type.get<slice_type>())
+ else if (auto array = resolved_type.get<array_type>())
{
- if (field_name == "length")
- {
- return lookup_primitive_type("Word");
- }
- else if (field_name == "ptr")
+ if (auto field = lookup_pointer_like_field(field_name, array->base))
{
- return type(std::make_shared<pointer_type>(slice->base));
+ return field.value();
}
}
- else if (auto primitive = resolved_type.get<primitive_type>(); primitive != nullptr && primitive->identifier == "String")
+ else if (auto slice = resolved_type.get<slice_type>())
{
- if (field_name == "length")
+ if (auto field = lookup_pointer_like_field(field_name, slice->base))
{
- return lookup_primitive_type("Word");
- }
- else if (field_name == "ptr")
- {
- return type(std::make_shared<pointer_type>(lookup_primitive_type("Char")));
+ return field.value();
}
}
return type();
@@ -303,17 +386,6 @@ namespace elna::boot
}
else
{
- type actual;
-
- if (auto var = base_symbol->is_variable())
- {
- actual = var->symbol;
- }
- else if (auto proc = base_symbol->is_procedure())
- {
- actual = type(std::make_shared<procedure_type>(proc->symbol));
- }
- add_error<base_type_error>(actual, expression->position());
this->current_type = type();
return;
}
@@ -513,10 +585,6 @@ for (const auto& member : expression->members)
{
call->type_decoration = procedure->return_type.proper_type;
}
- else if (call->callable().is_named() != nullptr)
- {
- call->type_decoration = this->current_type;
- }
for (expression *const argument : call->arguments)
{
argument->accept(this);
@@ -653,10 +721,6 @@ for (const auto& member : expression->members)
{
expression->type_decoration = slice->base;
}
- else if (resolved_base == lookup_primitive_type("String"))
- {
- expression->type_decoration = lookup_primitive_type("Char");
- }
// Elements of a constant array are constant themselves since a static
// array is a holistic type.
if (!expression->type_decoration.empty())
@@ -765,7 +829,8 @@ for (const auto& member : expression->members)
void name_analysis_visitor::visit(literal<std::string> *literal)
{
- literal->type_decoration = lookup_primitive_type("String");
+ literal->type_decoration = type(std::make_shared<slice_type>(
+ type(std::make_shared<constant_type>(lookup_primitive_type("Char")))));
}
declaration_visitor::declaration_visitor()
diff --git a/boot/symbol.cc b/boot/symbol.cc
index 738e4e1..da948fd 100644
--- a/boot/symbol.cc
+++ b/boot/symbol.cc
@@ -329,8 +329,6 @@ namespace elna::boot
result->enter("Char", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Char"))));
result->enter("Pointer", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Pointer"))));
result->enter("Float", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Float"))));
- result->enter("String", std::make_shared<type_info>(type(std::make_shared<primitive_type>("String"))));
-
type const boolean = type(std::make_shared<primitive_type>("Bool"));
result->enter("Bool", std::make_shared<type_info>(boolean));
diff --git a/boot/type_check.cc b/boot/type_check.cc
index 5d0c7a6..7989925 100644
--- a/boot/type_check.cc
+++ b/boot/type_check.cc
@@ -56,72 +56,6 @@ namespace elna::boot
+ "', because it is constant or contains constant members";
}
- field_not_found_error::field_not_found_error(const identifier& field_name,
- type composite_type)
- : error(field_name.position()), field_name(field_name.name()), composite_type(std::move(composite_type))
- {
- }
-
- std::string field_not_found_error::what() const
- {
- type const resolved = resolve_underlying_type(composite_type);
- bool const is_enum = resolved.get<enumeration_type>() != nullptr;
- bool const is_record = resolved.get<record_type>() != nullptr;
-
- if (is_enum || is_record)
- {
- std::string message = is_enum ? "Enumeration" : "Record";
-
- if (auto alias = composite_type.get<alias_type>())
- {
- message += " '" + alias->name + "'";
- }
- message += " does not have a ";
- message += is_enum ? "member" : "field";
- message += " named '" + field_name + "'";
- return message;
- }
- return "Type '" + composite_type.to_string()
- + "' does not have a field named '" + field_name + "'";
- }
-
- duplicate_member_error::duplicate_member_error(const boot::identifier& member_name,
- type aggregate, std::optional<source_position> original,
- std::optional<std::string> base_name)
- : error(member_name.position()), member_name(member_name.name()), aggregate(std::move(aggregate)),
- original(original), base_name(std::move(base_name))
- {
- }
-
- std::string duplicate_member_error::what() const
- {
- type const resolved = resolve_underlying_type(aggregate);
- bool const is_enum = resolved.get<enumeration_type>() != nullptr;
- std::string const kind = is_enum ? "member" : "field";
- std::string message = is_enum ? "Enumeration" : "Record";
-
- if (auto alias = aggregate.get<alias_type>())
- {
- message += " '" + alias->name + "'";
- }
- message += " already has a " + kind + " named '" + member_name + "'";
-
- if (base_name.has_value())
- {
- message += " (defined in base type '" + *base_name + "')";
- }
- return message;
- }
-
- std::optional<std::pair<std::string, source_position>> duplicate_member_error::note() const
- {
- if (original.has_value() && original->start().available())
- {
- return std::make_pair("previously declared here", *original);
- }
- return std::nullopt;
- }
-
cyclic_declaration_error::cyclic_declaration_error(const std::vector<std::string>& cycle,
const source_position position)
: error(position), cycle(cycle)
@@ -189,18 +123,6 @@ namespace elna::boot
}
}
- unsupported_trait_type_error::unsupported_trait_type_error(const identifier& trait,
- type actual)
- : error(trait.position()), actual(std::move(actual)), trait_name(trait.name())
- {
- }
-
- std::string unsupported_trait_type_error::what() const
- {
- return "Type '" + actual.to_string()
- + "' does not support trait '#" + trait_name + "'";
- }
-
unary_operation_error::unary_operation_error(const source_position position,
type actual, unary_operator operation)
: error(position), actual(std::move(actual)), op(operation)
@@ -215,6 +137,8 @@ namespace elna::boot
case reference:
return '@';
case negation:
+ case bitwise_negation:
+ case logical_negation:
return '~';
case minus:
return '-';
@@ -368,10 +292,36 @@ namespace elna::boot
return verdict::pass;
}
+ assign_check::verdict assign_check::check_slice_conversion() const
+ {
+ auto assignee_slice = ctx.resolved_assignee.get<slice_type>();
+ auto assignment_slice = ctx.resolved_assignment.get<slice_type>();
+
+ if (assignee_slice == nullptr || assignment_slice == nullptr)
+ {
+ return verdict::pass;
+ }
+ auto assignee_element_const = resolve_aliases(assignee_slice->base).get<constant_type>();
+ auto assignment_element_const = resolve_aliases(assignment_slice->base).get<constant_type>();
+
+ // Const can be added to the element type, but not removed.
+ if (assignee_element_const != nullptr
+ && assignee_element_const->unqualified == assignment_slice->base)
+ {
+ return verdict::accept;
+ }
+ if (assignment_element_const != nullptr && assignee_element_const == nullptr)
+ {
+ return verdict::reject;
+ }
+ return verdict::pass;
+ }
+
bool assign_check::run()
{
for (auto handler : {&assign_check::guard_const_laundering,
&assign_check::check_exact_match,
+ &assign_check::check_slice_conversion,
&assign_check::check_pointer_hatch,
&assign_check::check_pointer_conversion})
{
@@ -534,10 +484,18 @@ namespace elna::boot
{
if (expression->base.has_value())
{
- type const base_type = resolve_underlying_type(this->bag.lookup(expression->base.value().name())->is_type()->symbol);
- if (base_type.get<record_type>() == nullptr)
+ auto const base_symbol = this->bag.lookup(expression->base.value().name());
+ if (base_symbol == nullptr || base_symbol->is_type() == nullptr)
{
- add_error<base_type_error>(base_type, expression->position());
+ add_error<base_type_error>(type(), expression->position());
+ }
+ else
+ {
+ type const base_type = resolve_underlying_type(base_symbol->is_type()->symbol);
+ if (base_type.get<record_type>() == nullptr)
+ {
+ add_error<base_type_error>(base_type, expression->position());
+ }
}
}
walking_visitor::visit(expression);
@@ -571,6 +529,12 @@ namespace elna::boot
call->arguments.size(), call->position());
}
}
+ else if (!call->callable().type_decoration.empty())
+ {
+ add_error<type_mismatch_error>(call->position(),
+ type(std::make_shared<procedure_type>()),
+ call->callable().type_decoration);
+ }
}
void type_analysis_visitor::visit(record_constructor_expression *expression)
@@ -660,12 +624,136 @@ namespace elna::boot
}
else if (operation == unary_operator::negation)
{
- if (!is_primitive_type(resolved, "Bool")
- && !is_integral_type(resolved))
+ if (is_primitive_type(resolved, "Bool"))
+ {
+ expression->operation(unary_operator::logical_negation);
+ }
+ else if (is_integral_type(resolved))
+ {
+ expression->operation(unary_operator::bitwise_negation);
+ }
+ else
{
add_error<unary_operation_error>(expression->position(),
expression->operand().type_decoration, operation);
}
}
+ else if (operation == unary_operator::reference)
+ {
+ auto *designator = expression->operand().is_designator();
+ if (designator == nullptr || designator->is_slicing() != nullptr)
+ {
+ add_error<unary_operation_error>(expression->position(),
+ expression->operand().type_decoration, operation);
+ }
+ }
+ }
+
+ binary_operation_error::binary_operation_error(const source_position position,
+ type left, type right, binary_operator operation)
+ : error(position), left(std::move(left)), right(std::move(right)), op(operation)
+ {
+ }
+
+ std::string binary_operation_error::what() const
+ {
+ return "Invalid operands of type '" + left.to_string()
+ + "' and '" + right.to_string()
+ + "' for operator " + print_binary_operator(op);
+ }
+
+ void type_analysis_visitor::visit(binary_expression *expression)
+ {
+ walking_visitor::visit(expression);
+
+ auto operation = expression->operation();
+ type const lhs_resolved = resolve_underlying_type(expression->lhs().type_decoration);
+ type const rhs_resolved = resolve_underlying_type(expression->rhs().type_decoration);
+ bool valid = false;
+
+ switch (operation)
+ {
+ using enum binary_operator;
+ case sum:
+ valid = (is_any_pointer_type(lhs_resolved) && is_integral_type(rhs_resolved))
+ || (is_integral_type(lhs_resolved) && is_any_pointer_type(rhs_resolved))
+ || (is_numeric_type(lhs_resolved) && lhs_resolved == rhs_resolved);
+ break;
+ case subtraction:
+ valid = (is_any_pointer_type(lhs_resolved) && is_integral_type(rhs_resolved))
+ || (is_any_pointer_type(lhs_resolved) && is_any_pointer_type(rhs_resolved))
+ || (is_numeric_type(lhs_resolved) && lhs_resolved == rhs_resolved);
+ break;
+ case division:
+ case remainder:
+ case multiplication:
+ valid = is_numeric_type(lhs_resolved) && lhs_resolved == rhs_resolved;
+ break;
+ case less:
+ case greater:
+ case less_equal:
+ case greater_equal:
+ valid = (is_numeric_type(lhs_resolved) && lhs_resolved == rhs_resolved)
+ || (is_any_pointer_type(lhs_resolved) && is_any_pointer_type(rhs_resolved));
+ break;
+ case conjunction:
+ case disjunction:
+ case exclusive_disjunction:
+ if (is_primitive_type(lhs_resolved, "Bool") && lhs_resolved == rhs_resolved)
+ {
+ switch (operation)
+ {
+ case conjunction:
+ expression->operation(logical_conjunction);
+ break;
+ case disjunction:
+ expression->operation(logical_disjunction);
+ break;
+ case exclusive_disjunction:
+ expression->operation(logical_exclusive_disjunction);
+ break;
+ default:
+ break;
+ }
+ valid = true;
+ }
+ else if (is_integral_type(lhs_resolved) && lhs_resolved == rhs_resolved)
+ {
+ switch (operation)
+ {
+ case conjunction:
+ expression->operation(bitwise_conjunction);
+ break;
+ case disjunction:
+ expression->operation(bitwise_disjunction);
+ break;
+ case exclusive_disjunction:
+ expression->operation(bitwise_exclusive_disjunction);
+ break;
+ default:
+ break;
+ }
+ valid = true;
+ }
+ break;
+ case equals:
+ case not_equals:
+ valid = lhs_resolved == rhs_resolved
+ || (is_primitive_type(lhs_resolved, "Pointer") && is_any_pointer_type(rhs_resolved))
+ || (is_any_pointer_type(lhs_resolved) && is_primitive_type(rhs_resolved, "Pointer"))
+ || (lhs_resolved.get<slice_type>() && rhs_resolved.get<slice_type>())
+ || (lhs_resolved.get<record_type>() && rhs_resolved.get<record_type>());
+ break;
+ case shift_left:
+ case shift_right:
+ valid = is_integral_type(lhs_resolved)
+ && is_primitive_type(rhs_resolved, "Word");
+ break;
+ }
+ if (!valid)
+ {
+ add_error<binary_operation_error>(expression->position(),
+ expression->lhs().type_decoration, expression->rhs().type_decoration, operation);
+ }
}
}