diff options
| author | Eugen Wissner <belka@caraus.de> | 2026-09-18 12:19:13 +0200 |
|---|---|---|
| committer | Eugen Wissner <belka@caraus.de> | 2026-09-18 12:19:13 +0200 |
| commit | dc9d03915c4b169fec749081b45d56f722105813 (patch) | |
| tree | 0a0719bc95f942b675fb52865d9c5bac9e105767 | |
| parent | b575036fe804f14b78da74897a74b010cc94f730 (diff) | |
| download | elna-dc9d03915c4b169fec749081b45d56f722105813.tar.gz | |
Allow block-local variables
| -rw-r--r-- | boot/ast.cc | 157 | ||||
| -rw-r--r-- | boot/name_analysis.cc | 86 | ||||
| -rw-r--r-- | boot/parser.yy | 67 | ||||
| -rw-r--r-- | boot/type_check.cc | 45 | ||||
| -rw-r--r-- | boot/validation.cc | 29 | ||||
| -rw-r--r-- | gcc/gcc/elna-generic.cc | 39 | ||||
| -rw-r--r-- | include/elna/boot/ast.h | 86 | ||||
| -rw-r--r-- | include/elna/boot/name_analysis.h | 10 | ||||
| -rw-r--r-- | include/elna/boot/type_check.h | 1 | ||||
| -rw-r--r-- | include/elna/boot/validation.h | 2 | ||||
| -rw-r--r-- | include/elna/gcc/elna-generic.h | 5 | ||||
| -rw-r--r-- | testsuite/runnable/block_local_variable.elna | 14 |
12 files changed, 270 insertions, 271 deletions
diff --git a/boot/ast.cc b/boot/ast.cc index 4a596e5..b38e8d1 100644 --- a/boot/ast.cc +++ b/boot/ast.cc @@ -253,81 +253,66 @@ namespace elna::boot statement->rvalue().accept(this); } - void walking_visitor::visit(if_statement *statement) + void walking_visitor::traverse_block(block& body) { - statement->branch().prerequisite().accept(this); - for (auto *branch_statement : statement->branch().statements) + for (variable_declaration *const variable : body.variables) { - branch_statement->accept(this); + variable->accept(this); + } + for (statement *const body_statement : body.statements) + { + body_statement->accept(this); } + } + + void walking_visitor::visit(if_statement *statement) + { + statement->branch().prerequisite().accept(this); + traverse_block(statement->branch().body); + for (conditional_statements *branch : statement->branches) { branch->prerequisite().accept(this); - - for (auto *branch_statement : branch->statements) - { - branch_statement->accept(this); - } + traverse_block(branch->body); } if (statement->alternative != nullptr) { - for (auto *branch_statement : *statement->alternative) - { - branch_statement->accept(this); - } + traverse_block(*statement->alternative); } } void walking_visitor::visit(while_statement *statement) { statement->branch().prerequisite().accept(this); - for (auto *branch_statement : statement->branch().statements) - { - branch_statement->accept(this); - } + traverse_block(statement->branch().body); + for (conditional_statements *branch : statement->branches) { branch->prerequisite().accept(this); - - for (auto *branch_statement : branch->statements) - { - branch_statement->accept(this); - } + traverse_block(branch->body); } } void walking_visitor::visit(repeat_statement *statement) { statement->condition().accept(this); - for (auto *body_statement : statement->body) - { - body_statement->accept(this); - } + traverse_block(statement->body); } void walking_visitor::visit(for_statement *statement) { statement->range().accept(this); - for (auto *body_statement : statement->body) - { - body_statement->accept(this); - } + traverse_block(statement->body); } void walking_visitor::visit(defer_statement *statement) { - for (auto *body_statement : statement->statements) - { - body_statement->accept(this); - } + traverse_block(statement->body); } void walking_visitor::visit(block_statement *statement) { - for (auto *body_statement : statement->statements) - { - body_statement->accept(this); - } + traverse_block(statement->body); } void walking_visitor::visit(break_statement *) @@ -341,23 +326,17 @@ namespace elna::boot void walking_visitor::visit(case_statement *statement) { statement->condition().accept(this); - for (const switch_case& case_block : statement->cases) + for (switch_case& case_block : statement->cases) { for (expression *case_label : case_block.labels) { case_label->accept(this); } - for (auto *body_statement : case_block.statements) - { - body_statement->accept(this); - } + traverse_block(case_block.body); } if (statement->alternative != nullptr) { - for (auto *body_statement : *statement->alternative) - { - body_statement->accept(this); - } + traverse_block(*statement->alternative); } } @@ -1091,21 +1070,12 @@ namespace elna::boot return *m_underlying_type; } - procedure_body::procedure_body(std::vector<variable_declaration *>&& variables, - std::vector<statement *>&& entry_point, expression *return_expression) - : variables(std::move(variables)), - statements(std::move(entry_point)), return_expression(return_expression) + block::block(std::vector<variable_declaration *>&& variables, std::vector<statement *>&& statements) + : variables(std::move(variables)), statements(std::move(statements)) { } - procedure_body::procedure_body(procedure_body&& that) noexcept - : variables(std::move(const_cast<std::vector<variable_declaration *>&>(that.variables))), - statements(std::move(const_cast<std::vector<statement *>&>(that.statements))), - return_expression(that.return_expression) - { - } - - procedure_body::~procedure_body() + block::~block() { for (const statement *body_statement : this->statements) { @@ -1117,6 +1087,11 @@ namespace elna::boot } } + procedure_body::procedure_body(block&& body, expression *return_expression) + : block(std::move(body)), return_expression(return_expression) + { + } + void traverse_body(parser_visitor *visitor, const procedure_body& body) { for (variable_declaration *variable : body.variables) @@ -1175,8 +1150,8 @@ namespace elna::boot return this; } - defer_statement::defer_statement(const source_position position, std::vector<statement *>&& statements) - : node(position), statements(std::move(statements)) + defer_statement::defer_statement(const source_position position, block&& body) + : node(position), body(std::move(body)) { } @@ -1185,17 +1160,8 @@ namespace elna::boot visitor->visit(this); } - defer_statement::~defer_statement() - { - for (const statement *body_statement : statements) - { - delete body_statement; - } - } - - block_statement::block_statement(const source_position position, identifier&& name, - std::vector<statement *>&& statements) - : node(position), name(std::move(name)), statements(std::move(statements)) + block_statement::block_statement(const source_position position, identifier&& name, block&& body) + : node(position), name(std::move(name)), body(std::move(body)) { } @@ -1204,14 +1170,6 @@ namespace elna::boot visitor->visit(this); } - block_statement::~block_statement() - { - for (const statement *body_statement : statements) - { - delete body_statement; - } - } - break_statement::break_statement(const source_position position, identifier&& label) : node(position), label(std::move(label)) { @@ -1618,8 +1576,8 @@ namespace elna::boot return this; } - conditional_statements::conditional_statements(expression *prerequisite, std::vector<statement *>&& statements) - : m_prerequisite(prerequisite), statements(std::move(statements)) + conditional_statements::conditional_statements(expression *prerequisite, block&& body) + : m_prerequisite(prerequisite), body(std::move(body)) { } @@ -1631,14 +1589,10 @@ namespace elna::boot conditional_statements::~conditional_statements() { delete m_prerequisite; - for (auto *statement : statements) - { - delete statement; - } } case_statement::case_statement(const source_position position, - expression *condition, std::vector<switch_case>&& cases, std::vector<statement *> *alternative) + expression *condition, std::vector<switch_case>&& cases, block *alternative) : node(position), m_condition(condition), cases(std::move(cases)), alternative(alternative) { } @@ -1653,6 +1607,19 @@ namespace elna::boot return *m_condition; } + case_statement::~case_statement() + { + delete m_condition; + for (const switch_case& case_block : this->cases) + { + for (const expression *case_label : case_block.labels) + { + delete case_label; + } + } + delete this->alternative; + } + assign_statement::assign_statement(const source_position position, designator_expression *lvalue, expression *rvalue) : node(position), m_lvalue(lvalue), m_rvalue(rvalue) @@ -1705,8 +1672,7 @@ namespace elna::boot } if_statement::if_statement(const source_position position, conditional_statements *branch, - std::vector<conditional_statements *>&& branches, - std::vector<statement *> *alternative) + std::vector<conditional_statements *>&& branches, block *alternative) : node(position), m_branch(branch), branches(std::move(branches)), alternative(alternative) { } @@ -1766,7 +1732,7 @@ namespace elna::boot } } - repeat_statement::repeat_statement(const source_position position, std::vector<statement *>&& body, + repeat_statement::repeat_statement(const source_position position, block&& body, expression *condition) : node(position), m_condition(condition), body(std::move(body)) { @@ -1775,10 +1741,6 @@ namespace elna::boot repeat_statement::~repeat_statement() { delete this->m_condition; - for (const statement *body_statement : this->body) - { - delete body_statement; - } } void repeat_statement::accept(parser_visitor *visitor) @@ -1792,7 +1754,7 @@ namespace elna::boot } for_statement::for_statement(const source_position position, identifier&& control_variable, - expression *range, std::vector<statement *>&& body, identifier *const counter = nullptr) + expression *range, block&& body, identifier *const counter = nullptr) : node(position), m_range(range), control_variable(std::move(control_variable)), counter(counter), body(std::move(body)) { @@ -1802,11 +1764,6 @@ namespace elna::boot { delete this->m_range; delete this->counter; - - for (const statement *body_statement : this->body) - { - delete body_statement; - } } void for_statement::accept(parser_visitor *visitor) diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc index 9ba030c..18aaca2 100644 --- a/boot/name_analysis.cc +++ b/boot/name_analysis.cc @@ -733,65 +733,63 @@ namespace elna::boot statement->rvalue().accept(this); } - void resolving_visitor::visit(if_statement *statement) + void resolving_visitor::traverse_block(block& body) { - statement->branch().prerequisite().accept(this); - for (auto *branch_statement : statement->branch().statements) + for (variable_declaration *const variable : body.variables) + { + variable->accept(this); + } + for (statement *const body_statement : body.statements) { - branch_statement->accept(this); + body_statement->accept(this); } + } + + void resolving_visitor::visit_block(block& body) + { + body.symbols = this->bag.enter(); + traverse_block(body); + this->bag.leave(); + } + + void resolving_visitor::visit(if_statement *statement) + { + statement->branch().prerequisite().accept(this); + visit_block(statement->branch().body); + for (conditional_statements *branch : statement->branches) { branch->prerequisite().accept(this); - - for (auto *branch_statement : branch->statements) - { - branch_statement->accept(this); - } + visit_block(branch->body); } if (statement->alternative != nullptr) { - for (auto *branch_statement : *statement->alternative) - { - branch_statement->accept(this); - } + visit_block(*statement->alternative); } } void resolving_visitor::visit(while_statement *statement) { statement->branch().prerequisite().accept(this); - for (auto *branch_statement : statement->branch().statements) - { - branch_statement->accept(this); - } + visit_block(statement->branch().body); + for (conditional_statements *branch : statement->branches) { branch->prerequisite().accept(this); - - for (auto *branch_statement : branch->statements) - { - branch_statement->accept(this); - } + visit_block(branch->body); } } void resolving_visitor::visit(repeat_statement *statement) { statement->condition().accept(this); - for (auto *body_statement : statement->body) - { - body_statement->accept(this); - } + visit_block(statement->body); } void resolving_visitor::visit(defer_statement *statement) { ++this->defer_depth; - for (auto *body_statement : statement->statements) - { - body_statement->accept(this); - } + visit_block(statement->body); --this->defer_depth; } @@ -799,7 +797,7 @@ namespace elna::boot { const std::string& label_name = statement->name.name(); - this->bag.enter(); + statement->body.symbols = this->bag.enter(); auto label_symbol = std::make_shared<label_info>(this->defer_depth); label_symbol->position.emplace(statement->name.position()); @@ -815,10 +813,7 @@ namespace elna::boot add_error<symbol_declaration_error>(statement->name.position(), label_name, original_definition); } - for (auto *body_statement : statement->statements) - { - body_statement->accept(this); - } + traverse_block(statement->body); this->bag.leave(); } @@ -855,23 +850,17 @@ namespace elna::boot void resolving_visitor::visit(case_statement *statement) { statement->condition().accept(this); - for (const switch_case& case_block : statement->cases) + for (switch_case& case_block : statement->cases) { for (expression *case_label : case_block.labels) { case_label->accept(this); } - for (auto *body_statement : case_block.statements) - { - body_statement->accept(this); - } + visit_block(case_block.body); } if (statement->alternative != nullptr) { - for (auto *body_statement : *statement->alternative) - { - body_statement->accept(this); - } + visit_block(*statement->alternative); } } @@ -1552,6 +1541,7 @@ namespace elna::boot { const std::shared_ptr<procedure_info> info = this->bag.lookup("")->is_procedure(); + unit->entry_point->symbols = info->scope; this->bag.enter(info->scope); traverse_body(this, unit->entry_point.value()); this->bag.leave(); @@ -1598,6 +1588,7 @@ namespace elna::boot const std::shared_ptr<procedure_info> info = this->bag.lookup(declaration->identifier.name())->is_procedure(); + declaration->body->symbols = info->scope; this->bag.enter(info->scope); traverse_body(this, declaration->body.value()); this->bag.leave(); @@ -1612,7 +1603,7 @@ namespace elna::boot const type control_variable_pointer_type = type(std::make_shared<pointer_type>(control_variable_base_type)); const type control_variable_const_type = type(std::make_shared<constant_type>(control_variable_pointer_type)); - statement->symbols = this->bag.enter(); + statement->body.symbols = this->bag.enter(); register_variable(statement->control_variable.name(), control_variable_const_type, statement->control_variable.position()); @@ -1621,10 +1612,7 @@ namespace elna::boot register_variable(statement->counter->name(), lookup_primitive_type("Word"), statement->counter->position()); } - for (auto *body_statement : statement->body) - { - body_statement->accept(this); - } + traverse_block(statement->body); this->bag.leave(); } } diff --git a/boot/parser.yy b/boot/parser.yy index eaf1a7c..01dfa80 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -135,7 +135,7 @@ along with GCC; see the file COPYING3. If not see %type <elna::boot::switch_case> switch_case; %type <std::vector<elna::boot::switch_case>> switch_cases; %type <elna::boot::variable_declaration *> variable_declaration; -%type <std::vector<elna::boot::variable_declaration *>> variable_declarations; +%type <std::vector<elna::boot::variable_declaration *>> variable_declarations variable_part; %type <elna::boot::type_expression *> type_expression; %type <std::vector<elna::boot::type_expression *>> type_expressions; %type <elna::boot::expression *> expression operand simple_expression procedure_return unary_expression; @@ -145,6 +145,7 @@ along with GCC; see the file COPYING3. If not see %type <std::unique_ptr<elna::boot::procedure_call>> call_expression; %type <elna::boot::statement *> statement; %type <std::vector<elna::boot::statement *>> statements statement_part; +%type <elna::boot::block> block; %type <elna::boot::procedure_declaration *> procedure_declaration; %type <std::unique_ptr<elna::boot::procedure_type_expression>> procedure_heading; %type <elna::boot::procedure_type_expression::return_t> return_declaration; @@ -157,7 +158,7 @@ along with GCC; see the file COPYING3. If not see %type <std::unique_ptr<elna::boot::field_initializer>> field_initializer; %type <std::vector<elna::boot::field_initializer>> field_initializers; %type <std::vector<elna::boot::conditional_statements *>> elsif_then_statements elsif_do_statements; -%type <std::vector<elna::boot::statement *> *> else_statements; +%type <elna::boot::block *> else_statements; %type <std::unique_ptr<elna::boot::identifier>> identifier with_counter; %type <std::unique_ptr<elna::boot::identifier_definition>> identifier_definition attributed_identifier; %type <std::vector<elna::boot::identifier_definition>> identifier_definitions; @@ -188,9 +189,9 @@ program: } module_declaration: "type" type_declarations { $$ = $2; } - | "var" variable_declarations + | variable_part { - std::vector<boot::variable_declaration *> variables = $2; + std::vector<boot::variable_declaration *> variables = $1; $$.insert($$.end(), variables.begin(), variables.end()); } @@ -215,12 +216,19 @@ module_declarations: $$.insert($$.end(), appendix.begin(), appendix.end()); } procedure_body: - "var" variable_declarations statement_part procedure_return - { $$ = std::make_unique<boot::procedure_body>($2, $3, $4); } - | statement_part procedure_return - { - $$ = std::make_unique<boot::procedure_body>(std::vector<boot::variable_declaration *>(), $1, $2); - } + block procedure_return + { $$ = std::make_unique<boot::procedure_body>(std::move($1), $2); } + +variable_part: + "var" variable_declarations { $$ = $2; } + +block: + variable_part statement_part + { $$ = boot::block(std::move($1), std::move($2)); } + | "begin" statements + { $$ = boot::block(std::vector<boot::variable_declaration *>(), std::move($2)); } + | statements + { $$ = boot::block(std::vector<boot::variable_declaration *>(), std::move($1)); } statement_part: %empty {} @@ -284,20 +292,20 @@ with_counter: "with" identifier { $$ = $2; } | %empty { $$ = nullptr; } elsif_do_statements: - "elsif" expression "do" statements elsif_do_statements + "elsif" expression "do" block elsif_do_statements { - boot::conditional_statements *branch = new boot::conditional_statements($2, $4); + boot::conditional_statements *branch = new boot::conditional_statements($2, std::move($4)); $$ = $5; $$.emplace($$.begin(), branch); } | %empty {} else_statements: - "else" statements { $$ = new std::vector<boot::statement *>($2); } + "else" block { $$ = new boot::block(std::move($2)); } | %empty { $$ = nullptr; } elsif_then_statements: - "elsif" expression "then" statements elsif_then_statements + "elsif" expression "then" block elsif_then_statements { - boot::conditional_statements *branch = new boot::conditional_statements($2, $4); + boot::conditional_statements *branch = new boot::conditional_statements($2, std::move($4)); $$ = $5; $$.emplace($$.begin(), branch); } @@ -563,36 +571,37 @@ name_reference: statement: designator_expression ":=" expression { $$ = new boot::assign_statement(boot::make_position(@$), $1, $3); } - | "while" expression "do" statements elsif_do_statements "end" + | "while" expression "do" block elsif_do_statements "end" { - boot::conditional_statements *body = new boot::conditional_statements($2, $4); + boot::conditional_statements *body = new boot::conditional_statements($2, std::move($4)); $$ = new boot::while_statement(boot::make_position(@$), body, $5); } - | "repeat" statements "until" expression + | "repeat" block "until" expression { - $$ = new boot::repeat_statement(boot::make_position(@$), $2, $4); + $$ = new boot::repeat_statement(boot::make_position(@$), std::move($2), $4); } - | "for" identifier "of" expression with_counter "do" statements "end" + | "for" identifier "of" expression with_counter "do" block "end" { - $$ = new boot::for_statement(boot::make_position(@$), std::move(*$2), $4, $7, $5.release()); + $$ = new boot::for_statement(boot::make_position(@$), std::move(*$2), $4, + std::move($7), $5.release()); } - | "if" expression "then" statements elsif_then_statements else_statements "end" + | "if" expression "then" block elsif_then_statements else_statements "end" { - boot::conditional_statements *then = new boot::conditional_statements($2, $4); + boot::conditional_statements *then = new boot::conditional_statements($2, std::move($4)); $$ = new boot::if_statement(boot::make_position(@$), then, $5, $6); } | call_expression { $$ = $1.release(); } - | "defer" statements "end" - { $$ = new boot::defer_statement(boot::make_position(@$), $2); } - | "block" identifier statements "end" - { $$ = new boot::block_statement(boot::make_position(@$), std::move(*$2), $3); } + | "defer" block "end" + { $$ = new boot::defer_statement(boot::make_position(@$), std::move($2)); } + | "block" identifier block "end" + { $$ = new boot::block_statement(boot::make_position(@$), std::move(*$2), std::move($3)); } | "break" identifier { $$ = new boot::break_statement(boot::make_position(@$), std::move(*$2)); } | "case" expression "of" switch_cases else_statements "end" { $$ = new boot::case_statement(boot::make_position(@$), $2, $4, $5); } | %empty { $$ = new boot::empty_statement(boot::make_position(@$)); } -switch_case: case_labels ":" statements - { $$ = { .labels = $1, .statements = $3 }; } +switch_case: case_labels ":" block + { $$ = { .labels = $1, .body = std::move($3) }; } switch_cases: switch_case "|" switch_cases { diff --git a/boot/type_check.cc b/boot/type_check.cc index 2b82bec..5d5bc59 100644 --- a/boot/type_check.cc +++ b/boot/type_check.cc @@ -876,66 +876,47 @@ namespace elna::boot add_error<type_requirement_error>(statement->range().position(), statement->range().type_decoration, type_requirement_error::kind::for_range); } - this->bag.enter(statement->symbols); - for (auto *body_statement : statement->body) - { - body_statement->accept(this); - } + traverse_block(statement->body); + } + + void type_analysis_visitor::traverse_block(block& body) + { + this->bag.enter(body.symbols); + walking_visitor::traverse_block(body); this->bag.leave(); } void type_analysis_visitor::visit(repeat_statement *statement) { visit_and_validate_condition(statement->condition()); - - for (auto *body_statement : statement->body) - { - body_statement->accept(this); - } + traverse_block(statement->body); } void type_analysis_visitor::visit(while_statement *statement) { visit_and_validate_condition(statement->branch().prerequisite()); + traverse_block(statement->branch().body); - for (auto *branch_statement : statement->branch().statements) - { - branch_statement->accept(this); - } for (conditional_statements *branch : statement->branches) { visit_and_validate_condition(branch->prerequisite()); - - for (auto *branch_statement : branch->statements) - { - branch_statement->accept(this); - } + traverse_block(branch->body); } } void type_analysis_visitor::visit(if_statement *statement) { visit_and_validate_condition(statement->branch().prerequisite()); + traverse_block(statement->branch().body); - for (auto *branch_statement : statement->branch().statements) - { - branch_statement->accept(this); - } for (conditional_statements *branch : statement->branches) { visit_and_validate_condition(branch->prerequisite()); - - for (auto *branch_statement : branch->statements) - { - branch_statement->accept(this); - } + traverse_block(branch->body); } if (statement->alternative != nullptr) { - for (auto *branch_statement : *statement->alternative) - { - branch_statement->accept(this); - } + traverse_block(*statement->alternative); } } diff --git a/boot/validation.cc b/boot/validation.cc index e628cd8..facef04 100644 --- a/boot/validation.cc +++ b/boot/validation.cc @@ -49,6 +49,13 @@ namespace elna::boot } } + void validation_visitor::visit_block(const block& body) + { + this->bag.enter(body.symbols); + visit_statements(body.statements); + this->bag.leave(); + } + void validation_visitor::visit(unit *unit) { for (declaration *unit_declaration : unit->declarations) @@ -88,11 +95,11 @@ namespace elna::boot { for (const switch_case& case_block : statement->cases) { - visit_statements(case_block.statements); + visit_block(case_block.body); } if (statement->alternative != nullptr) { - visit_statements(*statement->alternative); + visit_block(*statement->alternative); } std::unordered_map<constant_value, source_position, constant_value_hash> seen; @@ -122,44 +129,44 @@ namespace elna::boot void validation_visitor::visit(if_statement *statement) { - visit_statements(statement->branch().statements); + visit_block(statement->branch().body); for (const conditional_statements *branch : statement->branches) { - visit_statements(branch->statements); + visit_block(branch->body); } if (statement->alternative != nullptr) { - visit_statements(*statement->alternative); + visit_block(*statement->alternative); } } void validation_visitor::visit(while_statement *statement) { - visit_statements(statement->branch().statements); + visit_block(statement->branch().body); for (const conditional_statements *branch : statement->branches) { - visit_statements(branch->statements); + visit_block(branch->body); } } void validation_visitor::visit(repeat_statement *statement) { - visit_statements(statement->body); + visit_block(statement->body); } void validation_visitor::visit(for_statement *statement) { - visit_statements(statement->body); + visit_block(statement->body); } void validation_visitor::visit(defer_statement *statement) { - visit_statements(statement->statements); + visit_block(statement->body); } void validation_visitor::visit(block_statement *statement) { - visit_statements(statement->statements); + visit_block(statement->body); } void validation_visitor::visit(break_statement *) diff --git a/gcc/gcc/elna-generic.cc b/gcc/gcc/elna-generic.cc index 60ed82b..f4cb3c3 100644 --- a/gcc/gcc/elna-generic.cc +++ b/gcc/gcc/elna-generic.cc @@ -980,7 +980,7 @@ namespace elna::gcc if (statement->alternative != nullptr) { enter_scope(); - visit_statements(*statement->alternative); + visit_block(*statement->alternative); result = leave_scope(); } @@ -1001,7 +1001,7 @@ namespace elna::gcc tree condition = this->current_expression; enter_scope(); - visit_statements(branch.statements); + visit_block(branch.body); if (goto_append != NULL_TREE) { append_statement(goto_append); @@ -1032,7 +1032,7 @@ namespace elna::gcc append_statement(build1(LABEL_EXPR, void_type_node, loop_label)); enter_scope(); - visit_statements(statement->body); + visit_block(statement->body); tree repeat_binding = leave_scope(); append_statement(repeat_binding); @@ -1077,7 +1077,7 @@ namespace elna::gcc enter_scope(); // Declare control variable with the unqualified type. The constant_type wrapper // is for semantic checking only, because GENERIC needs to modify the control variable. - auto control_variable_info = statement->symbols->lookup(statement->control_variable.name()); + auto control_variable_info = statement->body.symbols->lookup(statement->control_variable.name()); const boot::variable_info unqualified_info( boot::resolve_underlying_type(control_variable_info->is_variable()->symbol), control_variable_info->is_variable()->is_extern); @@ -1087,7 +1087,7 @@ namespace elna::gcc tree counter_declaration{ NULL_TREE }; if (statement->counter != nullptr) { - auto counter_info = statement->symbols->lookup(statement->counter->name()); + auto counter_info = statement->body.symbols->lookup(statement->counter->name()); counter_declaration = declare_local_variable(*statement->counter, *counter_info->is_variable(), elna_word_one_node); } @@ -1097,7 +1097,7 @@ namespace elna::gcc // The body gets its own scope, so that defer statements in it are run on // every iteration and not once for the whole loop. enter_scope(); - visit_statements(statement->body); + visit_block(statement->body); tree body_binding = leave_scope(); append_statement(body_binding); @@ -1158,10 +1158,21 @@ namespace elna::gcc } } + void generic_visitor::visit_block(boot::block& body) + { + this->bag.enter(body.symbols); + for (boot::variable_declaration *const variable : body.variables) + { + variable->accept(this); + } + visit_statements(body.statements); + this->bag.leave(); + } + void generic_visitor::visit(boot::defer_statement *statement) { enter_scope(); - visit_statements(statement->statements); + visit_block(statement->body); defer(leave_scope()); } @@ -1172,7 +1183,7 @@ namespace elna::gcc enter_scope(); this->symbols->enter(statement->name.name(), end_label); - visit_statements(statement->statements); + visit_block(statement->body); append_statement(leave_scope()); // The label sits outside the binding, so that a break leaving the block @@ -1216,7 +1227,7 @@ namespace elna::gcc tree end_label_declaration = create_artificial_label(get_location(&statement->position())); tree switch_statements = alloc_stmt_list(); - for (const boot::switch_case& case_block : statement->cases) + for (boot::switch_case& case_block : statement->cases) { for (boot::expression *const case_label : case_block.labels) { @@ -1230,7 +1241,7 @@ namespace elna::gcc append_to_statement_list(case_expression, &switch_statements); } enter_scope(); - visit_statements(case_block.statements); + visit_block(case_block.body); append_to_statement_list(leave_scope(), &switch_statements); tree goto_end = build1(GOTO_EXPR, void_type_node, end_label_declaration); @@ -1245,7 +1256,7 @@ namespace elna::gcc append_to_statement_list(case_expression, &switch_statements); enter_scope(); - visit_statements(*statement->alternative); + visit_block(*statement->alternative); append_to_statement_list(leave_scope(), &switch_statements); TREE_USED(end_label_declaration) = 1; @@ -1267,10 +1278,10 @@ namespace elna::gcc if (statement->alternative != nullptr) { enter_scope(); - visit_statements(*statement->alternative); + visit_block(*statement->alternative); result = leave_scope(); } - for (const boot::switch_case& case_block : statement->cases | std::views::reverse) + for (boot::switch_case& case_block : statement->cases | std::views::reverse) { tree case_condition = boolean_false_node; for (boot::expression *const case_label : case_block.labels) @@ -1285,7 +1296,7 @@ namespace elna::gcc } enter_scope(); - visit_statements(case_block.statements); + visit_block(case_block.body); tree then_body = leave_scope(); result = build3(COND_EXPR, void_type_node, case_condition, then_body, result); diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h index 4eea412..52461e6 100644 --- a/include/elna/boot/ast.h +++ b/include/elna/boot/ast.h @@ -111,6 +111,7 @@ namespace elna::boot template<typename T> concept literal_type = requires { literal_type_id<T>::value; }; + class statement; class variable_declaration; class procedure_declaration; class type_declaration; @@ -153,6 +154,27 @@ namespace elna::boot class empty_statement; /** + * Variable declarations and the statements they are visible in. + */ + struct block + { + std::vector<variable_declaration *> variables; + std::vector<statement *> statements; + /// Scope containing the variables, entered by the name analysis. + std::shared_ptr<symbol_table> symbols; + + block() = default; + block(std::vector<variable_declaration *>&& variables, std::vector<statement *>&& statements); + block(const block&) = delete; + block(block&& that) noexcept = default; + + block& operator=(const block&) = delete; + block& operator=(block&& that) noexcept = default; + + ~block(); + }; + + /** * Interface for AST visitors. */ struct parser_visitor @@ -304,6 +326,8 @@ namespace elna::boot protected: /// Passes needing the entry point's scope override this. virtual void visit_entry_point(unit *unit); + /// Passes needing the scope of a block override this. + virtual void traverse_block(block& body); }; /** @@ -659,21 +683,14 @@ namespace elna::boot procedure_type_expression *is_procedure() override; }; - struct procedure_body + struct procedure_body : block { - const std::vector<variable_declaration *> variables; - const std::vector<statement *> statements; - expression *const return_expression{ nullptr }; + expression *return_expression{ nullptr }; - procedure_body(std::vector<variable_declaration *>&& variables, - std::vector<statement *>&& entry_point, - expression *return_expression = nullptr); - procedure_body(const procedure_body&) = delete; - procedure_body(procedure_body&& that) noexcept; + procedure_body(block&& body, expression *return_expression = nullptr); + procedure_body(procedure_body&& that) noexcept = default; - procedure_body& operator=(const procedure_body&) = delete; - - virtual ~procedure_body(); + virtual ~procedure_body() = default; }; void traverse_body(parser_visitor *visitor, const procedure_body& body); @@ -687,7 +704,7 @@ namespace elna::boot public: const identifier_definition identifier; - const std::optional<procedure_body> body; + std::optional<procedure_body> body; /** * Type parameters bound by this declaration. Empty for a plain @@ -771,9 +788,9 @@ namespace elna::boot expression *m_prerequisite; public: - const std::vector<statement *> statements; + block body; - conditional_statements(expression *prerequisite, std::vector<statement *>&& statements); + conditional_statements(expression *prerequisite, block&& body); expression& prerequisite(); @@ -783,7 +800,7 @@ namespace elna::boot struct switch_case { std::vector<expression *> labels; - std::vector<statement *> statements; + block body; }; class case_statement : public statement @@ -791,13 +808,15 @@ namespace elna::boot expression *m_condition; public: - const std::vector<switch_case> cases; - const std::vector<statement *> *alternative; + std::vector<switch_case> cases; + block *alternative; case_statement(const source_position position, expression *condition, - std::vector<switch_case>&& cases, std::vector<statement *> *alternative = nullptr); + std::vector<switch_case>&& cases, block *alternative = nullptr); void accept(parser_visitor *visitor) override; expression& condition(); + + ~case_statement() override; }; class designator_expression : public expression @@ -965,11 +984,11 @@ namespace elna::boot public: const std::vector<conditional_statements *> branches; - const std::vector<statement *> *alternative; + block *alternative; if_statement(const source_position position, conditional_statements *branch, std::vector<conditional_statements *>&& branches, - std::vector<statement *> *alternative = nullptr); + block *alternative = nullptr); void accept(parser_visitor *visitor) override; conditional_statements& branch(); @@ -1016,9 +1035,9 @@ namespace elna::boot expression *m_condition; public: - const std::vector<statement *> body; + block body; - repeat_statement(const source_position position, std::vector<statement *>&& body, + repeat_statement(const source_position position, block&& body, expression *condition); ~repeat_statement() override; @@ -1037,11 +1056,11 @@ namespace elna::boot public: const identifier control_variable; identifier *const counter; - const std::vector<statement *> body; - std::shared_ptr<symbol_table> symbols; + /// The control variable and the counter live in the body's scope. + block body; for_statement(const source_position position, identifier&& control_variable, - expression *range, std::vector<statement *>&& body, identifier *const counter); + expression *range, block&& body, identifier *const counter); ~for_statement() override; void accept(parser_visitor *visitor) override; @@ -1057,7 +1076,7 @@ namespace elna::boot public: const std::vector<import_declaration *> imports; const std::vector<declaration *> declarations; - const std::optional<procedure_body> entry_point; + std::optional<procedure_body> entry_point; const std::vector<identifier> parameters; const std::optional<source_position> entry_position; @@ -1104,12 +1123,10 @@ namespace elna::boot class defer_statement : public statement { public: - const std::vector<statement *> statements; + block body; - defer_statement(const source_position position, std::vector<statement *>&& statements); + defer_statement(const source_position position, block&& body); void accept(parser_visitor *visitor) override; - - ~defer_statement() override; }; /** @@ -1119,13 +1136,10 @@ namespace elna::boot { public: const identifier name; - const std::vector<statement *> statements; + block body; - block_statement(const source_position position, identifier&& name, - std::vector<statement *>&& statements); + block_statement(const source_position position, identifier&& name, block&& body); void accept(parser_visitor *visitor) override; - - ~block_statement() override; }; /** diff --git a/include/elna/boot/name_analysis.h b/include/elna/boot/name_analysis.h index 67cd713..004aeae 100644 --- a/include/elna/boot/name_analysis.h +++ b/include/elna/boot/name_analysis.h @@ -298,6 +298,16 @@ namespace elna::boot std::optional<type> lookup_pointer_like_field(const std::string& field_name, const type& element_type); type lookup_field(const type& composite_type, const std::string& field_name); + /* + * Visits the declarations and the statements of a block in the scope + * the caller has opened for it. + */ + void traverse_block(block& body); + /* + * Opens the scope of a block, remembers it on the block for the later + * passes and visits the block in it. + */ + void visit_block(block& body); public: void visit(array_type_expression *expression) override; diff --git a/include/elna/boot/type_check.h b/include/elna/boot/type_check.h index 582b887..94f8961 100644 --- a/include/elna/boot/type_check.h +++ b/include/elna/boot/type_check.h @@ -300,6 +300,7 @@ namespace elna::boot protected: void visit_entry_point(unit *unit) override; + void traverse_block(block& body) override; public: explicit type_analysis_visitor(symbol_bag bag, const target_info& target); diff --git a/include/elna/boot/validation.h b/include/elna/boot/validation.h index 87e0b18..d3a22d1 100644 --- a/include/elna/boot/validation.h +++ b/include/elna/boot/validation.h @@ -49,6 +49,8 @@ namespace elna::boot evaluator constant_evaluator; void visit_statements(const std::vector<statement *>& statements); + /// Visits the statements of a block in its own scope. + void visit_block(const block& body); public: validation_visitor(symbol_bag& bag, const target_info& target); diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h index b05a8f9..54f320e 100644 --- a/include/elna/gcc/elna-generic.h +++ b/include/elna/gcc/elna-generic.h @@ -67,6 +67,11 @@ namespace elna::gcc void build_volatile_store_builtin(location_t call_location, boot::procedure_call *call); void visit_statements(const std::vector<boot::statement *>& statements); + /* + * Declares the variables of a block and generates its statements. The + * caller opens the binding level the variables are declared in. + */ + void visit_block(boot::block& body); void assert_constant(); tree declare_local_variable(const boot::identifier& name, const boot::variable_info& info, tree initial_value); diff --git a/testsuite/runnable/block_local_variable.elna b/testsuite/runnable/block_local_variable.elna new file mode 100644 index 0000000..6287eec --- /dev/null +++ b/testsuite/runnable/block_local_variable.elna @@ -0,0 +1,14 @@ +program() +var + total: Int := 0 +begin + if total = 0 then + var + inner: Int := 7 + begin + total := total + inner + end; + assert(total = 7) +return 0u8 + +end. |
