Browse Source

Labels for block and if statements (break only)

gingerBill 6 years ago
parent
commit
d05837ab6d
6 changed files with 97 additions and 22 deletions
  1. 33 7
      examples/demo/demo.odin
  2. 32 8
      src/check_stmt.cpp
  3. 4 2
      src/entity.cpp
  4. 20 5
      src/ir.cpp
  5. 6 0
      src/parser.cpp
  6. 2 0
      src/parser.hpp

+ 33 - 7
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();

+ 32 - 8
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;

+ 4 - 2
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;
 }

+ 20 - 5
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);

+ 6 - 0
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);

+ 2 - 0
src/parser.hpp

@@ -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;      \