/* Name analysis.
Copyright (C) 2025 Free Software Foundation, Inc.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
. */
#include "elna/boot/semantic.h"
#include
namespace elna::boot
{
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 declaration_error::what() const
{
switch (this->error_kind)
{
case kind::undeclared:
return "Type '" + identifier + "' not declared";
case kind::already:
return "Symbol '" + identifier + "' has been already declared";
case kind::local_export:
return "Local symbol '" + this->identifier + "' cannot be exported";
default:
__builtin_unreachable();
}
}
type_expectation_error::type_expectation_error(const kind error_kind, const struct position position)
: error(position), error_kind(error_kind)
{
}
std::string type_expectation_error::what() const
{
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)
: error(position), field_name(field_name)
{
}
std::string field_duplication_error::what() const
{
return "Repeated field name '" + field_name + "'";
}
cyclic_declaration_error::cyclic_declaration_error(const std::vector& cycle,
const struct position position)
: error(position), cycle(cycle)
{
}
std::string cyclic_declaration_error::what() const
{
auto segment = std::cbegin(this->cycle);
std::string message = "Type declaration forms a cycle: " + *segment;
++segment;
for (; segment != std::cend(this->cycle); ++segment)
{
message += " -> " + *segment;
}
return message;
}
return_error::return_error(const std::string& identifier, const struct position position)
: error(position), identifier(identifier)
{
}
std::string return_error::what() const
{
return "Procedure '" + this->identifier + "' is expected to return, but does not have a return statement";
}
base_type_error::base_type_error(const std::string& base, const struct position position)
: error(position), base(base)
{
}
std::string base_type_error::what() const
{
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)
{
}
void type_analysis_visitor::visit(procedure_declaration *declaration)
{
this->current_procedure = this->bag.lookup(declaration->identifier.name)->is_procedure();
if (declaration->body.has_value())
{
this->bag.enter(this->current_procedure->scope);
}
walking_visitor::visit(declaration);
if (declaration->body.has_value())
{
if (declaration->body.value().return_expression != nullptr)
{
expression *return_expr = declaration->body.value().return_expression;
type return_type = this->current_procedure->symbol.return_type.proper_type;
if (!return_type.empty())
{
if (!is_assignable_from(return_type, return_expr->type_decoration))
{
add_error(type_expectation_error::kind::result, return_expr->position());
}
}
else
{
add_error(type_expectation_error::kind::result, return_expr->position());
}
}
else if (declaration->heading().return_type.proper_type != nullptr)
{
add_error(declaration->identifier.name, declaration->position());
}
this->bag.leave();
}
this->current_procedure.reset();
}
void type_analysis_visitor::visit(unit *unit)
{
walking_visitor::visit(unit);
if (unit->has_body())
{
if (unit->return_expression != nullptr)
{
type return_type = this->bag.lookup("Int")->is_type()->symbol;
if (!is_assignable_from(return_type, unit->return_expression->type_decoration))
{
add_error(type_expectation_error::kind::result,
unit->return_expression->position());
}
}
else
{
add_error("module", unit->position());
}
}
}
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::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::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::kind::assignment,
case_label->position());
}
}
}
}
bool type_analysis_visitor::check_unresolved_symbol(std::shared_ptr alias,
std::vector& 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())
{
return check_unresolved_symbol(another_alias, alias_path);
}
return true;
}
void type_analysis_visitor::visit(type_declaration *declaration)
{
std::vector alias_path;
auto unresolved_type = this->bag.lookup(declaration->identifier.name)->is_type()->symbol.get();
if (!check_unresolved_symbol(unresolved_type, alias_path))
{
add_error(alias_path, declaration->position());
}
else
{
walking_visitor::visit(declaration);
}
}
void type_analysis_visitor::visit(record_type_expression *expression)
{
if (expression->base.has_value())
{
type base_type = inner_aliased_type(this->bag.lookup(expression->base.value())->is_type()->symbol);
if (base_type.get() == nullptr)
{
add_error(expression->base.value(), expression->position());
}
}
walking_visitor::visit(expression);
}
void type_analysis_visitor::visit(procedure_call *call)
{
call->callable().accept(this);
if (auto procedure = call->callable().type_decoration.get())
{
std::vector::const_iterator argument_iterator = std::cbegin(call->arguments);
std::vector::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::kind::argument,
(*argument_iterator)->position());
}
++argument_iterator;
++type_iterator;
}
if (call->arguments.size() != procedure->parameters.size())
{
add_error(procedure->parameters.size(),
call->arguments.size(), call->position());
}
}
}
void type_analysis_visitor::visit(record_constructor_expression *expression)
{
auto record = inner_aliased_type(expression->type_decoration).get();
if (record == nullptr)
{
add_error(type_expectation_error::kind::result, expression->position());
return;
}
for (const field_initializer& initializer : expression->field_initializers)
{
for (const type_field& field : record->fields)
{
if (field.first == initializer.name)
{
if (!is_assignable_from(field.second, initializer.value->type_decoration))
{
add_error(type_expectation_error::kind::argument,
initializer.value->position());
}
break;
}
}
}
}
void type_analysis_visitor::visit(array_constructor_expression *expression)
{
auto array = inner_aliased_type(expression->type_decoration).get();
if (array == nullptr)
{
add_error(type_expectation_error::kind::result, expression->position());
return;
}
if (expression->elements.size() > array->size)
{
add_error(array->size, expression->elements.size(),
expression->position());
return;
}
for (auto element : expression->elements)
{
if (!is_assignable_from(array->base, element->type_decoration))
{
add_error(type_expectation_error::kind::argument,
element->position());
}
}
}
name_analysis_visitor::name_analysis_visitor(symbol_bag bag)
: error_container(), bag(bag)
{
}
std::pair> name_analysis_visitor::build_procedure(
procedure_type_expression& expression)
{
procedure_type::return_t result_return;
if (expression.return_type.no_return)
{
result_return = procedure_type::return_t(std::monostate{});
}
else if (expression.return_type.proper_type != nullptr)
{
expression.return_type.proper_type->accept(this);
result_return = procedure_type::return_t(this->current_type);
}
else
{
result_return = procedure_type::return_t();
}
std::pair> result_type{
procedure_type(result_return), std::vector()
};
for (auto& [parameter_names, parameters_type] : expression.parameters)
{
parameters_type->accept(this);
for (auto& parameter_name : parameter_names)
{
result_type.first.parameters.push_back(this->current_type);
result_type.second.push_back(parameter_name);
}
}
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;
if constexpr (std::is_same_v)
{
return lookup_primitive_type("Int");
}
else if constexpr (std::is_same_v)
{
return lookup_primitive_type("Word");
}
else if constexpr (std::is_same_v)
{
return lookup_primitive_type("Float");
}
else if constexpr (std::is_same_v)
{
return lookup_primitive_type("Bool");
}
else if constexpr (std::is_same_v)
{
return lookup_primitive_type("Char");
}
else if constexpr (std::is_same_v)
{
return lookup_primitive_type("Pointer");
}
else if constexpr (std::is_same_v)
{
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())
{
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 != nullptr && primitive->identifier == "String")
{
if (field_name == "length")
{
return lookup_primitive_type("Word");
}
else if (field_name == "ptr")
{
return type(std::make_shared(lookup_primitive_type("Char")));
}
}
return type();
}
void name_analysis_visitor::visit(type_declaration *declaration)
{
walking_visitor::visit(declaration);
auto resolved = this->bag.resolve(declaration->identifier.name, this->current_type);
auto info = std::make_shared(type(resolved));
info->exported = declaration->identifier.exported;
this->bag.enter(declaration->identifier.name, info);
}
void name_analysis_visitor::visit(pointer_type_expression *expression)
{
walking_visitor::visit(expression);
this->current_type = type(std::make_shared(this->current_type));
}
void name_analysis_visitor::visit(array_type_expression *expression)
{
walking_visitor::visit(expression);
auto result_type = std::make_shared(this->current_type, expression->size);
this->current_type = type(result_type);
}
/**
* Collects field names from a record type recursively, base first.
*/
static void collect_field_names(const type& composite_type, std::set& names)
{
auto record = inner_aliased_type(composite_type).get();
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 name_analysis_visitor::build_composite_type(
const std::vector& fields, std::set& field_names)
{
std::vector result;
for (auto& field : fields)
{
field.second->accept(this);
for (auto& field_name : field.first)
{
if (!field_names.insert(field_name).second)
{
add_error(field_name, field.second->position());
}
else
{
result.push_back(std::make_pair(field_name, this->current_type));
}
}
}
return result;
}
void name_analysis_visitor::visit(record_type_expression *expression)
{
std::shared_ptr result_type;
if (expression->base.has_value())
{
if (auto unresolved_alias = this->bag.declared(expression->base.value()))
{
result_type = std::make_shared(type(unresolved_alias));
}
else if (auto base_symbol = this->bag.lookup(expression->base.value()))
{
if (auto base_type_info = base_symbol->is_type())
{
result_type = std::make_shared(base_type_info->symbol);
}
else
{
add_error(expression->base.value(), expression->position());
this->current_type = type();
return;
}
}
else
{
add_error(declaration_error::kind::undeclared,
expression->base.value(), expression->position());
this->current_type = type();
return;
}
}
else
{
result_type = std::make_shared();
}
std::set 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);
}
void name_analysis_visitor::visit(record_constructor_expression *expression)
{
if (auto type_symbol = this->bag.lookup(expression->type_name))
{
if (auto type_info = type_symbol->is_type())
{
expression->type_decoration = type_info->symbol;
}
}
for (const field_initializer& initializer : expression->field_initializers)
{
initializer.value->accept(this);
if (!expression->type_decoration.empty()
&& lookup_field(expression->type_decoration, initializer.name).empty())
{
add_error(declaration_error::kind::undeclared,
initializer.name, expression->position());
}
}
}
void name_analysis_visitor::visit(array_constructor_expression *expression)
{
expression->m_element_type->accept(this);
auto element_type = this->current_type;
for (auto element : expression->elements)
{
element->accept(this);
}
expression->type_decoration = type(std::make_shared(element_type, expression->size));
}
void name_analysis_visitor::visit(procedure_type_expression *expression)
{
std::shared_ptr result_type =
std::make_shared(std::move(build_procedure(*expression).first));
this->current_type = type(result_type);
}
void name_analysis_visitor::visit(enumeration_type_expression *expression)
{
std::shared_ptr result_type = std::make_shared(expression->members);
this->current_type = type(result_type);
}
std::shared_ptr name_analysis_visitor::register_variable(const std::string& name,
const bool is_extern, const struct position position)
{
auto variable_symbol = std::make_shared(this->current_type, is_extern);
if (!this->bag.enter(name, variable_symbol))
{
add_error(declaration_error::kind::already, name, position);
}
return variable_symbol;
}
void name_analysis_visitor::visit(variable_declaration *declaration)
{
declaration->variable_type().accept(this);
auto variable_type = this->current_type;
if (declaration->initializer != nullptr)
{
declaration->initializer->accept(this);
this->current_type = variable_type;
}
for (const identifier_definition& variable_identifier : declaration->identifiers)
{
auto variable_symbol = register_variable(variable_identifier.name, declaration->is_extern,
declaration->position());
variable_symbol->exported = variable_identifier.exported;
}
}
void name_analysis_visitor::visit(constant_declaration *declaration)
{
walking_visitor::visit(declaration);
auto constant_symbol = std::make_shared(this->current_literal);
constant_symbol->exported = declaration->identifier.exported;
this->bag.enter(declaration->identifier.name, constant_symbol);
}
void name_analysis_visitor::visit(procedure_declaration *declaration)
{
std::shared_ptr info;
auto [heading, parameter_names] = build_procedure(declaration->heading());
if (declaration->body.has_value())
{
info = std::make_shared(heading, std::move(parameter_names), this->bag.enter());
auto name_iterator = std::cbegin(info->names);
auto type_iterator = std::cbegin(heading.parameters);
while (name_iterator != std::cend(info->names) && type_iterator != std::cend(heading.parameters))
{
this->current_type = *type_iterator;
auto variable_symbol = register_variable(*name_iterator, false, declaration->heading().position());
variable_symbol->exported = false;
++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);
}
for (statement *const statement : declaration->body.value().entry_point)
{
statement->accept(this);
}
if (declaration->body.value().return_expression != nullptr)
{
declaration->body.value().return_expression->accept(this);
}
this->bag.leave();
}
else
{
info = std::make_shared(heading, std::move(parameter_names));
}
info->exported = declaration->identifier.exported;
this->bag.enter(declaration->identifier.name, info);
}
void name_analysis_visitor::visit(procedure_call *call)
{
call->callable().accept(this);
if (auto procedure = call->callable().type_decoration.get())
{
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);
}
}
void name_analysis_visitor::visit(unit *unit)
{
for (type_declaration *const type : unit->types)
{
type->accept(this);
}
for (variable_declaration *const variable : unit->variables)
{
variable->accept(this);
}
for (constant_declaration *const constant : unit->constants)
{
constant->accept(this);
}
for (procedure_declaration *const procedure : unit->procedures)
{
procedure->accept(this);
}
if (unit->has_body())
{
this->bag.enter();
auto variable_type = lookup_primitive_type("Int");
this->bag.enter("count", std::make_shared(variable_type, false));
variable_type = lookup_primitive_type("Char");
variable_type = type(std::make_shared(variable_type));
variable_type = type(std::make_shared(variable_type));
this->bag.enter("parameters", std::make_shared(variable_type, false));
for (statement *const statement : unit->entry_point)
{
statement->accept(this);
}
if (unit->return_expression != nullptr)
{
unit->return_expression->accept(this);
}
this->bag.leave();
}
}
void name_analysis_visitor::visit(traits_expression *trait)
{
if (!trait->parameters.empty())
{
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;
case binary_operator::subtraction:
if (expression->lhs().type_decoration.get()
&& expression->rhs().type_decoration.get())
{
expression->type_decoration = lookup_primitive_type("Int");
}
else
{
expression->type_decoration = expression->lhs().type_decoration;
}
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(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())
{
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())
{
expression->type_decoration = pointer->base;
}
}
void name_analysis_visitor::visit(cast_expression *expression)
{
walking_visitor::visit(expression);
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);
}
else if (auto from_symbol_table = this->bag.lookup(expression->name))
{
if (auto type_symbol = from_symbol_table->is_type())
{
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_symbol->symbol));
}
}
else
{
add_error(declaration_error::kind::undeclared,
expression->name, expression->position());
}
}
void name_analysis_visitor::visit(literal *literal)
{
this->current_literal = literal->value;
literal->type_decoration = lookup_primitive_type("Int");
}
void name_analysis_visitor::visit(literal *literal)
{
this->current_literal = literal->value;
literal->type_decoration = lookup_primitive_type("Word");
}
void name_analysis_visitor::visit(literal *literal)
{
this->current_literal = literal->value;
literal->type_decoration = lookup_primitive_type("Float");
}
void name_analysis_visitor::visit(literal *literal)
{
this->current_literal = literal->value;
literal->type_decoration = lookup_primitive_type("Bool");
}
void name_analysis_visitor::visit(literal *literal)
{
this->current_literal = literal->value;
literal->type_decoration = lookup_primitive_type("Char");
}
void name_analysis_visitor::visit(literal *literal)
{
this->current_literal = literal->value;
literal->type_decoration = lookup_primitive_type("Pointer");
}
void name_analysis_visitor::visit(literal *literal)
{
this->current_literal = literal->value;
literal->type_decoration = lookup_primitive_type("String");
}
declaration_visitor::declaration_visitor()
: error_container()
{
}
void declaration_visitor::visit(import_declaration *)
{
}
void declaration_visitor::visit(unit *unit)
{
for (import_declaration *const _import : unit->imports)
{
_import->accept(this);
}
for (type_declaration *const type : unit->types)
{
type->accept(this);
}
for (procedure_declaration *const procedure : unit->procedures)
{
procedure->accept(this);
}
}
void declaration_visitor::visit(type_declaration *declaration)
{
const std::string& type_identifier = declaration->identifier.name;
if (!this->unresolved.insert({ type_identifier, std::make_shared(type_identifier) }).second)
{
add_error(declaration_error::kind::already,
declaration->identifier.name, declaration->position());
}
}
void declaration_visitor::visit(procedure_declaration *declaration)
{
if (!declaration->body.has_value())
{
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);
}
}
void declaration_visitor::visit(variable_declaration *declaration)
{
for (const identifier_definition& variable_identifier : declaration->identifiers)
{
if (variable_identifier.exported)
{
add_error(declaration_error::kind::local_export,
variable_identifier.name, declaration->position());
}
}
}
void declaration_visitor::visit(constant_declaration *declaration)
{
if (declaration->identifier.exported)
{
add_error(declaration_error::kind::local_export,
declaration->identifier.name, declaration->position());
}
}
bool is_base_of(std::shared_ptr base, std::shared_ptr derived)
{
type current = derived->base;
while (!current.empty())
{
auto current_record = inner_aliased_type(current).get();
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 lhs_primitive = lhs_type.get();
std::shared_ptr rhs_primitive = rhs_type.get();
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() != nullptr)
|| (rhs_is_primitive_pointer && lhs_type.get() != nullptr)
|| (lhs_is_primitive_pointer && rhs_type.get() != nullptr)
|| (rhs_is_primitive_pointer && lhs_type.get() != 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 assignee_pointer = resolved_assignee.get();
std::shared_ptr assignment_pointer = resolved_assignment.get();
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 assignee_record = assignee_pointee.get();
std::shared_ptr assignment_record = assignment_pointee.get();
return assignee_record != nullptr && assignment_record != nullptr
&& is_base_of(assignee_record, assignment_record);
}
}