Labels for block and if statements (break only)

This commit is contained in:
gingerBill
2018-12-08 14:12:52 +00:00
parent 4369a1714e
commit d05837ab6d
6 changed files with 97 additions and 22 deletions
+33 -7
View File
@@ -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();
+32 -8
View File
@@ -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;
+4 -2
View File
@@ -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;
}
+20 -5
View File
@@ -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);
+6
View File
@@ -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);
+2
View File
@@ -283,10 +283,12 @@ AST_KIND(_StmtBegin, "", bool) \
AST_KIND(_ComplexStmtBegin, "", bool) \
AST_KIND(BlockStmt, "block statement", struct { \
Array<Ast *> stmts; \
Ast *label; \
Token open, close; \
}) \
AST_KIND(IfStmt, "if statement", struct { \
Token token; \
Ast *label; \
Ast * init; \
Ast * cond; \
Ast * body; \