/* Visitor generating a GENERIC tree. 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 #include "elna/gcc/elna-generic.h" #include "elna/gcc/elna-diagnostic.h" #include "elna/gcc/elna1.h" #include "ggc.h" #include "function.h" #include "cgraph.h" #include "gimplify.h" #include "stringpool.h" #include "diagnostic.h" #include "realmpfr.h" #include "stor-layout.h" #include "varasm.h" #include "fold-const.h" #include "langhooks.h" #include namespace elna { namespace gcc { generic_visitor::generic_visitor(std::shared_ptr> symbol_table) { this->symbol_map = symbol_table; } void generic_visitor::build_procedure_call(location_t call_location, tree symbol, const std::vector& arguments) { vec *argument_trees = nullptr; tree current_parameter = TYPE_ARG_TYPES(TREE_TYPE(symbol)); vec_alloc(argument_trees, arguments.size()); for (boot::expression *const argument : arguments) { location_t argument_location = get_location(&argument->position()); if (is_void_type(TREE_VALUE(current_parameter))) { error_at(argument_location, "too many arguments, expected %i, got %lu", list_length(TYPE_ARG_TYPES(TREE_TYPE(symbol))) - 1, arguments.size()); this->current_expression = error_mark_node; break; } argument->accept(this); 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 = build_call_expr_loc_vec(call_location, symbol, argument_trees); if (!is_void_type(TREE_VALUE(current_parameter))) { error_at(call_location, "too few arguments, expected %i, got %lu", list_length(TYPE_ARG_TYPES(TREE_TYPE(symbol))) - 1, arguments.size()); this->current_expression = error_mark_node; } else if (TREE_TYPE(TREE_TYPE(symbol)) == void_type_node) { append_statement(stmt); this->current_expression = NULL_TREE; } else { this->current_expression = stmt; } } void generic_visitor::build_record_call(location_t call_location, tree symbol, const std::vector& arguments) { vec *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); if (!is_assignable_from(TREE_TYPE(record_fields), 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); } } void generic_visitor::visit(boot::call_expression *expression) { tree symbol = this->lookup(expression->name()); location_t call_location = get_location(&expression->position()); if (symbol == NULL_TREE) { error_at(call_location, "procedure '%s' not declared", expression->name().c_str()); this->current_expression = error_mark_node; } else if (DECL_P(symbol) && is_procedure_type(TREE_TYPE(symbol))) { build_procedure_call(call_location, symbol, expression->arguments()); } else if (TYPE_P(symbol) && TREE_CODE(symbol) == RECORD_TYPE) { build_record_call(call_location, symbol, expression->arguments()); } else { error_at(call_location, "'%s' cannot be called, it is neither a procedure nor record", print_type(TYPE_P(symbol) ? symbol : TREE_TYPE(symbol)).c_str()); this->current_expression = error_mark_node; } } void generic_visitor::visit(boot::cast_expression *expression) { tree cast_target = build_type(expression->target()); gcc_assert(cast_target != NULL_TREE); expression->value().accept(this); this->current_expression = build1_loc(get_location(&expression->position()), CONVERT_EXPR, cast_target, this->current_expression); } void generic_visitor::visit(boot::type_expression *expression) { this->current_expression = build_type(expression->body()); } void generic_visitor::visit(boot::program *program) { for (boot::constant_definition *const constant : program->constants) { constant->accept(this); } for (boot::type_definition *const type : program->types) { type->accept(this); } for (boot::variable_declaration *const variable : program->variables) { variable->accept(this); } for (boot::procedure_definition *const procedure : program->procedures) { procedure->accept(this); } tree declaration_type = build_function_type_list(integer_type_node, integer_type_node, build_pointer_type(build_pointer_type(char_type_node)), NULL_TREE); tree fndecl = build_fn_decl("main", declaration_type); tree resdecl = build_decl(UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, integer_type_node); DECL_CONTEXT(resdecl) = fndecl; DECL_RESULT(fndecl) = resdecl; push_struct_function(fndecl, false); DECL_STRUCT_FUNCTION(fndecl)->language = ggc_cleared_alloc(); enter_scope(); tree parameter_type = TYPE_ARG_TYPES(declaration_type); for (const char *argument_name : std::array{ "count", "parameters" }) { tree declaration_tree = build_decl(UNKNOWN_LOCATION, PARM_DECL, get_identifier(argument_name), TREE_VALUE(parameter_type)); DECL_CONTEXT(declaration_tree) = fndecl; DECL_ARG_TYPE(declaration_tree) = TREE_VALUE(parameter_type); this->symbol_map->enter(argument_name, declaration_tree); DECL_ARGUMENTS(fndecl) = chainon(DECL_ARGUMENTS(fndecl), declaration_tree); parameter_type = TREE_CHAIN(parameter_type); } for (boot::statement *const body_statement : program->body) { body_statement->accept(this); } tree set_result = build2(INIT_EXPR, void_type_node, DECL_RESULT(fndecl), build_int_cst_type(integer_type_node, 0)); tree return_stmt = build1(RETURN_EXPR, void_type_node, set_result); append_statement(return_stmt); tree mapping = leave_scope(); BLOCK_SUPERCONTEXT(BIND_EXPR_BLOCK(mapping)) = fndecl; DECL_INITIAL(fndecl) = BIND_EXPR_BLOCK(mapping); DECL_SAVED_TREE(fndecl) = mapping; DECL_EXTERNAL(fndecl) = 0; DECL_PRESERVE_P(fndecl) = 1; pop_cfun(); gimplify_function_tree(fndecl); cgraph_node::finalize_function(fndecl, true); } void generic_visitor::visit(boot::procedure_definition *definition) { std::vector parameter_types(definition->parameters.size()); for (std::size_t i = 0; i < definition->parameters.size(); ++i) { parameter_types[i] = build_type(definition->parameters.at(i)->variable_type()); } tree return_type = definition->return_type() == nullptr ? void_type_node : build_type(*definition->return_type()); tree declaration_type = build_function_type_array(return_type, definition->parameters.size(), parameter_types.data()); tree fndecl = build_fn_decl(definition->identifier.c_str(), declaration_type); this->symbol_map->enter(definition->identifier, fndecl); if (definition->body() != nullptr) { tree resdecl = build_decl(UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, return_type); DECL_CONTEXT(resdecl) = fndecl; DECL_RESULT(fndecl) = resdecl; push_struct_function(fndecl, false); DECL_STRUCT_FUNCTION(fndecl)->language = ggc_cleared_alloc(); enter_scope(); } tree argument_chain = NULL_TREE; for (std::size_t i = 0; i < definition->parameters.size(); ++i) { auto parameter = definition->parameters.at(i); tree declaration_tree = build_decl(get_location(¶meter->position()), PARM_DECL, get_identifier(parameter->identifier.c_str()), parameter_types[i]); DECL_CONTEXT(declaration_tree) = fndecl; DECL_ARG_TYPE(declaration_tree) = parameter_types[i]; if (definition->body() != nullptr) { this->symbol_map->enter(parameter->identifier, declaration_tree); } argument_chain = chainon(argument_chain, declaration_tree); } DECL_ARGUMENTS(fndecl) = argument_chain; TREE_PUBLIC(fndecl) = definition->exported; if (definition->body() != nullptr) { definition->body()->accept(this); tree mapping = leave_scope(); BLOCK_SUPERCONTEXT(BIND_EXPR_BLOCK(mapping)) = fndecl; DECL_INITIAL(fndecl) = BIND_EXPR_BLOCK(mapping); DECL_SAVED_TREE(fndecl) = mapping; DECL_EXTERNAL(fndecl) = 0; DECL_PRESERVE_P(fndecl) = 1; pop_cfun(); gimplify_function_tree(fndecl); cgraph_node::finalize_function(fndecl, true); } else { DECL_EXTERNAL(fndecl) = 1; } } void generic_visitor::enter_scope() { this->symbol_map = std::make_shared>(this->symbol_map); // Chain the binding levels. struct binding_level *new_level = ggc_cleared_alloc(); new_level->level_chain = f_binding_level; new_level->statement_list = alloc_stmt_list(); f_binding_level = new_level; } tree generic_visitor::leave_scope() { // Variables are only defined in the top function scope. tree variables = f_binding_level->level_chain == nullptr ? f_names : NULL_TREE; tree new_block = build_block(variables, f_binding_level->blocks, NULL_TREE, NULL_TREE); for (tree it = f_binding_level->blocks; it != NULL_TREE; it = BLOCK_CHAIN(it)) { BLOCK_SUPERCONTEXT(it) = new_block; } tree bind_expr = build3(BIND_EXPR, void_type_node, variables, chain_defer(), new_block); this->symbol_map = this->symbol_map->scope(); f_binding_level = f_binding_level->level_chain; if (f_binding_level != nullptr) { f_binding_level->blocks = chainon(f_binding_level->blocks, new_block); } return bind_expr; } tree generic_visitor::lookup(const std::string& name) { if (name == "Int") { return elna_int_type_node; } if (name == "Word") { return elna_word_type_node; } if (name == "Char") { return elna_char_type_node; } if (name == "Bool") { return elna_bool_type_node; } if (name == "Byte") { return elna_byte_type_node; } if (name == "Float") { return elna_float_type_node; } if (name == "String") { return elna_string_type_node; } return this->symbol_map->lookup(name); } void generic_visitor::visit(boot::number_literal *literal) { this->current_expression = build_int_cst(elna_int_type_node, literal->value); } void generic_visitor::visit(boot::number_literal *literal) { this->current_expression = build_int_cstu(elna_word_type_node, literal->value); } void generic_visitor::visit(boot::number_literal *literal) { REAL_VALUE_TYPE real_value1; mpfr_t number; mpfr_init2(number, SIGNIFICAND_BITS); mpfr_set_d(number, literal->value, MPFR_RNDN); real_from_mpfr(&real_value1, number, double_type_node, MPFR_RNDN); this->current_expression = build_real(double_type_node, real_value1); mpfr_clear(number); } void generic_visitor::visit(boot::number_literal *boolean) { this->current_expression = boolean->value ? boolean_true_node : boolean_false_node; } void generic_visitor::visit(boot::number_literal *character) { this->current_expression = build_int_cstu(elna_char_type_node, character->value); } void generic_visitor::visit(boot::number_literal *) { this->current_expression = elna_pointer_nil_node; } void generic_visitor::visit(boot::number_literal *string) { tree index_constant = build_int_cstu(elna_word_type_node, string->value.size()); tree string_type = build_array_type(elna_char_type_node, build_index_type(index_constant)); tree string_literal = build_string(string->value.size(), string->value.c_str()); TREE_TYPE(string_literal) = string_type; TREE_CONSTANT(string_literal) = 1; TREE_READONLY(string_literal) = 1; TREE_STATIC(string_literal) = 1; string_type = TREE_TYPE(elna_string_ptr_field_node); string_literal = build4(ARRAY_REF, elna_char_type_node, string_literal, integer_zero_node, NULL_TREE, NULL_TREE); string_literal = build1(ADDR_EXPR, string_type, string_literal); vec *elms = nullptr; CONSTRUCTOR_APPEND_ELT(elms, elna_string_ptr_field_node, string_literal); CONSTRUCTOR_APPEND_ELT(elms, elna_string_length_field_node, index_constant); this->current_expression = build_constructor(elna_string_type_node, elms); } tree generic_visitor::build_arithmetic_operation(boot::binary_expression *expression, tree_code operator_code, tree left, tree right) { return build_binary_operation(is_numeric_type(TREE_TYPE(left)), expression, operator_code, left, right, TREE_TYPE(left)); } tree generic_visitor::build_comparison_operation(boot::binary_expression *expression, tree_code operator_code, tree left, tree right) { return build_binary_operation(is_numeric_type(TREE_TYPE(left)) || is_pointer_type(TREE_TYPE(left)), expression, operator_code, left, right, elna_bool_type_node); } tree generic_visitor::build_bit_logic_operation(boot::binary_expression *expression, tree left, tree right) { location_t expression_location = get_location(&expression->position()); tree left_type = TREE_TYPE(left); tree right_type = TREE_TYPE(right); tree_code logical_code, bit_code; if (expression->operation() == boot::binary_operator::conjunction) { bit_code = BIT_AND_EXPR; logical_code = TRUTH_ANDIF_EXPR; } else if (expression->operation() == boot::binary_operator::disjunction) { bit_code = BIT_IOR_EXPR; logical_code = TRUTH_ORIF_EXPR; } else if (expression->operation() == boot::binary_operator::exclusive_disjunction) { bit_code = BIT_XOR_EXPR; logical_code = TRUTH_XOR_EXPR; } else { gcc_unreachable(); } if (left_type == elna_bool_type_node) { return build2_loc(expression_location, logical_code, elna_bool_type_node, left, right); } else if (is_integral_type(left_type)) { return build2_loc(expression_location, bit_code, left_type, left, right); } else { error_at(expression_location, "invalid operands of type '%s' and '%s' for operator %s", print_type(left_type).c_str(), print_type(right_type).c_str(), elna::boot::print_binary_operator(expression->operation())); return error_mark_node; } } tree generic_visitor::build_equality_operation(boot::binary_expression *expression, tree left, tree right) { location_t expression_location = get_location(&expression->position()); tree_code equality_code, combination_code; if (expression->operation() == boot::binary_operator::equals) { equality_code = EQ_EXPR; combination_code = TRUTH_ANDIF_EXPR; } else if (expression->operation() == boot::binary_operator::not_equals) { equality_code = NE_EXPR; combination_code = TRUTH_ORIF_EXPR; } else { gcc_unreachable(); } if (TREE_TYPE(left) == elna_string_type_node) { tree lhs_length = build3(COMPONENT_REF, TREE_TYPE(elna_string_length_field_node), left, elna_string_length_field_node, NULL_TREE); tree lhs_ptr = build3(COMPONENT_REF, TREE_TYPE(elna_string_ptr_field_node), left, elna_string_ptr_field_node, NULL_TREE); tree rhs_length = build3(COMPONENT_REF, TREE_TYPE(elna_string_length_field_node), right, elna_string_length_field_node, NULL_TREE); tree rhs_ptr = build3(COMPONENT_REF, TREE_TYPE(elna_string_ptr_field_node), right, elna_string_ptr_field_node, NULL_TREE); tree length_equality = build2(equality_code, elna_bool_type_node, lhs_length, rhs_length); tree *memcmp = elna_global_decls->get("__builtin_memcmp"); gcc_assert(memcmp != nullptr); tree fndecl_type = build_function_type(integer_type_node, TYPE_ARG_TYPES(*memcmp)); tree memcmp_addr = build1(ADDR_EXPR, build_pointer_type(fndecl_type), *memcmp); tree memcmp_call = build_call_nary(integer_type_node, memcmp_addr, 3, lhs_ptr, rhs_ptr, lhs_length); tree equals_zero = build2(equality_code, elna_bool_type_node, memcmp_call, integer_zero_node); return build2(combination_code, elna_bool_type_node, length_equality, equals_zero); } else { return build2_loc(expression_location, equality_code, elna_bool_type_node, left, right); } } void generic_visitor::visit(boot::binary_expression *expression) { expression->lhs().accept(this); tree left = this->current_expression; tree left_type = TREE_TYPE(left); expression->rhs().accept(this); tree right = this->current_expression; tree right_type = TREE_TYPE(right); location_t expression_location = get_location(&expression->position()); if ((is_pointer_type(left_type) || is_pointer_type(right_type)) && (expression->operation() == boot::binary_operator::sum || expression->operation() == boot::binary_operator::subtraction)) { this->current_expression = do_pointer_arithmetic(expression->operation(), left, right); if (this->current_expression == error_mark_node) { error_at(expression_location, "invalid operation %s on a pointer and an integral type", boot::print_binary_operator(expression->operation())); } else if (TREE_TYPE(this->current_expression) == ssizetype) { this->current_expression = fold_convert(elna_int_type_node, this->current_expression); } return; } if (left_type != right_type && !are_compatible_pointers(left_type, right) && !are_compatible_pointers(right_type, left)) { error_at(expression_location, "invalid operands of type %s and %s for operator %s", print_type(left_type).c_str(), print_type(right_type).c_str(), boot::print_binary_operator(expression->operation())); this->current_expression = error_mark_node; return; } switch (expression->operation()) { case boot::binary_operator::sum: this->current_expression = build_arithmetic_operation(expression, PLUS_EXPR, left, right); break; case boot::binary_operator::subtraction: this->current_expression = build_arithmetic_operation(expression, MINUS_EXPR, left, right); break; case boot::binary_operator::division: this->current_expression = build_arithmetic_operation(expression, TRUNC_DIV_EXPR, left, right); break; case boot::binary_operator::remainder: this->current_expression = build_arithmetic_operation(expression, TRUNC_MOD_EXPR, left, right); break; case boot::binary_operator::multiplication: this->current_expression = build_arithmetic_operation(expression, MULT_EXPR, left, right); break; case boot::binary_operator::less: this->current_expression = build_comparison_operation(expression, LT_EXPR, left, right); break; case boot::binary_operator::greater: this->current_expression = build_comparison_operation(expression, GT_EXPR, left, right); break; case boot::binary_operator::less_equal: this->current_expression = build_comparison_operation(expression, LE_EXPR, left, right); break; case boot::binary_operator::greater_equal: this->current_expression = build_comparison_operation(expression, GE_EXPR, left, right); break; case boot::binary_operator::conjunction: this->current_expression = build_bit_logic_operation(expression, left, right); break; case boot::binary_operator::disjunction: this->current_expression = build_bit_logic_operation(expression, left, right); break; case boot::binary_operator::exclusive_disjunction: this->current_expression = build_bit_logic_operation(expression, left, right); break; case boot::binary_operator::equals: this->current_expression = build_equality_operation(expression, left, right); break; case boot::binary_operator::not_equals: this->current_expression = build_equality_operation(expression, left, right); break; } } void generic_visitor::visit(boot::unary_expression *expression) { expression->operand().accept(this); location_t location = get_location(&expression->position()); switch (expression->operation()) { case boot::unary_operator::reference: TREE_ADDRESSABLE(this->current_expression) = 1; this->current_expression = build_fold_addr_expr_with_type_loc(location, this->current_expression, build_pointer_type_for_mode(TREE_TYPE(this->current_expression), VOIDmode, true)); TREE_NO_TRAMPOLINE(this->current_expression) = 1; break; case boot::unary_operator::negation: if (TREE_TYPE(this->current_expression) == elna_bool_type_node) { this->current_expression = build1_loc(location, TRUTH_NOT_EXPR, boolean_type_node, this->current_expression); } else if (is_integral_type(TREE_TYPE(this->current_expression))) { this->current_expression = build1_loc(location, BIT_NOT_EXPR, TREE_TYPE(this->current_expression), this->current_expression); } else { error_at(location, "type '%s' cannot be negated", print_type(TREE_TYPE(this->current_expression)).c_str()); this->current_expression = error_mark_node; } break; case boot::unary_operator::minus: if (is_integral_type(TREE_TYPE(this->current_expression))) { this->current_expression = fold_build1(NEGATE_EXPR, TREE_TYPE(this->current_expression), this->current_expression); } else { error_at(location, "type '%s' cannot be negated", print_type(TREE_TYPE(this->current_expression)).c_str()); this->current_expression = error_mark_node; } } } void generic_visitor::visit(boot::constant_definition *definition) { location_t definition_location = get_location(&definition->position()); definition->body().accept(this); tree definition_tree = build_decl(definition_location, CONST_DECL, get_identifier(definition->identifier.c_str()), TREE_TYPE(this->current_expression)); auto result = this->symbol_map->enter(definition->identifier, definition_tree); if (result) { DECL_INITIAL(definition_tree) = this->current_expression; TREE_CONSTANT(definition_tree) = 1; TREE_READONLY(definition_tree) = 1; TREE_PUBLIC(definition_tree) = definition->exported; if (!lang_hooks.decls.global_bindings_p()) { auto declaration_statement = build1_loc(definition_location, DECL_EXPR, void_type_node, definition_tree); append_statement(declaration_statement); } } else { error_at(definition_location, "variable '%s' already declared in this scope", definition->identifier.c_str()); } this->current_expression = NULL_TREE; } void generic_visitor::visit(boot::type_definition *definition) { location_t definition_location = get_location(&definition->position()); tree tree_type = build_type(definition->body()); tree definition_tree = build_decl(definition_location, TYPE_DECL, get_identifier(definition->identifier.c_str()), tree_type); auto result = this->symbol_map->enter(definition->identifier, tree_type); if (result) { TREE_PUBLIC(definition_tree) = definition->exported; } else { error_at(get_location(&definition->position()), "type '%s' already declared in this scope", definition->identifier.c_str()); } } tree generic_visitor::build_type(boot::top_type& type) { if (boot::basic_type *basic_type = type.is_basic()) { tree symbol = this->lookup(basic_type->base_name()); if (symbol != NULL_TREE && TYPE_P(symbol)) { return symbol; } error_at(get_location(&basic_type->position()), "type '%s' not declared", basic_type->base_name().c_str()); return error_mark_node; } else if (boot::array_type *array_type = type.is_array()) { tree lower_bound = build_int_cst_type(integer_type_node, 0); tree upper_bound = build_int_cst_type(integer_type_node, array_type->size); tree base_type = build_type(array_type->base()); if (base_type == NULL_TREE || base_type == error_mark_node) { return base_type; } tree range_type = build_range_type(integer_type_node, lower_bound, upper_bound); return build_array_type(base_type, range_type); } else if (boot::pointer_type *pointer_type = type.is_pointer()) { tree base_type = build_type(pointer_type->base()); if (base_type == NULL_TREE || base_type == error_mark_node) { return base_type; } return build_pointer_type_for_mode(base_type, VOIDmode, true); } else if (boot::record_type *record_type = type.is_record()) { std::set field_names; tree record_type_node = make_node(RECORD_TYPE); for (auto& field : record_type->fields) { if (field_names.find(field.first) != field_names.cend()) { error_at(get_location(&field.second->position()), "repeated field name"); return error_mark_node; } field_names.insert(field.first); tree field_type = build_type(*field.second); if (field_type == NULL_TREE || field_type == error_mark_node) { return field_type; } tree field_declaration = build_field(get_location(&field.second->position()), record_type_node, field.first, field_type); TYPE_FIELDS(record_type_node) = chainon(TYPE_FIELDS(record_type_node), field_declaration); } layout_type(record_type_node); return record_type_node; } else if (boot::union_type *union_type = type.is_union()) { std::set field_names; tree union_type_node = make_node(UNION_TYPE); for (auto& field : union_type->fields) { if (field_names.find(field.first) != field_names.cend()) { error_at(get_location(&field.second->position()), "repeated field name"); return error_mark_node; } field_names.insert(field.first); tree field_type = build_type(*field.second); if (field_type == NULL_TREE || field_type == error_mark_node) { return field_type; } tree field_declaration = build_field(get_location(&field.second->position()), union_type_node, field.first, field_type); TYPE_FIELDS(union_type_node) = chainon(TYPE_FIELDS(union_type_node), field_declaration); } layout_type(union_type_node); return union_type_node; } return NULL_TREE; } void generic_visitor::visit(boot::variable_declaration *declaration) { tree declaration_type = build_type(declaration->variable_type()); gcc_assert(declaration_type != NULL_TREE); location_t declaration_location = get_location(&declaration->position()); tree declaration_tree = build_decl(declaration_location, VAR_DECL, get_identifier(declaration->identifier.c_str()), declaration_type); bool result = this->symbol_map->enter(declaration->identifier, declaration_tree); if (is_pointer_type(declaration_type)) { DECL_INITIAL(declaration_tree) = elna_pointer_nil_node; } if (!result) { error_at(declaration_location, "variable '%s' already declared in this scope", declaration->identifier.c_str()); } else if (lang_hooks.decls.global_bindings_p()) { TREE_STATIC(declaration_tree) = 1; varpool_node::get_create(declaration_tree); varpool_node::finalize_decl(declaration_tree); } else { DECL_CONTEXT(declaration_tree) = current_function_decl; f_names = chainon(f_names, declaration_tree); auto declaration_statement = build1_loc(declaration_location, DECL_EXPR, void_type_node, declaration_tree); append_statement(declaration_statement); } } void generic_visitor::visit(boot::variable_expression *expression) { auto symbol = this->lookup(expression->name()); if (symbol == NULL_TREE) { error_at(get_location(&expression->position()), "variable '%s' not declared in the current scope", expression->name().c_str()); this->current_expression = error_mark_node; return; } this->current_expression = symbol; } void generic_visitor::visit(boot::array_access_expression *expression) { expression->base().accept(this); tree designator = this->current_expression; location_t location = get_location(&expression->position()); expression->index().accept(this); if (!is_integral_type(TREE_TYPE(this->current_expression))) { error_at(location, "type '%s' cannot be used as index", print_type(TREE_TYPE(this->current_expression)).c_str()); this->current_expression = error_mark_node; return; } if (this->current_expression != elna_word_type_node) { this->current_expression = fold_convert(elna_word_type_node, this->current_expression); } tree offset = build2(MINUS_EXPR, elna_word_type_node, this->current_expression, size_one_node); if (is_array_type(TREE_TYPE(designator))) { tree element_type = TREE_TYPE(TREE_TYPE(designator)); this->current_expression = build4_loc(location, ARRAY_REF, element_type, designator, offset, NULL_TREE, NULL_TREE); } else if (TREE_TYPE(designator) == elna_string_type_node) { tree string_ptr = build3_loc(location, COMPONENT_REF, TREE_TYPE(elna_string_ptr_field_node), designator, elna_string_ptr_field_node, NULL_TREE); tree target_pointer = do_pointer_arithmetic(boot::binary_operator::sum, string_ptr, offset); this->current_expression = build1_loc(location, INDIRECT_REF, elna_char_type_node, target_pointer); } else { error_at(location, "indexing is not allowed on type '%s'", print_type(TREE_TYPE(designator)).c_str()); this->current_expression = error_mark_node; } } void generic_visitor::visit(boot::field_access_expression *expression) { expression->base().accept(this); location_t expression_location = get_location(&expression->position()); if (TYPE_P(this->current_expression)) { if (expression->field() == "size") { this->current_expression = build1(CONVERT_EXPR, elna_word_type_node, size_in_bytes(this->current_expression)); } else if (expression->field() == "alignment") { this->current_expression = build_int_cstu(elna_word_type_node, TYPE_ALIGN_UNIT(this->current_expression)); } else { error_at(expression_location, "type '%s' does not have property '%s'", print_type(this->current_expression).c_str(), expression->field().c_str()); this->current_expression = error_mark_node; } } else if (is_aggregate_type(TREE_TYPE(this->current_expression))) { tree field_declaration = TYPE_FIELDS(TREE_TYPE(this->current_expression)); while (field_declaration != NULL_TREE) { tree declaration_name = DECL_NAME(field_declaration); const char *identifier_pointer = IDENTIFIER_POINTER(declaration_name); if (expression->field() == identifier_pointer) { break; } field_declaration = TREE_CHAIN(field_declaration); } if (field_declaration == NULL_TREE) { error_at(expression_location, "record type does not have a field named '%s'", expression->field().c_str()); this->current_expression = error_mark_node; } else { this->current_expression = build3_loc(expression_location, COMPONENT_REF, TREE_TYPE(field_declaration), this->current_expression, field_declaration, NULL_TREE); } } } void generic_visitor::visit(boot::dereference_expression *expression) { expression->base().accept(this); this->current_expression = build1_loc(get_location(&expression->position()), INDIRECT_REF, TREE_TYPE(TREE_TYPE(this->current_expression)), this->current_expression); } void generic_visitor::visit(boot::assign_statement *statement) { statement->lvalue().accept(this); auto lvalue = this->current_expression; auto statement_location = get_location(&statement->position()); statement->rvalue().accept(this); if (TREE_CODE(lvalue) == CONST_DECL) { error_at(statement_location, "cannot modify constant '%s'", statement->lvalue().is_variable()->name().c_str()); this->current_expression = error_mark_node; return; } if (is_assignable_from(TREE_TYPE(lvalue), this->current_expression)) { tree assignment = build2_loc(statement_location, MODIFY_EXPR, void_type_node, lvalue, this->current_expression); append_statement(assignment); this->current_expression = NULL_TREE; } else { error_at(statement_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(lvalue)).c_str()); this->current_expression = error_mark_node; } } void generic_visitor::visit(boot::if_statement *statement) { tree endif_label_decl = build_label_decl("endif", UNKNOWN_LOCATION); tree goto_endif = build1(GOTO_EXPR, void_type_node, endif_label_decl); make_if_branch(statement->body(), goto_endif); for (const auto branch : statement->branches) { make_if_branch(*branch, goto_endif); } if (statement->alternative() != nullptr) { enter_scope(); for (const auto body_statement : *statement->alternative()) { body_statement->accept(this); } tree mapping = leave_scope(); append_statement(mapping); } tree endif_label_expr = build1(LABEL_EXPR, void_type_node, endif_label_decl); append_statement(endif_label_expr); this->current_expression = NULL_TREE; } void generic_visitor::make_if_branch(boot::conditional_statements& branch, tree goto_endif) { branch.prerequisite().accept(this); if (TREE_TYPE(this->current_expression) != boolean_type_node) { error_at(get_location(&branch.prerequisite().position()), "expected expression of boolean type but its type is %s", print_type(TREE_TYPE(this->current_expression)).c_str()); this->current_expression = error_mark_node; return; } tree then_label_decl = build_label_decl("then", UNKNOWN_LOCATION); tree goto_then = build1(GOTO_EXPR, void_type_node, then_label_decl); tree else_label_decl = build_label_decl("else", UNKNOWN_LOCATION); tree goto_else = build1(GOTO_EXPR, void_type_node, else_label_decl); auto cond_expr = build3(COND_EXPR, void_type_node, this->current_expression, goto_then, goto_else); append_statement(cond_expr); tree then_label_expr = build1(LABEL_EXPR, void_type_node, then_label_decl); append_statement(then_label_expr); enter_scope(); for (const auto body_statement : branch.statements) { body_statement->accept(this); } tree mapping = leave_scope(); append_statement(mapping); append_statement(goto_endif); tree else_label_expr = build1(LABEL_EXPR, void_type_node, else_label_decl); append_statement(else_label_expr); } tree generic_visitor::build_label_decl(const char *name, location_t loc) { auto label_decl = build_decl(loc, LABEL_DECL, get_identifier(name), void_type_node); DECL_CONTEXT(label_decl) = current_function_decl; return label_decl; } void generic_visitor::visit(boot::while_statement *statement) { statement->body().prerequisite().accept(this); if (TREE_TYPE(this->current_expression) != boolean_type_node) { error_at(get_location(&statement->body().prerequisite().position()), "expected expression of boolean type but its type is %s", print_type(TREE_TYPE(this->current_expression)).c_str()); this->current_expression = error_mark_node; return; } enter_scope(); auto prerequisite_location = get_location(&statement->body().prerequisite().position()); auto body_location = get_location(&statement->position()); auto prerequisite_label_decl = build_label_decl("while_check", prerequisite_location); auto prerequisite_label_expr = build1_loc(prerequisite_location, LABEL_EXPR, void_type_node, prerequisite_label_decl); append_statement(prerequisite_label_expr); auto body_label_decl = build_label_decl("while_body", body_location); auto end_label_decl = build_label_decl("end_while", UNKNOWN_LOCATION); auto goto_body = build1_loc(prerequisite_location, GOTO_EXPR, void_type_node, body_label_decl); auto goto_end = build1_loc(prerequisite_location, GOTO_EXPR, void_type_node, end_label_decl); auto cond_expr = build3_loc(prerequisite_location, COND_EXPR, void_type_node, this->current_expression, goto_body, goto_end); append_statement(cond_expr); auto body_label_expr = build1_loc(body_location, LABEL_EXPR, void_type_node, body_label_decl); append_statement(body_label_expr); for (const auto body_statement : statement->body().statements) { body_statement->accept(this); } tree mapping = leave_scope(); append_statement(mapping); auto goto_check = build1(GOTO_EXPR, void_type_node, prerequisite_label_decl); append_statement(goto_check); auto endif_label_expr = build1(LABEL_EXPR, void_type_node, end_label_decl); append_statement(endif_label_expr); this->current_expression = NULL_TREE; } void generic_visitor::visit(boot::call_statement *statement) { statement->body().accept(this); append_statement(this->current_expression); this->current_expression = NULL_TREE; } void generic_visitor::visit(boot::return_statement *statement) { boot::expression *return_expression = statement->return_expression(); if (return_expression == nullptr) { return; } return_expression->accept(this); tree set_result = build2(INIT_EXPR, void_type_node, DECL_RESULT(current_function_decl), this->current_expression); tree return_stmt = build1(RETURN_EXPR, void_type_node, set_result); append_statement(return_stmt); } void generic_visitor::visit(boot::defer_statement *statement) { enter_scope(); for (boot::statement *const body_statement : statement->statements) { body_statement->accept(this); } defer(leave_scope()); } } }