mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 07:38:48 +00: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");
|
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();
|
foo();
|
||||||
}
|
}
|
||||||
|
|
||||||
foreign export {
|
|
||||||
bar :: proc "c" () -> i32 {
|
|
||||||
return 123;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
when false {
|
when true {
|
||||||
general_stuff();
|
general_stuff();
|
||||||
union_type();
|
union_type();
|
||||||
parametric_polymorphism();
|
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) {
|
if (label == nullptr) {
|
||||||
return;
|
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);
|
add_entity(ctx->checker, ctx->scope, l->name, e);
|
||||||
e->parent_proc_decl = ctx->curr_proc_decl;
|
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);
|
check_open_scope(ctx, node);
|
||||||
defer (check_close_scope(ctx));
|
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) {
|
if (ss->init != nullptr) {
|
||||||
check_stmt(ctx, ss->init, 0);
|
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);
|
check_open_scope(ctx, node);
|
||||||
defer (check_close_scope(ctx));
|
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) {
|
if (ss->tag->kind != Ast_AssignStmt) {
|
||||||
error(ss->tag, "Expected an 'in' assignment for this type switch statement");
|
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);
|
case_ast_node(bs, BlockStmt, node);
|
||||||
check_open_scope(ctx, node);
|
check_open_scope(ctx, node);
|
||||||
|
check_label(ctx, bs->label, node);
|
||||||
|
|
||||||
check_stmt_list(ctx, bs->stmts, flags);
|
check_stmt_list(ctx, bs->stmts, flags);
|
||||||
check_close_scope(ctx);
|
check_close_scope(ctx);
|
||||||
case_end;
|
case_end;
|
||||||
@@ -1183,6 +1185,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
|||||||
case_ast_node(is, IfStmt, node);
|
case_ast_node(is, IfStmt, node);
|
||||||
check_open_scope(ctx, node);
|
check_open_scope(ctx, node);
|
||||||
|
|
||||||
|
check_label(ctx, is->label, node);
|
||||||
|
|
||||||
if (is->init != nullptr) {
|
if (is->init != nullptr) {
|
||||||
check_stmt(ctx, is->init, 0);
|
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;
|
u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
|
||||||
|
|
||||||
check_open_scope(ctx, node);
|
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) {
|
if (fs->init != nullptr) {
|
||||||
check_stmt(ctx, fs->init, 0);
|
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;
|
u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
|
||||||
|
|
||||||
check_open_scope(ctx, node);
|
check_open_scope(ctx, node);
|
||||||
check_label(ctx, rs->label);
|
check_label(ctx, rs->label, node);
|
||||||
|
|
||||||
Type *val0 = nullptr;
|
Type *val0 = nullptr;
|
||||||
Type *val1 = nullptr;
|
Type *val1 = nullptr;
|
||||||
@@ -1528,18 +1532,20 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
|||||||
Token token = bs->token;
|
Token token = bs->token;
|
||||||
switch (token.kind) {
|
switch (token.kind) {
|
||||||
case Token_break:
|
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");
|
error(token, "'break' only allowed in loops or 'switch' statements");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Token_continue:
|
case Token_continue:
|
||||||
if ((flags & Stmt_ContinueAllowed) == 0) {
|
if ((flags & Stmt_ContinueAllowed) == 0 && bs->label == nullptr) {
|
||||||
error(token, "'continue' only allowed in loops");
|
error(token, "'continue' only allowed in loops");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Token_fallthrough:
|
case Token_fallthrough:
|
||||||
if ((flags & Stmt_FallthroughAllowed) == 0) {
|
if ((flags & Stmt_FallthroughAllowed) == 0) {
|
||||||
error(token, "'fallthrough' statement in illegal position, expected at the end of a 'case' block");
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -1565,6 +1571,24 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
|
|||||||
error(ident, "'%.*s' is not a label", LIT(name));
|
error(ident, "'%.*s' is not a label", LIT(name));
|
||||||
return;
|
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;
|
case_end;
|
||||||
|
|||||||
+4
-2
@@ -147,8 +147,9 @@ struct Entity {
|
|||||||
} LibraryName;
|
} LibraryName;
|
||||||
i32 Nil;
|
i32 Nil;
|
||||||
struct {
|
struct {
|
||||||
String name;
|
String name;
|
||||||
Ast *node;
|
Ast *node;
|
||||||
|
Ast *parent;
|
||||||
} Label;
|
} Label;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -318,9 +319,10 @@ Entity *alloc_entity_nil(String name, Type *type) {
|
|||||||
return entity;
|
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 *entity = alloc_entity(Entity_Label, scope, token, type);
|
||||||
entity->Label.node = node;
|
entity->Label.node = node;
|
||||||
|
entity->Label.parent = parent;
|
||||||
entity->state = EntityState_Resolved;
|
entity->state = EntityState_Resolved;
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-5
@@ -4616,8 +4616,6 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) {
|
|||||||
} else {
|
} else {
|
||||||
GB_PANIC("invalid subtype cast");
|
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_end;
|
||||||
|
|
||||||
case_ast_node(bs, BlockStmt, node);
|
case_ast_node(bs, BlockStmt, node);
|
||||||
ir_open_scope(proc);
|
if (bs->label != nullptr) {
|
||||||
ir_build_stmt_list(proc, bs->stmts);
|
irBlock *done = ir_new_block(proc, node, "block.done");
|
||||||
ir_close_scope(proc, irDeferExit_Default, nullptr);
|
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_end;
|
||||||
|
|
||||||
case_ast_node(ds, DeferStmt, node);
|
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_build_cond(proc, is->cond, then, else_);
|
||||||
ir_start_block(proc, then);
|
ir_start_block(proc, then);
|
||||||
|
|
||||||
|
if (is->label != nullptr) {
|
||||||
|
ir_push_target_list(proc, is->label, done, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
ir_open_scope(proc);
|
ir_open_scope(proc);
|
||||||
ir_build_stmt(proc, is->body);
|
ir_build_stmt(proc, is->body);
|
||||||
ir_close_scope(proc, irDeferExit_Default, nullptr);
|
ir_close_scope(proc, irDeferExit_Default, nullptr);
|
||||||
|
|||||||
@@ -223,9 +223,11 @@ Ast *clone_ast(Ast *node) {
|
|||||||
n->IncDecStmt.expr = clone_ast(n->IncDecStmt.expr);
|
n->IncDecStmt.expr = clone_ast(n->IncDecStmt.expr);
|
||||||
break;
|
break;
|
||||||
case Ast_BlockStmt:
|
case Ast_BlockStmt:
|
||||||
|
n->BlockStmt.label = clone_ast(n->BlockStmt.label);
|
||||||
n->BlockStmt.stmts = clone_ast_array(n->BlockStmt.stmts);
|
n->BlockStmt.stmts = clone_ast_array(n->BlockStmt.stmts);
|
||||||
break;
|
break;
|
||||||
case Ast_IfStmt:
|
case Ast_IfStmt:
|
||||||
|
n->IfStmt.label = clone_ast(n->IfStmt.label);
|
||||||
n->IfStmt.init = clone_ast(n->IfStmt.init);
|
n->IfStmt.init = clone_ast(n->IfStmt.init);
|
||||||
n->IfStmt.cond = clone_ast(n->IfStmt.cond);
|
n->IfStmt.cond = clone_ast(n->IfStmt.cond);
|
||||||
n->IfStmt.body = clone_ast(n->IfStmt.body);
|
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");
|
expect_token_after(f, Token_Colon, "identifier list");
|
||||||
if ((flags&StmtAllowFlag_Label) && lhs.count == 1) {
|
if ((flags&StmtAllowFlag_Label) && lhs.count == 1) {
|
||||||
switch (f->curr_token.kind) {
|
switch (f->curr_token.kind) {
|
||||||
|
case Token_OpenBrace: // block statement
|
||||||
|
case Token_if:
|
||||||
case Token_for:
|
case Token_for:
|
||||||
case Token_switch: {
|
case Token_switch: {
|
||||||
Ast *name = lhs[0];
|
Ast *name = lhs[0];
|
||||||
@@ -2644,6 +2648,8 @@ Ast *parse_simple_stmt(AstFile *f, u32 flags) {
|
|||||||
Ast *stmt = parse_stmt(f);
|
Ast *stmt = parse_stmt(f);
|
||||||
#define _SET_LABEL(Kind_, label_) case GB_JOIN2(Ast_, Kind_): (stmt->Kind_).label = label_; break
|
#define _SET_LABEL(Kind_, label_) case GB_JOIN2(Ast_, Kind_): (stmt->Kind_).label = label_; break
|
||||||
switch (stmt->kind) {
|
switch (stmt->kind) {
|
||||||
|
_SET_LABEL(BlockStmt, label);
|
||||||
|
_SET_LABEL(IfStmt, label);
|
||||||
_SET_LABEL(ForStmt, label);
|
_SET_LABEL(ForStmt, label);
|
||||||
_SET_LABEL(RangeStmt, label);
|
_SET_LABEL(RangeStmt, label);
|
||||||
_SET_LABEL(SwitchStmt, label);
|
_SET_LABEL(SwitchStmt, label);
|
||||||
|
|||||||
@@ -283,10 +283,12 @@ AST_KIND(_StmtBegin, "", bool) \
|
|||||||
AST_KIND(_ComplexStmtBegin, "", bool) \
|
AST_KIND(_ComplexStmtBegin, "", bool) \
|
||||||
AST_KIND(BlockStmt, "block statement", struct { \
|
AST_KIND(BlockStmt, "block statement", struct { \
|
||||||
Array<Ast *> stmts; \
|
Array<Ast *> stmts; \
|
||||||
|
Ast *label; \
|
||||||
Token open, close; \
|
Token open, close; \
|
||||||
}) \
|
}) \
|
||||||
AST_KIND(IfStmt, "if statement", struct { \
|
AST_KIND(IfStmt, "if statement", struct { \
|
||||||
Token token; \
|
Token token; \
|
||||||
|
Ast *label; \
|
||||||
Ast * init; \
|
Ast * init; \
|
||||||
Ast * cond; \
|
Ast * cond; \
|
||||||
Ast * body; \
|
Ast * body; \
|
||||||
|
|||||||
Reference in New Issue
Block a user