浏览代码

Separate `check_stmt` code into separate procedures

gingerBill 2 年之前
父节点
当前提交
51ae21a029
共有 1 个文件被更改,包括 288 次插入271 次删除
  1. 288 271
      src/check_stmt.cpp

+ 288 - 271
src/check_stmt.cpp

@@ -1912,337 +1912,354 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f
 	}
 	}
 }
 }
 
 
-gb_internal void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
-	u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
-	switch (node->kind) {
-	case_ast_node(_, EmptyStmt, node); case_end;
-	case_ast_node(_, BadStmt,   node); case_end;
-	case_ast_node(_, BadDecl,   node); case_end;
+gb_internal void check_expr_stmt(CheckerContext *ctx, Ast *node) {
+	ast_node(es, ExprStmt, node);
 
 
-	case_ast_node(es, ExprStmt, node)
-		Operand operand = {Addressing_Invalid};
-		ExprKind kind = check_expr_base(ctx, &operand, es->expr, nullptr);
-		switch (operand.mode) {
-		case Addressing_Type: {
+	Operand operand = {Addressing_Invalid};
+	ExprKind kind = check_expr_base(ctx, &operand, es->expr, nullptr);
+	switch (operand.mode) {
+	case Addressing_Type:
+		{
 			gbString str = type_to_string(operand.type);
 			gbString str = type_to_string(operand.type);
 			error(node, "'%s' is not an expression", str);
 			error(node, "'%s' is not an expression", str);
 			gb_string_free(str);
 			gb_string_free(str);
-
 			break;
 			break;
 		}
 		}
-		case Addressing_NoValue:
-			return;
-		default: {
-			if (kind == Expr_Stmt) {
-				return;
-			}
+	case Addressing_NoValue:
+		return;
+	}
+	if (kind == Expr_Stmt) {
+		return;
+	}
 
 
-			Ast *expr = strip_or_return_expr(operand.expr);
-			if (expr->kind == Ast_CallExpr) {
-				BuiltinProcId builtin_id = BuiltinProc_Invalid;
-				bool do_require = false;
+	Ast *expr = strip_or_return_expr(operand.expr);
+	if (expr->kind == Ast_CallExpr) {
+		BuiltinProcId builtin_id = BuiltinProc_Invalid;
+		bool do_require = false;
 
 
-				AstCallExpr *ce = &expr->CallExpr;
-				Type *t = base_type(type_of_expr(ce->proc));
-				if (t->kind == Type_Proc) {
-					do_require = t->Proc.require_results;
-				} else if (check_stmt_internal_builtin_proc_id(ce->proc, &builtin_id)) {
-					auto const &bp = builtin_procs[builtin_id];
-					do_require = bp.kind == Expr_Expr && !bp.ignore_results;
-				}
-				if (do_require) {
-					gbString expr_str = expr_to_string(ce->proc);
-					error(node, "'%s' requires that its results must be handled", expr_str);
-					gb_string_free(expr_str);
-				}
-				return;
-			} else if (expr->kind == Ast_SelectorCallExpr) {
-				BuiltinProcId builtin_id = BuiltinProc_Invalid;
-				bool do_require = false;
-
-				AstSelectorCallExpr *se = &expr->SelectorCallExpr;
-				ast_node(ce, CallExpr, se->call);
-				Type *t = base_type(type_of_expr(ce->proc));
-				if (t == nullptr) {
-					gbString expr_str = expr_to_string(ce->proc);
-					error(node, "'%s' is not a value field nor procedure", expr_str);
-					gb_string_free(expr_str);
-					return;
-				}
-				if (t->kind == Type_Proc) {
-					do_require = t->Proc.require_results;
-				} else if (check_stmt_internal_builtin_proc_id(ce->proc, &builtin_id)) {
-					auto const &bp = builtin_procs[builtin_id];
-					do_require = bp.kind == Expr_Expr && !bp.ignore_results;
-				}
-				if (do_require) {
-					gbString expr_str = expr_to_string(ce->proc);
-					error(node, "'%s' requires that its results must be handled", expr_str);
-					gb_string_free(expr_str);
-				}
-				return;
-			}
-			gbString expr_str = expr_to_string(operand.expr);
-			error(node, "Expression is not used: '%s'", expr_str);
+		AstCallExpr *ce = &expr->CallExpr;
+		Type *t = base_type(type_of_expr(ce->proc));
+		if (t->kind == Type_Proc) {
+			do_require = t->Proc.require_results;
+		} else if (check_stmt_internal_builtin_proc_id(ce->proc, &builtin_id)) {
+			auto const &bp = builtin_procs[builtin_id];
+			do_require = bp.kind == Expr_Expr && !bp.ignore_results;
+		}
+		if (do_require) {
+			gbString expr_str = expr_to_string(ce->proc);
+			error(node, "'%s' requires that its results must be handled", expr_str);
 			gb_string_free(expr_str);
 			gb_string_free(expr_str);
-			if (operand.expr->kind == Ast_BinaryExpr) {
-				ast_node(be, BinaryExpr, operand.expr);
-				if (be->op.kind != Token_CmpEq) {
-					break;
-				}
+		}
+		return;
+	} else if (expr->kind == Ast_SelectorCallExpr) {
+		BuiltinProcId builtin_id = BuiltinProc_Invalid;
+		bool do_require = false;
+
+		AstSelectorCallExpr *se = &expr->SelectorCallExpr;
+		ast_node(ce, CallExpr, se->call);
+		Type *t = base_type(type_of_expr(ce->proc));
+		if (t == nullptr) {
+			gbString expr_str = expr_to_string(ce->proc);
+			error(node, "'%s' is not a value field nor procedure", expr_str);
+			gb_string_free(expr_str);
+			return;
+		}
+		if (t->kind == Type_Proc) {
+			do_require = t->Proc.require_results;
+		} else if (check_stmt_internal_builtin_proc_id(ce->proc, &builtin_id)) {
+			auto const &bp = builtin_procs[builtin_id];
+			do_require = bp.kind == Expr_Expr && !bp.ignore_results;
+		}
+		if (do_require) {
+			gbString expr_str = expr_to_string(ce->proc);
+			error(node, "'%s' requires that its results must be handled", expr_str);
+			gb_string_free(expr_str);
+		}
+		return;
+	}
+	gbString expr_str = expr_to_string(operand.expr);
+	error(node, "Expression is not used: '%s'", expr_str);
+	gb_string_free(expr_str);
+	if (operand.expr->kind == Ast_BinaryExpr) {
+		ast_node(be, BinaryExpr, operand.expr);
+		if (be->op.kind != Token_CmpEq) {
+			return;
+		}
 
 
-				switch (be->left->tav.mode) {
-				case Addressing_Context:
-				case Addressing_Variable:
-				case Addressing_MapIndex:
-				case Addressing_SoaVariable:
-					{
-						gbString lhs = expr_to_string(be->left);
-						gbString rhs = expr_to_string(be->right);
-						error_line("\tSuggestion: Did you mean to do an assignment?\n", lhs, rhs);
-						error_line("\t            '%s = %s;'\n", lhs, rhs);
-						gb_string_free(rhs);
-						gb_string_free(lhs);
-					}
-					break;
-				}
+		switch (be->left->tav.mode) {
+		case Addressing_Context:
+		case Addressing_Variable:
+		case Addressing_MapIndex:
+		case Addressing_SoaVariable:
+			{
+				gbString lhs = expr_to_string(be->left);
+				gbString rhs = expr_to_string(be->right);
+				error_line("\tSuggestion: Did you mean to do an assignment?\n", lhs, rhs);
+				error_line("\t            '%s = %s;'\n", lhs, rhs);
+				gb_string_free(rhs);
+				gb_string_free(lhs);
 			}
 			}
-
 			break;
 			break;
 		}
 		}
-		}
-	case_end;
+	}
+}
 
 
-	case_ast_node(as, AssignStmt, node);
-		switch (as->op.kind) {
-		case Token_Eq: {
-			// a, b, c = 1, 2, 3;  // Multisided
+gb_internal void check_assign_stmt(CheckerContext *ctx, Ast *node) {
+	ast_node(as, AssignStmt, node);
 
 
-			isize lhs_count = as->lhs.count;
-			if (lhs_count == 0) {
-				error(as->op, "Missing lhs in assignment statement");
-				return;
-			}
+	if (as->op.kind == Token_Eq) {
+		// a, b, c = 1, 2, 3;  // Multisided
 
 
-			TEMPORARY_ALLOCATOR_GUARD();
+		isize lhs_count = as->lhs.count;
+		if (lhs_count == 0) {
+			error(as->op, "Missing lhs in assignment statement");
+			return;
+		}
 
 
-			// NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
-			// an extra allocation
-			auto lhs_operands = array_make<Operand>(temporary_allocator(), lhs_count);
-			auto rhs_operands = array_make<Operand>(temporary_allocator(), 0, 2*lhs_count);
+		TEMPORARY_ALLOCATOR_GUARD();
 
 
-			for_array(i, as->lhs) {
-				if (is_blank_ident(as->lhs[i])) {
-					Operand *o = &lhs_operands[i];
-					o->expr = as->lhs[i];
-					o->mode = Addressing_Value;
-				} else {
-					ctx->assignment_lhs_hint = unparen_expr(as->lhs[i]);
-					check_expr(ctx, &lhs_operands[i], as->lhs[i]);
-				}
+		// NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
+		// an extra allocation
+		auto lhs_operands = array_make<Operand>(temporary_allocator(), lhs_count);
+		auto rhs_operands = array_make<Operand>(temporary_allocator(), 0, 2*lhs_count);
+
+		for_array(i, as->lhs) {
+			if (is_blank_ident(as->lhs[i])) {
+				Operand *o = &lhs_operands[i];
+				o->expr = as->lhs[i];
+				o->mode = Addressing_Value;
+			} else {
+				ctx->assignment_lhs_hint = unparen_expr(as->lhs[i]);
+				check_expr(ctx, &lhs_operands[i], as->lhs[i]);
 			}
 			}
-			ctx->assignment_lhs_hint = nullptr; // Reset the assignment_lhs_hint
+		}
+		ctx->assignment_lhs_hint = nullptr; // Reset the assignment_lhs_hint
 
 
-			check_assignment_arguments(ctx, lhs_operands, &rhs_operands, as->rhs);
+		check_assignment_arguments(ctx, lhs_operands, &rhs_operands, as->rhs);
 
 
-			auto lhs_to_ignore = array_make<bool>(temporary_allocator(), lhs_count);
+		auto lhs_to_ignore = array_make<bool>(temporary_allocator(), lhs_count);
 
 
-			isize rhs_count = rhs_operands.count;
-			isize max = gb_min(lhs_count, rhs_count);
-			for (isize i = 0; i < max; i++) {
-				if (lhs_to_ignore[i]) {
-					continue;
-				}
-				check_assignment_variable(ctx, &lhs_operands[i], &rhs_operands[i]);
-			}
-			if (lhs_count != rhs_count) {
-				error(as->lhs[0], "Assignment count mismatch '%td' = '%td'", lhs_count, rhs_count);
+		isize rhs_count = rhs_operands.count;
+		isize max = gb_min(lhs_count, rhs_count);
+		for (isize i = 0; i < max; i++) {
+			if (lhs_to_ignore[i]) {
+				continue;
 			}
 			}
-			break;
+			check_assignment_variable(ctx, &lhs_operands[i], &rhs_operands[i]);
+		}
+		if (lhs_count != rhs_count) {
+			error(as->lhs[0], "Assignment count mismatch '%td' = '%td'", lhs_count, rhs_count);
 		}
 		}
 
 
-		default: {
-			// a += 1; // Single-sided
-			Token op = as->op;
-			if (as->lhs.count != 1 || as->rhs.count != 1) {
-				error(op, "Assignment operation '%.*s' requires single-valued expressions", LIT(op.string));
-				return;
-			}
-			if (!gb_is_between(op.kind, Token__AssignOpBegin+1, Token__AssignOpEnd-1)) {
-				error(op, "Unknown Assignment operation '%.*s'", LIT(op.string));
-				return;
-			}
-			Operand lhs = {Addressing_Invalid};
-			Operand rhs = {Addressing_Invalid};
-			Ast *binary_expr = alloc_ast_node(node->file(), Ast_BinaryExpr);
-			ast_node(be, BinaryExpr, binary_expr);
-			be->op = op;
-			be->op.kind = cast(TokenKind)(cast(i32)be->op.kind - (Token_AddEq - Token_Add));
-			 // NOTE(bill): Only use the first one will be used
-			be->left  = as->lhs[0];
-			be->right = as->rhs[0];
-
-			check_expr(ctx, &lhs, as->lhs[0]);
-			check_binary_expr(ctx, &rhs, binary_expr, nullptr, true);
-			if (rhs.mode == Addressing_Invalid) {
-				return;
-			}
+	} else {
+		// a += 1; // Single-sided
+		Token op = as->op;
+		if (as->lhs.count != 1 || as->rhs.count != 1) {
+			error(op, "Assignment operation '%.*s' requires single-valued expressions", LIT(op.string));
+			return;
+		}
+		if (!gb_is_between(op.kind, Token__AssignOpBegin+1, Token__AssignOpEnd-1)) {
+			error(op, "Unknown Assignment operation '%.*s'", LIT(op.string));
+			return;
+		}
+		Operand lhs = {Addressing_Invalid};
+		Operand rhs = {Addressing_Invalid};
+		Ast *binary_expr = alloc_ast_node(node->file(), Ast_BinaryExpr);
+		ast_node(be, BinaryExpr, binary_expr);
+		be->op = op;
+		be->op.kind = cast(TokenKind)(cast(i32)be->op.kind - (Token_AddEq - Token_Add));
+		 // NOTE(bill): Only use the first one will be used
+		be->left  = as->lhs[0];
+		be->right = as->rhs[0];
+
+		check_expr(ctx, &lhs, as->lhs[0]);
+		check_binary_expr(ctx, &rhs, binary_expr, nullptr, true);
+		if (rhs.mode != Addressing_Invalid) {
 			// NOTE(bill): Only use the first one will be used
 			// NOTE(bill): Only use the first one will be used
 			check_assignment_variable(ctx, &lhs, &rhs);
 			check_assignment_variable(ctx, &lhs, &rhs);
-
-			break;
-		}
 		}
 		}
-	case_end;
+	}
+}
 
 
-	case_ast_node(bs, BlockStmt, node);
-		check_open_scope(ctx, node);
-		check_label(ctx, bs->label, node);
+gb_internal void check_if_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
+	ast_node(is, IfStmt, node);
+	check_open_scope(ctx, node);
 
 
-		check_stmt_list(ctx, bs->stmts, flags);
-		check_block_stmt_for_errors(ctx, node);
-		check_close_scope(ctx);
-	case_end;
+	check_label(ctx, is->label, node);
 
 
-	case_ast_node(is, IfStmt, node);
-		check_open_scope(ctx, node);
+	if (is->init != nullptr) {
+		check_stmt(ctx, is->init, 0);
+	}
 
 
-		check_label(ctx, is->label, node);
+	Operand operand = {Addressing_Invalid};
+	check_expr(ctx, &operand, is->cond);
+	if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) {
+		error(is->cond, "Non-boolean condition in 'if' statement");
+	}
 
 
-		if (is->init != nullptr) {
-			check_stmt(ctx, is->init, 0);
-		}
+	check_stmt(ctx, is->body, mod_flags);
 
 
-		Operand operand = {Addressing_Invalid};
-		check_expr(ctx, &operand, is->cond);
-		if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) {
-			error(is->cond, "Non-boolean condition in 'if' statement");
+	if (is->else_stmt != nullptr) {
+		switch (is->else_stmt->kind) {
+		case Ast_IfStmt:
+		case Ast_BlockStmt:
+			check_stmt(ctx, is->else_stmt, mod_flags);
+			break;
+		default:
+			error(is->else_stmt, "Invalid 'else' statement in 'if' statement");
+			break;
 		}
 		}
+	}
 
 
-		check_stmt(ctx, is->body, mod_flags);
+	check_close_scope(ctx);
+}
 
 
-		if (is->else_stmt != nullptr) {
-			switch (is->else_stmt->kind) {
-			case Ast_IfStmt:
-			case Ast_BlockStmt:
-				check_stmt(ctx, is->else_stmt, mod_flags);
-				break;
-			default:
-				error(is->else_stmt, "Invalid 'else' statement in 'if' statement");
-				break;
-			}
-		}
+gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) {
+	ast_node(rs, ReturnStmt, node);
 
 
-		check_close_scope(ctx);
-	case_end;
+	GB_ASSERT(ctx->curr_proc_sig != nullptr);
 
 
-	case_ast_node(ws, WhenStmt, node);
-		check_when_stmt(ctx, ws, flags);
-	case_end;
+	if (ctx->in_defer) {
+		error(rs->token, "'return' cannot be used within a defer statement");
+		return;
+	}
 
 
-	case_ast_node(rs, ReturnStmt, node);
-		GB_ASSERT(ctx->curr_proc_sig != nullptr);
+	Type *proc_type = ctx->curr_proc_sig;
+	GB_ASSERT(proc_type != nullptr);
+	GB_ASSERT(proc_type->kind == Type_Proc);
 
 
-		if (ctx->in_defer) {
-			error(rs->token, "'return' cannot be used within a defer statement");
-			break;
-		}
+	TypeProc *pt = &proc_type->Proc;
+	if (pt->diverging) {
+		error(rs->token, "Diverging procedures may not return");
+		return;
+	}
+
+	Entity **result_entities = nullptr;
+	isize result_count = 0;
+	bool has_named_results = pt->has_named_results;
+	if (pt->results) {
+		result_entities = proc_type->Proc.results->Tuple.variables.data;
+		result_count = proc_type->Proc.results->Tuple.variables.count;
+	}
 
 
-		Type *proc_type = ctx->curr_proc_sig;
-		GB_ASSERT(proc_type != nullptr);
-		GB_ASSERT(proc_type->kind == Type_Proc);
+	auto operands = array_make<Operand>(heap_allocator(), 0, 2*rs->results.count);
+	defer (array_free(&operands));
 
 
-		TypeProc *pt = &proc_type->Proc;
-		if (pt->diverging) {
-			error(rs->token, "Diverging procedures may not return");
-			break;
+	check_unpack_arguments(ctx, result_entities, result_count, &operands, rs->results, true, false);
+
+	if (result_count == 0 && rs->results.count > 0) {
+		error(rs->results[0], "No return values expected");
+	} else if (has_named_results && operands.count == 0) {
+		// Okay
+	} else if (operands.count != result_count) {
+		// Ignore error message as it has most likely already been reported
+		if (all_operands_valid(operands)) {
+			error(node, "Expected %td return values, got %td", result_count, operands.count);
 		}
 		}
+	} else {
+		for (isize i = 0; i < result_count; i++) {
+			Entity *e = pt->results->Tuple.variables[i];
+			Operand *o = &operands[i];
+			check_assignment(ctx, o, e->type, str_lit("return statement"));
+			if (is_type_untyped(o->type)) {
+				update_untyped_expr_type(ctx, o->expr, e->type, true);
+			}
+
+
+			// NOTE(bill): This is very basic escape analysis
+			// This needs to be improved tremendously, and a lot of it done during the
+			// middle-end (or LLVM side) to improve checks and error messages
+			Ast *expr = unparen_expr(o->expr);
+			if (expr->kind == Ast_UnaryExpr && expr->UnaryExpr.op.kind == Token_And) {
+				Ast *x = unparen_expr(expr->UnaryExpr.expr);
+				if (x->kind == Ast_CompoundLit) {
+					error(expr, "Cannot return the address to a stack value from a procedure");
+				} else if (x->kind == Ast_IndexExpr) {
+					Ast *array = x->IndexExpr.expr;
+					if (is_type_array_like(type_of_expr(array)) && check_expr_is_stack_variable(array)) {
+						gbString t = type_to_string(type_of_expr(array));
+						error(expr, "Cannot return the address to an element of stack variable from a procedure, of type %s", t);
+						gb_string_free(t);
+					}
+				} else {
+					if (check_expr_is_stack_variable(x)) {
+						error(expr, "Cannot return the address to a stack variable from a procedure");
+					}
+				}
+			}
+		}
+	}
+}
+
+gb_internal void check_for_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
+	ast_node(fs, ForStmt, node);
+	mod_flags |= Stmt_BreakAllowed | Stmt_ContinueAllowed;
+
+	check_open_scope(ctx, node);
+	check_label(ctx, fs->label, node); // TODO(bill): What should the label's "scope" be?
 
 
-		Entity **result_entities = nullptr;
-		isize result_count = 0;
-		bool has_named_results = pt->has_named_results;
-		if (pt->results) {
-			result_entities = proc_type->Proc.results->Tuple.variables.data;
-			result_count = proc_type->Proc.results->Tuple.variables.count;
+	if (fs->init != nullptr) {
+		check_stmt(ctx, fs->init, 0);
+	}
+	if (fs->cond != nullptr) {
+		Operand o = {Addressing_Invalid};
+		check_expr(ctx, &o, fs->cond);
+		if (o.mode != Addressing_Invalid && !is_type_boolean(o.type)) {
+			error(fs->cond, "Non-boolean condition in 'for' statement");
 		}
 		}
+	}
+	if (fs->post != nullptr) {
+		check_stmt(ctx, fs->post, 0);
 
 
-		auto operands = array_make<Operand>(heap_allocator(), 0, 2*rs->results.count);
-		defer (array_free(&operands));
+		if (fs->post->kind != Ast_AssignStmt) {
+			error(fs->post, "'for' statement post statement must be a simple statement");
+		}
+	}
+	check_stmt(ctx, fs->body, mod_flags);
 
 
-		check_unpack_arguments(ctx, result_entities, result_count, &operands, rs->results, true, false);
+	check_close_scope(ctx);
+}
 
 
-		if (result_count == 0 && rs->results.count > 0) {
-			error(rs->results[0], "No return values expected");
-		} else if (has_named_results && operands.count == 0) {
-			// Okay
-		} else if (operands.count != result_count) {
-			// Ignore error message as it has most likely already been reported
-			if (all_operands_valid(operands)) {
-				error(node, "Expected %td return values, got %td", result_count, operands.count);
-			}
-		} else {
-			for (isize i = 0; i < result_count; i++) {
-				Entity *e = pt->results->Tuple.variables[i];
-				Operand *o = &operands[i];
-				check_assignment(ctx, o, e->type, str_lit("return statement"));
-				if (is_type_untyped(o->type)) {
-					update_untyped_expr_type(ctx, o->expr, e->type, true);
-				}
 
 
+gb_internal void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
+	u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
+	switch (node->kind) {
+	case_ast_node(_, EmptyStmt, node); case_end;
+	case_ast_node(_, BadStmt,   node); case_end;
+	case_ast_node(_, BadDecl,   node); case_end;
 
 
-				// NOTE(bill): This is very basic escape analysis
-				// This needs to be improved tremendously, and a lot of it done during the
-				// middle-end (or LLVM side) to improve checks and error messages
-				Ast *expr = unparen_expr(o->expr);
-				if (expr->kind == Ast_UnaryExpr && expr->UnaryExpr.op.kind == Token_And) {
-					Ast *x = unparen_expr(expr->UnaryExpr.expr);
-					if (x->kind == Ast_CompoundLit) {
-						error(expr, "Cannot return the address to a stack value from a procedure");
-					} else if (x->kind == Ast_IndexExpr) {
-						Ast *array = x->IndexExpr.expr;
-						if (is_type_array_like(type_of_expr(array)) && check_expr_is_stack_variable(array)) {
-							gbString t = type_to_string(type_of_expr(array));
-							error(expr, "Cannot return the address to an element of stack variable from a procedure, of type %s", t);
-							gb_string_free(t);
-						}
-					} else {
-						if (check_expr_is_stack_variable(x)) {
-							error(expr, "Cannot return the address to a stack variable from a procedure");
-						}
-					}
-				}
-			}
-		}
+	case_ast_node(es, ExprStmt, node)
+		check_expr_stmt(ctx, node);
 	case_end;
 	case_end;
 
 
-	case_ast_node(fs, ForStmt, node);
-		u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
+	case_ast_node(as, AssignStmt, node);
+		check_assign_stmt(ctx, node);
+	case_end;
 
 
+	case_ast_node(bs, BlockStmt, node);
 		check_open_scope(ctx, node);
 		check_open_scope(ctx, node);
-		check_label(ctx, fs->label, node); // TODO(bill): What should the label's "scope" be?
+		check_label(ctx, bs->label, node);
 
 
-		if (fs->init != nullptr) {
-			check_stmt(ctx, fs->init, 0);
-		}
-		if (fs->cond != nullptr) {
-			Operand o = {Addressing_Invalid};
-			check_expr(ctx, &o, fs->cond);
-			if (o.mode != Addressing_Invalid && !is_type_boolean(o.type)) {
-				error(fs->cond, "Non-boolean condition in 'for' statement");
-			}
-		}
-		if (fs->post != nullptr) {
-			check_stmt(ctx, fs->post, 0);
+		check_stmt_list(ctx, bs->stmts, flags);
+		check_block_stmt_for_errors(ctx, node);
+		check_close_scope(ctx);
+	case_end;
 
 
-			if (fs->post->kind != Ast_AssignStmt) {
-				error(fs->post, "'for' statement post statement must be a simple statement");
-			}
-		}
-		check_stmt(ctx, fs->body, new_flags);
+	case_ast_node(is, IfStmt, node);
+		check_if_stmt(ctx, node, mod_flags);
+	case_end;
 
 
-		check_close_scope(ctx);
+	case_ast_node(ws, WhenStmt, node);
+		check_when_stmt(ctx, ws, flags);
 	case_end;
 	case_end;
 
 
+	case_ast_node(rs, ReturnStmt, node);
+		check_return_stmt(ctx, node);
+	case_end;
+
+	case_ast_node(fs, ForStmt, node);
+		check_for_stmt(ctx, node, mod_flags);
+	case_end;
 
 
 	case_ast_node(rs, RangeStmt, node);
 	case_ast_node(rs, RangeStmt, node);
 		check_range_stmt(ctx, node, mod_flags);
 		check_range_stmt(ctx, node, mod_flags);