From d05837ab6dc291ee8ee3d94b33f86a6472c5847f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 8 Dec 2018 14:12:52 +0000 Subject: [PATCH] Labels for block and if statements (break only) --- examples/demo/demo.odin | 40 +++++++++++++++++++++++++++++++++------- src/check_stmt.cpp | 40 ++++++++++++++++++++++++++++++++-------- src/entity.cpp | 6 ++++-- src/ir.cpp | 25 ++++++++++++++++++++----- src/parser.cpp | 6 ++++++ src/parser.hpp | 2 ++ 6 files changed, 97 insertions(+), 22 deletions(-) diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 5f4f0dfb2..2d4303682 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -126,6 +126,38 @@ general_stuff :: proc() { fmt.println("Y is not defined"); } } + + { // Labelled control blocks + block: { + if true { + fmt.println("break block;"); + break block; + } + } + + { + branch: if true { + fmt.println("break branch;"); + break branch; + } + } + + { + loop: for true { + fmt.println("break loop;"); + break loop; + } + } + + { + cases: switch { + case: + fmt.println("break cases;"); + break cases; + } + } + + } } @@ -836,14 +868,8 @@ diverging_procedures :: proc() { foo(); } -foreign export { - bar :: proc "c" () -> i32 { - return 123; - } -} - main :: proc() { - when false { + when true { general_stuff(); union_type(); parametric_polymorphism(); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 3a9e18da1..2430eee9f 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -396,7 +396,7 @@ void check_when_stmt(CheckerContext *ctx, AstWhenStmt *ws, u32 flags) { } } -void check_label(CheckerContext *ctx, Ast *label) { +void check_label(CheckerContext *ctx, Ast *label, Ast *parent) { if (label == nullptr) { return; } @@ -428,7 +428,7 @@ void check_label(CheckerContext *ctx, Ast *label) { } } - Entity *e = alloc_entity_label(ctx->scope, l->name->Ident.token, t_invalid, label); + Entity *e = alloc_entity_label(ctx->scope, l->name->Ident.token, t_invalid, label, parent); add_entity(ctx->checker, ctx->scope, l->name, e); e->parent_proc_decl = ctx->curr_proc_decl; @@ -608,7 +608,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { check_open_scope(ctx, node); defer (check_close_scope(ctx)); - check_label(ctx, ss->label); // TODO(bill): What should the label's "scope" be? + check_label(ctx, ss->label, node); // TODO(bill): What should the label's "scope" be? if (ss->init != nullptr) { check_stmt(ctx, ss->init, 0); @@ -842,7 +842,7 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { check_open_scope(ctx, node); defer (check_close_scope(ctx)); - check_label(ctx, ss->label); // TODO(bill): What should the label's "scope" be? + check_label(ctx, ss->label, node); // TODO(bill): What should the label's "scope" be? if (ss->tag->kind != Ast_AssignStmt) { error(ss->tag, "Expected an 'in' assignment for this type switch statement"); @@ -1176,6 +1176,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { case_ast_node(bs, BlockStmt, node); check_open_scope(ctx, node); + check_label(ctx, bs->label, node); + check_stmt_list(ctx, bs->stmts, flags); check_close_scope(ctx); case_end; @@ -1183,6 +1185,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { case_ast_node(is, IfStmt, node); check_open_scope(ctx, node); + check_label(ctx, is->label, node); + if (is->init != nullptr) { check_stmt(ctx, is->init, 0); } @@ -1264,7 +1268,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed; check_open_scope(ctx, node); - check_label(ctx, fs->label); // TODO(bill): What should the label's "scope" be? + check_label(ctx, fs->label, node); // TODO(bill): What should the label's "scope" be? if (fs->init != nullptr) { check_stmt(ctx, fs->init, 0); @@ -1293,7 +1297,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed; check_open_scope(ctx, node); - check_label(ctx, rs->label); + check_label(ctx, rs->label, node); Type *val0 = nullptr; Type *val1 = nullptr; @@ -1528,18 +1532,20 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { Token token = bs->token; switch (token.kind) { case Token_break: - if ((flags & Stmt_BreakAllowed) == 0) { + if ((flags & Stmt_BreakAllowed) == 0 && bs->label == nullptr) { error(token, "'break' only allowed in loops or 'switch' statements"); } break; case Token_continue: - if ((flags & Stmt_ContinueAllowed) == 0) { + if ((flags & Stmt_ContinueAllowed) == 0 && bs->label == nullptr) { error(token, "'continue' only allowed in loops"); } break; case Token_fallthrough: if ((flags & Stmt_FallthroughAllowed) == 0) { error(token, "'fallthrough' statement in illegal position, expected at the end of a 'case' block"); + } else if (bs->label != nullptr) { + error(token, "'fallthrough' cannot have a label"); } break; default: @@ -1565,6 +1571,24 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { error(ident, "'%.*s' is not a label", LIT(name)); return; } + Ast *parent = e->Label.parent; + GB_ASSERT(parent != nullptr); + switch (parent->kind) { + case Ast_BlockStmt: + case Ast_IfStmt: + case Ast_SwitchStmt: + if (token.kind != Token_break) { + error(bs->label, "Label '%.*s' can only be used with 'break'", LIT(e->token.string)); + } + break; + case Ast_RangeStmt: + case Ast_ForStmt: + if ((token.kind != Token_break) && (token.kind != Token_continue)) { + error(bs->label, "Label '%.*s' can only be used with 'break' and 'continue'", LIT(e->token.string)); + } + break; + + } } case_end; diff --git a/src/entity.cpp b/src/entity.cpp index 707a04962..f3e11492a 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -147,8 +147,9 @@ struct Entity { } LibraryName; i32 Nil; struct { - String name; + String name; Ast *node; + Ast *parent; } Label; }; }; @@ -318,9 +319,10 @@ Entity *alloc_entity_nil(String name, Type *type) { return entity; } -Entity *alloc_entity_label(Scope *scope, Token token, Type *type, Ast *node) { +Entity *alloc_entity_label(Scope *scope, Token token, Type *type, Ast *node, Ast *parent) { Entity *entity = alloc_entity(Entity_Label, scope, token, type); entity->Label.node = node; + entity->Label.parent = parent; entity->state = EntityState_Resolved; return entity; } diff --git a/src/ir.cpp b/src/ir.cpp index 44095a4a9..1cec41e2c 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4616,8 +4616,6 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) { } else { GB_PANIC("invalid subtype cast"); } - } else { - GB_PANIC("invalid subtype cast"); } } @@ -7988,9 +7986,22 @@ void ir_build_stmt_internal(irProcedure *proc, Ast *node) { case_end; case_ast_node(bs, BlockStmt, node); - ir_open_scope(proc); - ir_build_stmt_list(proc, bs->stmts); - ir_close_scope(proc, irDeferExit_Default, nullptr); + if (bs->label != nullptr) { + irBlock *done = ir_new_block(proc, node, "block.done"); + ir_push_target_list(proc, bs->label, done, nullptr, nullptr); + + ir_open_scope(proc); + ir_build_stmt_list(proc, bs->stmts); + ir_close_scope(proc, irDeferExit_Default, nullptr); + + ir_emit_jump(proc, done); + ir_start_block(proc, done); + + } else { + ir_open_scope(proc); + ir_build_stmt_list(proc, bs->stmts); + ir_close_scope(proc, irDeferExit_Default, nullptr); + } case_end; case_ast_node(ds, DeferStmt, node); @@ -8089,6 +8100,10 @@ void ir_build_stmt_internal(irProcedure *proc, Ast *node) { ir_build_cond(proc, is->cond, then, else_); ir_start_block(proc, then); + if (is->label != nullptr) { + ir_push_target_list(proc, is->label, done, nullptr, nullptr); + } + ir_open_scope(proc); ir_build_stmt(proc, is->body); ir_close_scope(proc, irDeferExit_Default, nullptr); diff --git a/src/parser.cpp b/src/parser.cpp index bce0260cb..dc1a22780 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -223,9 +223,11 @@ Ast *clone_ast(Ast *node) { n->IncDecStmt.expr = clone_ast(n->IncDecStmt.expr); break; case Ast_BlockStmt: + n->BlockStmt.label = clone_ast(n->BlockStmt.label); n->BlockStmt.stmts = clone_ast_array(n->BlockStmt.stmts); break; case Ast_IfStmt: + n->IfStmt.label = clone_ast(n->IfStmt.label); n->IfStmt.init = clone_ast(n->IfStmt.init); n->IfStmt.cond = clone_ast(n->IfStmt.cond); n->IfStmt.body = clone_ast(n->IfStmt.body); @@ -2637,6 +2639,8 @@ Ast *parse_simple_stmt(AstFile *f, u32 flags) { expect_token_after(f, Token_Colon, "identifier list"); if ((flags&StmtAllowFlag_Label) && lhs.count == 1) { switch (f->curr_token.kind) { + case Token_OpenBrace: // block statement + case Token_if: case Token_for: case Token_switch: { Ast *name = lhs[0]; @@ -2644,6 +2648,8 @@ Ast *parse_simple_stmt(AstFile *f, u32 flags) { Ast *stmt = parse_stmt(f); #define _SET_LABEL(Kind_, label_) case GB_JOIN2(Ast_, Kind_): (stmt->Kind_).label = label_; break switch (stmt->kind) { + _SET_LABEL(BlockStmt, label); + _SET_LABEL(IfStmt, label); _SET_LABEL(ForStmt, label); _SET_LABEL(RangeStmt, label); _SET_LABEL(SwitchStmt, label); diff --git a/src/parser.hpp b/src/parser.hpp index 6a59f05a6..8ed521823 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -283,10 +283,12 @@ AST_KIND(_StmtBegin, "", bool) \ AST_KIND(_ComplexStmtBegin, "", bool) \ AST_KIND(BlockStmt, "block statement", struct { \ Array stmts; \ + Ast *label; \ Token open, close; \ }) \ AST_KIND(IfStmt, "if statement", struct { \ Token token; \ + Ast *label; \ Ast * init; \ Ast * cond; \ Ast * body; \