From 68db9d5074cece234efa3a5352fe6cd36d210455 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 4 Sep 2018 15:28:13 -0400 Subject: [PATCH] add compile error for comptime control flow inside runtime block closes #834 --- src/all_types.hpp | 18 ++++++ src/analyze.cpp | 8 +++ src/analyze.hpp | 1 + src/codegen.cpp | 2 + src/ir.cpp | 120 ++++++++++++++++++++++++++++++---------- src/ir_print.cpp | 11 ++++ test/compile_errors.zig | 110 ++++++++++++++++++++++++++++++++++++ 7 files changed, 241 insertions(+), 29 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index fd8801f994..01bf264c99 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1836,6 +1836,7 @@ enum ScopeId { ScopeIdFnDef, ScopeIdCompTime, ScopeIdCoroPrelude, + ScopeIdRuntime, }; struct Scope { @@ -1928,6 +1929,15 @@ struct ScopeLoop { ZigList *incoming_blocks; }; +// This scope blocks certain things from working such as comptime continue +// inside a runtime if expression. +// NodeTypeIfBoolExpr, NodeTypeWhileExpr, NodeTypeForExpr +struct ScopeRuntime { + Scope base; + + IrInstruction *is_comptime; +}; + // This scope is created for a suspend block in order to have labeled // suspend for breaking out of a suspend and for detecting if a suspend // block is inside a suspend block. @@ -2147,6 +2157,7 @@ enum IrInstructionId { IrInstructionIdErrSetCast, IrInstructionIdToBytes, IrInstructionIdFromBytes, + IrInstructionIdCheckRuntimeScope, }; struct IrInstruction { @@ -3236,6 +3247,13 @@ struct IrInstructionSqrt { IrInstruction *op; }; +struct IrInstructionCheckRuntimeScope { + IrInstruction base; + + IrInstruction *scope_is_comptime; + IrInstruction *is_comptime; +}; + static const size_t slice_ptr_index = 0; static const size_t slice_len_index = 1; diff --git a/src/analyze.cpp b/src/analyze.cpp index 85b34cb017..99cd496fd9 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -157,6 +157,13 @@ ScopeLoop *create_loop_scope(AstNode *node, Scope *parent) { return scope; } +Scope *create_runtime_scope(AstNode *node, Scope *parent, IrInstruction *is_comptime) { + ScopeRuntime *scope = allocate(1); + scope->is_comptime = is_comptime; + init_scope(&scope->base, ScopeIdRuntime, node, parent); + return &scope->base; +} + ScopeSuspend *create_suspend_scope(AstNode *node, Scope *parent) { assert(node->type == NodeTypeSuspend); ScopeSuspend *scope = allocate(1); @@ -3770,6 +3777,7 @@ FnTableEntry *scope_get_fn_if_root(Scope *scope) { case ScopeIdSuspend: case ScopeIdCompTime: case ScopeIdCoroPrelude: + case ScopeIdRuntime: scope = scope->parent; continue; case ScopeIdFnDef: diff --git a/src/analyze.hpp b/src/analyze.hpp index 0b52e9a5e6..316499bf95 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -111,6 +111,7 @@ ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_en ScopeDecls *create_decls_scope(AstNode *node, Scope *parent, TypeTableEntry *container_type, ImportTableEntry *import); Scope *create_comptime_scope(AstNode *node, Scope *parent); Scope *create_coro_prelude_scope(AstNode *node, Scope *parent); +Scope *create_runtime_scope(AstNode *node, Scope *parent, IrInstruction *is_comptime); void init_const_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str); ConstExprValue *create_const_str_lit(CodeGen *g, Buf *str); diff --git a/src/codegen.cpp b/src/codegen.cpp index a15ecbaf6e..5a897517d4 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -678,6 +678,7 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) { case ScopeIdSuspend: case ScopeIdCompTime: case ScopeIdCoroPrelude: + case ScopeIdRuntime: return get_di_scope(g, scope->parent); } zig_unreachable(); @@ -4869,6 +4870,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdFromBytes: case IrInstructionIdToBytes: case IrInstructionIdEnumToInt: + case IrInstructionIdCheckRuntimeScope: zig_unreachable(); case IrInstructionIdReturn: diff --git a/src/ir.cpp b/src/ir.cpp index 5ac321c0f3..af98e3e53d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -832,6 +832,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSqrt *) { return IrInstructionIdSqrt; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckRuntimeScope *) { + return IrInstructionIdCheckRuntimeScope; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -2974,6 +2978,17 @@ static IrInstruction *ir_build_sqrt(IrBuilder *irb, Scope *scope, AstNode *sourc return &instruction->base; } +static IrInstruction *ir_build_check_runtime_scope(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *scope_is_comptime, IrInstruction *is_comptime) { + IrInstructionCheckRuntimeScope *instruction = ir_build_instruction(irb, scope, source_node); + instruction->scope_is_comptime = scope_is_comptime; + instruction->is_comptime = is_comptime; + + ir_ref_instruction(scope_is_comptime, irb->current_basic_block); + ir_ref_instruction(is_comptime, irb->current_basic_block); + + return &instruction->base; +} + static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; @@ -2999,6 +3014,7 @@ static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_sco case ScopeIdLoop: case ScopeIdSuspend: case ScopeIdCompTime: + case ScopeIdRuntime: scope = scope->parent; continue; case ScopeIdDeferExpr: @@ -3051,6 +3067,7 @@ static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o case ScopeIdLoop: case ScopeIdSuspend: case ScopeIdCompTime: + case ScopeIdRuntime: scope = scope->parent; continue; case ScopeIdDeferExpr: @@ -4980,7 +4997,8 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode ir_set_cursor_at_end_and_append_block(irb, then_block); - IrInstruction *then_expr_result = ir_gen_node(irb, then_node, scope); + Scope *subexpr_scope = create_runtime_scope(node, scope, is_comptime); + IrInstruction *then_expr_result = ir_gen_node(irb, then_node, subexpr_scope); if (then_expr_result == irb->codegen->invalid_instruction) return then_expr_result; IrBasicBlock *after_then_block = irb->current_basic_block; @@ -4990,7 +5008,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode ir_set_cursor_at_end_and_append_block(irb, else_block); IrInstruction *else_expr_result; if (else_node) { - else_expr_result = ir_gen_node(irb, else_node, scope); + else_expr_result = ir_gen_node(irb, else_node, subexpr_scope); if (else_expr_result == irb->codegen->invalid_instruction) return else_expr_result; } else { @@ -5271,6 +5289,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n ir_should_inline(irb->exec, scope) || node->data.while_expr.is_inline); ir_build_br(irb, scope, node, cond_block, is_comptime); + Scope *subexpr_scope = create_runtime_scope(node, scope, is_comptime); Buf *var_symbol = node->data.while_expr.var_symbol; Buf *err_symbol = node->data.while_expr.err_symbol; if (err_symbol != nullptr) { @@ -5281,13 +5300,13 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n VariableTableEntry *payload_var; if (var_symbol) { // TODO make it an error to write to payload variable - payload_var = ir_create_var(irb, symbol_node, scope, var_symbol, + payload_var = ir_create_var(irb, symbol_node, subexpr_scope, var_symbol, true, false, false, is_comptime); payload_scope = payload_var->child_scope; } else { - payload_scope = scope; + payload_scope = subexpr_scope; } - IrInstruction *err_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, scope, LValPtr); + IrInstruction *err_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope, LValPtr); if (err_val_ptr == irb->codegen->invalid_instruction) return err_val_ptr; IrInstruction *err_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, err_val_ptr); @@ -5367,12 +5386,14 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items); } else if (var_symbol != nullptr) { ir_set_cursor_at_end_and_append_block(irb, cond_block); + Scope *subexpr_scope = create_runtime_scope(node, scope, is_comptime); // TODO make it an error to write to payload variable AstNode *symbol_node = node; // TODO make more accurate - VariableTableEntry *payload_var = ir_create_var(irb, symbol_node, scope, var_symbol, + + VariableTableEntry *payload_var = ir_create_var(irb, symbol_node, subexpr_scope, var_symbol, true, false, false, is_comptime); Scope *child_scope = payload_var->child_scope; - IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, scope, LValPtr); + IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope, LValPtr); if (maybe_val_ptr == irb->codegen->invalid_instruction) return maybe_val_ptr; IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr); @@ -5456,7 +5477,9 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n ZigList incoming_values = {0}; ZigList incoming_blocks = {0}; - ScopeLoop *loop_scope = create_loop_scope(node, scope); + Scope *subexpr_scope = create_runtime_scope(node, scope, is_comptime); + + ScopeLoop *loop_scope = create_loop_scope(node, subexpr_scope); loop_scope->break_block = end_block; loop_scope->continue_block = continue_block; loop_scope->is_comptime = is_comptime; @@ -5474,7 +5497,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n if (continue_expr_node) { ir_set_cursor_at_end_and_append_block(irb, continue_block); - IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, scope); + IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, subexpr_scope); if (expr_result == irb->codegen->invalid_instruction) return expr_result; if (!instr_is_unreachable(expr_result)) @@ -5485,7 +5508,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n if (else_node) { ir_set_cursor_at_end_and_append_block(irb, else_block); - else_result = ir_gen_node(irb, else_node, scope); + else_result = ir_gen_node(irb, else_node, subexpr_scope); if (else_result == irb->codegen->invalid_instruction) return else_result; if (!instr_is_unreachable(else_result)) @@ -5828,20 +5851,21 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no ir_set_cursor_at_end_and_append_block(irb, then_block); + Scope *subexpr_scope = create_runtime_scope(node, scope, is_comptime); Scope *var_scope; if (var_symbol) { IrInstruction *var_type = nullptr; bool is_shadowable = false; bool is_const = true; - VariableTableEntry *var = ir_create_var(irb, node, scope, + VariableTableEntry *var = ir_create_var(irb, node, subexpr_scope, var_symbol, is_const, is_const, is_shadowable, is_comptime); - IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, scope, node, maybe_val_ptr, false); - IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, scope, node, var_ptr_value); - ir_build_var_decl(irb, scope, node, var, var_type, nullptr, var_value); + IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, subexpr_scope, node, maybe_val_ptr, false); + IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, subexpr_scope, node, var_ptr_value); + ir_build_var_decl(irb, subexpr_scope, node, var, var_type, nullptr, var_value); var_scope = var->child_scope; } else { - var_scope = scope; + var_scope = subexpr_scope; } IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var_scope); if (then_expr_result == irb->codegen->invalid_instruction) @@ -5853,7 +5877,7 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no ir_set_cursor_at_end_and_append_block(irb, else_block); IrInstruction *else_expr_result; if (else_node) { - else_expr_result = ir_gen_node(irb, else_node, scope); + else_expr_result = ir_gen_node(irb, else_node, subexpr_scope); if (else_expr_result == irb->codegen->invalid_instruction) return else_expr_result; } else { @@ -5902,20 +5926,21 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * ir_set_cursor_at_end_and_append_block(irb, ok_block); + Scope *subexpr_scope = create_runtime_scope(node, scope, is_comptime); Scope *var_scope; if (var_symbol) { IrInstruction *var_type = nullptr; bool is_shadowable = false; - IrInstruction *var_is_comptime = force_comptime ? ir_build_const_bool(irb, scope, node, true) : ir_build_test_comptime(irb, scope, node, err_val); - VariableTableEntry *var = ir_create_var(irb, node, scope, + IrInstruction *var_is_comptime = force_comptime ? ir_build_const_bool(irb, subexpr_scope, node, true) : ir_build_test_comptime(irb, subexpr_scope, node, err_val); + VariableTableEntry *var = ir_create_var(irb, node, subexpr_scope, var_symbol, var_is_const, var_is_const, is_shadowable, var_is_comptime); - IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, scope, node, err_val_ptr, false); - IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, scope, node, var_ptr_value); - ir_build_var_decl(irb, scope, node, var, var_type, nullptr, var_value); + IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, subexpr_scope, node, err_val_ptr, false); + IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, subexpr_scope, node, var_ptr_value); + ir_build_var_decl(irb, subexpr_scope, node, var, var_type, nullptr, var_value); var_scope = var->child_scope; } else { - var_scope = scope; + var_scope = subexpr_scope; } IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var_scope); if (then_expr_result == irb->codegen->invalid_instruction) @@ -5933,14 +5958,14 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * IrInstruction *var_type = nullptr; bool is_shadowable = false; bool is_const = true; - VariableTableEntry *var = ir_create_var(irb, node, scope, + VariableTableEntry *var = ir_create_var(irb, node, subexpr_scope, err_symbol, is_const, is_const, is_shadowable, is_comptime); - IrInstruction *var_value = ir_build_unwrap_err_code(irb, scope, node, err_val_ptr); - ir_build_var_decl(irb, scope, node, var, var_type, nullptr, var_value); + IrInstruction *var_value = ir_build_unwrap_err_code(irb, subexpr_scope, node, err_val_ptr); + ir_build_var_decl(irb, subexpr_scope, node, var, var_type, nullptr, var_value); err_var_scope = var->child_scope; } else { - err_var_scope = scope; + err_var_scope = subexpr_scope; } else_expr_result = ir_gen_node(irb, else_node, err_var_scope); if (else_expr_result == irb->codegen->invalid_instruction) @@ -6037,6 +6062,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * ZigList check_ranges = {0}; // First do the else and the ranges + Scope *subexpr_scope = create_runtime_scope(node, scope, is_comptime); Scope *comptime_scope = create_comptime_scope(node, scope); AstNode *else_prong = nullptr; for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) { @@ -6054,7 +6080,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * IrBasicBlock *prev_block = irb->current_basic_block; ir_set_cursor_at_end_and_append_block(irb, else_block); - if (!ir_gen_switch_prong_expr(irb, scope, node, prong_node, end_block, + if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, is_comptime, var_is_comptime, target_value_ptr, nullptr, &incoming_blocks, &incoming_values)) { return irb->codegen->invalid_instruction; @@ -6121,7 +6147,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * range_block_no, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, range_block_yes); - if (!ir_gen_switch_prong_expr(irb, scope, node, prong_node, end_block, + if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, is_comptime, var_is_comptime, target_value_ptr, nullptr, &incoming_blocks, &incoming_values)) { return irb->codegen->invalid_instruction; @@ -6165,7 +6191,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * IrBasicBlock *prev_block = irb->current_basic_block; ir_set_cursor_at_end_and_append_block(irb, prong_block); - if (!ir_gen_switch_prong_expr(irb, scope, node, prong_node, end_block, + if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, is_comptime, var_is_comptime, target_value_ptr, only_item_value, &incoming_blocks, &incoming_values)) { return irb->codegen->invalid_instruction; @@ -6308,6 +6334,8 @@ static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *continue_scope, Ast // * defer expression scope => error, cannot break out of defer expression // * loop scope => OK + ZigList runtime_scopes = {}; + Scope *search_scope = continue_scope; ScopeLoop *loop_scope; for (;;) { @@ -6330,6 +6358,9 @@ static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *continue_scope, Ast loop_scope = this_loop_scope; break; } + } else if (search_scope->id == ScopeIdRuntime) { + ScopeRuntime *scope_runtime = (ScopeRuntime *)search_scope; + runtime_scopes.append(scope_runtime); } search_scope = search_scope->parent; } @@ -6341,6 +6372,11 @@ static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *continue_scope, Ast is_comptime = loop_scope->is_comptime; } + for (size_t i = 0; i < runtime_scopes.length; i += 1) { + ScopeRuntime *scope_runtime = runtime_scopes.at(i); + ir_mark_gen(ir_build_check_runtime_scope(irb, continue_scope, node, scope_runtime->is_comptime, is_comptime)); + } + IrBasicBlock *dest_block = loop_scope->continue_block; ir_gen_defers_for_block(irb, continue_scope, dest_block->scope, false); return ir_mark_gen(ir_build_br(irb, continue_scope, node, dest_block, is_comptime)); @@ -20910,6 +20946,29 @@ static TypeTableEntry *ir_analyze_instruction_int_to_enum(IrAnalyze *ira, IrInst return result->value.type; } +static TypeTableEntry *ir_analyze_instruction_check_runtime_scope(IrAnalyze *ira, IrInstructionCheckRuntimeScope *instruction) { + IrInstruction *block_comptime_inst = instruction->scope_is_comptime->other; + bool scope_is_comptime; + if (!ir_resolve_bool(ira, block_comptime_inst, &scope_is_comptime)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *is_comptime_inst = instruction->is_comptime->other; + bool is_comptime; + if (!ir_resolve_bool(ira, is_comptime_inst, &is_comptime)) + return ira->codegen->builtin_types.entry_invalid; + + if (!scope_is_comptime && is_comptime) { + ErrorMsg *msg = ir_add_error(ira, &instruction->base, + buf_sprintf("comptime control flow inside runtime block")); + add_error_note(ira->codegen, msg, block_comptime_inst->source_node, + buf_sprintf("runtime block created here")); + return ira->codegen->builtin_types.entry_invalid; + } + + ir_build_const_from(ira, &instruction->base); + return ira->codegen->builtin_types.entry_void; +} + static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -21186,6 +21245,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_int_to_enum(ira, (IrInstructionIntToEnum *)instruction); case IrInstructionIdEnumToInt: return ir_analyze_instruction_enum_to_int(ira, (IrInstructionEnumToInt *)instruction); + case IrInstructionIdCheckRuntimeScope: + return ir_analyze_instruction_check_runtime_scope(ira, (IrInstructionCheckRuntimeScope *)instruction); } zig_unreachable(); } @@ -21301,6 +21362,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdOverflowOp: // TODO when we support multiple returns this can be side effect free case IrInstructionIdCheckSwitchProngs: case IrInstructionIdCheckStatementIsVoid: + case IrInstructionIdCheckRuntimeScope: case IrInstructionIdPanic: case IrInstructionIdSetEvalBranchQuota: case IrInstructionIdPtrType: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 77c7ef47b6..d57d554c72 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -957,6 +957,14 @@ static void ir_print_enum_to_int(IrPrint *irp, IrInstructionEnumToInt *instructi fprintf(irp->f, ")"); } +static void ir_print_check_runtime_scope(IrPrint *irp, IrInstructionCheckRuntimeScope *instruction) { + fprintf(irp->f, "@checkRuntimeScope("); + ir_print_other_instruction(irp, instruction->scope_is_comptime); + fprintf(irp->f, ","); + ir_print_other_instruction(irp, instruction->is_comptime); + fprintf(irp->f, ")"); +} + static void ir_print_int_to_err(IrPrint *irp, IrInstructionIntToErr *instruction) { fprintf(irp->f, "inttoerr "); ir_print_other_instruction(irp, instruction->target); @@ -1749,6 +1757,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdEnumToInt: ir_print_enum_to_int(irp, (IrInstructionEnumToInt *)instruction); break; + case IrInstructionIdCheckRuntimeScope: + ir_print_check_runtime_scope(irp, (IrInstructionCheckRuntimeScope *)instruction); + break; } fprintf(irp->f, "\n"); } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 9979ed666c..6930f346e2 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,116 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "comptime continue inside runtime switch", + \\export fn entry() void { + \\ var p: i32 = undefined; + \\ comptime var q = true; + \\ inline while (q) { + \\ switch (p) { + \\ 11 => continue, + \\ else => {}, + \\ } + \\ q = false; + \\ } + \\} + , + ".tmp_source.zig:6:19: error: comptime control flow inside runtime block", + ".tmp_source.zig:5:9: note: runtime block created here", + ); + + cases.add( + "comptime continue inside runtime while error", + \\export fn entry() void { + \\ var p: error!usize = undefined; + \\ comptime var q = true; + \\ outer: inline while (q) { + \\ while (p) |_| { + \\ continue :outer; + \\ } else |_| {} + \\ q = false; + \\ } + \\} + , + ".tmp_source.zig:6:13: error: comptime control flow inside runtime block", + ".tmp_source.zig:5:9: note: runtime block created here", + ); + + cases.add( + "comptime continue inside runtime while optional", + \\export fn entry() void { + \\ var p: ?usize = undefined; + \\ comptime var q = true; + \\ outer: inline while (q) { + \\ while (p) |_| continue :outer; + \\ q = false; + \\ } + \\} + , + ".tmp_source.zig:5:23: error: comptime control flow inside runtime block", + ".tmp_source.zig:5:9: note: runtime block created here", + ); + + cases.add( + "comptime continue inside runtime while bool", + \\export fn entry() void { + \\ var p: usize = undefined; + \\ comptime var q = true; + \\ outer: inline while (q) { + \\ while (p == 11) continue :outer; + \\ q = false; + \\ } + \\} + , + ".tmp_source.zig:5:25: error: comptime control flow inside runtime block", + ".tmp_source.zig:5:9: note: runtime block created here", + ); + + cases.add( + "comptime continue inside runtime if error", + \\export fn entry() void { + \\ var p: error!i32 = undefined; + \\ comptime var q = true; + \\ inline while (q) { + \\ if (p) |_| continue else |_| {} + \\ q = false; + \\ } + \\} + , + ".tmp_source.zig:5:20: error: comptime control flow inside runtime block", + ".tmp_source.zig:5:9: note: runtime block created here", + ); + + cases.add( + "comptime continue inside runtime if optional", + \\export fn entry() void { + \\ var p: ?i32 = undefined; + \\ comptime var q = true; + \\ inline while (q) { + \\ if (p) |_| continue; + \\ q = false; + \\ } + \\} + , + ".tmp_source.zig:5:20: error: comptime control flow inside runtime block", + ".tmp_source.zig:5:9: note: runtime block created here", + ); + + cases.add( + "comptime continue inside runtime if bool", + \\export fn entry() void { + \\ var p: usize = undefined; + \\ comptime var q = true; + \\ inline while (q) { + \\ if (p == 11) continue; + \\ q = false; + \\ } + \\} + , + ".tmp_source.zig:5:22: error: comptime control flow inside runtime block", + ".tmp_source.zig:5:9: note: runtime block created here", + ); + cases.add( "switch with invalid expression parameter", \\export fn entry() void {