mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-18 20:02:22 -07:00
Labels for block and if statements (break only)
This commit is contained in:
+33
-7
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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; \
|
||||
|
||||
Reference in New Issue
Block a user