From 62120e3d0e382274dec2962f376ceb1e645f1a30 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 12 Jul 2022 13:52:39 +0300 Subject: [PATCH 01/18] Sema: fix non-exhaustive union switch checks --- src/Sema.zig | 105 +++++++++--------- test/behavior/union.zig | 1 - .../helpful_return_type_error_message.zig | 0 .../switching_with_non-exhaustive_enums.zig | 19 +++- 4 files changed, 66 insertions(+), 59 deletions(-) rename test/cases/compile_errors/{stage1/test => }/helpful_return_type_error_message.zig (100%) rename test/cases/compile_errors/{stage1/test => }/switching_with_non-exhaustive_enums.zig (53%) diff --git a/src/Sema.zig b/src/Sema.zig index 702991bf5e..2ac27cf6b3 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -8247,7 +8247,7 @@ fn zirSwitchCond( ) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); - const operand_src = src; // TODO make this point at the switch operand + const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node }; const operand_ptr = try sema.resolveInst(inst_data.operand); const operand = if (is_ref) try sema.analyzeLoad(block, src, operand_ptr, operand_src) @@ -8345,12 +8345,19 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError }, }; + const union_originally = blk: { + const zir_data = sema.code.instructions.items(.data); + const cond_index = Zir.refToIndex(extra.data.operand).?; + const raw_operand = sema.resolveInst(zir_data[cond_index].un_node.operand) catch unreachable; + break :blk sema.typeOf(raw_operand).zigTypeTag() == .Union; + }; + const operand_ty = sema.typeOf(operand); var else_error_ty: ?Type = null; // Validate usage of '_' prongs. - if (special_prong == .under and !operand_ty.isNonexhaustiveEnum()) { + if (special_prong == .under and (!operand_ty.isNonexhaustiveEnum() or union_originally)) { const msg = msg: { const msg = try sema.errMsg( block, @@ -8375,6 +8382,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError // Validate for duplicate items, missing else prong, and invalid range. switch (operand_ty.zigTypeTag()) { + .Union => unreachable, // handled in zirSwitchCond .Enum => { var seen_fields = try gpa.alloc(?Module.SwitchProngSrc, operand_ty.enumFieldCount()); defer gpa.free(seen_fields); @@ -8432,60 +8440,54 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError } const all_tags_handled = for (seen_fields) |seen_src| { if (seen_src == null) break false; - } else !operand_ty.isNonexhaustiveEnum(); + } else true; - switch (special_prong) { - .none => { - if (!all_tags_handled) { - const msg = msg: { - const msg = try sema.errMsg( - block, - src, - "switch must handle all possibilities", - .{}, - ); - errdefer msg.destroy(sema.gpa); - for (seen_fields) |seen_src, i| { - if (seen_src != null) continue; + if (special_prong == .@"else") { + if (all_tags_handled and !operand_ty.isNonexhaustiveEnum()) return sema.fail( + block, + special_prong_src, + "unreachable else prong; all cases already handled", + .{}, + ); + } else if (!all_tags_handled) { + const msg = msg: { + const msg = try sema.errMsg( + block, + src, + "switch must handle all possibilities", + .{}, + ); + errdefer msg.destroy(sema.gpa); + for (seen_fields) |seen_src, i| { + if (seen_src != null) continue; - const field_name = operand_ty.enumFieldName(i); + const field_name = operand_ty.enumFieldName(i); - // TODO have this point to the tag decl instead of here - try sema.errNote( - block, - src, - msg, - "unhandled enumeration value: '{s}'", - .{field_name}, - ); - } - try sema.mod.errNoteNonLazy( - operand_ty.declSrcLoc(sema.mod), - msg, - "enum '{}' declared here", - .{operand_ty.fmt(sema.mod)}, - ); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); + const field_src = src; // TODO better source location + try sema.errNote( + block, + field_src, + msg, + "unhandled enumeration value: '{s}'", + .{field_name}, + ); } - }, - .under => { - if (all_tags_handled) return sema.fail( - block, - special_prong_src, - "unreachable '_' prong; all cases already handled", - .{}, + try sema.mod.errNoteNonLazy( + operand_ty.declSrcLoc(sema.mod), + msg, + "enum '{}' declared here", + .{operand_ty.fmt(sema.mod)}, ); - }, - .@"else" => { - if (all_tags_handled) return sema.fail( - block, - special_prong_src, - "unreachable else prong; all cases already handled", - .{}, - ); - }, + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } else if (special_prong == .none and operand_ty.isNonexhaustiveEnum() and !union_originally) { + return sema.fail( + block, + src, + "switch on non-exhaustive enum must include 'else' or '_' prong", + .{}, + ); } }, .ErrorSet => { @@ -8625,7 +8627,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError else_error_ty = try Type.Tag.error_set_merged.create(sema.arena, names); } }, - .Union => return sema.fail(block, src, "TODO validate switch .Union", .{}), .Int, .ComptimeInt => { var range_set = RangeSet.init(gpa, sema.mod); defer range_set.deinit(); diff --git a/test/behavior/union.zig b/test/behavior/union.zig index caa78be977..1787a9f30e 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1016,7 +1016,6 @@ test "switching on non exhaustive union" { switch (a) { .a => |val| try expect(val == 2), .b => return error.Fail, - _ => return error.Fail, } } }; diff --git a/test/cases/compile_errors/stage1/test/helpful_return_type_error_message.zig b/test/cases/compile_errors/helpful_return_type_error_message.zig similarity index 100% rename from test/cases/compile_errors/stage1/test/helpful_return_type_error_message.zig rename to test/cases/compile_errors/helpful_return_type_error_message.zig diff --git a/test/cases/compile_errors/stage1/test/switching_with_non-exhaustive_enums.zig b/test/cases/compile_errors/switching_with_non-exhaustive_enums.zig similarity index 53% rename from test/cases/compile_errors/stage1/test/switching_with_non-exhaustive_enums.zig rename to test/cases/compile_errors/switching_with_non-exhaustive_enums.zig index ed46c5f2f1..e33d686d86 100644 --- a/test/cases/compile_errors/stage1/test/switching_with_non-exhaustive_enums.zig +++ b/test/cases/compile_errors/switching_with_non-exhaustive_enums.zig @@ -7,16 +7,21 @@ const U = union(E) { a: i32, b: u32, }; -pub export fn entry() void { +pub export fn entry1() void { var e: E = .b; switch (e) { // error: switch not handling the tag `b` .a => {}, _ => {}, } +} +pub export fn entry2() void { + var e: E = .b; switch (e) { // error: switch on non-exhaustive enum must include `else` or `_` prong .a => {}, .b => {}, } +} +pub export fn entry3() void { var u = U{.a = 2}; switch (u) { // error: `_` prong not allowed when switching on tagged union .a => {}, @@ -26,10 +31,12 @@ pub export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native -// is_test=1 // -// tmp.zig:12:5: error: enumeration value 'E.b' not handled in switch -// tmp.zig:16:5: error: switch on non-exhaustive enum must include `else` or `_` prong -// tmp.zig:21:5: error: `_` prong not allowed when switching on tagged union +// :12:5: error: switch must handle all possibilities +// :12:5: note: unhandled enumeration value: 'b' +// :1:11: note: enum 'tmp.E' declared here +// :19:5: error: switch on non-exhaustive enum must include 'else' or '_' prong +// :26:5: error: '_' prong only allowed when switching on non-exhaustive enums +// :29:11: note: '_' prong here From 29815fe9de50be3b07e39cd02ccfcc801a54c455 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 12 Jul 2022 14:22:57 +0300 Subject: [PATCH 02/18] Sema: disallow 'align' on functions on wasm --- src/Sema.zig | 4 ++++ ...expr_function_pointers_is_a_compile_error.zig | 9 +++++++++ .../obj => }/array_access_of_non_array.zig | 6 +++--- .../array_access_with_non_integer_index.zig | 6 +++--- .../control_reaches_end_of_non-void_function.zig | 9 +++++++++ .../endless_loop_in_function_evaluation.zig | 15 +++++++++++++++ ...bytes_from_comptime_reinterpreted_pointer.zig | 4 ++-- .../aligned_variable_of_zero-bit_type.zig | 0 ...asting_undefined_c_pointer_to_zig_pointer.zig | 0 ...expr_function_pointers_is_a_compile_error.zig | 9 --------- .../control_reaches_end_of_non-void_function.zig | 8 -------- .../obj/endless_loop_in_function_evaluation.zig | 12 ------------ ...ts_to_assign_null_to_non-nullable_pointer.zig | 14 -------------- ...ts_to_assign_null_to_non-nullable_pointer.zig | 16 ++++++++++++++++ 14 files changed, 61 insertions(+), 51 deletions(-) create mode 100644 test/cases/compile_errors/align_n_expr_function_pointers_is_a_compile_error.zig rename test/cases/compile_errors/{stage1/obj => }/array_access_of_non_array.zig (55%) rename test/cases/compile_errors/{stage1/obj => }/array_access_with_non_integer_index.zig (60%) create mode 100644 test/cases/compile_errors/control_reaches_end_of_non-void_function.zig create mode 100644 test/cases/compile_errors/endless_loop_in_function_evaluation.zig rename test/cases/compile_errors/{stage1/obj => }/load_too_many_bytes_from_comptime_reinterpreted_pointer.zig (68%) rename test/cases/compile_errors/stage1/{obj => }/aligned_variable_of_zero-bit_type.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/implicit_casting_undefined_c_pointer_to_zig_pointer.zig (100%) delete mode 100644 test/cases/compile_errors/stage1/obj/align_n_expr_function_pointers_is_a_compile_error.zig delete mode 100644 test/cases/compile_errors/stage1/obj/control_reaches_end_of_non-void_function.zig delete mode 100644 test/cases/compile_errors/stage1/obj/endless_loop_in_function_evaluation.zig delete mode 100644 test/cases/compile_errors/stage1/obj/use_implicit_casts_to_assign_null_to_non-nullable_pointer.zig create mode 100644 test/cases/compile_errors/use_implicit_casts_to_assign_null_to_non-nullable_pointer.zig diff --git a/src/Sema.zig b/src/Sema.zig index 2ac27cf6b3..b821a32fc7 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -17713,6 +17713,10 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A break :blk lib_name; } else null; + if ((extra.data.bits.has_align_body or extra.data.bits.has_align_ref) and sema.mod.getTarget().cpu.arch.isWasm()) { + return sema.fail(block, align_src, "'align' is not allowed on functions in wasm", .{}); + } + const @"align": ?u32 = if (extra.data.bits.has_align_body) blk: { const body_len = sema.code.extra[extra_index]; extra_index += 1; diff --git a/test/cases/compile_errors/align_n_expr_function_pointers_is_a_compile_error.zig b/test/cases/compile_errors/align_n_expr_function_pointers_is_a_compile_error.zig new file mode 100644 index 0000000000..7ceed52edf --- /dev/null +++ b/test/cases/compile_errors/align_n_expr_function_pointers_is_a_compile_error.zig @@ -0,0 +1,9 @@ +export fn foo() align(1) void { + return; +} + +// error +// backend=stage2 +// target=wasm32-freestanding-none +// +// :1:8: error: 'align' is not allowed on functions in wasm diff --git a/test/cases/compile_errors/stage1/obj/array_access_of_non_array.zig b/test/cases/compile_errors/array_access_of_non_array.zig similarity index 55% rename from test/cases/compile_errors/stage1/obj/array_access_of_non_array.zig rename to test/cases/compile_errors/array_access_of_non_array.zig index 7045f2fdbd..06fa1569e6 100644 --- a/test/cases/compile_errors/stage1/obj/array_access_of_non_array.zig +++ b/test/cases/compile_errors/array_access_of_non_array.zig @@ -8,8 +8,8 @@ export fn g() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:8: error: array access of non-array type 'bool' -// tmp.zig:7:12: error: array access of non-array type 'bool' +// :3:8: error: element access of non-indexable type 'bool' +// :7:12: error: element access of non-indexable type 'bool' diff --git a/test/cases/compile_errors/stage1/obj/array_access_with_non_integer_index.zig b/test/cases/compile_errors/array_access_with_non_integer_index.zig similarity index 60% rename from test/cases/compile_errors/stage1/obj/array_access_with_non_integer_index.zig rename to test/cases/compile_errors/array_access_with_non_integer_index.zig index de0a58656c..3d7bde7364 100644 --- a/test/cases/compile_errors/stage1/obj/array_access_with_non_integer_index.zig +++ b/test/cases/compile_errors/array_access_with_non_integer_index.zig @@ -10,8 +10,8 @@ export fn g() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:11: error: expected type 'usize', found 'bool' -// tmp.zig:9:15: error: expected type 'usize', found 'bool' +// :4:11: error: expected type 'usize', found 'bool' +// :9:15: error: expected type 'usize', found 'bool' diff --git a/test/cases/compile_errors/control_reaches_end_of_non-void_function.zig b/test/cases/compile_errors/control_reaches_end_of_non-void_function.zig new file mode 100644 index 0000000000..c92b6b0927 --- /dev/null +++ b/test/cases/compile_errors/control_reaches_end_of_non-void_function.zig @@ -0,0 +1,9 @@ +fn a() i32 {} +export fn entry() void { _ = a(); } + +// error +// backend=stage2 +// target=native +// +// :1:13: error: expected type 'i32', found 'void' +// :1:8: note: function return type declared here diff --git a/test/cases/compile_errors/endless_loop_in_function_evaluation.zig b/test/cases/compile_errors/endless_loop_in_function_evaluation.zig new file mode 100644 index 0000000000..7616bfa5e7 --- /dev/null +++ b/test/cases/compile_errors/endless_loop_in_function_evaluation.zig @@ -0,0 +1,15 @@ +const seventh_fib_number = fibonacci(7); +fn fibonacci(x: i32) i32 { + return fibonacci(x - 1) + fibonacci(x - 2); +} + +export fn entry() usize { return @sizeOf(@TypeOf(&seventh_fib_number)); } + +// error +// backend=stage2 +// target=native +// +// :3:21: error: evaluation exceeded 1000 backwards branches +// :3:21: note: use @setEvalBranchQuota() to raise the branch limit from 1000 +// :3:21: note: called from here (999 times) +// :1:37: note: called from here diff --git a/test/cases/compile_errors/stage1/obj/load_too_many_bytes_from_comptime_reinterpreted_pointer.zig b/test/cases/compile_errors/load_too_many_bytes_from_comptime_reinterpreted_pointer.zig similarity index 68% rename from test/cases/compile_errors/stage1/obj/load_too_many_bytes_from_comptime_reinterpreted_pointer.zig rename to test/cases/compile_errors/load_too_many_bytes_from_comptime_reinterpreted_pointer.zig index b275dbbeed..a86fbc0127 100644 --- a/test/cases/compile_errors/stage1/obj/load_too_many_bytes_from_comptime_reinterpreted_pointer.zig +++ b/test/cases/compile_errors/load_too_many_bytes_from_comptime_reinterpreted_pointer.zig @@ -7,7 +7,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:5:28: error: attempt to read 8 bytes from pointer to f32 which is 4 bytes +// :5:28: error: dereference of '*const i64' exceeds bounds of containing decl of type 'f32' diff --git a/test/cases/compile_errors/stage1/obj/aligned_variable_of_zero-bit_type.zig b/test/cases/compile_errors/stage1/aligned_variable_of_zero-bit_type.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/aligned_variable_of_zero-bit_type.zig rename to test/cases/compile_errors/stage1/aligned_variable_of_zero-bit_type.zig diff --git a/test/cases/compile_errors/stage1/obj/implicit_casting_undefined_c_pointer_to_zig_pointer.zig b/test/cases/compile_errors/stage1/implicit_casting_undefined_c_pointer_to_zig_pointer.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/implicit_casting_undefined_c_pointer_to_zig_pointer.zig rename to test/cases/compile_errors/stage1/implicit_casting_undefined_c_pointer_to_zig_pointer.zig diff --git a/test/cases/compile_errors/stage1/obj/align_n_expr_function_pointers_is_a_compile_error.zig b/test/cases/compile_errors/stage1/obj/align_n_expr_function_pointers_is_a_compile_error.zig deleted file mode 100644 index bd41b3e657..0000000000 --- a/test/cases/compile_errors/stage1/obj/align_n_expr_function_pointers_is_a_compile_error.zig +++ /dev/null @@ -1,9 +0,0 @@ -export fn foo() align(1) void { - return; -} - -// error -// backend=stage1 -// target=wasm32-freestanding-none -// -// tmp.zig:1:23: error: align(N) expr is not allowed on function prototypes in wasm32/wasm64 diff --git a/test/cases/compile_errors/stage1/obj/control_reaches_end_of_non-void_function.zig b/test/cases/compile_errors/stage1/obj/control_reaches_end_of_non-void_function.zig deleted file mode 100644 index e15c1bd40b..0000000000 --- a/test/cases/compile_errors/stage1/obj/control_reaches_end_of_non-void_function.zig +++ /dev/null @@ -1,8 +0,0 @@ -fn a() i32 {} -export fn entry() void { _ = a(); } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:1:12: error: expected type 'i32', found 'void' diff --git a/test/cases/compile_errors/stage1/obj/endless_loop_in_function_evaluation.zig b/test/cases/compile_errors/stage1/obj/endless_loop_in_function_evaluation.zig deleted file mode 100644 index 302ee242e0..0000000000 --- a/test/cases/compile_errors/stage1/obj/endless_loop_in_function_evaluation.zig +++ /dev/null @@ -1,12 +0,0 @@ -const seventh_fib_number = fibonacci(7); -fn fibonacci(x: i32) i32 { - return fibonacci(x - 1) + fibonacci(x - 2); -} - -export fn entry() usize { return @sizeOf(@TypeOf(seventh_fib_number)); } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:3:21: error: evaluation exceeded 1000 backwards branches diff --git a/test/cases/compile_errors/stage1/obj/use_implicit_casts_to_assign_null_to_non-nullable_pointer.zig b/test/cases/compile_errors/stage1/obj/use_implicit_casts_to_assign_null_to_non-nullable_pointer.zig deleted file mode 100644 index 4263f38e3e..0000000000 --- a/test/cases/compile_errors/stage1/obj/use_implicit_casts_to_assign_null_to_non-nullable_pointer.zig +++ /dev/null @@ -1,14 +0,0 @@ -export fn entry() void { - var x: i32 = 1234; - var p: *i32 = &x; - var pp: *?*i32 = &p; - pp.* = null; - var y = p.*; - _ = y; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:4:23: error: expected type '*?*i32', found '**i32' diff --git a/test/cases/compile_errors/use_implicit_casts_to_assign_null_to_non-nullable_pointer.zig b/test/cases/compile_errors/use_implicit_casts_to_assign_null_to_non-nullable_pointer.zig new file mode 100644 index 0000000000..0842e98895 --- /dev/null +++ b/test/cases/compile_errors/use_implicit_casts_to_assign_null_to_non-nullable_pointer.zig @@ -0,0 +1,16 @@ +export fn entry() void { + var x: i32 = 1234; + var p: *i32 = &x; + var pp: *?*i32 = &p; + pp.* = null; + var y = p.*; + _ = y; +} + +// error +// backend=stage2 +// target=native +// +// :4:22: error: expected type '*?*i32', found '**i32' +// :4:22: note: pointer type child '*i32' cannot cast into pointer type child '?*i32' +// :4:22: note: mutable '*i32' allows illegal null values stored to type '?*i32' From d72917320449f449bcdeb6a20d5a98b1dfb901d4 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 12 Jul 2022 16:08:23 +0300 Subject: [PATCH 03/18] stage2: better pointer source location --- src/AstGen.zig | 47 ++------ src/Module.zig | 108 ++++++++++++++++++ src/Sema.zig | 93 +++++++-------- src/Zir.zig | 21 ++-- src/print_zir.zig | 23 +--- ..._ABI_compatible_type_or_has_align_attr.zig | 14 +++ .../obj => }/C_pointer_to_anyopaque.zig | 4 +- .../compile_errors/c_pointer_to_void.zig | 4 +- .../container_init_with_non-type.zig | 2 +- .../{stage1/obj => }/pointer_to_noreturn.zig | 4 +- ...e_passed_as_array_init_type_with_elems.zig | 4 +- ...attempt_to_use_0_bit_type_in_extern_fn.zig | 0 ..._from_undefined_array_pointer_to_slice.zig | 0 ..._ABI_compatible_type_or_has_align_attr.zig | 12 -- .../obj/unknown_length_pointer_to_opaque.zig | 7 -- .../unknown_length_pointer_to_opaque.zig | 7 ++ ..._unknown_len_ptr_type_instead_of_array.zig | 4 +- 17 files changed, 202 insertions(+), 152 deletions(-) create mode 100644 test/cases/compile_errors/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig rename test/cases/compile_errors/{stage1/obj => }/C_pointer_to_anyopaque.zig (61%) rename test/cases/compile_errors/{stage1/obj => }/pointer_to_noreturn.zig (54%) rename test/cases/compile_errors/stage1/{obj => }/attempt_to_use_0_bit_type_in_extern_fn.zig (100%) rename test/cases/compile_errors/stage1/{obj => }/issue_2687_coerce_from_undefined_array_pointer_to_slice.zig (100%) delete mode 100644 test/cases/compile_errors/stage1/obj/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig delete mode 100644 test/cases/compile_errors/stage1/obj/unknown_length_pointer_to_opaque.zig create mode 100644 test/cases/compile_errors/unknown_length_pointer_to_opaque.zig rename test/cases/compile_errors/{stage1/obj => }/using_an_unknown_len_ptr_type_instead_of_array.zig (56%) diff --git a/src/AstGen.zig b/src/AstGen.zig index 252610aeeb..9941fe54e8 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1347,7 +1347,7 @@ fn arrayInitExpr( } } const array_type_inst = try typeExpr(gz, scope, array_init.ast.type_expr); - _ = try gz.addUnNode(.validate_array_init_ty, array_type_inst, node); + _ = try gz.addUnNode(.validate_array_init_ty, array_type_inst, array_init.ast.type_expr); break :inst .{ .array = array_type_inst, .elem = .none, @@ -2332,7 +2332,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner .err_union_code, .err_union_code_ptr, .ptr_type, - .ptr_type_simple, + .overflow_arithmetic_ptr, .enum_literal, .merge_error_sets, .error_union_type, @@ -3110,24 +3110,6 @@ fn ptrType( const elem_type = try typeExpr(gz, scope, ptr_info.ast.child_type); - const simple = ptr_info.ast.align_node == 0 and - ptr_info.ast.addrspace_node == 0 and - ptr_info.ast.sentinel == 0 and - ptr_info.ast.bit_range_start == 0; - - if (simple) { - const result = try gz.add(.{ .tag = .ptr_type_simple, .data = .{ - .ptr_type_simple = .{ - .is_allowzero = ptr_info.allowzero_token != null, - .is_mutable = ptr_info.const_token == null, - .is_volatile = ptr_info.volatile_token != null, - .size = ptr_info.size, - .elem_type = elem_type, - }, - } }); - return rvalue(gz, rl, result, node); - } - var sentinel_ref: Zir.Inst.Ref = .none; var align_ref: Zir.Inst.Ref = .none; var addrspace_ref: Zir.Inst.Ref = .none; @@ -3160,7 +3142,10 @@ fn ptrType( try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.PtrType).Struct.fields.len + trailing_count); - const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.PtrType{ .elem_type = elem_type }); + const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.PtrType{ + .elem_type = elem_type, + .src_node = gz.nodeIndexToRelative(node), + }); if (sentinel_ref != .none) { gz.astgen.extra.appendAssumeCapacity(@enumToInt(sentinel_ref)); } @@ -7588,15 +7573,7 @@ fn builtinCall( .shl_with_overflow => { const int_type = try typeExpr(gz, scope, params[0]); const log2_int_type = try gz.addUnNode(.log2_int_type, int_type, params[0]); - const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{ - .ptr_type_simple = .{ - .is_allowzero = false, - .is_mutable = true, - .is_volatile = false, - .size = .One, - .elem_type = int_type, - }, - } }); + const ptr_type = try gz.addUnNode(.overflow_arithmetic_ptr, int_type, params[0]); const lhs = try expr(gz, scope, .{ .ty = int_type }, params[1]); const rhs = try expr(gz, scope, .{ .ty = log2_int_type }, params[2]); const ptr = try expr(gz, scope, .{ .ty = ptr_type }, params[3]); @@ -7987,15 +7964,7 @@ fn overflowArithmetic( tag: Zir.Inst.Extended, ) InnerError!Zir.Inst.Ref { const int_type = try typeExpr(gz, scope, params[0]); - const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{ - .ptr_type_simple = .{ - .is_allowzero = false, - .is_mutable = true, - .is_volatile = false, - .size = .One, - .elem_type = int_type, - }, - } }); + const ptr_type = try gz.addUnNode(.overflow_arithmetic_ptr, int_type, params[0]); const lhs = try expr(gz, scope, .{ .ty = int_type }, params[1]); const rhs = try expr(gz, scope, .{ .ty = int_type }, params[2]); const ptr = try expr(gz, scope, .{ .ty = ptr_type }, params[3]); diff --git a/src/Module.zig b/src/Module.zig index 4a5257a4f3..0f38da13e9 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2466,6 +2466,90 @@ pub const SrcLoc = struct { return nodeToSpan(tree, node_datas[node].lhs); }, + .node_offset_ptr_elem => |node_off| { + const tree = try src_loc.file_scope.getTree(gpa); + const node_tags = tree.nodes.items(.tag); + const parent_node = src_loc.declRelativeToNodeIndex(node_off); + + const full: Ast.full.PtrType = switch (node_tags[parent_node]) { + .ptr_type_aligned => tree.ptrTypeAligned(parent_node), + .ptr_type_sentinel => tree.ptrTypeSentinel(parent_node), + .ptr_type => tree.ptrType(parent_node), + .ptr_type_bit_range => tree.ptrTypeBitRange(parent_node), + else => unreachable, + }; + return nodeToSpan(tree, full.ast.child_type); + }, + .node_offset_ptr_sentinel => |node_off| { + const tree = try src_loc.file_scope.getTree(gpa); + const node_tags = tree.nodes.items(.tag); + const parent_node = src_loc.declRelativeToNodeIndex(node_off); + + const full: Ast.full.PtrType = switch (node_tags[parent_node]) { + .ptr_type_aligned => tree.ptrTypeAligned(parent_node), + .ptr_type_sentinel => tree.ptrTypeSentinel(parent_node), + .ptr_type => tree.ptrType(parent_node), + .ptr_type_bit_range => tree.ptrTypeBitRange(parent_node), + else => unreachable, + }; + return nodeToSpan(tree, full.ast.sentinel); + }, + .node_offset_ptr_align => |node_off| { + const tree = try src_loc.file_scope.getTree(gpa); + const node_tags = tree.nodes.items(.tag); + const parent_node = src_loc.declRelativeToNodeIndex(node_off); + + const full: Ast.full.PtrType = switch (node_tags[parent_node]) { + .ptr_type_aligned => tree.ptrTypeAligned(parent_node), + .ptr_type_sentinel => tree.ptrTypeSentinel(parent_node), + .ptr_type => tree.ptrType(parent_node), + .ptr_type_bit_range => tree.ptrTypeBitRange(parent_node), + else => unreachable, + }; + return nodeToSpan(tree, full.ast.align_node); + }, + .node_offset_ptr_addrspace => |node_off| { + const tree = try src_loc.file_scope.getTree(gpa); + const node_tags = tree.nodes.items(.tag); + const parent_node = src_loc.declRelativeToNodeIndex(node_off); + + const full: Ast.full.PtrType = switch (node_tags[parent_node]) { + .ptr_type_aligned => tree.ptrTypeAligned(parent_node), + .ptr_type_sentinel => tree.ptrTypeSentinel(parent_node), + .ptr_type => tree.ptrType(parent_node), + .ptr_type_bit_range => tree.ptrTypeBitRange(parent_node), + else => unreachable, + }; + return nodeToSpan(tree, full.ast.addrspace_node); + }, + .node_offset_ptr_bitoffset => |node_off| { + const tree = try src_loc.file_scope.getTree(gpa); + const node_tags = tree.nodes.items(.tag); + const parent_node = src_loc.declRelativeToNodeIndex(node_off); + + const full: Ast.full.PtrType = switch (node_tags[parent_node]) { + .ptr_type_aligned => tree.ptrTypeAligned(parent_node), + .ptr_type_sentinel => tree.ptrTypeSentinel(parent_node), + .ptr_type => tree.ptrType(parent_node), + .ptr_type_bit_range => tree.ptrTypeBitRange(parent_node), + else => unreachable, + }; + return nodeToSpan(tree, full.ast.bit_range_start); + }, + .node_offset_ptr_hostsize => |node_off| { + const tree = try src_loc.file_scope.getTree(gpa); + const node_tags = tree.nodes.items(.tag); + const parent_node = src_loc.declRelativeToNodeIndex(node_off); + + const full: Ast.full.PtrType = switch (node_tags[parent_node]) { + .ptr_type_aligned => tree.ptrTypeAligned(parent_node), + .ptr_type_sentinel => tree.ptrTypeSentinel(parent_node), + .ptr_type => tree.ptrType(parent_node), + .ptr_type_bit_range => tree.ptrTypeBitRange(parent_node), + else => unreachable, + }; + return nodeToSpan(tree, full.ast.bit_range_end); + }, } } @@ -2739,6 +2823,24 @@ pub const LazySrcLoc = union(enum) { /// The source location points to the operand of an unary expression. /// The Decl is determined contextually. node_offset_un_op: i32, + /// The source location points to the elem type of a pointer. + /// The Decl is determined contextually. + node_offset_ptr_elem: i32, + /// The source location points to the sentinel of a pointer. + /// The Decl is determined contextually. + node_offset_ptr_sentinel: i32, + /// The source location points to the align expr of a pointer. + /// The Decl is determined contextually. + node_offset_ptr_align: i32, + /// The source location points to the addrspace expr of a pointer. + /// The Decl is determined contextually. + node_offset_ptr_addrspace: i32, + /// The source location points to the bit-offset of a pointer. + /// The Decl is determined contextually. + node_offset_ptr_bitoffset: i32, + /// The source location points to the host size of a pointer. + /// The Decl is determined contextually. + node_offset_ptr_hostsize: i32, pub const nodeOffset = if (TracedOffset.want_tracing) nodeOffsetDebug else nodeOffsetRelease; @@ -2803,6 +2905,12 @@ pub const LazySrcLoc = union(enum) { .node_offset_array_type_sentinel, .node_offset_array_type_elem, .node_offset_un_op, + .node_offset_ptr_elem, + .node_offset_ptr_sentinel, + .node_offset_ptr_align, + .node_offset_ptr_addrspace, + .node_offset_ptr_bitoffset, + .node_offset_ptr_hostsize, => .{ .file_scope = decl.getFileScope(), .parent_decl_node = decl.src_node, diff --git a/src/Sema.zig b/src/Sema.zig index b821a32fc7..94909de78c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -768,7 +768,7 @@ fn analyzeBodyInner( .optional_type => try sema.zirOptionalType(block, inst), .param_type => try sema.zirParamType(block, inst), .ptr_type => try sema.zirPtrType(block, inst), - .ptr_type_simple => try sema.zirPtrTypeSimple(block, inst), + .overflow_arithmetic_ptr => try sema.zirOverflowArithmeticPtr(block, inst), .ref => try sema.zirRef(block, inst), .ret_err_value_code => try sema.zirRetErrValueCode(inst), .shr => try sema.zirShr(block, inst, .shr), @@ -13835,22 +13835,21 @@ fn floatOpAllowed(tag: Zir.Inst.Tag) bool { }; } -fn zirPtrTypeSimple(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { +fn zirOverflowArithmeticPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); - const inst_data = sema.code.instructions.items(.data)[inst].ptr_type_simple; - const elem_ty_src = sema.src; // TODO better source location - const elem_type = try sema.resolveType(block, elem_ty_src, inst_data.elem_type); + const inst_data = sema.code.instructions.items(.data)[inst].un_node; + const elem_ty_src = inst_data.src(); + const elem_type = try sema.resolveType(block, elem_ty_src, inst_data.operand); const ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = elem_type, .@"addrspace" = .generic, - .mutable = inst_data.is_mutable, - .@"allowzero" = inst_data.is_allowzero or inst_data.size == .C, - .@"volatile" = inst_data.is_volatile, - .size = inst_data.size, + .mutable = true, + .@"allowzero" = false, + .@"volatile" = false, + .size = .One, }); - try sema.validatePtrTy(block, elem_ty_src, ty); return sema.addType(ty); } @@ -13858,14 +13857,15 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const tracy = trace(@src()); defer tracy.end(); - const src: LazySrcLoc = sema.src; // TODO better source location - const elem_ty_src: LazySrcLoc = sema.src; // TODO better source location - const sentinel_src: LazySrcLoc = sema.src; // TODO better source location - const addrspace_src: LazySrcLoc = sema.src; // TODO better source location - const bitoffset_src: LazySrcLoc = sema.src; // TODO better source location - const hostsize_src: LazySrcLoc = sema.src; // TODO better source location const inst_data = sema.code.instructions.items(.data)[inst].ptr_type; const extra = sema.code.extraData(Zir.Inst.PtrType, inst_data.payload_index); + const elem_ty_src: LazySrcLoc = .{ .node_offset_ptr_elem = extra.data.src_node }; + const sentinel_src: LazySrcLoc = .{ .node_offset_ptr_sentinel = extra.data.src_node }; + const align_src: LazySrcLoc = .{ .node_offset_ptr_align = extra.data.src_node }; + const addrspace_src: LazySrcLoc = .{ .node_offset_ptr_addrspace = extra.data.src_node }; + const bitoffset_src: LazySrcLoc = .{ .node_offset_ptr_bitoffset = extra.data.src_node }; + const hostsize_src: LazySrcLoc = .{ .node_offset_ptr_hostsize = extra.data.src_node }; + const unresolved_elem_ty = try sema.resolveType(block, elem_ty_src, extra.data.elem_type); const target = sema.mod.getTarget(); @@ -13880,8 +13880,8 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const abi_align: u32 = if (inst_data.flags.has_align) blk: { const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]); extra_i += 1; - const coerced = try sema.coerce(block, Type.u32, try sema.resolveInst(ref), src); - const val = try sema.resolveConstValue(block, src, coerced); + const coerced = try sema.coerce(block, Type.u32, try sema.resolveInst(ref), align_src); + const val = try sema.resolveConstValue(block, align_src, coerced); // Check if this happens to be the lazy alignment of our element type, in // which case we can make this 0 without resolving it. if (val.castTag(.lazy_align)) |payload| { @@ -13889,7 +13889,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air break :blk 0; } } - const abi_align = (try val.getUnsignedIntAdvanced(target, sema.kit(block, src))).?; + const abi_align = (try val.getUnsignedIntAdvanced(target, sema.kit(block, align_src))).?; break :blk @intCast(u32, abi_align); } else 0; @@ -13914,7 +13914,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air } else 0; if (host_size != 0 and bit_offset >= host_size * 8) { - return sema.fail(block, src, "bit offset starts after end of host integer", .{}); + return sema.fail(block, bitoffset_src, "bit offset starts after end of host integer", .{}); } const elem_ty = if (abi_align == 0) @@ -13924,6 +13924,30 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air try sema.resolveTypeLayout(block, elem_ty_src, elem_ty); break :t elem_ty; }; + + if (elem_ty.zigTypeTag() == .NoReturn) { + return sema.fail(block, elem_ty_src, "pointer to noreturn not allowed", .{}); + } else if (inst_data.size == .Many and elem_ty.zigTypeTag() == .Opaque) { + return sema.fail(block, elem_ty_src, "unknown-length pointer to opaque not allowed", .{}); + } else if (inst_data.size == .C) { + if (!(try sema.validateExternType(elem_ty, .other))) { + const msg = msg: { + const msg = try sema.errMsg(block, elem_ty_src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(sema.mod)}); + errdefer msg.destroy(sema.gpa); + + const src_decl = sema.mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsNotExtern(block, elem_ty_src, msg, elem_ty_src.toSrcLoc(src_decl), elem_ty, .other); + + try sema.addDeclaredHereNote(msg, elem_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + if (elem_ty.zigTypeTag() == .Opaque) { + return sema.fail(block, elem_ty_src, "C pointers cannot point to opaque types", .{}); + } + } + const ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = elem_ty, .sentinel = sentinel, @@ -13936,38 +13960,9 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air .@"volatile" = inst_data.flags.is_volatile, .size = inst_data.size, }); - try sema.validatePtrTy(block, elem_ty_src, ty); return sema.addType(ty); } -fn validatePtrTy(sema: *Sema, block: *Block, elem_src: LazySrcLoc, ty: Type) CompileError!void { - const ptr_info = ty.ptrInfo().data; - const pointee_tag = ptr_info.pointee_type.zigTypeTag(); - if (pointee_tag == .NoReturn) { - return sema.fail(block, elem_src, "pointer to noreturn not allowed", .{}); - } else if (ptr_info.size == .Many and pointee_tag == .Opaque) { - return sema.fail(block, elem_src, "unknown-length pointer to opaque not allowed", .{}); - } else if (ptr_info.size == .C) { - const elem_ty = ptr_info.pointee_type; - if (!(try sema.validateExternType(elem_ty, .other))) { - const msg = msg: { - const msg = try sema.errMsg(block, elem_src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(sema.mod)}); - errdefer msg.destroy(sema.gpa); - - const src_decl = sema.mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotExtern(block, elem_src, msg, elem_src.toSrcLoc(src_decl), elem_ty, .other); - - try sema.addDeclaredHereNote(msg, elem_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } - if (pointee_tag == .Opaque) { - return sema.fail(block, elem_src, "C pointers cannot point to opaque types", .{}); - } - } -} - fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); diff --git a/src/Zir.zig b/src/Zir.zig index b3126ea2f3..73ba396e75 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -534,9 +534,9 @@ pub const Inst = struct { /// Obtains the return type of the in-scope function. /// Uses the `node` union field. ret_type, - /// Create a pointer type that does not have a sentinel, alignment, address space, or bit range specified. - /// Uses the `ptr_type_simple` union field. - ptr_type_simple, + /// Create a pointer type for overflow arithmetic. + /// TODO remove when doing https://github.com/ziglang/zig/issues/10248 + overflow_arithmetic_ptr, /// Create a pointer type which can have a sentinel, alignment, address space, and/or bit range. /// Uses the `ptr_type` union field. ptr_type, @@ -1121,7 +1121,7 @@ pub const Inst = struct { .err_union_code, .err_union_code_ptr, .ptr_type, - .ptr_type_simple, + .overflow_arithmetic_ptr, .ensure_err_payload_void, .enum_literal, .merge_error_sets, @@ -1417,7 +1417,7 @@ pub const Inst = struct { .err_union_code, .err_union_code_ptr, .ptr_type, - .ptr_type_simple, + .overflow_arithmetic_ptr, .enum_literal, .merge_error_sets, .error_union_type, @@ -1659,7 +1659,7 @@ pub const Inst = struct { .ret_err_value_code = .str_tok, .ret_ptr = .node, .ret_type = .node, - .ptr_type_simple = .ptr_type_simple, + .overflow_arithmetic_ptr = .un_node, .ptr_type = .ptr_type, .slice_start = .pl_node, .slice_end = .pl_node, @@ -2499,13 +2499,6 @@ pub const Inst = struct { node: i32, int: u64, float: f64, - ptr_type_simple: struct { - is_allowzero: bool, - is_mutable: bool, - is_volatile: bool, - size: std.builtin.Type.Pointer.Size, - elem_type: Ref, - }, ptr_type: struct { flags: packed struct { is_allowzero: bool, @@ -2608,7 +2601,6 @@ pub const Inst = struct { node, int, float, - ptr_type_simple, ptr_type, int_type, bool_br, @@ -2869,6 +2861,7 @@ pub const Inst = struct { /// 4. host_size: Ref // if `has_bit_range` flag is set pub const PtrType = struct { elem_type: Ref, + src_node: i32, }; pub const ArrayTypeSentinel = struct { diff --git a/src/print_zir.zig b/src/print_zir.zig index 878fa0c14c..96923c6d86 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -233,6 +233,7 @@ const Writer = struct { .validate_struct_init_ty, .make_ptr_const, .validate_deref, + .overflow_arithmetic_ptr, => try self.writeUnNode(stream, inst), .ref, @@ -247,7 +248,6 @@ const Writer = struct { .array_type_sentinel => try self.writeArrayTypeSentinel(stream, inst), .param_type => try self.writeParamType(stream, inst), - .ptr_type_simple => try self.writePtrTypeSimple(stream, inst), .ptr_type => try self.writePtrType(stream, inst), .int => try self.writeInt(stream, inst), .int_big => try self.writeIntBig(stream, inst), @@ -601,24 +601,6 @@ const Writer = struct { try stream.print(", {d})", .{inst_data.param_index}); } - fn writePtrTypeSimple( - self: *Writer, - stream: anytype, - inst: Zir.Inst.Index, - ) (@TypeOf(stream).Error || error{OutOfMemory})!void { - const inst_data = self.code.instructions.items(.data)[inst].ptr_type_simple; - const str_allowzero = if (inst_data.is_allowzero) "allowzero, " else ""; - const str_const = if (!inst_data.is_mutable) "const, " else ""; - const str_volatile = if (inst_data.is_volatile) "volatile, " else ""; - try self.writeInstRef(stream, inst_data.elem_type); - try stream.print(", {s}{s}{s}{s})", .{ - str_allowzero, - str_const, - str_volatile, - @tagName(inst_data.size), - }); - } - fn writePtrType( self: *Writer, stream: anytype, @@ -660,7 +642,8 @@ const Writer = struct { try self.writeInstRef(stream, @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index])); try stream.writeAll(")"); } - try stream.writeAll(")"); + try stream.writeAll(") "); + try self.writeSrc(stream, LazySrcLoc.nodeOffset(extra.data.src_node)); } fn writeInt(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { diff --git a/test/cases/compile_errors/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig b/test/cases/compile_errors/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig new file mode 100644 index 0000000000..1b9118aa64 --- /dev/null +++ b/test/cases/compile_errors/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig @@ -0,0 +1,14 @@ +const Foo = struct {}; +export fn a() void { + const T = [*c]Foo; + var t: T = undefined; + _ = t; +} + +// error +// backend=stage2 +// target=native +// +// :3:19: error: C pointers cannot point to non-C-ABI-compatible type 'tmp.Foo' +// :3:19: note: only structs with packed or extern layout are extern compatible +// :1:13: note: struct declared here diff --git a/test/cases/compile_errors/stage1/obj/C_pointer_to_anyopaque.zig b/test/cases/compile_errors/C_pointer_to_anyopaque.zig similarity index 61% rename from test/cases/compile_errors/stage1/obj/C_pointer_to_anyopaque.zig rename to test/cases/compile_errors/C_pointer_to_anyopaque.zig index 36c9789d20..c2a0ad088d 100644 --- a/test/cases/compile_errors/stage1/obj/C_pointer_to_anyopaque.zig +++ b/test/cases/compile_errors/C_pointer_to_anyopaque.zig @@ -5,7 +5,7 @@ export fn a() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:16: error: C pointers cannot point to opaque types +// :3:16: error: C pointers cannot point to opaque types diff --git a/test/cases/compile_errors/c_pointer_to_void.zig b/test/cases/compile_errors/c_pointer_to_void.zig index aeb5f42dec..116f5a8948 100644 --- a/test/cases/compile_errors/c_pointer_to_void.zig +++ b/test/cases/compile_errors/c_pointer_to_void.zig @@ -7,5 +7,5 @@ export fn entry() void { // backend=stage2 // target=native // -// :1:1: error: C pointers cannot point to non-C-ABI-compatible type 'void' -// :1:1: note: 'void' is a zero bit type; for C 'void' use 'anyopaque' +// :2:16: error: C pointers cannot point to non-C-ABI-compatible type 'void' +// :2:16: note: 'void' is a zero bit type; for C 'void' use 'anyopaque' diff --git a/test/cases/compile_errors/container_init_with_non-type.zig b/test/cases/compile_errors/container_init_with_non-type.zig index ea23020611..aa62be6dc5 100644 --- a/test/cases/compile_errors/container_init_with_non-type.zig +++ b/test/cases/compile_errors/container_init_with_non-type.zig @@ -7,4 +7,4 @@ export fn entry() usize { return @sizeOf(@TypeOf(a)); } // backend=stage2 // target=native // -// :2:15: error: expected type 'type', found 'i32' +// :2:11: error: expected type 'type', found 'i32' diff --git a/test/cases/compile_errors/stage1/obj/pointer_to_noreturn.zig b/test/cases/compile_errors/pointer_to_noreturn.zig similarity index 54% rename from test/cases/compile_errors/stage1/obj/pointer_to_noreturn.zig rename to test/cases/compile_errors/pointer_to_noreturn.zig index 5afcaacaad..0891fd3699 100644 --- a/test/cases/compile_errors/stage1/obj/pointer_to_noreturn.zig +++ b/test/cases/compile_errors/pointer_to_noreturn.zig @@ -2,7 +2,7 @@ fn a() *noreturn {} export fn entry() void { _ = a(); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:1:9: error: pointer to noreturn not allowed +// :1:9: error: pointer to noreturn not allowed diff --git a/test/cases/compile_errors/slice_passed_as_array_init_type_with_elems.zig b/test/cases/compile_errors/slice_passed_as_array_init_type_with_elems.zig index e336d2d05e..7af505e20d 100644 --- a/test/cases/compile_errors/slice_passed_as_array_init_type_with_elems.zig +++ b/test/cases/compile_errors/slice_passed_as_array_init_type_with_elems.zig @@ -7,5 +7,5 @@ export fn entry() void { // backend=stage2 // target=native // -// :2:19: error: type '[]u8' does not support array initialization syntax -// :2:19: note: inferred array length is specified with an underscore: '[_]u8' +// :2:15: error: type '[]u8' does not support array initialization syntax +// :2:15: note: inferred array length is specified with an underscore: '[_]u8' diff --git a/test/cases/compile_errors/stage1/obj/attempt_to_use_0_bit_type_in_extern_fn.zig b/test/cases/compile_errors/stage1/attempt_to_use_0_bit_type_in_extern_fn.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/attempt_to_use_0_bit_type_in_extern_fn.zig rename to test/cases/compile_errors/stage1/attempt_to_use_0_bit_type_in_extern_fn.zig diff --git a/test/cases/compile_errors/stage1/obj/issue_2687_coerce_from_undefined_array_pointer_to_slice.zig b/test/cases/compile_errors/stage1/issue_2687_coerce_from_undefined_array_pointer_to_slice.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/issue_2687_coerce_from_undefined_array_pointer_to_slice.zig rename to test/cases/compile_errors/stage1/issue_2687_coerce_from_undefined_array_pointer_to_slice.zig diff --git a/test/cases/compile_errors/stage1/obj/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig b/test/cases/compile_errors/stage1/obj/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig deleted file mode 100644 index 72c58adbd6..0000000000 --- a/test/cases/compile_errors/stage1/obj/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig +++ /dev/null @@ -1,12 +0,0 @@ -const Foo = struct {}; -export fn a() void { - const T = [*c]Foo; - var t: T = undefined; - _ = t; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:3:19: error: C pointers cannot point to non-C-ABI-compatible type 'Foo' diff --git a/test/cases/compile_errors/stage1/obj/unknown_length_pointer_to_opaque.zig b/test/cases/compile_errors/stage1/obj/unknown_length_pointer_to_opaque.zig deleted file mode 100644 index d155b986d7..0000000000 --- a/test/cases/compile_errors/stage1/obj/unknown_length_pointer_to_opaque.zig +++ /dev/null @@ -1,7 +0,0 @@ -export const T = [*]opaque {}; - -// error -// backend=stage1 -// target=native -// -// tmp.zig:1:21: error: unknown-length pointer to opaque diff --git a/test/cases/compile_errors/unknown_length_pointer_to_opaque.zig b/test/cases/compile_errors/unknown_length_pointer_to_opaque.zig new file mode 100644 index 0000000000..2d2d116ec5 --- /dev/null +++ b/test/cases/compile_errors/unknown_length_pointer_to_opaque.zig @@ -0,0 +1,7 @@ +export const T = [*]opaque {}; + +// error +// backend=stage2 +// target=native +// +// :1:21: error: unknown-length pointer to opaque not allowed diff --git a/test/cases/compile_errors/stage1/obj/using_an_unknown_len_ptr_type_instead_of_array.zig b/test/cases/compile_errors/using_an_unknown_len_ptr_type_instead_of_array.zig similarity index 56% rename from test/cases/compile_errors/stage1/obj/using_an_unknown_len_ptr_type_instead_of_array.zig rename to test/cases/compile_errors/using_an_unknown_len_ptr_type_instead_of_array.zig index 3124b057a8..c1f593b9bd 100644 --- a/test/cases/compile_errors/stage1/obj/using_an_unknown_len_ptr_type_instead_of_array.zig +++ b/test/cases/compile_errors/using_an_unknown_len_ptr_type_instead_of_array.zig @@ -7,7 +7,7 @@ comptime { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:1:21: error: expected array type or [_], found '[*][*]const u8' +// :1:22: error: type '[*][*]const u8' does not support array initialization syntax From 195c3cd89fe2b8e1b3aefe12de074878244d9e94 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 14 Jul 2022 12:40:14 +0300 Subject: [PATCH 04/18] fix errors in tests not tested locally or on CI --- test/cases/compile_errors/dereference_anyopaque.zig | 12 ++++++++---- .../function_call_assigned_to_incorrect_type.zig | 1 + 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/test/cases/compile_errors/dereference_anyopaque.zig b/test/cases/compile_errors/dereference_anyopaque.zig index e09f086016..a69eed946d 100644 --- a/test/cases/compile_errors/dereference_anyopaque.zig +++ b/test/cases/compile_errors/dereference_anyopaque.zig @@ -45,7 +45,11 @@ pub export fn entry() void { // backend=llvm // // :11:22: error: comparison of 'void' with null -// :25:51: error: unable to resolve comptime value -// :25:51: error: unable to resolve comptime value -// :25:51: error: unable to resolve comptime value -// :25:51: error: unable to resolve comptime value +// :25:51: error: values of type 'anyopaque' must be comptime known, but operand value is runtime known +// :25:51: note: opaque type 'anyopaque' has undefined size +// :25:51: error: values of type 'fn(*anyopaque, usize, u29, u29, usize) error{OutOfMemory}![]u8' must be comptime known, but operand value is runtime known +// :25:51: note: use '*const fn(*anyopaque, usize, u29, u29, usize) error{OutOfMemory}![]u8' for a function pointer type +// :25:51: error: values of type 'fn(*anyopaque, []u8, u29, usize, u29, usize) ?usize' must be comptime known, but operand value is runtime known +// :25:51: note: use '*const fn(*anyopaque, []u8, u29, usize, u29, usize) ?usize' for a function pointer type +// :25:51: error: values of type 'fn(*anyopaque, []u8, u29, usize) void' must be comptime known, but operand value is runtime known +// :25:51: note: use '*const fn(*anyopaque, []u8, u29, usize) void' for a function pointer type diff --git a/test/cases/compile_errors/function_call_assigned_to_incorrect_type.zig b/test/cases/compile_errors/function_call_assigned_to_incorrect_type.zig index 41d5b588da..fcbfabe297 100644 --- a/test/cases/compile_errors/function_call_assigned_to_incorrect_type.zig +++ b/test/cases/compile_errors/function_call_assigned_to_incorrect_type.zig @@ -11,3 +11,4 @@ fn concat() [16]f32 { // target=native // // :3:17: error: expected type '[4]f32', found '[16]f32' +// :3:17: note: array of length 16 cannot cast into an array of length 4 From 76e7959a90ae025a1934a4ad1c689663f523a4bc Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 13 Jul 2022 00:35:09 +0300 Subject: [PATCH 05/18] Sema: explain why comptime is needed --- src/Module.zig | 16 +- src/Sema.zig | 641 ++++++++++-------- .../compile_errors/asm_at_compile_time.zig | 2 +- ... on bound fn referring to var instance.zig | 2 +- ...float_conversion_to_comptime_int-float.zig | 2 + ...h_struct_return_value_outside_function.zig | 2 +- .../non-pure_function_returns_type.zig | 2 +- .../non_constant_expression_in_array_size.zig | 2 +- test/cases/extern_variable_has_no_type.0.zig | 2 +- test/cases/x86_64-linux/assert_function.8.zig | 1 + test/stage2/cbe.zig | 4 +- 11 files changed, 359 insertions(+), 317 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index 0f38da13e9..2472ca926a 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -4065,18 +4065,6 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void { var wip_captures = try WipCaptureScope.init(gpa, new_decl_arena_allocator, null); defer wip_captures.deinit(); - var block_scope: Sema.Block = .{ - .parent = null, - .sema = &sema, - .src_decl = new_decl_index, - .namespace = &struct_obj.namespace, - .wip_capture_scope = wip_captures.scope, - .instructions = .{}, - .inlining = null, - .is_comptime = true, - }; - defer block_scope.instructions.deinit(gpa); - if (sema.analyzeStructDecl(new_decl, main_struct_inst, struct_obj)) |_| { try wip_captures.finalize(); new_decl.analysis = .complete; @@ -4185,7 +4173,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { const result_ref = (try sema.analyzeBodyBreak(&block_scope, body)).?.operand; try wip_captures.finalize(); const src = LazySrcLoc.nodeOffset(0); - const decl_tv = try sema.resolveInstValue(&block_scope, src, result_ref); + const decl_tv = try sema.resolveInstValue(&block_scope, .unneeded, result_ref, undefined); const decl_align: u32 = blk: { const align_ref = decl.zirAlignRef(); if (align_ref == .none) break :blk 0; @@ -4194,7 +4182,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { const decl_linksection: ?[*:0]const u8 = blk: { const linksection_ref = decl.zirLinksectionRef(); if (linksection_ref == .none) break :blk null; - const bytes = try sema.resolveConstString(&block_scope, src, linksection_ref); + const bytes = try sema.resolveConstString(&block_scope, src, linksection_ref, "linksection must be comptime known"); break :blk (try decl_arena_allocator.dupeZ(u8, bytes)).ptr; }; const target = sema.mod.getTarget(); diff --git a/src/Sema.zig b/src/Sema.zig index 94909de78c..ef3cd8a04c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -892,7 +892,7 @@ fn analyzeBodyInner( .shl_sat => try sema.zirShl(block, inst, .shl_sat), .ret_ptr => try sema.zirRetPtr(block, inst), - .ret_type => try sema.zirRetType(block, inst), + .ret_type => try sema.addType(sema.fn_ret_ty), // Instructions that we know to *always* be noreturn based solely on their tag. // These functions match the return type of analyzeBody so that we can @@ -1173,7 +1173,7 @@ fn analyzeBodyInner( } else { const src_node = sema.code.instructions.items(.data)[inst].node; const src = LazySrcLoc.nodeOffset(src_node); - try sema.requireRuntimeBlock(block, src); + try sema.requireFunctionBlock(block, src); break always_noreturn; } }, @@ -1303,7 +1303,7 @@ fn analyzeBodyInner( const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index); const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len]; const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; - const cond = try sema.resolveInstConst(block, cond_src, extra.data.condition); + const cond = try sema.resolveInstConst(block, cond_src, extra.data.condition, "condition in comptime branch must be comptime known"); const inline_body = if (cond.val.toBool()) then_body else else_body; const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse break always_noreturn; @@ -1319,7 +1319,7 @@ fn analyzeBodyInner( const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index); const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len]; const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; - const cond = try sema.resolveInstConst(block, cond_src, extra.data.condition); + const cond = try sema.resolveInstConst(block, cond_src, extra.data.condition, "condition in comptime branch must be comptime known"); const inline_body = if (cond.val.toBool()) then_body else else_body; const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse break always_noreturn; @@ -1339,7 +1339,7 @@ fn analyzeBodyInner( const err_union = try sema.resolveInst(extra.data.operand); const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union); assert(is_non_err != .none); - const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err); + const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err, "try operand inside comptime block must be comptime known"); if (is_non_err_tv.val.toBool()) { const err_union_ty = sema.typeOf(err_union); break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false); @@ -1395,7 +1395,7 @@ fn analyzeBodyInner( const err_union = try sema.analyzeLoad(block, src, operand, operand_src); const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union); assert(is_non_err != .none); - const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err); + const is_non_err_tv = try sema.resolveInstConst(block, operand_src, is_non_err, "try operand inside comptime block must be comptime known"); if (is_non_err_tv.val.toBool()) { break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); } @@ -1478,11 +1478,12 @@ fn resolveConstBool( block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref, + reason: []const u8, ) !bool { const air_inst = try sema.resolveInst(zir_ref); const wanted_type = Type.bool; const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); - const val = try sema.resolveConstValue(block, src, coerced_inst); + const val = try sema.resolveConstValue(block, src, coerced_inst, reason); return val.toBool(); } @@ -1491,11 +1492,12 @@ pub fn resolveConstString( block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref, + reason: []const u8, ) ![]u8 { const air_inst = try sema.resolveInst(zir_ref); const wanted_type = Type.initTag(.const_slice_u8); const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); - const val = try sema.resolveConstValue(block, src, coerced_inst); + const val = try sema.resolveConstValue(block, src, coerced_inst, reason); return val.toAllocatedBytes(wanted_type, sema.arena, sema.mod); } @@ -1514,7 +1516,7 @@ fn analyzeAsType( ) !Type { const wanted_type = Type.initTag(.@"type"); const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); - const val = try sema.resolveConstValue(block, src, coerced_inst); + const val = try sema.resolveConstValue(block, src, coerced_inst, "types must be comptime known"); var buffer: Value.ToTypeBuffer = undefined; const ty = val.toType(&buffer); return ty.copy(sema.arena); @@ -1567,12 +1569,13 @@ fn resolveValue( block: *Block, src: LazySrcLoc, air_ref: Air.Inst.Ref, + reason: []const u8, ) CompileError!Value { if (try sema.resolveMaybeUndefValAllowVariables(block, src, air_ref)) |val| { if (val.tag() == .generic_poison) return error.GenericPoison; return val; } - return sema.failWithNeededComptime(block, src); + return sema.failWithNeededComptime(block, src, reason); } /// Value Tag `variable` will cause a compile error. @@ -1582,15 +1585,16 @@ fn resolveConstMaybeUndefVal( block: *Block, src: LazySrcLoc, inst: Air.Inst.Ref, + reason: []const u8, ) CompileError!Value { if (try sema.resolveMaybeUndefValAllowVariables(block, src, inst)) |val| { switch (val.tag()) { - .variable => return sema.failWithNeededComptime(block, src), + .variable => return sema.failWithNeededComptime(block, src, reason), .generic_poison => return error.GenericPoison, else => return val, } } - return sema.failWithNeededComptime(block, src); + return sema.failWithNeededComptime(block, src, reason); } /// Will not return Value Tags: `variable`, `undef`. Instead they will emit compile errors. @@ -1600,16 +1604,17 @@ fn resolveConstValue( block: *Block, src: LazySrcLoc, air_ref: Air.Inst.Ref, + reason: []const u8, ) CompileError!Value { if (try sema.resolveMaybeUndefValAllowVariables(block, src, air_ref)) |val| { switch (val.tag()) { .undef => return sema.failWithUseOfUndef(block, src), - .variable => return sema.failWithNeededComptime(block, src), + .variable => return sema.failWithNeededComptime(block, src, reason), .generic_poison => return error.GenericPoison, else => return val, } } - return sema.failWithNeededComptime(block, src); + return sema.failWithNeededComptime(block, src, reason); } /// Value Tag `variable` causes this function to return `null`. @@ -1697,8 +1702,15 @@ fn resolveMaybeUndefValAllowVariables( } } -fn failWithNeededComptime(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError { - return sema.fail(block, src, "unable to resolve comptime value", .{}); +fn failWithNeededComptime(sema: *Sema, block: *Block, src: LazySrcLoc, reason: []const u8) CompileError { + const msg = msg: { + const msg = try sema.errMsg(block, src, "unable to resolve comptime value", .{}); + errdefer msg.destroy(sema.gpa); + + try sema.errNote(block, src, msg, "{s}", .{reason}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); } fn failWithUseOfUndef(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError { @@ -1870,7 +1882,7 @@ fn analyzeAsAlign( src: LazySrcLoc, air_ref: Air.Inst.Ref, ) !u32 { - const alignment_big = try sema.analyzeAsInt(block, src, air_ref, align_ty); + const alignment_big = try sema.analyzeAsInt(block, src, air_ref, align_ty, "alignment must be comptime known"); const alignment = @intCast(u32, alignment_big); // We coerce to u16 in the prev line. if (alignment == 0) return sema.fail(block, src, "alignment must be >= 1", .{}); if (!std.math.isPowerOfTwo(alignment)) { @@ -1897,9 +1909,10 @@ fn resolveInt( src: LazySrcLoc, zir_ref: Zir.Inst.Ref, dest_ty: Type, + reason: []const u8, ) !u64 { const air_ref = try sema.resolveInst(zir_ref); - return analyzeAsInt(sema, block, src, air_ref, dest_ty); + return analyzeAsInt(sema, block, src, air_ref, dest_ty, reason); } fn analyzeAsInt( @@ -1908,9 +1921,10 @@ fn analyzeAsInt( src: LazySrcLoc, air_ref: Air.Inst.Ref, dest_ty: Type, + reason: []const u8, ) !u64 { const coerced = try sema.coerce(block, dest_ty, air_ref, src); - const val = try sema.resolveConstValue(block, src, coerced); + const val = try sema.resolveConstValue(block, src, coerced, reason); const target = sema.mod.getTarget(); return (try val.getUnsignedIntAdvanced(target, sema.kit(block, src))).?; } @@ -1922,9 +1936,10 @@ pub fn resolveInstConst( block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref, + reason: []const u8, ) CompileError!TypedValue { const air_ref = try sema.resolveInst(zir_ref); - const val = try sema.resolveConstValue(block, src, air_ref); + const val = try sema.resolveConstValue(block, src, air_ref, reason); return TypedValue{ .ty = sema.typeOf(air_ref), .val = val, @@ -1938,9 +1953,10 @@ pub fn resolveInstValue( block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref, + reason: []const u8, ) CompileError!TypedValue { const air_ref = try sema.resolveInst(zir_ref); - const val = try sema.resolveValue(block, src, air_ref); + const val = try sema.resolveValue(block, src, air_ref, reason); return TypedValue{ .ty = sema.typeOf(air_ref), .val = val, @@ -1974,7 +1990,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE defer trash_block.instructions.deinit(sema.gpa); const operand = try trash_block.addBitCast(pointee_ty, .void_value); - try sema.requireRuntimeBlock(block, src); + try sema.requireFunctionBlock(block, src); const ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = pointee_ty, .@"align" = inferred_alloc.alignment, @@ -2259,7 +2275,7 @@ fn createAnonymousDeclTypeNamed( const arg = sema.inst_map.get(zir_inst).?; // The comptime call code in analyzeCall already did this, so we're // just repeating it here and it's guaranteed to work. - const arg_val = sema.resolveConstMaybeUndefVal(block, .unneeded, arg) catch unreachable; + const arg_val = sema.resolveConstMaybeUndefVal(block, .unneeded, arg, undefined) catch unreachable; if (arg_i != 0) try buf.appendSlice(","); try buf.writer().print("{}", .{arg_val.fmtValue(sema.typeOf(arg), sema.mod)}); @@ -2515,7 +2531,7 @@ fn zirEnumDecl( // TODO: if we need to report an error here, use a source location // that points to this default value expression rather than the struct. // But only resolve the source location if we need to emit a compile error. - const tag_val = (try sema.resolveInstConst(block, src, tag_val_ref)).val; + const tag_val = (try sema.resolveInstConst(block, src, tag_val_ref, "enum tag value must be comptime known")).val; last_tag_val = tag_val; const copied_tag_val = try tag_val.copy(new_decl_arena_allocator); enum_obj.values.putAssumeCapacityNoClobberContext(copied_tag_val, {}, .{ @@ -2738,7 +2754,6 @@ fn zirRetPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const inst_data = sema.code.instructions.items(.data)[inst].node; const src = LazySrcLoc.nodeOffset(inst_data); - try sema.requireFunctionBlock(block, src); if (block.is_comptime or try sema.typeRequiresComptime(block, src, sema.fn_ret_ty)) { const fn_ret_ty = try sema.resolveTypeFields(block, src, sema.fn_ret_ty); @@ -2770,16 +2785,6 @@ fn zirRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins return sema.analyzeRef(block, inst_data.src(), operand); } -fn zirRetType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - const tracy = trace(@src()); - defer tracy.end(); - - const inst_data = sema.code.instructions.items(.data)[inst].node; - const src = LazySrcLoc.nodeOffset(inst_data); - try sema.requireFunctionBlock(block, src); - return sema.addType(sema.fn_ret_ty); -} - fn zirEnsureResultUsed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { const tracy = trace(@src()); defer tracy.end(); @@ -2925,7 +2930,7 @@ fn zirAllocExtended( try sema.validateVarType(block, ty_src, var_ty, false); } const target = sema.mod.getTarget(); - try sema.requireRuntimeBlock(block, src); + try sema.requireFunctionBlock(block, src); try sema.resolveTypeLayout(block, src, var_ty); const ptr_type = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = var_ty, @@ -3024,7 +3029,7 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro return sema.addConstant(const_ptr_ty, val); } - try sema.requireRuntimeBlock(block, src); + try sema.requireFunctionBlock(block, src); return block.addBitCast(const_ptr_ty, alloc); } @@ -3061,7 +3066,7 @@ fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I .pointee_type = var_ty, .@"addrspace" = target_util.defaultAddressSpace(target, .local), }); - try sema.requireRuntimeBlock(block, var_decl_src); + try sema.requireFunctionBlock(block, var_decl_src); try sema.queueFullTypeResolution(var_ty); return block.addTy(.alloc, ptr_type); } @@ -3083,7 +3088,7 @@ fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .pointee_type = var_ty, .@"addrspace" = target_util.defaultAddressSpace(target, .local), }); - try sema.requireRuntimeBlock(block, var_decl_src); + try sema.requireFunctionBlock(block, var_decl_src); try sema.queueFullTypeResolution(var_ty); return block.addTy(.alloc, ptr_type); } @@ -3272,7 +3277,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com return; } - try sema.requireRuntimeBlock(block, src); + try sema.requireFunctionBlock(block, src); try sema.queueFullTypeResolution(final_elem_ty); // Change it to a normal alloc. @@ -3593,7 +3598,7 @@ fn validateUnionInit( return; } - try sema.requireRuntimeBlock(block, init_src); + try sema.requireFunctionBlock(block, init_src); const new_tag = try sema.addConstant(union_obj.tag_ty, tag_val); _ = try block.addBinOp(.set_union_tag, union_ptr, new_tag); } @@ -3859,7 +3864,7 @@ fn zirValidateArrayInit( // any ZIR instructions at comptime; we need to do that here. if (array_ty.sentinel()) |sentinel_val| { const array_len_ref = try sema.addIntUnsigned(Type.usize, array_len); - const sentinel_ptr = try sema.elemPtrArray(block, init_src, array_ptr, init_src, array_len_ref, true); + const sentinel_ptr = try sema.elemPtrArray(block, init_src, init_src, array_ptr, init_src, array_len_ref, true); const sentinel = try sema.addConstant(array_ty.childType(), sentinel_val); try sema.storePtr2(block, init_src, sentinel_ptr, init_src, sentinel, init_src, .store); } @@ -4207,10 +4212,8 @@ fn storeToInferredAllocComptime( const operand_ty = sema.typeOf(operand); // There will be only one store_to_inferred_ptr because we are running at comptime. // The alloc will turn into a Decl. - if (try sema.resolveMaybeUndefValAllowVariables(block, src, operand)) |operand_val| { - if (operand_val.tag() == .variable) { - return sema.failWithNeededComptime(block, src); - } + if (try sema.resolveMaybeUndefValAllowVariables(block, src, operand)) |operand_val| store: { + if (operand_val.tag() == .variable) break :store; var anon_decl = try block.startAnonDecl(src); defer anon_decl.deinit(); iac.data.decl_index = try anon_decl.finish( @@ -4219,15 +4222,15 @@ fn storeToInferredAllocComptime( iac.data.alignment, ); return; - } else { - return sema.failWithNeededComptime(block, src); } + + return sema.failWithNeededComptime(block, src, "value being stored to a comptime variable must be comptime known"); } fn zirSetEvalBranchQuota(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); - const quota = @intCast(u32, try sema.resolveInt(block, src, inst_data.operand, Type.u32)); + const quota = @intCast(u32, try sema.resolveInt(block, src, inst_data.operand, Type.u32, "eval branch quota must be comptime known")); sema.branch_quota = @maximum(sema.branch_quota, quota); } @@ -4282,7 +4285,7 @@ fn zirParamType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A var param_index = inst_data.param_index; const fn_ty = if (callee_ty.tag() == .bound_fn) fn_ty: { - const bound_fn_val = try sema.resolveConstValue(block, callee_src, callee); + const bound_fn_val = try sema.resolveConstValue(block, .unneeded, callee, undefined); const bound_fn = bound_fn_val.castTag(.bound_fn).?.data; const fn_ty = sema.typeOf(bound_fn.func_inst); param_index += 1; @@ -4417,7 +4420,7 @@ fn zirCompileError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; - const msg = try sema.resolveConstString(block, operand_src, inst_data.operand); + const msg = try sema.resolveConstString(block, operand_src, inst_data.operand, "compile error string must be comptime known"); return sema.fail(block, src, "{s}", .{msg}); } @@ -4466,7 +4469,7 @@ fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index, force_comptime: bo if (block.is_comptime or force_comptime) { return sema.fail(block, src, "encountered @panic at comptime", .{}); } - try sema.requireRuntimeBlock(block, src); + try sema.requireFunctionBlock(block, src); return sema.panicWithMsg(block, src, msg_inst); } @@ -4854,7 +4857,7 @@ fn zirExportValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; - const operand = try sema.resolveInstConst(block, operand_src, extra.operand); + const operand = try sema.resolveInstConst(block, operand_src, extra.operand, "export target must be comptime known"); const options = try sema.resolveExportOptions(block, options_src, extra.options); const decl_index = switch (operand.val.tag()) { .function => operand.val.castTag(.function).?.data.owner_decl, @@ -4989,7 +4992,7 @@ fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst fn zirSetCold(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { const inst_data = sema.code.instructions.items(.data)[inst].un_node; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; - const is_cold = try sema.resolveConstBool(block, operand_src, inst_data.operand); + const is_cold = try sema.resolveConstBool(block, operand_src, inst_data.operand, "operand to @setCold must be comptime known"); const func = sema.func orelse return; // does nothing outside a function func.is_cold = is_cold; } @@ -4997,7 +5000,7 @@ fn zirSetCold(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!voi fn zirSetFloatMode(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; - const float_mode = try sema.resolveBuiltinEnum(block, src, extra.operand, "FloatMode"); + const float_mode = try sema.resolveBuiltinEnum(block, src, extra.operand, "FloatMode", "operand to @setFloatMode must be comptime known"); switch (float_mode) { .Strict => return, .Optimized => { @@ -5009,7 +5012,7 @@ fn zirSetFloatMode(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD fn zirSetRuntimeSafety(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { const inst_data = sema.code.instructions.items(.data)[inst].un_node; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; - block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand); + block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand, "operand to @setRuntimeSafety must be comptime known"); } fn zirFence(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { @@ -5017,7 +5020,7 @@ fn zirFence(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) Co const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; const order_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; - const order = try sema.resolveAtomicOrder(block, order_src, extra.operand); + const order = try sema.resolveAtomicOrder(block, order_src, extra.operand, "atomic order of @fence must be comptime known"); if (@enumToInt(order) < @enumToInt(std.builtin.AtomicOrder.Acquire)) { return sema.fail(block, order_src, "atomic ordering must be Acquire or stricter", .{}); @@ -5292,7 +5295,7 @@ fn zirCall( // Desugar bound functions here if (func_type.tag() == .bound_fn) { - const bound_func = try sema.resolveValue(block, func_src, func); + const bound_func = try sema.resolveValue(block, .unneeded, func, undefined); const bound_data = &bound_func.cast(Value.Payload.BoundFn).?.data; func = bound_data.func_inst; resolved_args = try sema.arena.alloc(Air.Inst.Ref, args.len + 1); @@ -5489,7 +5492,8 @@ fn analyzeCall( } const result: Air.Inst.Ref = if (is_inline_call) res: { - const func_val = try sema.resolveConstValue(block, func_src, func); + // TODO explain why function is being called at comptime + const func_val = try sema.resolveConstValue(block, func_src, func, "function being called at comptime must be comptime known"); const module_fn = switch (func_val.tag()) { .decl_ref => mod.declPtr(func_val.castTag(.decl_ref).?.data).val.castTag(.function).?.data, .function => func_val.castTag(.function).?.data, @@ -5606,7 +5610,8 @@ fn analyzeCall( try sema.inst_map.putNoClobber(gpa, inst, casted_arg); if (is_comptime_call) { - const arg_val = try sema.resolveConstMaybeUndefVal(&child_block, arg_src, casted_arg); + // TODO explain why function is being called at comptime + const arg_val = try sema.resolveConstMaybeUndefVal(&child_block, arg_src, casted_arg, "argument to function being called at comptime must be comptime known"); switch (arg_val.tag()) { .generic_poison, .generic_poison_type => { // This function is currently evaluated as part of an as-of-yet unresolvable @@ -5638,7 +5643,8 @@ fn analyzeCall( if (is_comptime_call) { const arg_src = call_src; // TODO: better source location - const arg_val = try sema.resolveConstMaybeUndefVal(&child_block, arg_src, uncasted_arg); + // TODO explain why function is being called at comptime + const arg_val = try sema.resolveConstMaybeUndefVal(&child_block, arg_src, uncasted_arg, "argument to function being called at comptime must be comptime known"); switch (arg_val.tag()) { .generic_poison, .generic_poison_type => { // This function is currently evaluated as part of an as-of-yet unresolvable @@ -5764,7 +5770,7 @@ fn analyzeCall( } if (should_memoize and is_comptime_call) { - const result_val = try sema.resolveConstMaybeUndefVal(block, call_src, result); + const result_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, result, undefined); // TODO: check whether any external comptime memory was mutated by the // comptime function call. If so, then do not memoize the call here. @@ -5795,7 +5801,7 @@ fn analyzeCall( break :res res2; } else res: { assert(!func_ty_info.is_generic); - try sema.requireRuntimeBlock(block, call_src); + try sema.requireFunctionBlock(block, call_src); const args = try sema.arena.alloc(Air.Inst.Ref, uncasted_args.len); for (uncasted_args) |uncasted_arg, i| { @@ -5849,7 +5855,7 @@ fn instantiateGenericCall( const mod = sema.mod; const gpa = sema.gpa; - const func_val = try sema.resolveConstValue(block, func_src, func); + const func_val = try sema.resolveConstValue(block, func_src, func, "generic function being called must be comptime known"); const module_fn = switch (func_val.tag()) { .function => func_val.castTag(.function).?.data, .decl_ref => mod.declPtr(func_val.castTag(.decl_ref).?.data).val.castTag(.function).?.data, @@ -5902,7 +5908,7 @@ fn instantiateGenericCall( if (is_comptime) { const arg_src = call_src; // TODO better source location const arg_ty = sema.typeOf(uncasted_args[i]); - const arg_val = try sema.resolveValue(block, arg_src, uncasted_args[i]); + const arg_val = try sema.resolveValue(block, arg_src, uncasted_args[i], "parameter is comptime"); try sema.resolveLazyValue(block, arg_src, arg_val); arg_val.hash(arg_ty, &hasher, mod); if (is_anytype) { @@ -6066,12 +6072,12 @@ fn instantiateGenericCall( const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val); child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); } else { - return sema.failWithNeededComptime(block, arg_src); + return sema.failWithNeededComptime(block, arg_src, "parameter is comptime"); } } else if (is_anytype) { const arg_ty = sema.typeOf(arg); if (try sema.typeRequiresComptime(block, arg_src, arg_ty)) { - const arg_val = try sema.resolveConstValue(block, arg_src, arg); + const arg_val = try sema.resolveConstValue(block, arg_src, arg, "type of anytype parameter requires comptime"); const child_arg = try child_sema.addConstant(arg_ty, arg_val); child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); } else { @@ -6093,7 +6099,7 @@ fn instantiateGenericCall( } return err; }; - const new_func_val = child_sema.resolveConstValue(&child_block, .unneeded, new_func_inst) catch unreachable; + const new_func_val = child_sema.resolveConstValue(&child_block, .unneeded, new_func_inst, undefined) catch unreachable; const new_func = new_func_val.castTag(.function).?.data; errdefer new_func.deinit(gpa); assert(new_func == new_module_func); @@ -6195,7 +6201,7 @@ fn instantiateGenericCall( const callee_inst = try sema.analyzeDeclVal(block, func_src, callee.owner_decl); // Make a runtime call to the new function, making sure to omit the comptime args. - try sema.requireRuntimeBlock(block, call_src); + try sema.requireFunctionBlock(block, call_src); const comptime_args = callee.comptime_args.?; const new_fn_info = mod.declPtr(callee.owner_decl).ty.fnInfo(); @@ -6314,7 +6320,7 @@ fn zirVectorType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const elem_type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; - const len = try sema.resolveInt(block, len_src, extra.lhs, Type.u32); + const len = try sema.resolveInt(block, len_src, extra.lhs, Type.u32, "vector length must be comptime known"); const elem_type = try sema.resolveType(block, elem_type_src, extra.rhs); try sema.checkVectorElemType(block, elem_type_src, elem_type); const vector_type = try Type.Tag.vector.create(sema.arena, .{ @@ -6332,7 +6338,7 @@ fn zirArrayType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const len_src: LazySrcLoc = .{ .node_offset_array_type_len = inst_data.src_node }; const elem_src: LazySrcLoc = .{ .node_offset_array_type_elem = inst_data.src_node }; - const len = try sema.resolveInt(block, len_src, extra.lhs, Type.usize); + const len = try sema.resolveInt(block, len_src, extra.lhs, Type.usize, "array length must be comptime known"); const elem_type = try sema.resolveType(block, elem_src, extra.rhs); const array_ty = try Type.array(sema.arena, len, null, elem_type, sema.mod); @@ -6348,11 +6354,11 @@ fn zirArrayTypeSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil const len_src: LazySrcLoc = .{ .node_offset_array_type_len = inst_data.src_node }; const sentinel_src: LazySrcLoc = .{ .node_offset_array_type_sentinel = inst_data.src_node }; const elem_src: LazySrcLoc = .{ .node_offset_array_type_elem = inst_data.src_node }; - const len = try sema.resolveInt(block, len_src, extra.len, Type.usize); + const len = try sema.resolveInt(block, len_src, extra.len, Type.usize, "array length must be comptime known"); const elem_type = try sema.resolveType(block, elem_src, extra.elem_type); const uncasted_sentinel = try sema.resolveInst(extra.sentinel); const sentinel = try sema.coerce(block, elem_type, uncasted_sentinel, sentinel_src); - const sentinel_val = try sema.resolveConstValue(block, sentinel_src, sentinel); + const sentinel_val = try sema.resolveConstValue(block, sentinel_src, sentinel, "array sentinel value must be comptime known"); const array_ty = try Type.array(sema.arena, len, sentinel_val, elem_type, sema.mod); return sema.addType(array_ty); @@ -6452,7 +6458,7 @@ fn zirErrorToInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat } } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, operand_src); return block.addBitCast(result_ty, operand); } @@ -6478,7 +6484,7 @@ fn zirIntToError(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat }; return sema.addConstant(Type.anyerror, Value.initPayload(&payload.base)); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, operand_src); if (block.wantSafety()) { const is_lt_len = try block.addUnOp(.cmp_lt_errors_len, operand); try sema.addSafetyCheck(block, is_lt_len, .invalid_error_code); @@ -6598,7 +6604,7 @@ fn zirEnumToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A return sema.addConstant(int_tag_ty, try val.copy(sema.arena)); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, operand_src); return block.addBitCast(int_tag_ty, enum_tag); } @@ -6645,7 +6651,7 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A return sema.addConstant(dest_ty, int_val); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, operand_src); // TODO insert safety check to make sure the value matches an enum value return block.addTyOp(.intcast, dest_ty, operand); } @@ -6696,7 +6702,7 @@ fn analyzeOptionalPayloadPtr( // If the pointer resulting from this function was stored at comptime, // the optional non-null bit would be set that way. But in this case, // we need to emit a runtime instruction to do it. - try sema.requireRuntimeBlock(block, src); + try sema.requireFunctionBlock(block, src); _ = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr); } return sema.addConstant( @@ -6722,7 +6728,7 @@ fn analyzeOptionalPayloadPtr( } } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); if (safety_check and block.wantSafety()) { const is_non_null = try block.addUnOp(.is_non_null_ptr, optional_ptr); try sema.addSafetyCheck(block, is_non_null, .unwrap_null); @@ -6778,7 +6784,7 @@ fn zirOptionalPayload( return sema.addConstant(result_ty, val); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); if (safety_check and block.wantSafety()) { const is_non_null = try block.addUnOp(.is_non_null, operand); try sema.addSafetyCheck(block, is_non_null, .unwrap_null); @@ -6827,7 +6833,7 @@ fn analyzeErrUnionPayload( return sema.addConstant(payload_ty, data); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); // If the error set has no fields then no safety check is needed. if (safety_check and block.wantSafety() and @@ -6887,7 +6893,7 @@ fn analyzeErrUnionPayloadPtr( // If the pointer resulting from this function was stored at comptime, // the error union error code would be set that way. But in this case, // we need to emit a runtime instruction to do it. - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); _ = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand); } return sema.addConstant( @@ -6913,7 +6919,7 @@ fn analyzeErrUnionPayloadPtr( } } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); // If the error set has no fields then no safety check is needed. if (safety_check and block.wantSafety() and @@ -6951,7 +6957,7 @@ fn zirErrUnionCode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro return sema.addConstant(result_ty, val); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addTyOp(.unwrap_errunion_err, result_ty, operand); } @@ -6981,7 +6987,7 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE } } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addTyOp(.unwrap_errunion_err_ptr, result_ty, operand); } @@ -7037,7 +7043,7 @@ fn zirFunc( const ret_ty_body = sema.code.extra[extra_index..][0..extra.data.ret_body_len]; extra_index += ret_ty_body.len; - const ret_ty_val = try sema.resolveGenericBody(block, ret_ty_src, ret_ty_body, inst, Type.type); + const ret_ty_val = try sema.resolveGenericBody(block, ret_ty_src, ret_ty_body, inst, Type.type, "return type must be comptime known"); var buffer: Value.ToTypeBuffer = undefined; break :blk try ret_ty_val.toType(&buffer).copy(sema.arena); }, @@ -7085,6 +7091,7 @@ fn resolveGenericBody( body: []const Zir.Inst.Index, func_inst: Zir.Inst.Index, dest_ty: Type, + reason: []const u8, ) !Value { assert(body.len != 0); @@ -7098,7 +7105,7 @@ fn resolveGenericBody( } const uncasted = sema.resolveBody(block, body, func_inst) catch |err| break :err err; const result = sema.coerce(block, dest_ty, uncasted, src) catch |err| break :err err; - const val = sema.resolveConstValue(block, src, result) catch |err| break :err err; + const val = sema.resolveConstValue(block, src, result, reason) catch |err| break :err err; return val; }; switch (err) { @@ -7629,7 +7636,7 @@ fn zirPtrToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai if (try sema.resolveMaybeUndefVal(block, ptr_src, ptr)) |ptr_val| { return sema.addConstant(Type.usize, ptr_val); } - try sema.requireRuntimeBlock(block, ptr_src); + try sema.requireRuntimeBlock(block, ptr_src, ptr_src); return block.addUnOp(.ptrtoint, ptr); } @@ -7681,7 +7688,7 @@ fn zirFieldValNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data; const object = try sema.resolveInst(extra.lhs); - const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name); + const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name, "field name must be comptime known"); return sema.fieldVal(block, src, object, field_name, field_name_src); } @@ -7694,7 +7701,7 @@ fn zirFieldPtrNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data; const object_ptr = try sema.resolveInst(extra.lhs); - const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name); + const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name, "field name must be comptime known"); return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src); } @@ -7706,7 +7713,7 @@ fn zirFieldCallBindNamed(sema: *Sema, block: *Block, extended: Zir.Inst.Extended const src = LazySrcLoc.nodeOffset(extra.node); const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; const object_ptr = try sema.resolveInst(extra.lhs); - const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name); + const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name, "field name must be comptime known"); return sema.fieldCallBind(block, src, object_ptr, field_name, field_name_src); } @@ -7722,12 +7729,13 @@ fn zirIntCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); const operand = try sema.resolveInst(extra.rhs); - return sema.intCast(block, dest_ty, dest_ty_src, operand, operand_src, true); + return sema.intCast(block, inst_data.src(), dest_ty, dest_ty_src, operand, operand_src, true); } fn intCast( sema: *Sema, block: *Block, + src: LazySrcLoc, dest_ty: Type, dest_ty_src: LazySrcLoc, operand: Air.Inst.Ref, @@ -7750,7 +7758,7 @@ fn intCast( if ((try sema.typeHasOnePossibleValue(block, dest_ty_src, dest_ty))) |opv| { // requirement: intCast(u0, input) iff input == 0 if (runtime_safety and block.wantSafety()) { - try sema.requireRuntimeBlock(block, operand_src); + try sema.requireRuntimeBlock(block, src, operand_src); const target = sema.mod.getTarget(); const wanted_info = dest_scalar_ty.intInfo(target); const wanted_bits = wanted_info.bits; @@ -7765,7 +7773,7 @@ fn intCast( return sema.addConstant(dest_ty, opv); } - try sema.requireRuntimeBlock(block, operand_src); + try sema.requireRuntimeBlock(block, src, operand_src); if (runtime_safety and block.wantSafety()) { const target = sema.mod.getTarget(); const actual_info = operand_scalar_ty.intInfo(target); @@ -7972,7 +7980,7 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A if (dst_bits >= src_bits) { return sema.coerce(block, dest_ty, operand, operand_src); } - try sema.requireRuntimeBlock(block, operand_src); + try sema.requireRuntimeBlock(block, inst_data.src(), operand_src); return block.addTyOp(.fptrunc, dest_ty, operand); } @@ -8141,7 +8149,7 @@ fn zirSwitchCapture( const first_item = try sema.resolveInst(items[0]); // Previous switch validation ensured this will succeed - const first_item_val = sema.resolveConstValue(block, .unneeded, first_item) catch unreachable; + const first_item_val = sema.resolveConstValue(block, .unneeded, first_item, undefined) catch unreachable; const first_field_index = @intCast(u32, enum_ty.enumTagFieldIndex(first_item_val, sema.mod).?); const first_field = union_obj.fields.values()[first_field_index]; @@ -8149,7 +8157,7 @@ fn zirSwitchCapture( for (items[1..]) |item| { const item_ref = try sema.resolveInst(item); // Previous switch validation ensured this will succeed - const item_val = sema.resolveConstValue(block, .unneeded, item_ref) catch unreachable; + const item_val = sema.resolveConstValue(block, .unneeded, item_ref, undefined) catch unreachable; const field_index = enum_ty.enumTagFieldIndex(item_val, sema.mod).?; const field = union_obj.fields.values()[field_index]; @@ -8186,7 +8194,7 @@ fn zirSwitchCapture( }), ); } - try sema.requireRuntimeBlock(block, operand_src); + try sema.requireRuntimeBlock(block, operand_src, null); return block.addStructFieldPtr(operand_ptr, first_field_index, field_ty_ptr); } @@ -8196,7 +8204,7 @@ fn zirSwitchCapture( operand_val.castTag(.@"union").?.data.val, ); } - try sema.requireRuntimeBlock(block, operand_src); + try sema.requireRuntimeBlock(block, operand_src, null); return block.addStructFieldVal(operand, first_field_index, first_field.ty); }, .ErrorSet => { @@ -8206,7 +8214,7 @@ fn zirSwitchCapture( for (items) |item| { const item_ref = try sema.resolveInst(item); // Previous switch validation ensured this will succeed - const item_val = sema.resolveConstValue(block, .unneeded, item_ref) catch unreachable; + const item_val = sema.resolveConstValue(block, .unneeded, item_ref, undefined) catch unreachable; names.putAssumeCapacityNoClobber( item_val.getError().?, {}, @@ -8220,7 +8228,7 @@ fn zirSwitchCapture( } else { const item_ref = try sema.resolveInst(items[0]); // Previous switch validation ensured this will succeed - const item_val = sema.resolveConstValue(block, .unneeded, item_ref) catch unreachable; + const item_val = sema.resolveConstValue(block, .unneeded, item_ref, undefined) catch unreachable; const item_ty = try Type.Tag.error_set_single.create(sema.arena, item_val.getError().?); return sema.bitCast(block, item_ty, operand, operand_src); @@ -8924,7 +8932,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const item = try sema.resolveInst(item_ref); // Validation above ensured these will succeed. - const item_val = sema.resolveConstValue(&child_block, .unneeded, item) catch unreachable; + const item_val = sema.resolveConstValue(&child_block, .unneeded, item, undefined) catch unreachable; if (operand_val.eql(item_val, operand_ty, sema.mod)) { return sema.resolveBlockBody(block, src, &child_block, body, inst, merges); } @@ -8946,7 +8954,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError for (items) |item_ref| { const item = try sema.resolveInst(item_ref); // Validation above ensured these will succeed. - const item_val = sema.resolveConstValue(&child_block, .unneeded, item) catch unreachable; + const item_val = sema.resolveConstValue(&child_block, .unneeded, item, undefined) catch unreachable; if (operand_val.eql(item_val, operand_ty, sema.mod)) { return sema.resolveBlockBody(block, src, &child_block, body, inst, merges); } @@ -8960,8 +8968,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError extra_index += 1; // Validation above ensured these will succeed. - const first_tv = sema.resolveInstConst(&child_block, .unneeded, item_first) catch unreachable; - const last_tv = sema.resolveInstConst(&child_block, .unneeded, item_last) catch unreachable; + const first_tv = sema.resolveInstConst(&child_block, .unneeded, item_first, undefined) catch unreachable; + const last_tv = sema.resolveInstConst(&child_block, .unneeded, item_last, undefined) catch unreachable; if ((try sema.compare(block, src, operand_val, .gte, first_tv.val, operand_ty)) and (try sema.compare(block, src, operand_val, .lte, last_tv.val, operand_ty))) { @@ -8982,7 +8990,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError return sema.resolveBlockBody(block, src, &child_block, special.body, inst, merges); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, operand_src); const estimated_cases_extra = (scalar_cases_len + multi_cases_len) * @typeInfo(Air.SwitchBr.Case).Struct.fields.len + 2; @@ -9271,14 +9279,14 @@ fn resolveSwitchItemVal( // Constructing a LazySrcLoc is costly because we only have the switch AST node. // Only if we know for sure we need to report a compile error do we resolve the // full source locations. - if (sema.resolveConstValue(block, .unneeded, item)) |val| { + if (sema.resolveConstValue(block, .unneeded, item, undefined)) |val| { return TypedValue{ .ty = item_ty, .val = val }; } else |err| switch (err) { error.NeededSourceLocation => { const src = switch_prong_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), switch_node_offset, range_expand); return TypedValue{ .ty = item_ty, - .val = try sema.resolveConstValue(block, src, item), + .val = try sema.resolveConstValue(block, src, item, "switch prong values must be comptime known"), }; }, else => |e| return e, @@ -9464,7 +9472,7 @@ fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const unresolved_ty = try sema.resolveType(block, ty_src, extra.lhs); - const field_name = try sema.resolveConstString(block, name_src, extra.rhs); + const field_name = try sema.resolveConstString(block, name_src, extra.rhs, "field name must be comptime known"); const ty = try sema.resolveTypeFields(block, ty_src, unresolved_ty); const has_field = hf: { @@ -9506,7 +9514,7 @@ fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const container_type = try sema.resolveType(block, lhs_src, extra.lhs); - const decl_name = try sema.resolveConstString(block, rhs_src, extra.rhs); + const decl_name = try sema.resolveConstString(block, rhs_src, extra.rhs, "decl name must be comptime known"); try checkNamespaceType(sema, block, lhs_src, container_type); @@ -9553,7 +9561,7 @@ fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; - const name = try sema.resolveConstString(block, operand_src, inst_data.operand); + const name = try sema.resolveConstString(block, operand_src, inst_data.operand, "file path name must be comptime known"); const embed_file = mod.embedFile(block.getFileScope(), name) catch |err| switch (err) { error.ImportOutsidePkgPath => { @@ -9707,13 +9715,13 @@ fn zirShl( try lhs_ty.maxInt(sema.arena, target), ); const rhs_limited = try sema.analyzeMinMax(block, rhs_src, rhs, max_int, .min, rhs_src, rhs_src); - break :rhs try sema.intCast(block, lhs_ty, rhs_src, rhs_limited, rhs_src, false); + break :rhs try sema.intCast(block, src, lhs_ty, rhs_src, rhs_limited, rhs_src, false); } else { break :rhs rhs; } } else rhs; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); if (block.wantSafety()) { const maybe_op_ov: ?Air.Inst.Tag = switch (air_tag) { .shl_exact => .shl_with_overflow, @@ -9824,7 +9832,7 @@ fn zirShr( } } else rhs_src; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addBinOp(air_tag, lhs, rhs); } @@ -9883,7 +9891,7 @@ fn zirBitwise( } }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addBinOp(air_tag, casted_lhs, casted_rhs); } @@ -9927,7 +9935,7 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. } } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addTyOp(.not, operand_type, operand); } @@ -9940,6 +9948,7 @@ fn analyzeTupleCat( ) CompileError!Air.Inst.Ref { const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); + const src = LazySrcLoc.nodeOffset(src_node); const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = src_node }; const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node }; @@ -9987,7 +9996,7 @@ fn analyzeTupleCat( return sema.addConstant(tuple_ty, tuple_val); }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len); for (lhs_tuple.types) |_, i| { @@ -10050,8 +10059,8 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const rhs_sent = try sema.addConstant(rhs_info.elem_type, rhs_sent_val); const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src); const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src); - const lhs_sent_casted_val = try sema.resolveConstValue(block, lhs_src, lhs_sent_casted); - const rhs_sent_casted_val = try sema.resolveConstValue(block, rhs_src, rhs_sent_casted); + const lhs_sent_casted_val = try sema.resolveConstValue(block, lhs_src, lhs_sent_casted, "array sentinel value must be comptime known"); + const rhs_sent_casted_val = try sema.resolveConstValue(block, rhs_src, rhs_sent_casted, "array sentinel value must be comptime known"); if (try sema.valuesEqual(block, src, lhs_sent_casted_val, rhs_sent_casted_val, resolved_elem_ty)) { break :s lhs_sent_casted_val; } else { @@ -10059,14 +10068,14 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } } else { const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src); - const lhs_sent_casted_val = try sema.resolveConstValue(block, lhs_src, lhs_sent_casted); + const lhs_sent_casted_val = try sema.resolveConstValue(block, lhs_src, lhs_sent_casted, "array sentinel value must be comptime known"); break :s lhs_sent_casted_val; } } else { if (rhs_info.sentinel) |rhs_sent_val| { const rhs_sent = try sema.addConstant(rhs_info.elem_type, rhs_sent_val); const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src); - const rhs_sent_casted_val = try sema.resolveConstValue(block, rhs_src, rhs_sent_casted); + const rhs_sent_casted_val = try sema.resolveConstValue(block, rhs_src, rhs_sent_casted, "array sentinel value must be comptime known"); break :s rhs_sent_casted_val; } else { break :s null; @@ -10121,7 +10130,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } else break :rs rhs_src; } else lhs_src; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); if (ptr_addrspace) |ptr_as| { const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{ @@ -10187,7 +10196,7 @@ fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Ins // has a sentinel, and this code should compute the length based // on the sentinel value. .Slice, .Many => { - const val = try sema.resolveConstValue(block, src, operand); + const val = try sema.resolveConstValue(block, src, operand, "slice value being concatenated must be comptime known"); return Type.ArrayInfo{ .elem_type = ptr_info.pointee_type, .sentinel = ptr_info.sentinel, @@ -10216,6 +10225,7 @@ fn analyzeTupleMul( ) CompileError!Air.Inst.Ref { const operand_ty = sema.typeOf(operand); const operand_tuple = operand_ty.tupleFields(); + const src = LazySrcLoc.nodeOffset(src_node); const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = src_node }; const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node }; @@ -10259,7 +10269,7 @@ fn analyzeTupleMul( return sema.addConstant(tuple_ty, tuple_val); }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len); for (operand_tuple.types) |_, i| { @@ -10287,7 +10297,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; // In `**` rhs must be comptime-known, but lhs can be runtime-known - const factor = try sema.resolveInt(block, rhs_src, extra.rhs, Type.usize); + const factor = try sema.resolveInt(block, rhs_src, extra.rhs, Type.usize, "array multiplication factor must be comptime known"); if (lhs_ty.isTuple()) { return sema.analyzeTupleMul(block, inst_data.src_node, lhs, factor); @@ -10338,7 +10348,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai return sema.addConstantMaybeRef(block, src, result_ty, val, ptr_addrspace != null); } - try sema.requireRuntimeBlock(block, lhs_src); + try sema.requireRuntimeBlock(block, src, lhs_src); if (ptr_addrspace) |ptr_as| { const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{ @@ -10412,7 +10422,7 @@ fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const target = sema.mod.getTarget(); return sema.addConstant(rhs_ty, try rhs_val.floatNeg(rhs_ty, sema.arena, target)); } - try sema.requireRuntimeBlock(block, rhs_src); + try sema.requireRuntimeBlock(block, src, null); return block.addUnOp(.neg, rhs); } @@ -10636,7 +10646,8 @@ fn zirOverflowArithmetic( else => unreachable, }; - try sema.requireRuntimeBlock(block, src); + const runtime_src = if (maybe_lhs_val == null) lhs_src else rhs_src; + try sema.requireRuntimeBlock(block, src, runtime_src); const tuple = try block.addInst(.{ .tag = air_tag, @@ -11543,7 +11554,7 @@ fn analyzeArithmetic( } }; - try sema.requireRuntimeBlock(block, rs.src); + try sema.requireRuntimeBlock(block, src, rs.src); if (block.wantSafety()) { if (scalar_tag == .Int) { const maybe_op_ov: ?Air.Inst.Tag = switch (rs.air_tag) { @@ -11667,7 +11678,7 @@ fn analyzePtrArithmetic( } else break :rs ptr_src; }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, op_src, runtime_src); return block.addInst(.{ .tag = air_tag, .data = .{ .ty_pl = .{ @@ -11686,7 +11697,7 @@ fn zirLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.In const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); - const ptr_src: LazySrcLoc = .{ .node_offset_deref_ptr = inst_data.src_node }; + const ptr_src = src; // TODO better source location const ptr = try sema.resolveInst(inst_data.operand); return sema.analyzeLoad(block, src, ptr, ptr_src); } @@ -11734,7 +11745,7 @@ fn zirAsm( } if (block.is_comptime) { - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); } var extra_i = extra.end; @@ -11896,10 +11907,10 @@ fn zirCmpEq( } if (lhs_ty_tag == .Union and (rhs_ty_tag == .EnumLiteral or rhs_ty_tag == .Enum)) { - return sema.analyzeCmpUnionTag(block, lhs, lhs_src, rhs, rhs_src, op); + return sema.analyzeCmpUnionTag(block, src, lhs, lhs_src, rhs, rhs_src, op); } if (rhs_ty_tag == .Union and (lhs_ty_tag == .EnumLiteral or lhs_ty_tag == .Enum)) { - return sema.analyzeCmpUnionTag(block, rhs, rhs_src, lhs, lhs_src, op); + return sema.analyzeCmpUnionTag(block, src, rhs, rhs_src, lhs, lhs_src, op); } if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) { @@ -11926,7 +11937,7 @@ fn zirCmpEq( break :src lhs_src; } }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addBinOp(air_tag, lhs, rhs); } if (lhs_ty_tag == .Type and rhs_ty_tag == .Type) { @@ -11944,6 +11955,7 @@ fn zirCmpEq( fn analyzeCmpUnionTag( sema: *Sema, block: *Block, + src: LazySrcLoc, un: Air.Inst.Ref, un_src: LazySrcLoc, tag: Air.Inst.Ref, @@ -11965,7 +11977,7 @@ fn analyzeCmpUnionTag( const coerced_tag = try sema.coerce(block, union_tag_ty, tag, tag_src); const coerced_union = try sema.coerce(block, union_tag_ty, un, un_src); - return sema.cmpSelf(block, coerced_union, coerced_tag, op, un_src, tag_src); + return sema.cmpSelf(block, src, coerced_union, coerced_tag, op, un_src, tag_src); } /// Only called for non-equality operators. See also `zirCmpEq`. @@ -12021,7 +12033,7 @@ fn analyzeCmp( } const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); - return sema.cmpSelf(block, casted_lhs, casted_rhs, op, lhs_src, rhs_src); + return sema.cmpSelf(block, src, casted_lhs, casted_rhs, op, lhs_src, rhs_src); } fn compareOperatorName(comp: std.math.CompareOperator) []const u8 { @@ -12038,6 +12050,7 @@ fn compareOperatorName(comp: std.math.CompareOperator) []const u8 { fn cmpSelf( sema: *Sema, block: *Block, + src: LazySrcLoc, casted_lhs: Air.Inst.Ref, casted_rhs: Air.Inst.Ref, op: std.math.CompareOperator, @@ -12065,7 +12078,7 @@ fn cmpSelf( } else { if (resolved_type.zigTypeTag() == .Bool) { // We can lower bool eq/neq more efficiently. - return sema.runtimeBoolCmp(block, op, casted_rhs, lhs_val.toBool(), rhs_src); + return sema.runtimeBoolCmp(block, src, op, casted_rhs, lhs_val.toBool(), rhs_src); } break :src rhs_src; } @@ -12075,13 +12088,13 @@ fn cmpSelf( if (resolved_type.zigTypeTag() == .Bool) { if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| { if (rhs_val.isUndef()) return sema.addConstUndef(Type.bool); - return sema.runtimeBoolCmp(block, op, casted_lhs, rhs_val.toBool(), lhs_src); + return sema.runtimeBoolCmp(block, src, op, casted_lhs, rhs_val.toBool(), lhs_src); } } break :src lhs_src; } }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); if (resolved_type.zigTypeTag() == .Vector) { const result_ty = try Type.vector(sema.arena, resolved_type.vectorLen(), Type.@"bool"); const result_ty_ref = try sema.addType(result_ty); @@ -12098,13 +12111,14 @@ fn cmpSelf( fn runtimeBoolCmp( sema: *Sema, block: *Block, + src: LazySrcLoc, op: std.math.CompareOperator, lhs: Air.Inst.Ref, rhs: bool, runtime_src: LazySrcLoc, ) CompileError!Air.Inst.Ref { if ((op == .neq) == rhs) { - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addTyOp(.not, Type.bool, lhs); } else { return lhs; @@ -12226,7 +12240,7 @@ fn zirRetAddr( extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { const src = LazySrcLoc.nodeOffset(@bitCast(i32, extended.operand)); - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return try block.addNoOp(.ret_addr); } @@ -12236,7 +12250,7 @@ fn zirFrameAddress( extended: Zir.Inst.Extended.InstData, ) CompileError!Air.Inst.Ref { const src = LazySrcLoc.nodeOffset(@bitCast(i32, extended.operand)); - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return try block.addNoOp(.frame_addr); } @@ -13305,7 +13319,7 @@ fn zirBoolNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air else Air.Inst.Ref.bool_true; } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addTyOp(.not, Type.bool, operand); } @@ -13690,7 +13704,7 @@ fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError if (block.is_comptime or inst_data.force_comptime) { return sema.fail(block, src, "reached unreachable code", .{}); } - try sema.requireRuntimeBlock(block, src); + try sema.requireFunctionBlock(block, src); // TODO Add compile error for @optimizeFor occurring too late in a scope. try block.addUnreachable(src, true); return always_noreturn; @@ -13752,7 +13766,6 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir const operand = try sema.analyzeLoad(block, src, ret_ptr, src); return sema.analyzeRet(block, operand, src); } - try sema.requireRuntimeBlock(block, src); _ = try block.addUnOp(.ret_load, ret_ptr); return always_noreturn; } @@ -13874,14 +13887,14 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const sentinel = if (inst_data.flags.has_sentinel) blk: { const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]); extra_i += 1; - break :blk (try sema.resolveInstConst(block, sentinel_src, ref)).val; + break :blk (try sema.resolveInstConst(block, sentinel_src, ref, "pointer sentinel value must be comptime known")).val; } else null; const abi_align: u32 = if (inst_data.flags.has_align) blk: { const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]); extra_i += 1; const coerced = try sema.coerce(block, Type.u32, try sema.resolveInst(ref), align_src); - const val = try sema.resolveConstValue(block, align_src, coerced); + const val = try sema.resolveConstValue(block, align_src, coerced, "pointer alignment must be comptime known"); // Check if this happens to be the lazy alignment of our element type, in // which case we can make this 0 without resolving it. if (val.castTag(.lazy_align)) |payload| { @@ -13902,14 +13915,14 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const bit_offset = if (inst_data.flags.has_bit_range) blk: { const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]); extra_i += 1; - const bit_offset = try sema.resolveInt(block, bitoffset_src, ref, Type.u16); + const bit_offset = try sema.resolveInt(block, bitoffset_src, ref, Type.u16, "pointer bit-offset must be comptime known"); break :blk @intCast(u16, bit_offset); } else 0; const host_size: u16 = if (inst_data.flags.has_bit_range) blk: { const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]); extra_i += 1; - const host_size = try sema.resolveInt(block, hostsize_src, ref, Type.u16); + const host_size = try sema.resolveInt(block, hostsize_src, ref, Type.u16, "pointer host size must be comptime known"); break :blk @intCast(u16, host_size); } else 0; @@ -14014,7 +14027,7 @@ fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const init_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.UnionInit, inst_data.payload_index).data; const union_ty = try sema.resolveType(block, ty_src, extra.union_type); - const field_name = try sema.resolveConstString(block, field_src, extra.field_name); + const field_name = try sema.resolveConstString(block, field_src, extra.field_name, "name of field being initialized must be comptime known"); const init = try sema.resolveInst(extra.init); return sema.unionInit(block, init, init_src, union_ty, ty_src, field_name, field_src); } @@ -14041,7 +14054,7 @@ fn unionInit( })); } - try sema.requireRuntimeBlock(block, init_src); + try sema.requireRuntimeBlock(block, init_src, null); _ = union_ty_src; try sema.queueFullTypeResolution(union_ty); return block.addUnionInit(union_ty, field_index, init); @@ -14146,7 +14159,7 @@ fn zirStructInit( return alloc; } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); try sema.queueFullTypeResolution(resolved_ty); return block.addUnionInit(resolved_ty, field_index, init_inst); } else if (resolved_ty.isAnonStruct()) { @@ -14251,7 +14264,7 @@ fn finishStructInit( return alloc; } - try sema.requireRuntimeBlock(block, dest_src); + try sema.requireRuntimeBlock(block, dest_src, null); try sema.queueFullTypeResolution(struct_ty); return block.addAggregateInit(struct_ty, field_inits); } @@ -14301,7 +14314,7 @@ fn zirStructInitAnon( return sema.addConstantMaybeRef(block, src, tuple_ty, tuple_val, is_ref); }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); if (is_ref) { const target = sema.mod.getTarget(); @@ -14393,7 +14406,7 @@ fn zirArrayInit( return sema.addConstantMaybeRef(block, src, array_ty, array_val, is_ref); }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); try sema.queueFullTypeResolution(array_ty); if (is_ref) { @@ -14479,7 +14492,7 @@ fn zirArrayInitAnon( return sema.addConstantMaybeRef(block, src, tuple_ty, tuple_val, is_ref); }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); if (is_ref) { const target = sema.mod.getTarget(); @@ -14538,7 +14551,7 @@ fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro const ty_src = inst_data.src(); const field_src = inst_data.src(); const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type); - const field_name = try sema.resolveConstString(block, field_src, extra.field_name); + const field_name = try sema.resolveConstString(block, field_src, extra.field_name, "field name must be comptime known"); return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src); } @@ -14728,7 +14741,7 @@ fn zirUnaryMath( ); } - try sema.requireRuntimeBlock(block, operand_src); + try sema.requireRuntimeBlock(block, operand_src, null); return block.addUnOp(air_tag, operand); }, .ComptimeFloat, .Float => { @@ -14739,7 +14752,7 @@ fn zirUnaryMath( return sema.addConstant(operand_ty, result_val); } - try sema.requireRuntimeBlock(block, operand_src); + try sema.requireRuntimeBlock(block, operand_src, null); return block.addUnOp(air_tag, operand); }, else => unreachable, @@ -14757,7 +14770,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air try sema.resolveTypeLayout(block, operand_src, operand_ty); const enum_ty = switch (operand_ty.zigTypeTag()) { .EnumLiteral => { - const val = try sema.resolveConstValue(block, operand_src, operand); + const val = try sema.resolveConstValue(block, .unneeded, operand, undefined); const bytes = val.castTag(.enum_literal).?.data; return sema.addStrLit(block, bytes); }, @@ -14809,7 +14822,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const uncasted_operand = try sema.resolveInst(inst_data.operand); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src); - const val = try sema.resolveConstValue(block, operand_src, type_info); + const val = try sema.resolveConstValue(block, operand_src, type_info, "operand to @Type must be comptime known"); const union_val = val.cast(Value.Payload.Union).?.data; const tag_ty = type_info_ty.unionTagType().?; const target = mod.getTarget(); @@ -15493,10 +15506,10 @@ fn zirFloatToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const result_val = try sema.floatToInt(block, operand_src, val, operand_ty, dest_ty); return sema.addConstant(dest_ty, result_val); } else if (dest_ty.zigTypeTag() == .ComptimeInt) { - return sema.failWithNeededComptime(block, operand_src); + return sema.failWithNeededComptime(block, operand_src, "value being casted to 'comptime_int' must be comptime known"); } - try sema.requireRuntimeBlock(block, operand_src); + try sema.requireRuntimeBlock(block, inst_data.src(), operand_src); return block.addTyOp(.float_to_int, dest_ty, operand); } @@ -15517,10 +15530,10 @@ fn zirIntToFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const result_val = try val.intToFloat(sema.arena, operand_ty, dest_ty, target); return sema.addConstant(dest_ty, result_val); } else if (dest_ty.zigTypeTag() == .ComptimeFloat) { - return sema.failWithNeededComptime(block, operand_src); + return sema.failWithNeededComptime(block, operand_src, "value being casted to 'comptime_float' must be comptime known"); } - try sema.requireRuntimeBlock(block, operand_src); + try sema.requireRuntimeBlock(block, inst_data.src(), operand_src); return block.addTyOp(.int_to_float, dest_ty, operand); } @@ -15556,7 +15569,7 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai return sema.addConstant(type_res, Value.initPayload(&val_payload.base)); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, operand_src); if (block.wantSafety()) { if (!type_res.isAllowzeroPtr()) { const is_non_zero = try block.addBinOp(.cmp_neq, operand_coerced, .zero_usize); @@ -15653,7 +15666,7 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat return sema.addConstant(dest_ty, val); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, operand_src); if (block.wantSafety() and !dest_ty.isAnyError()) { const err_int_inst = try block.addBitCast(Type.u16, operand); // TODO: Output a switch instead of chained OR's. @@ -15808,7 +15821,7 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai ); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, operand_src); return block.addTyOp(.trunc, dest_ty, operand); } @@ -15851,6 +15864,7 @@ fn zirBitCount( comptimeOp: fn (val: Value, ty: Type, target: std.Target) u64, ) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; + const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const operand = try sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); @@ -15883,7 +15897,7 @@ fn zirBitCount( try Value.Tag.aggregate.create(sema.arena, elems), ); } else { - try sema.requireRuntimeBlock(block, operand_src); + try sema.requireRuntimeBlock(block, src, operand_src); return block.addTyOp(air_tag, result_ty, operand); } }, @@ -15892,7 +15906,7 @@ fn zirBitCount( if (val.isUndef()) return sema.addConstUndef(result_scalar_ty); return sema.addIntUnsigned(result_scalar_ty, comptimeOp(val, operand_ty, target)); } else { - try sema.requireRuntimeBlock(block, operand_src); + try sema.requireRuntimeBlock(block, src, operand_src); return block.addTyOp(air_tag, result_scalar_ty, operand); } }, @@ -15902,6 +15916,7 @@ fn zirBitCount( fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; + const src = inst_data.src(); const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const operand = try sema.resolveInst(inst_data.operand); @@ -15930,7 +15945,7 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai return sema.addConstant(operand_ty, result_val); } else operand_src; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addTyOp(.byte_swap, operand_ty, operand); }, .Vector => { @@ -15951,7 +15966,7 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai ); } else operand_src; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addTyOp(.byte_swap, operand_ty, operand); }, else => unreachable, @@ -15960,6 +15975,7 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; + const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const operand = try sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); @@ -15978,7 +15994,7 @@ fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! return sema.addConstant(operand_ty, result_val); } else operand_src; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addTyOp(.bit_reverse, operand_ty, operand); }, .Vector => { @@ -15999,7 +16015,7 @@ fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! ); } else operand_src; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addTyOp(.bit_reverse, operand_ty, operand); }, else => unreachable, @@ -16026,7 +16042,7 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const ty = try sema.resolveType(block, lhs_src, extra.lhs); - const field_name = try sema.resolveConstString(block, rhs_src, extra.rhs); + const field_name = try sema.resolveConstString(block, rhs_src, extra.rhs, "name of field must be comptime known"); const target = sema.mod.getTarget(); try sema.resolveTypeLayout(block, lhs_src, ty); @@ -16447,19 +16463,19 @@ fn resolveExportOptions( const options = try sema.coerce(block, export_options_ty, air_ref, src); const name_operand = try sema.fieldVal(block, src, options, "name", src); - const name_val = try sema.resolveConstValue(block, src, name_operand); + const name_val = try sema.resolveConstValue(block, src, name_operand, "name of exported value must be comptime known"); const name_ty = Type.initTag(.const_slice_u8); const name = try name_val.toAllocatedBytes(name_ty, sema.arena, sema.mod); const linkage_operand = try sema.fieldVal(block, src, options, "linkage", src); - const linkage_val = try sema.resolveConstValue(block, src, linkage_operand); + const linkage_val = try sema.resolveConstValue(block, src, linkage_operand, "linkage of exported value must be comptime known"); const linkage = linkage_val.toEnum(std.builtin.GlobalLinkage); const section = try sema.fieldVal(block, src, options, "section", src); - const section_val = try sema.resolveConstValue(block, src, section); + const section_val = try sema.resolveConstValue(block, src, section, "linksection of exported value must be comptime known"); const visibility_operand = try sema.fieldVal(block, src, options, "visibility", src); - const visibility_val = try sema.resolveConstValue(block, src, visibility_operand); + const visibility_val = try sema.resolveConstValue(block, src, visibility_operand, "visibility of exported value must be comptime known"); const visibility = visibility_val.toEnum(std.builtin.SymbolVisibility); if (name.len < 1) { @@ -16490,11 +16506,12 @@ fn resolveBuiltinEnum( src: LazySrcLoc, zir_ref: Zir.Inst.Ref, comptime name: []const u8, + reason: []const u8, ) CompileError!@field(std.builtin, name) { const ty = try sema.getBuiltinType(block, src, name); const air_ref = try sema.resolveInst(zir_ref); const coerced = try sema.coerce(block, ty, air_ref, src); - const val = try sema.resolveConstValue(block, src, coerced); + const val = try sema.resolveConstValue(block, src, coerced, reason); return val.toEnum(@field(std.builtin, name)); } @@ -16503,8 +16520,9 @@ fn resolveAtomicOrder( block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref, + reason: []const u8, ) CompileError!std.builtin.AtomicOrder { - return resolveBuiltinEnum(sema, block, src, zir_ref, "AtomicOrder"); + return resolveBuiltinEnum(sema, block, src, zir_ref, "AtomicOrder", reason); } fn resolveAtomicRmwOp( @@ -16513,7 +16531,7 @@ fn resolveAtomicRmwOp( src: LazySrcLoc, zir_ref: Zir.Inst.Ref, ) CompileError!std.builtin.AtomicRmwOp { - return resolveBuiltinEnum(sema, block, src, zir_ref, "AtomicRmwOp"); + return resolveBuiltinEnum(sema, block, src, zir_ref, "AtomicRmwOp", "@atomicRmW operation must be comptime known"); } fn zirCmpxchg( @@ -16546,8 +16564,8 @@ fn zirCmpxchg( const uncasted_ptr = try sema.resolveInst(extra.ptr); const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); const new_value = try sema.coerce(block, elem_ty, try sema.resolveInst(extra.new_value), new_value_src); - const success_order = try sema.resolveAtomicOrder(block, success_order_src, extra.success_order); - const failure_order = try sema.resolveAtomicOrder(block, failure_order_src, extra.failure_order); + const success_order = try sema.resolveAtomicOrder(block, success_order_src, extra.success_order, "atomic order of cmpxchg success must be comptime known"); + const failure_order = try sema.resolveAtomicOrder(block, failure_order_src, extra.failure_order, "atomic order of cmpxchg failure must be comptime known"); if (@enumToInt(success_order) < @enumToInt(std.builtin.AtomicOrder.Monotonic)) { return sema.fail(block, success_order_src, "success atomic ordering must be Monotonic or stricter", .{}); @@ -16592,7 +16610,7 @@ fn zirCmpxchg( const flags: u32 = @as(u32, @enumToInt(success_order)) | (@as(u32, @enumToInt(failure_order)) << 3); - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addInst(.{ .tag = air_tag, .data = .{ .ty_pl = .{ @@ -16612,7 +16630,7 @@ fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const len_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; const scalar_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; - const len = @intCast(u32, try sema.resolveInt(block, len_src, extra.lhs, Type.u32)); + const len = @intCast(u32, try sema.resolveInt(block, len_src, extra.lhs, Type.u32, "vector splat destination length must be comptime known")); const scalar = try sema.resolveInst(extra.rhs); const scalar_ty = sema.typeOf(scalar); try sema.checkVectorElemType(block, scalar_src, scalar_ty); @@ -16629,7 +16647,7 @@ fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I ); } - try sema.requireRuntimeBlock(block, scalar_src); + try sema.requireRuntimeBlock(block, inst_data.src(), scalar_src); return block.addTyOp(.splat, vector_ty, scalar); } @@ -16638,7 +16656,7 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const op_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; - const operation = try sema.resolveBuiltinEnum(block, op_src, extra.lhs, "ReduceOp"); + const operation = try sema.resolveBuiltinEnum(block, op_src, extra.lhs, "ReduceOp", "@reduce operation must be comptime known"); const operand = try sema.resolveInst(extra.rhs); const operand_ty = sema.typeOf(operand); const target = sema.mod.getTarget(); @@ -16693,7 +16711,7 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. return sema.addConstant(scalar_ty, accum); } - try sema.requireRuntimeBlock(block, operand_src); + try sema.requireRuntimeBlock(block, inst_data.src(), operand_src); return block.addInst(.{ .tag = .reduce, .data = .{ .reduce = .{ @@ -16725,7 +16743,7 @@ fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air .elem_type = Type.@"i32", }); mask = try sema.coerce(block, mask_ty, mask, mask_src); - const mask_val = try sema.resolveConstMaybeUndefVal(block, mask_src, mask); + const mask_val = try sema.resolveConstMaybeUndefVal(block, mask_src, mask, "shuffle mask must be comptime known"); return sema.analyzeShuffle(block, inst_data.src_node, elem_ty, a, b, mask_val, @intCast(u32, mask_len)); } @@ -16897,6 +16915,7 @@ fn analyzeShuffle( fn zirSelect(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { const extra = sema.code.extraData(Zir.Inst.Select, extended.operand).data; + const src = LazySrcLoc.nodeOffset(extra.node); const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; const pred_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; const a_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = extra.node }; @@ -16968,7 +16987,7 @@ fn zirSelect(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) C break :rs pred_src; }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addInst(.{ .tag = .select, .data = .{ .pl_op = .{ @@ -16992,7 +17011,7 @@ fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); const uncasted_ptr = try sema.resolveInst(extra.ptr); const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, true); - const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering); + const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, "atomic order of @atomicLoad must be comptime known"); switch (order) { .Release, .AcqRel => { @@ -17016,7 +17035,7 @@ fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! } } - try sema.requireRuntimeBlock(block, ptr_src); + try sema.requireRuntimeBlock(block, inst_data.src(), ptr_src); return block.addInst(.{ .tag = .atomic_load, .data = .{ .atomic_load = .{ @@ -17056,7 +17075,7 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A }, else => {}, } - const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering); + const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, "atomic order of @atomicRmW must be comptime known"); if (order == .Unordered) { return sema.fail(block, order_src, "@atomicRmw atomic ordering must not be Unordered", .{}); @@ -17097,7 +17116,7 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const flags: u32 = @as(u32, @enumToInt(order)) | (@as(u32, @enumToInt(op)) << 3); - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addInst(.{ .tag = .atomic_rmw, .data = .{ .pl_op = .{ @@ -17124,7 +17143,7 @@ fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const elem_ty = sema.typeOf(operand); const uncasted_ptr = try sema.resolveInst(extra.ptr); const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); - const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering); + const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, "atomic order of @atomicStore must be comptime known"); const air_tag: Air.Inst.Tag = switch (order) { .Acquire, .AcqRel => { @@ -17196,7 +17215,7 @@ fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. break :rs mulend1_src; }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addInst(.{ .tag = .mul_add, .data = .{ .pl_op = .{ @@ -17229,10 +17248,10 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const coerced_options = try sema.coerce(block, call_options_ty, options, options_src); const modifier = try sema.fieldVal(block, options_src, coerced_options, "modifier", options_src); - const modifier_val = try sema.resolveConstValue(block, options_src, modifier); + const modifier_val = try sema.resolveConstValue(block, options_src, modifier, "call modifier must be comptime known"); const stack = try sema.fieldVal(block, options_src, coerced_options, "stack", options_src); - const stack_val = try sema.resolveConstValue(block, options_src, stack); + const stack_val = try sema.resolveConstValue(block, options_src, stack, "call stack value must be comptime known"); if (!stack_val.isNull()) { return sema.fail(block, options_src, "TODO: implement @call with stack", .{}); @@ -17293,7 +17312,7 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError // Desugar bound functions here if (sema.typeOf(func).tag() == .bound_fn) { - const bound_func = try sema.resolveValue(block, func_src, func); + const bound_func = try sema.resolveValue(block, .unneeded, func, undefined); const bound_data = &bound_func.cast(Value.Payload.BoundFn).?.data; func = bound_data.func_inst; resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_ty.structFieldCount() + 1); @@ -17320,7 +17339,7 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; const struct_ty = try sema.resolveType(block, ty_src, extra.parent_type); - const field_name = try sema.resolveConstString(block, name_src, extra.field_name); + const field_name = try sema.resolveConstString(block, name_src, extra.field_name, "field name must be comptime known"); const field_ptr = try sema.resolveInst(extra.field_ptr); const field_ptr_ty = sema.typeOf(field_ptr); @@ -17385,7 +17404,7 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr return sema.addConstant(result_ptr, payload.data.container_ptr); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, ptr_src); return block.addInst(.{ .tag = .field_parent_ptr, .data = .{ .ty_pl = .{ @@ -17466,7 +17485,7 @@ fn analyzeMinMax( break :rs lhs_src; }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addBinOp(air_tag, simd_op.lhs, simd_op.rhs); } @@ -17514,7 +17533,7 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void } else break :rs src_src; } else dest_src; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); _ = try block.addInst(.{ .tag = .memcpy, .data = .{ .pl_op = .{ @@ -17556,7 +17575,7 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void } else break :rs len_src; } else dest_src; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); _ = try block.addInst(.{ .tag = .memset, .data = .{ .pl_op = .{ @@ -17652,7 +17671,7 @@ fn zirVarExtended( uncasted_init; break :blk (try sema.resolveMaybeUndefVal(block, init_src, init)) orelse - return sema.failWithNeededComptime(block, init_src); + return sema.failWithNeededComptime(block, init_src, "container level variable initializers must be comptime known"); } else Value.initTag(.unreachable_value); try sema.validateVarType(block, name_src, var_ty, small.is_extern); @@ -17718,7 +17737,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const body = sema.code.extra[extra_index..][0..body_len]; extra_index += body.len; - const val = try sema.resolveGenericBody(block, align_src, body, inst, Type.u29); + const val = try sema.resolveGenericBody(block, align_src, body, inst, Type.u29, "alignment must be comptime known"); if (val.tag() == .generic_poison) { break :blk null; } @@ -17731,7 +17750,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A } else if (extra.data.bits.has_align_ref) blk: { const align_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; - const align_tv = sema.resolveInstConst(block, align_src, align_ref) catch |err| switch (err) { + const align_tv = sema.resolveInstConst(block, align_src, align_ref, "alignment must be comptime known") catch |err| switch (err) { error.GenericPoison => { break :blk null; }, @@ -17752,7 +17771,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A extra_index += body.len; const addrspace_ty = try sema.getBuiltinType(block, addrspace_src, "AddressSpace"); - const val = try sema.resolveGenericBody(block, addrspace_src, body, inst, addrspace_ty); + const val = try sema.resolveGenericBody(block, addrspace_src, body, inst, addrspace_ty, "addrespace must be comptime known"); if (val.tag() == .generic_poison) { break :blk null; } @@ -17760,7 +17779,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A } else if (extra.data.bits.has_addrspace_ref) blk: { const addrspace_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; - const addrspace_tv = sema.resolveInstConst(block, addrspace_src, addrspace_ref) catch |err| switch (err) { + const addrspace_tv = sema.resolveInstConst(block, addrspace_src, addrspace_ref, "addrespace must be comptime known") catch |err| switch (err) { error.GenericPoison => { break :blk null; }, @@ -17775,7 +17794,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const body = sema.code.extra[extra_index..][0..body_len]; extra_index += body.len; - const val = try sema.resolveGenericBody(block, section_src, body, inst, Type.initTag(.const_slice_u8)); + const val = try sema.resolveGenericBody(block, section_src, body, inst, Type.initTag(.const_slice_u8), "linksection must be comptime known"); if (val.tag() == .generic_poison) { break :blk FuncLinkSection{ .generic = {} }; } @@ -17784,7 +17803,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A } else if (extra.data.bits.has_section_ref) blk: { const section_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; - const section_tv = sema.resolveInstConst(block, section_src, section_ref) catch |err| switch (err) { + const section_tv = sema.resolveInstConst(block, section_src, section_ref, "linksection must be comptime known") catch |err| switch (err) { error.GenericPoison => { break :blk FuncLinkSection{ .generic = {} }; }, @@ -17801,7 +17820,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A extra_index += body.len; const cc_ty = try sema.getBuiltinType(block, addrspace_src, "CallingConvention"); - const val = try sema.resolveGenericBody(block, cc_src, body, inst, cc_ty); + const val = try sema.resolveGenericBody(block, cc_src, body, inst, cc_ty, "calling convention must be comptime known"); if (val.tag() == .generic_poison) { break :blk null; } @@ -17809,7 +17828,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A } else if (extra.data.bits.has_cc_ref) blk: { const cc_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; - const cc_tv = sema.resolveInstConst(block, cc_src, cc_ref) catch |err| switch (err) { + const cc_tv = sema.resolveInstConst(block, cc_src, cc_ref, "calling convention must be comptime known") catch |err| switch (err) { error.GenericPoison => { break :blk null; }, @@ -17824,14 +17843,14 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const body = sema.code.extra[extra_index..][0..body_len]; extra_index += body.len; - const val = try sema.resolveGenericBody(block, ret_src, body, inst, Type.type); + const val = try sema.resolveGenericBody(block, ret_src, body, inst, Type.type, "return type must be comptime known"); var buffer: Value.ToTypeBuffer = undefined; const ty = try val.toType(&buffer).copy(sema.arena); break :blk ty; } else if (extra.data.bits.has_ret_ty_ref) blk: { const ret_ty_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); extra_index += 1; - const ret_ty_tv = sema.resolveInstConst(block, ret_src, ret_ty_ref) catch |err| switch (err) { + const ret_ty_tv = sema.resolveInstConst(block, ret_src, ret_ty_ref, "return type must be comptime known") catch |err| switch (err) { error.GenericPoison => { break :blk Type.initTag(.generic_poison); }, @@ -17886,7 +17905,7 @@ fn zirCUndef( const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; - const name = try sema.resolveConstString(block, src, extra.operand); + const name = try sema.resolveConstString(block, src, extra.operand, "name of macro being undefined must be comptime known"); try block.c_import_buf.?.writer().print("#undefine {s}\n", .{name}); return Air.Inst.Ref.void_value; } @@ -17899,7 +17918,7 @@ fn zirCInclude( const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; const src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; - const name = try sema.resolveConstString(block, src, extra.operand); + const name = try sema.resolveConstString(block, src, extra.operand, "path being included must be comptime known"); try block.c_import_buf.?.writer().print("#include <{s}>\n", .{name}); return Air.Inst.Ref.void_value; } @@ -17913,10 +17932,10 @@ fn zirCDefine( const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; const val_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; - const name = try sema.resolveConstString(block, name_src, extra.lhs); + const name = try sema.resolveConstString(block, name_src, extra.lhs, "name of macro being undefined must be comptime known"); const rhs = try sema.resolveInst(extra.rhs); if (sema.typeOf(rhs).zigTypeTag() != .Void) { - const value = try sema.resolveConstString(block, val_src, extra.rhs); + const value = try sema.resolveConstString(block, val_src, extra.rhs, "value of macro being undefined must be comptime known"); try block.c_import_buf.?.writer().print("#define {s} {s}\n", .{ name, value }); } else { try block.c_import_buf.?.writer().print("#define {s}\n", .{name}); @@ -17937,8 +17956,8 @@ fn zirWasmMemorySize( return sema.fail(block, builtin_src, "builtin @wasmMemorySize is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)}); } - const index = @intCast(u32, try sema.resolveInt(block, index_src, extra.operand, Type.u32)); - try sema.requireRuntimeBlock(block, builtin_src); + const index = @intCast(u32, try sema.resolveInt(block, index_src, extra.operand, Type.u32, "wasm memory size index must be comptime known")); + try sema.requireRuntimeBlock(block, builtin_src, null); return block.addInst(.{ .tag = .wasm_memory_size, .data = .{ .pl_op = .{ @@ -17962,10 +17981,10 @@ fn zirWasmMemoryGrow( return sema.fail(block, builtin_src, "builtin @wasmMemoryGrow is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)}); } - const index = @intCast(u32, try sema.resolveInt(block, index_src, extra.lhs, Type.u32)); + const index = @intCast(u32, try sema.resolveInt(block, index_src, extra.lhs, Type.u32, "wasm memory size index must be comptime known")); const delta = try sema.coerce(block, Type.u32, try sema.resolveInst(extra.rhs), delta_src); - try sema.requireRuntimeBlock(block, builtin_src); + try sema.requireRuntimeBlock(block, builtin_src, null); return block.addInst(.{ .tag = .wasm_memory_grow, .data = .{ .pl_op = .{ @@ -17990,15 +18009,15 @@ fn zirPrefetch( const target = sema.mod.getTarget(); const rw = try sema.fieldVal(block, opts_src, options, "rw", opts_src); - const rw_val = try sema.resolveConstValue(block, opts_src, rw); + const rw_val = try sema.resolveConstValue(block, opts_src, rw, "prefetch read/write must be comptime known"); const rw_tag = rw_val.toEnum(std.builtin.PrefetchOptions.Rw); const locality = try sema.fieldVal(block, opts_src, options, "locality", opts_src); - const locality_val = try sema.resolveConstValue(block, opts_src, locality); + const locality_val = try sema.resolveConstValue(block, opts_src, locality, "prefetch locality must be comptime known"); const locality_int = @intCast(u2, locality_val.toUnsignedInt(target)); const cache = try sema.fieldVal(block, opts_src, options, "cache", opts_src); - const cache_val = try sema.resolveConstValue(block, opts_src, cache); + const cache_val = try sema.resolveConstValue(block, opts_src, cache, "prefetch cache must be comptime known"); const cache_tag = cache_val.toEnum(std.builtin.PrefetchOptions.Cache); if (!block.is_comptime) { @@ -18035,16 +18054,16 @@ fn zirBuiltinExtern( const options = try sema.coerce(block, extern_options_ty, options_inst, options_src); const name = try sema.fieldVal(block, options_src, options, "name", options_src); - const name_val = try sema.resolveConstValue(block, options_src, name); + const name_val = try sema.resolveConstValue(block, options_src, name, "name of the extern symbol must be comptime known"); const library_name_inst = try sema.fieldVal(block, options_src, options, "library_name", options_src); - const library_name_val = try sema.resolveConstValue(block, options_src, library_name_inst); + const library_name_val = try sema.resolveConstValue(block, options_src, library_name_inst, "library in which extern symbol is must be comptime known"); const linkage = try sema.fieldVal(block, options_src, options, "linkage", options_src); - const linkage_val = try sema.resolveConstValue(block, options_src, linkage); + const linkage_val = try sema.resolveConstValue(block, options_src, linkage, "linkage of the extern symbol must be comptime known"); const is_thread_local = try sema.fieldVal(block, options_src, options, "is_thread_local", options_src); - const is_thread_local_val = try sema.resolveConstValue(block, options_src, is_thread_local); + const is_thread_local_val = try sema.resolveConstValue(block, options_src, is_thread_local, "threadlocality of the extern symbol must be comptime known"); var library_name: ?[]const u8 = null; if (!library_name_val.isNull()) { @@ -18121,19 +18140,30 @@ fn zirBuiltinExtern( new_decl.value_arena = arena_state; const ref = try sema.analyzeDeclRef(new_decl_index); - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addBitCast(ty, ref); } +/// Asserts that the block is not comptime. fn requireFunctionBlock(sema: *Sema, block: *Block, src: LazySrcLoc) !void { + assert(!block.is_comptime); if (sema.func == null and !block.is_typeof and !block.is_coerce_result_ptr) { return sema.fail(block, src, "instruction illegal outside function body", .{}); } } -fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc) !void { +fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void { if (block.is_comptime) { - return sema.failWithNeededComptime(block, src); + const msg = msg: { + const msg = try sema.errMsg(block, src, "unable to evalutate comptime expression", .{}); + errdefer msg.destroy(sema.gpa); + + if (runtime_src) |some| { + try sema.errNote(block, some, msg, "operation is runtime due to this operand", .{}); + } + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); } try sema.requireFunctionBlock(block, src); } @@ -18972,7 +19002,7 @@ fn fieldPtr( }), ); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addTyOp(.ptr_slice_ptr_ptr, result_ty, inner_ptr); } else if (mem.eql(u8, field_name, "len")) { @@ -18992,7 +19022,7 @@ fn fieldPtr( }), ); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addTyOp(.ptr_slice_len_ptr, result_ty, inner_ptr); } else { @@ -19005,7 +19035,7 @@ fn fieldPtr( } }, .Type => { - _ = try sema.resolveConstValue(block, object_ptr_src, object_ptr); + _ = try sema.resolveConstValue(block, .unneeded, object_ptr, undefined); const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src); const inner = if (is_pointer_to) try sema.analyzeLoad(block, src, result, object_ptr_src) @@ -19238,7 +19268,7 @@ fn finishFieldCallBind( return sema.analyzeLoad(block, src, pointer, src); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); const ptr_inst = try block.addStructFieldPtr(object_ptr, field_index, ptr_field_ty); return sema.analyzeLoad(block, src, ptr_inst, src); } @@ -19425,7 +19455,7 @@ fn structFieldPtrByIndex( ); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addStructFieldPtr(struct_ptr, field_index, ptr_field_ty); } @@ -19469,7 +19499,7 @@ fn structFieldVal( return sema.addConstant(field.ty, field_values[field_index]); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addStructFieldVal(struct_byval, field_index, field.ty); }, else => unreachable, @@ -19533,7 +19563,7 @@ fn tupleFieldValByIndex( return sema.addConstant(field_ty, field_values[field_index]); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addStructFieldVal(tuple_byval, field_index, field_ty); } @@ -19597,7 +19627,7 @@ fn unionFieldPtr( ); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addStructFieldPtr(union_ptr, field_index, ptr_field_ty); } @@ -19655,7 +19685,7 @@ fn unionFieldVal( } } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addStructFieldVal(union_byval, field_index, field.ty); } @@ -19684,7 +19714,7 @@ fn elemPtr( // In all below cases, we have to deref the ptr operand to get the actual indexable pointer. const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src); switch (indexable_ty.ptrSize()) { - .Slice => return sema.elemPtrSlice(block, indexable_ptr_src, indexable, elem_index_src, elem_index), + .Slice => return sema.elemPtrSlice(block, src, indexable_ptr_src, indexable, elem_index_src, elem_index), .Many, .C => { const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_ptr_src, indexable); const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); @@ -19698,19 +19728,19 @@ fn elemPtr( }; const result_ty = try sema.elemPtrType(indexable_ty, null); - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addPtrElemPtr(indexable, elem_index, result_ty); }, .One => { assert(indexable_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable - return sema.elemPtrArray(block, indexable_ptr_src, indexable, elem_index_src, elem_index, init); + return sema.elemPtrArray(block, src, indexable_ptr_src, indexable, elem_index_src, elem_index, init); }, } }, - .Array, .Vector => return sema.elemPtrArray(block, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init), + .Array, .Vector => return sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init), .Struct => { // Tuple field access. - const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index); + const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, "tuple field access index must be comptime known"); const index = @intCast(u32, index_val.toUnsignedInt(target)); return sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index); }, @@ -19740,7 +19770,7 @@ fn elemVal( switch (indexable_ty.zigTypeTag()) { .Pointer => switch (indexable_ty.ptrSize()) { - .Slice => return sema.elemValSlice(block, indexable_src, indexable, elem_index_src, elem_index), + .Slice => return sema.elemValSlice(block, src, indexable_src, indexable, elem_index_src, elem_index), .Many, .C => { const maybe_indexable_val = try sema.resolveDefinedValue(block, indexable_src, indexable); const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); @@ -19756,7 +19786,7 @@ fn elemVal( break :rs indexable_src; }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); return block.addBinOp(.ptr_elem_val, indexable, elem_index); }, .One => { @@ -19765,14 +19795,14 @@ fn elemVal( return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src); }, }, - .Array => return elemValArray(sema, block, indexable_src, indexable, elem_index_src, elem_index), + .Array => return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index), .Vector => { // TODO: If the index is a vector, the result should be a vector. - return elemValArray(sema, block, indexable_src, indexable, elem_index_src, elem_index); + return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index); }, .Struct => { // Tuple field access. - const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index); + const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, "tuple field access index must be comptime known"); const index = @intCast(u32, index_val.toUnsignedInt(target)); return tupleField(sema, block, indexable_src, indexable, elem_index_src, index); }, @@ -19850,7 +19880,7 @@ fn tupleFieldPtr( try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_ptr_src); - try sema.requireRuntimeBlock(block, tuple_ptr_src); + try sema.requireRuntimeBlock(block, tuple_ptr_src, null); return block.addStructFieldPtr(tuple_ptr, field_index, ptr_field_ty); } @@ -19890,13 +19920,14 @@ fn tupleField( try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_src); - try sema.requireRuntimeBlock(block, tuple_src); + try sema.requireRuntimeBlock(block, tuple_src, null); return block.addStructFieldVal(tuple, field_index, field_ty); } fn elemValArray( sema: *Sema, block: *Block, + src: LazySrcLoc, array_src: LazySrcLoc, array: Air.Inst.Ref, elem_index_src: LazySrcLoc, @@ -19943,7 +19974,7 @@ fn elemValArray( try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, array_ty, array_src); const runtime_src = if (maybe_undef_array_val != null) elem_index_src else array_src; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); if (block.wantSafety()) { // Runtime check is only needed if unable to comptime check if (maybe_index_val == null) { @@ -19958,6 +19989,7 @@ fn elemValArray( fn elemPtrArray( sema: *Sema, block: *Block, + src: LazySrcLoc, array_ptr_src: LazySrcLoc, array_ptr: Air.Inst.Ref, elem_index_src: LazySrcLoc, @@ -20003,7 +20035,7 @@ fn elemPtrArray( } const runtime_src = if (maybe_undef_array_ptr_val != null) elem_index_src else array_ptr_src; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); // Runtime check is only needed if unable to comptime check. if (block.wantSafety() and offset == null) { @@ -20018,6 +20050,7 @@ fn elemPtrArray( fn elemValSlice( sema: *Sema, block: *Block, + src: LazySrcLoc, slice_src: LazySrcLoc, slice: Air.Inst.Ref, elem_index_src: LazySrcLoc, @@ -20057,7 +20090,7 @@ fn elemValSlice( try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, slice_ty, slice_src); - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); if (block.wantSafety()) { const len_inst = if (maybe_slice_val) |slice_val| try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(sema.mod)) @@ -20073,6 +20106,7 @@ fn elemValSlice( fn elemPtrSlice( sema: *Sema, block: *Block, + src: LazySrcLoc, slice_src: LazySrcLoc, slice: Air.Inst.Ref, elem_index_src: LazySrcLoc, @@ -20113,7 +20147,7 @@ fn elemPtrSlice( try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ptr_ty, slice_ty, slice_src); const runtime_src = if (maybe_undef_slice_val != null) elem_index_src else slice_src; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); if (block.wantSafety()) { const len_inst = len: { if (maybe_undef_slice_val) |slice_val| @@ -20177,7 +20211,7 @@ fn coerceExtra( // Keep the comptime Value representation; take the new type. return sema.addConstant(dest_ty, val); } - try sema.requireRuntimeBlock(block, inst_src); + try sema.requireRuntimeBlock(block, inst_src, null); return block.addBitCast(dest_ty, inst); } @@ -20222,7 +20256,7 @@ fn coerceExtra( // Function body to function pointer. if (inst_ty.zigTypeTag() == .Fn) { - const fn_val = try sema.resolveConstValue(block, inst_src, inst); + const fn_val = try sema.resolveConstValue(block, .unneeded, inst, undefined); const fn_decl = fn_val.castTag(.function).?.data.owner_decl; const inst_as_ptr = try sema.analyzeDeclRef(fn_decl); return sema.coerce(block, dest_ty, inst_as_ptr, inst_src); @@ -20489,7 +20523,7 @@ fn coerceExtra( // small enough unsigned ints can get casted to large enough signed ints (dst_info.signedness == .signed and dst_info.bits > src_info.bits)) { - try sema.requireRuntimeBlock(block, inst_src); + try sema.requireRuntimeBlock(block, inst_src, null); return block.addTyOp(.intcast, dest_ty, inst); } }, @@ -20500,7 +20534,7 @@ fn coerceExtra( }, .Float, .ComptimeFloat => switch (inst_ty.zigTypeTag()) { .ComptimeFloat => { - const val = try sema.resolveConstValue(block, inst_src, inst); + const val = try sema.resolveConstValue(block, .unneeded, inst, undefined); const result_val = try val.floatCast(sema.arena, dest_ty, target); return try sema.addConstant(dest_ty, result_val); }, @@ -20516,13 +20550,15 @@ fn coerceExtra( ); } return try sema.addConstant(dest_ty, result_val); + } else if (dest_ty.zigTypeTag() == .ComptimeFloat) { + return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_float' must be comptime known"); } // float widening const src_bits = inst_ty.floatBits(target); const dst_bits = dest_ty.floatBits(target); if (dst_bits >= src_bits) { - try sema.requireRuntimeBlock(block, inst_src); + try sema.requireRuntimeBlock(block, inst_src, null); return block.addTyOp(.fpext, dest_ty, inst); } }, @@ -20549,7 +20585,7 @@ fn coerceExtra( .Enum => switch (inst_ty.zigTypeTag()) { .EnumLiteral => { // enum literal to enum - const val = try sema.resolveConstValue(block, inst_src, inst); + const val = try sema.resolveConstValue(block, .unneeded, inst, undefined); const bytes = val.castTag(.enum_literal).?.data; const field_index = dest_ty.enumFieldIndex(bytes) orelse { const msg = msg: { @@ -21712,7 +21748,12 @@ fn storePtr2( return; } - try sema.requireRuntimeBlock(block, runtime_src); + if (block.is_comptime) { + // TODO ideally this would tell why the block is comptime + return sema.fail(block, ptr_src, "cannot store to runtime value in comptime block", .{}); + } + + try sema.requireRuntimeBlock(block, src, runtime_src); try sema.queueFullTypeResolution(elem_ty); if (is_ret) { _ = try block.addBinOp(.store, ptr, operand); @@ -22644,7 +22685,7 @@ fn bitCast( const result_val = try sema.bitCastVal(block, inst_src, val, old_ty, dest_ty, 0); return sema.addConstant(dest_ty, result_val); } - try sema.requireRuntimeBlock(block, inst_src); + try sema.requireRuntimeBlock(block, inst_src, null); return block.addBitCast(dest_ty, inst); } @@ -22727,7 +22768,7 @@ fn coerceArrayPtrToSlice( }); return sema.addConstant(dest_ty, slice_val); } - try sema.requireRuntimeBlock(block, inst_src); + try sema.requireRuntimeBlock(block, inst_src, null); return block.addTyOp(.array_to_slice, dest_ty, inst); } @@ -22743,7 +22784,7 @@ fn coerceCompatiblePtrs( // The comptime Value representation is compatible with both types. return sema.addConstant(dest_ty, val); } - try sema.requireRuntimeBlock(block, inst_src); + try sema.requireRuntimeBlock(block, inst_src, null); return sema.bitCast(block, dest_ty, inst, inst_src); } @@ -22807,7 +22848,7 @@ fn coerceEnumToUnion( })); } - try sema.requireRuntimeBlock(block, inst_src); + try sema.requireRuntimeBlock(block, inst_src, null); if (tag_ty.isNonexhaustiveEnum()) { const msg = msg: { @@ -22947,7 +22988,7 @@ fn coerceArrayLike( // These types share the same comptime value representation. return sema.addConstant(dest_ty, inst_val); } - try sema.requireRuntimeBlock(block, inst_src); + try sema.requireRuntimeBlock(block, inst_src, null); return block.addBitCast(dest_ty, inst); } @@ -22960,8 +23001,9 @@ fn coerceArrayLike( Type.usize, try Value.Tag.int_u64.create(sema.arena, i), ); + const src = inst_src; // TODO better source location const elem_src = inst_src; // TODO better source location - const elem_ref = try elemValArray(sema, block, inst_src, inst, elem_src, index_ref); + const elem_ref = try sema.elemValArray(block, src, inst_src, inst, elem_src, index_ref); const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src); element_refs[i] = coerced; if (runtime_src == null) { @@ -22974,7 +23016,7 @@ fn coerceArrayLike( } if (runtime_src) |rs| { - try sema.requireRuntimeBlock(block, rs); + try sema.requireRuntimeBlock(block, inst_src, rs); return block.addAggregateInit(dest_ty, element_refs); } @@ -23037,7 +23079,7 @@ fn coerceTupleToArray( } if (runtime_src) |rs| { - try sema.requireRuntimeBlock(block, rs); + try sema.requireRuntimeBlock(block, inst_src, rs); return block.addAggregateInit(dest_ty, element_refs); } @@ -23168,7 +23210,7 @@ fn coerceTupleToStruct( } if (runtime_src) |rs| { - try sema.requireRuntimeBlock(block, rs); + try sema.requireRuntimeBlock(block, inst_src, rs); return block.addAggregateInit(struct_ty, field_refs); } @@ -23282,7 +23324,7 @@ fn analyzeRef( )); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); const address_space = target_util.defaultAddressSpace(sema.mod.getTarget(), .local); const ptr_type = try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = operand_ty, @@ -23326,10 +23368,12 @@ fn analyzeLoad( } } - const valid_rt = try sema.validateRunTimeType(block, src, elem_ty, false); - if (!valid_rt) return sema.failWithNeededComptime(block, src); + if (block.is_comptime) { + // TODO ideally this would tell why the block is comptime + return sema.fail(block, ptr_src, "cannot load runtime value in comptime block", .{}); + } - try sema.requireRuntimeBlock(block, src); + try sema.requireFunctionBlock(block, src); return block.addTyOp(.load, elem_ty, ptr); } @@ -23346,7 +23390,7 @@ fn analyzeSlicePtr( if (val.isUndef()) return sema.addConstUndef(result_ty); return sema.addConstant(result_ty, val.slicePtr()); } - try sema.requireRuntimeBlock(block, slice_src); + try sema.requireRuntimeBlock(block, slice_src, null); return block.addTyOp(.slice_ptr, result_ty, slice); } @@ -23362,7 +23406,7 @@ fn analyzeSliceLen( } return sema.addIntUnsigned(Type.usize, slice_val.sliceLen(sema.mod)); } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addTyOp(.slice_len, Type.usize, slice_inst); } @@ -23386,7 +23430,7 @@ fn analyzeIsNull( return Air.Inst.Ref.bool_false; } } - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); const air_tag: Air.Inst.Tag = if (invert_logic) .is_non_null else .is_null; return block.addUnOp(air_tag, operand); } @@ -23476,7 +23520,7 @@ fn analyzeIsNonErr( ) CompileError!Air.Inst.Ref { const result = try sema.analyzeIsNonErrComptimeOnly(block, src, operand); if (result == .none) { - try sema.requireRuntimeBlock(block, src); + try sema.requireRuntimeBlock(block, src, null); return block.addUnOp(.is_non_err, operand); } else { return result; @@ -23661,7 +23705,7 @@ fn analyzeSlice( const sentinel = s: { if (sentinel_opt != .none) { const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src); - break :s try sema.resolveConstValue(block, sentinel_src, casted); + break :s try sema.resolveConstValue(block, sentinel_src, casted, "slice sentinel must be comptime known"); } // If we are slicing to the end of something that is sentinel-terminated // then the resulting slice type is also sentinel-terminated. @@ -23738,7 +23782,14 @@ fn analyzeSlice( .size = .Slice, }); - try sema.requireRuntimeBlock(block, src); + const runtime_src = if ((try sema.resolveMaybeUndefVal(block, ptr_src, ptr_or_slice)) == null) + ptr_src + else if ((try sema.resolveMaybeUndefVal(block, src, start)) == null) + start_src + else + end_src; + + try sema.requireRuntimeBlock(block, src, runtime_src); if (block.wantSafety()) { // requirement: slicing C ptr is non-null if (ptr_ptr_child_ty.isCPtr()) { @@ -23846,7 +23897,7 @@ fn cmpNumeric( // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout // of this function if we don't need to. - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); // For floats, emit a float comparison instruction. const lhs_is_float = switch (lhs_ty_tag) { @@ -24034,7 +24085,7 @@ fn cmpVector( } }; - try sema.requireRuntimeBlock(block, runtime_src); + try sema.requireRuntimeBlock(block, src, runtime_src); const result_ty_inst = try sema.addType(result_ty); return block.addCmpVector(lhs, rhs, op, result_ty_inst); } @@ -24050,7 +24101,7 @@ fn wrapOptional( return sema.addConstant(dest_ty, try Value.Tag.opt_payload.create(sema.arena, val)); } - try sema.requireRuntimeBlock(block, inst_src); + try sema.requireRuntimeBlock(block, inst_src, null); return block.addTyOp(.wrap_optional, dest_ty, inst); } @@ -24066,7 +24117,7 @@ fn wrapErrorUnionPayload( if (try sema.resolveMaybeUndefVal(block, inst_src, coerced)) |val| { return sema.addConstant(dest_ty, try Value.Tag.eu_payload.create(sema.arena, val)); } - try sema.requireRuntimeBlock(block, inst_src); + try sema.requireRuntimeBlock(block, inst_src, null); try sema.queueFullTypeResolution(dest_payload_ty); return block.addTyOp(.wrap_errunion_payload, dest_ty, coerced); } @@ -24122,7 +24173,7 @@ fn wrapErrorUnionSet( return sema.addConstant(dest_ty, val); } - try sema.requireRuntimeBlock(block, inst_src); + try sema.requireRuntimeBlock(block, inst_src, null); const coerced = try sema.coerce(block, dest_err_set_ty, inst, inst_src); return block.addTyOp(.wrap_errunion_err, dest_ty, coerced); } @@ -24140,7 +24191,7 @@ fn unionToTag( if (try sema.resolveMaybeUndefVal(block, un_src, un)) |un_val| { return sema.addConstant(enum_ty, un_val.unionTag()); } - try sema.requireRuntimeBlock(block, un_src); + try sema.requireRuntimeBlock(block, un_src, null); return block.addTyOp(.get_union_tag, enum_ty, un); } @@ -25311,7 +25362,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void const field = &struct_obj.fields.values()[i]; const coerced = try sema.coerce(&block_scope, field.ty, init, src); const default_val = (try sema.resolveMaybeUndefVal(&block_scope, src, coerced)) orelse - return sema.failWithNeededComptime(&block_scope, src); + return sema.failWithNeededComptime(&block_scope, src, "struct field default value must be comptime known"); field.default_val = try default_val.copy(decl_arena_allocator); } } @@ -25504,7 +25555,7 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil if (tag_ref != .none) { const tag_src = src; // TODO better source location const coerced = try sema.coerce(&block_scope, int_tag_ty, tag_ref, tag_src); - const val = try sema.resolveConstValue(&block_scope, tag_src, coerced); + const val = try sema.resolveConstValue(&block_scope, tag_src, coerced, "enum tag value must be comptime known"); last_tag_val = val; // This puts the memory into the union arena, not the enum arena, but @@ -26247,7 +26298,7 @@ pub fn analyzeAddrspace( zir_ref: Zir.Inst.Ref, ctx: AddressSpaceContext, ) !std.builtin.AddressSpace { - const addrspace_tv = try sema.resolveInstConst(block, src, zir_ref); + const addrspace_tv = try sema.resolveInstConst(block, src, zir_ref, "addresspace must be comptime known"); const address_space = addrspace_tv.val.toEnum(std.builtin.AddressSpace); const target = sema.mod.getTarget(); const arch = target.cpu.arch; diff --git a/test/cases/compile_errors/asm_at_compile_time.zig b/test/cases/compile_errors/asm_at_compile_time.zig index 601c89da9e..6edbd18a51 100644 --- a/test/cases/compile_errors/asm_at_compile_time.zig +++ b/test/cases/compile_errors/asm_at_compile_time.zig @@ -14,5 +14,5 @@ fn doSomeAsm() void { // backend=llvm // target=native // -// :6:5: error: unable to resolve comptime value +// :6:5: error: unable to evalutate comptime expression // :2:14: note: called from here diff --git a/test/cases/compile_errors/call method on bound fn referring to var instance.zig b/test/cases/compile_errors/call method on bound fn referring to var instance.zig index 10ff584124..b1f8cca35b 100644 --- a/test/cases/compile_errors/call method on bound fn referring to var instance.zig +++ b/test/cases/compile_errors/call method on bound fn referring to var instance.zig @@ -17,4 +17,4 @@ fn bad(ok: bool) void { // target=native // backend=stage2 // -// :12:18: error: unable to resolve comptime value +// :12:18: error: cannot load runtime value in comptime block diff --git a/test/cases/compile_errors/int-float_conversion_to_comptime_int-float.zig b/test/cases/compile_errors/int-float_conversion_to_comptime_int-float.zig index 0b77f03baa..909e036915 100644 --- a/test/cases/compile_errors/int-float_conversion_to_comptime_int-float.zig +++ b/test/cases/compile_errors/int-float_conversion_to_comptime_int-float.zig @@ -12,4 +12,6 @@ export fn bar() void { // target=native // // :3:35: error: unable to resolve comptime value +// :3:35: note: value being casted to 'comptime_int' must be comptime known // :7:37: error: unable to resolve comptime value +// :7:37: note: value being casted to 'comptime_float' must be comptime known diff --git a/test/cases/compile_errors/non-const_expression_function_call_with_struct_return_value_outside_function.zig b/test/cases/compile_errors/non-const_expression_function_call_with_struct_return_value_outside_function.zig index fc33fa531e..255e5dbb4c 100644 --- a/test/cases/compile_errors/non-const_expression_function_call_with_struct_return_value_outside_function.zig +++ b/test/cases/compile_errors/non-const_expression_function_call_with_struct_return_value_outside_function.zig @@ -14,5 +14,5 @@ export fn entry() usize { return @sizeOf(@TypeOf(a)); } // backend=stage2 // target=native // -// :6:26: error: unable to resolve comptime value +// :6:26: error: cannot store to runtime value in comptime block // :4:17: note: called from here diff --git a/test/cases/compile_errors/non-pure_function_returns_type.zig b/test/cases/compile_errors/non-pure_function_returns_type.zig index ceb3e0507f..01aef02964 100644 --- a/test/cases/compile_errors/non-pure_function_returns_type.zig +++ b/test/cases/compile_errors/non-pure_function_returns_type.zig @@ -21,5 +21,5 @@ export fn function_with_return_type_type() void { // backend=stage2 // target=native // -// :3:7: error: unable to resolve comptime value +// :3:7: error: cannot load runtime value in comptime block // :16:19: note: called from here diff --git a/test/cases/compile_errors/non_constant_expression_in_array_size.zig b/test/cases/compile_errors/non_constant_expression_in_array_size.zig index 8c0854feb0..e702246001 100644 --- a/test/cases/compile_errors/non_constant_expression_in_array_size.zig +++ b/test/cases/compile_errors/non_constant_expression_in_array_size.zig @@ -10,5 +10,5 @@ export fn entry() usize { return @offsetOf(Foo, "y"); } // backend=stage2 // target=native // -// :5:25: error: unable to resolve comptime value +// :5:25: error: cannot load runtime value in comptime block // :2:15: note: called from here diff --git a/test/cases/extern_variable_has_no_type.0.zig b/test/cases/extern_variable_has_no_type.0.zig index 2f03067b3a..046ad039c2 100644 --- a/test/cases/extern_variable_has_no_type.0.zig +++ b/test/cases/extern_variable_has_no_type.0.zig @@ -6,4 +6,4 @@ extern var foo: i32; // error // -// :2:15: error: unable to resolve comptime value +// :2:15: error: cannot load runtime value in comptime block diff --git a/test/cases/x86_64-linux/assert_function.8.zig b/test/cases/x86_64-linux/assert_function.8.zig index ae618cc063..f4377bf880 100644 --- a/test/cases/x86_64-linux/assert_function.8.zig +++ b/test/cases/x86_64-linux/assert_function.8.zig @@ -22,3 +22,4 @@ pub fn assert(ok: bool) void { // error // // :3:21: error: unable to resolve comptime value +// :3:21: note: condition in comptime branch must be comptime known diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index 4817f0502f..44667fbda6 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -51,8 +51,8 @@ pub fn addCases(ctx: *TestContext) !void { \\} \\var y: @import("std").builtin.CallingConvention = .C; , &.{ - ":2:22: error: unable to resolve comptime value", - ":5:26: error: unable to resolve comptime value", + ":2:22: error: cannot load runtime value in comptime block", + ":5:26: error: cannot load runtime value in comptime block", }); } From d851b24180fdf2b622b06e9a35e315541fb10aa1 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 17 Jul 2022 16:13:25 +0300 Subject: [PATCH 06/18] Sema: validate function pointer alignment --- src/Module.zig | 88 ++++++++++++++++++- src/Sema.zig | 35 ++++++-- test/behavior/align.zig | 1 - ...r_function_pointers_is_a_compile_error.zig | 2 +- .../function_alignment_non_power_of_2.zig | 4 +- .../compile_errors/function_ptr_alignment.zig | 25 ++++++ 6 files changed, 142 insertions(+), 13 deletions(-) rename test/cases/compile_errors/{stage1/obj => }/function_alignment_non_power_of_2.zig (56%) create mode 100644 test/cases/compile_errors/function_ptr_alignment.zig diff --git a/src/Module.zig b/src/Module.zig index 2472ca926a..b174b88596 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2348,7 +2348,72 @@ pub const SrcLoc = struct { } } else unreachable; }, - + .node_offset_fn_type_align => |node_off| { + const tree = try src_loc.file_scope.getTree(gpa); + const node_datas = tree.nodes.items(.data); + const node_tags = tree.nodes.items(.tag); + const node = src_loc.declRelativeToNodeIndex(node_off); + var params: [1]Ast.Node.Index = undefined; + const full = switch (node_tags[node]) { + .fn_proto_simple => tree.fnProtoSimple(¶ms, node), + .fn_proto_multi => tree.fnProtoMulti(node), + .fn_proto_one => tree.fnProtoOne(¶ms, node), + .fn_proto => tree.fnProto(node), + .fn_decl => switch (node_tags[node_datas[node].lhs]) { + .fn_proto_simple => tree.fnProtoSimple(¶ms, node_datas[node].lhs), + .fn_proto_multi => tree.fnProtoMulti(node_datas[node].lhs), + .fn_proto_one => tree.fnProtoOne(¶ms, node_datas[node].lhs), + .fn_proto => tree.fnProto(node_datas[node].lhs), + else => unreachable, + }, + else => unreachable, + }; + return nodeToSpan(tree, full.ast.align_expr); + }, + .node_offset_fn_type_addrspace => |node_off| { + const tree = try src_loc.file_scope.getTree(gpa); + const node_datas = tree.nodes.items(.data); + const node_tags = tree.nodes.items(.tag); + const node = src_loc.declRelativeToNodeIndex(node_off); + var params: [1]Ast.Node.Index = undefined; + const full = switch (node_tags[node]) { + .fn_proto_simple => tree.fnProtoSimple(¶ms, node), + .fn_proto_multi => tree.fnProtoMulti(node), + .fn_proto_one => tree.fnProtoOne(¶ms, node), + .fn_proto => tree.fnProto(node), + .fn_decl => switch (node_tags[node_datas[node].lhs]) { + .fn_proto_simple => tree.fnProtoSimple(¶ms, node_datas[node].lhs), + .fn_proto_multi => tree.fnProtoMulti(node_datas[node].lhs), + .fn_proto_one => tree.fnProtoOne(¶ms, node_datas[node].lhs), + .fn_proto => tree.fnProto(node_datas[node].lhs), + else => unreachable, + }, + else => unreachable, + }; + return nodeToSpan(tree, full.ast.addrspace_expr); + }, + .node_offset_fn_type_section => |node_off| { + const tree = try src_loc.file_scope.getTree(gpa); + const node_datas = tree.nodes.items(.data); + const node_tags = tree.nodes.items(.tag); + const node = src_loc.declRelativeToNodeIndex(node_off); + var params: [1]Ast.Node.Index = undefined; + const full = switch (node_tags[node]) { + .fn_proto_simple => tree.fnProtoSimple(¶ms, node), + .fn_proto_multi => tree.fnProtoMulti(node), + .fn_proto_one => tree.fnProtoOne(¶ms, node), + .fn_proto => tree.fnProto(node), + .fn_decl => switch (node_tags[node_datas[node].lhs]) { + .fn_proto_simple => tree.fnProtoSimple(¶ms, node_datas[node].lhs), + .fn_proto_multi => tree.fnProtoMulti(node_datas[node].lhs), + .fn_proto_one => tree.fnProtoOne(¶ms, node_datas[node].lhs), + .fn_proto => tree.fnProto(node_datas[node].lhs), + else => unreachable, + }, + else => unreachable, + }; + return nodeToSpan(tree, full.ast.section_expr); + }, .node_offset_fn_type_cc => |node_off| { const tree = try src_loc.file_scope.getTree(gpa); const node_datas = tree.nodes.items(.data); @@ -2778,6 +2843,24 @@ pub const LazySrcLoc = union(enum) { /// range nodes. The error applies to all of them. /// The Decl is determined contextually. node_offset_switch_range: i32, + /// The source location points to the align expr of a function type + /// expression, found by taking this AST node index offset from the containing + /// Decl AST node, which points to a function type AST node. Next, navigate to + /// the calling convention node. + /// The Decl is determined contextually. + node_offset_fn_type_align: i32, + /// The source location points to the addrspace expr of a function type + /// expression, found by taking this AST node index offset from the containing + /// Decl AST node, which points to a function type AST node. Next, navigate to + /// the calling convention node. + /// The Decl is determined contextually. + node_offset_fn_type_addrspace: i32, + /// The source location points to the linksection expr of a function type + /// expression, found by taking this AST node index offset from the containing + /// Decl AST node, which points to a function type AST node. Next, navigate to + /// the calling convention node. + /// The Decl is determined contextually. + node_offset_fn_type_section: i32, /// The source location points to the calling convention of a function type /// expression, found by taking this AST node index offset from the containing /// Decl AST node, which points to a function type AST node. Next, navigate to @@ -2897,6 +2980,9 @@ pub const LazySrcLoc = union(enum) { .node_offset_switch_operand, .node_offset_switch_special_prong, .node_offset_switch_range, + .node_offset_fn_type_align, + .node_offset_fn_type_addrspace, + .node_offset_fn_type_section, .node_offset_fn_type_cc, .node_offset_fn_type_ret_ty, .node_offset_anyframe_type, diff --git a/src/Sema.zig b/src/Sema.zig index ef3cd8a04c..0a6bd12284 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1884,13 +1884,22 @@ fn analyzeAsAlign( ) !u32 { const alignment_big = try sema.analyzeAsInt(block, src, air_ref, align_ty, "alignment must be comptime known"); const alignment = @intCast(u32, alignment_big); // We coerce to u16 in the prev line. + try sema.validateAlign(block, src, alignment); + return alignment; +} + +fn validateAlign( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + alignment: u32, +) !void { if (alignment == 0) return sema.fail(block, src, "alignment must be >= 1", .{}); if (!std.math.isPowerOfTwo(alignment)) { return sema.fail(block, src, "alignment value '{d}' is not a power of two", .{ alignment, }); } - return alignment; } pub fn resolveAlign( @@ -13902,8 +13911,9 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air break :blk 0; } } - const abi_align = (try val.getUnsignedIntAdvanced(target, sema.kit(block, align_src))).?; - break :blk @intCast(u32, abi_align); + const abi_align = @intCast(u32, (try val.getUnsignedIntAdvanced(target, sema.kit(block, align_src))).?); + try sema.validateAlign(block, align_src, abi_align); + break :blk abi_align; } else 0; const address_space = if (inst_data.flags.has_addrspace) blk: { @@ -13940,6 +13950,14 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air if (elem_ty.zigTypeTag() == .NoReturn) { return sema.fail(block, elem_ty_src, "pointer to noreturn not allowed", .{}); + } else if (elem_ty.zigTypeTag() == .Fn) { + if (inst_data.size != .One) { + return sema.fail(block, elem_ty_src, "function pointers must be single pointers", .{}); + } + const fn_align = elem_ty.abiAlignment(target); + if (inst_data.flags.has_align and abi_align != 0 and abi_align != fn_align) { + return sema.fail(block, align_src, "function pointer alignment disagrees with function alignment", .{}); + } } else if (inst_data.size == .Many and elem_ty.zigTypeTag() == .Opaque) { return sema.fail(block, elem_ty_src, "unknown-length pointer to opaque not allowed", .{}); } else if (inst_data.size == .C) { @@ -17709,15 +17727,14 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[inst].pl_node; - const src = inst_data.src(); const extra = sema.code.extraData(Zir.Inst.FuncFancy, inst_data.payload_index); const target = sema.mod.getTarget(); - const align_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at align - const addrspace_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at addrspace - const section_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at section + const align_src: LazySrcLoc = .{ .node_offset_fn_type_align = inst_data.src_node }; + const addrspace_src: LazySrcLoc = .{ .node_offset_fn_type_addrspace = inst_data.src_node }; + const section_src: LazySrcLoc = .{ .node_offset_fn_type_section = inst_data.src_node }; const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = inst_data.src_node }; - const ret_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at the return type + const ret_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = inst_data.src_node }; var extra_index: usize = extra.end; @@ -17742,6 +17759,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A break :blk null; } const alignment = @intCast(u32, val.toUnsignedInt(target)); + try sema.validateAlign(block, align_src, alignment); if (alignment == target_util.defaultFunctionAlignment(target)) { break :blk 0; } else { @@ -17757,6 +17775,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A else => |e| return e, }; const alignment = @intCast(u32, align_tv.val.toUnsignedInt(target)); + try sema.validateAlign(block, align_src, alignment); if (alignment == target_util.defaultFunctionAlignment(target)) { break :blk 0; } else { diff --git a/test/behavior/align.zig b/test/behavior/align.zig index b0df7c6523..ad9a6d9616 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -299,7 +299,6 @@ test "implicitly decreasing fn alignment" { try testImplicitlyDecreaseFnAlign(alignedBig, 5678); } -// TODO make it a compile error to put align on the fn proto instead of on the ptr fn testImplicitlyDecreaseFnAlign(ptr: *align(1) const fn () i32, answer: i32) !void { try expect(ptr() == answer); } diff --git a/test/cases/compile_errors/align_n_expr_function_pointers_is_a_compile_error.zig b/test/cases/compile_errors/align_n_expr_function_pointers_is_a_compile_error.zig index 7ceed52edf..ca6a3a6eee 100644 --- a/test/cases/compile_errors/align_n_expr_function_pointers_is_a_compile_error.zig +++ b/test/cases/compile_errors/align_n_expr_function_pointers_is_a_compile_error.zig @@ -6,4 +6,4 @@ export fn foo() align(1) void { // backend=stage2 // target=wasm32-freestanding-none // -// :1:8: error: 'align' is not allowed on functions in wasm +// :1:23: error: 'align' is not allowed on functions in wasm \ No newline at end of file diff --git a/test/cases/compile_errors/stage1/obj/function_alignment_non_power_of_2.zig b/test/cases/compile_errors/function_alignment_non_power_of_2.zig similarity index 56% rename from test/cases/compile_errors/stage1/obj/function_alignment_non_power_of_2.zig rename to test/cases/compile_errors/function_alignment_non_power_of_2.zig index 617939a120..11d6768dfd 100644 --- a/test/cases/compile_errors/stage1/obj/function_alignment_non_power_of_2.zig +++ b/test/cases/compile_errors/function_alignment_non_power_of_2.zig @@ -2,7 +2,7 @@ extern fn foo() align(3) void; export fn entry() void { return foo(); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:1:23: error: alignment value 3 is not a power of 2 +// :1:23: error: alignment value '3' is not a power of two diff --git a/test/cases/compile_errors/function_ptr_alignment.zig b/test/cases/compile_errors/function_ptr_alignment.zig new file mode 100644 index 0000000000..e242e666f2 --- /dev/null +++ b/test/cases/compile_errors/function_ptr_alignment.zig @@ -0,0 +1,25 @@ +comptime { + var a: *align(2) @TypeOf(foo) = undefined; + _ = a; +} +fn foo() void {} + +comptime { + var a: *align(1) fn () void = undefined; + _ = a; +} +comptime { + var a: *align(2) fn () align(2) void = undefined; + _ = a; +} +comptime { + var a: *align(2) fn () void = undefined; + _ = a; +} + +// error +// backend=stage2 +// target=native +// +// :2:19: error: function pointer alignment disagrees with function alignment +// :16:19: error: function pointer alignment disagrees with function alignment From 8feb3987608945040c955bd7b24be3841ebf74ac Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 17 Jul 2022 17:03:52 +0300 Subject: [PATCH 07/18] Sema: validate function parameter types and return type --- src/Module.zig | 11 +++ src/Sema.zig | 98 ++++++++++++++++--- src/type.zig | 22 ++++- .../obj => }/function_parameter_is_opaque.zig | 11 ++- .../function_returning_opaque_type.zig | 19 ++++ ...h_non-extern_non-packed_enum_parameter.zig | 11 +++ ...non-extern_non-packed_struct_parameter.zig | 14 +++ ..._non-extern_non-packed_union_parameter.zig | 14 +++ .../obj/function_returning_opaque_type.zig | 19 ---- ...h_non-extern_non-packed_enum_parameter.zig | 8 -- ...non-extern_non-packed_struct_parameter.zig | 12 --- ..._non-extern_non-packed_union_parameter.zig | 12 --- 12 files changed, 180 insertions(+), 71 deletions(-) rename test/cases/compile_errors/{stage1/obj => }/function_parameter_is_opaque.zig (57%) create mode 100644 test/cases/compile_errors/function_returning_opaque_type.zig create mode 100644 test/cases/compile_errors/function_with_non-extern_non-packed_enum_parameter.zig create mode 100644 test/cases/compile_errors/function_with_non-extern_non-packed_struct_parameter.zig create mode 100644 test/cases/compile_errors/function_with_non-extern_non-packed_union_parameter.zig delete mode 100644 test/cases/compile_errors/stage1/obj/function_returning_opaque_type.zig delete mode 100644 test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_enum_parameter.zig delete mode 100644 test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_struct_parameter.zig delete mode 100644 test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_union_parameter.zig diff --git a/src/Module.zig b/src/Module.zig index b174b88596..c9d956edcf 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2439,6 +2439,7 @@ pub const SrcLoc = struct { .node_offset_fn_type_ret_ty => |node_off| { const tree = try src_loc.file_scope.getTree(gpa); + const node_datas = tree.nodes.items(.data); const node_tags = tree.nodes.items(.tag); const node = src_loc.declRelativeToNodeIndex(node_off); var params: [1]Ast.Node.Index = undefined; @@ -2447,6 +2448,16 @@ pub const SrcLoc = struct { .fn_proto_multi => tree.fnProtoMulti(node), .fn_proto_one => tree.fnProtoOne(¶ms, node), .fn_proto => tree.fnProto(node), + .fn_decl => blk: { + const fn_proto = node_datas[node].lhs; + break :blk switch (node_tags[fn_proto]) { + .fn_proto_simple => tree.fnProtoSimple(¶ms, fn_proto), + .fn_proto_multi => tree.fnProtoMulti(fn_proto), + .fn_proto_one => tree.fnProtoOne(¶ms, fn_proto), + .fn_proto => tree.fnProto(fn_proto), + else => unreachable, + }; + }, else => unreachable, }; return nodeToSpan(tree, full.ast.return_type); diff --git a/src/Sema.zig b/src/Sema.zig index 0a6bd12284..166e5592c8 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7243,7 +7243,10 @@ fn funcCommon( break :new_func new_func; } destroy_fn_on_error = true; - break :new_func try sema.gpa.create(Module.Fn); + const new_func = try sema.gpa.create(Module.Fn); + // Set this here so that the inferred return type can be printed correctly if it appears in an error. + new_func.owner_decl = sema.owner_decl_index; + break :new_func new_func; }; errdefer if (destroy_fn_on_error) sema.gpa.destroy(new_func); @@ -7277,18 +7280,67 @@ fn funcCommon( } } + // These locals are pulled out from the init expression below to work around + // a stage1 compiler bug. + // In the case of generic calling convention, or generic alignment, we use + // default values which are only meaningful for the generic function, *not* + // the instantiation, which can depend on comptime parameters. + // Related proposal: https://github.com/ziglang/zig/issues/11834 + const cc_workaround = cc orelse .Unspecified; + const align_workaround = alignment orelse 0; + const param_types = try sema.arena.alloc(Type, block.params.items.len); const comptime_params = try sema.arena.alloc(bool, block.params.items.len); for (block.params.items) |param, i| { const param_src = LazySrcLoc.nodeOffset(src_node_offset); // TODO better soruce location param_types[i] = param.ty; - comptime_params[i] = param.is_comptime or - try sema.typeRequiresComptime(block, param_src, param.ty); + const requires_comptime = try sema.typeRequiresComptime(block, param_src, param.ty); + comptime_params[i] = param.is_comptime or requires_comptime; is_generic = is_generic or comptime_params[i] or param.ty.tag() == .generic_poison; if (is_extern and is_generic) { // TODO add note: function is generic because of this parameter return sema.fail(block, param_src, "extern function cannot be generic", .{}); } + if (!param.ty.isValidParamType()) { + const opaque_str = if (param.ty.zigTypeTag() == .Opaque) "opaque " else ""; + const msg = msg: { + const msg = try sema.errMsg(block, param_src, "parameter of {s}type '{}' not allowed", .{ + opaque_str, param.ty.fmt(sema.mod), + }); + errdefer msg.destroy(sema.gpa); + + try sema.addDeclaredHereNote(msg, param.ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + if (!Type.fnCallingConventionAllowsZigTypes(cc_workaround) and !(try sema.validateExternType(param.ty, .param_ty))) { + const msg = msg: { + const msg = try sema.errMsg(block, param_src, "parameter of type '{}' not allowed in function with calling convention '{s}'", .{ + param.ty.fmt(sema.mod), @tagName(cc_workaround), + }); + errdefer msg.destroy(sema.gpa); + + const src_decl = sema.mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsNotExtern(block, param_src, msg, param_src.toSrcLoc(src_decl), param.ty, .param_ty); + + try sema.addDeclaredHereNote(msg, param.ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + if (requires_comptime and !param.is_comptime) { + const msg = msg: { + const msg = try sema.errMsg(block, param_src, "parametter of type '{}' must be declared comptime", .{ + param.ty.fmt(sema.mod), + }); + errdefer msg.destroy(sema.gpa); + + try sema.addDeclaredHereNote(msg, param.ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } } const ret_poison = if (!is_generic) rp: { @@ -7318,14 +7370,34 @@ fn funcCommon( }); }; - // These locals are pulled out from the init expression below to work around - // a stage1 compiler bug. - // In the case of generic calling convention, or generic alignment, we use - // default values which are only meaningful for the generic function, *not* - // the instantiation, which can depend on comptime parameters. - // Related proposal: https://github.com/ziglang/zig/issues/11834 - const cc_workaround = cc orelse .Unspecified; - const align_workaround = alignment orelse 0; + if (!bare_return_type.isValidReturnType()) { + const opaque_str = if (bare_return_type.zigTypeTag() == .Opaque) "opaque " else ""; + const msg = msg: { + const msg = try sema.errMsg(block, ret_ty_src, "{s}return type '{}' not allowed", .{ + opaque_str, bare_return_type.fmt(sema.mod), + }); + errdefer msg.destroy(sema.gpa); + + try sema.addDeclaredHereNote(msg, bare_return_type); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + if (!Type.fnCallingConventionAllowsZigTypes(cc_workaround) and !(try sema.validateExternType(return_type, .ret_ty))) { + const msg = msg: { + const msg = try sema.errMsg(block, ret_ty_src, "return type '{}' not allowed in function with calling convention '{s}'", .{ + return_type.fmt(sema.mod), @tagName(cc_workaround), + }); + errdefer msg.destroy(sema.gpa); + + const src_decl = sema.mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsNotExtern(block, ret_ty_src, msg, ret_ty_src.toSrcLoc(src_decl), return_type, .ret_ty); + + try sema.addDeclaredHereNote(msg, return_type); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } const arch = sema.mod.getTarget().cpu.arch; if (switch (cc_workaround) { @@ -18399,7 +18471,7 @@ fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileEr .BoundFn, .Frame, => return false, - .Void => return position == .union_field, + .Void => return position == .union_field or position == .ret_ty, .NoReturn => return position == .ret_ty, .Opaque, .Bool, @@ -18411,7 +18483,7 @@ fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileEr 8, 16, 32, 64, 128 => return true, else => return false, }, - .Fn => return !ty.fnCallingConventionAllowsZigTypes(), + .Fn => return !Type.fnCallingConventionAllowsZigTypes(ty.fnCallingConvention()), .Enum => { var buf: Type.Payload.Bits = undefined; return sema.validateExternType(ty.intTagType(&buf), position); diff --git a/src/type.zig b/src/type.zig index 59f0668770..bdddaab070 100644 --- a/src/type.zig +++ b/src/type.zig @@ -4643,13 +4643,27 @@ pub const Type = extern union { } /// Asserts the type is a function. - pub fn fnCallingConventionAllowsZigTypes(self: Type) bool { - return switch (self.fnCallingConvention()) { + pub fn fnCallingConventionAllowsZigTypes(cc: std.builtin.CallingConvention) bool { + return switch (cc) { .Unspecified, .Async, .Inline, .PtxKernel => true, else => false, }; } + pub fn isValidParamType(self: Type) bool { + return switch (self.zigTypeTagOrPoison() catch return true) { + .Undefined, .Null, .Opaque, .NoReturn => false, + else => true, + }; + } + + pub fn isValidReturnType(self: Type) bool { + return switch (self.zigTypeTagOrPoison() catch return true) { + .Undefined, .Null, .Opaque => false, + else => true, + }; + } + /// Asserts the type is a function. pub fn fnIsVarArgs(self: Type) bool { return switch (self.tag()) { @@ -5650,6 +5664,10 @@ pub const Type = extern union { const union_obj = ty.cast(Payload.Union).?.data; return union_obj.srcLoc(mod); }, + .@"opaque" => { + const opaque_obj = ty.cast(Payload.Opaque).?.data; + return opaque_obj.srcLoc(mod); + }, .atomic_order, .atomic_rmw_op, .calling_convention, diff --git a/test/cases/compile_errors/stage1/obj/function_parameter_is_opaque.zig b/test/cases/compile_errors/function_parameter_is_opaque.zig similarity index 57% rename from test/cases/compile_errors/stage1/obj/function_parameter_is_opaque.zig rename to test/cases/compile_errors/function_parameter_is_opaque.zig index 5b52370d86..31c477e8bc 100644 --- a/test/cases/compile_errors/stage1/obj/function_parameter_is_opaque.zig +++ b/test/cases/compile_errors/function_parameter_is_opaque.zig @@ -20,10 +20,11 @@ export fn entry4() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:3:28: error: parameter of opaque type 'FooType' not allowed -// tmp.zig:8:28: error: parameter of type '@Type(.Null)' not allowed -// tmp.zig:12:11: error: parameter of opaque type 'FooType' not allowed -// tmp.zig:17:11: error: parameter of type '@Type(.Null)' not allowed +// :3:24: error: parameter of opaque type 'tmp.FooType' not allowed +// :1:17: note: opaque declared here +// :8:24: error: parameter of type '@TypeOf(null)' not allowed +// :12:1: error: parameter of opaque type 'tmp.FooType' not allowed +// :17:1: error: parameter of type '@TypeOf(null)' not allowed diff --git a/test/cases/compile_errors/function_returning_opaque_type.zig b/test/cases/compile_errors/function_returning_opaque_type.zig new file mode 100644 index 0000000000..caf5d74d40 --- /dev/null +++ b/test/cases/compile_errors/function_returning_opaque_type.zig @@ -0,0 +1,19 @@ +const FooType = opaque {}; +export fn bar() !FooType { + return error.InvalidValue; +} +export fn bav() !@TypeOf(null) { + return error.InvalidValue; +} +export fn baz() !@TypeOf(undefined) { + return error.InvalidValue; +} + +// error +// backend=stage2 +// target=native +// +// :2:18: error: opaque return type 'tmp.FooType' not allowed +// :1:17: note: opaque declared here +// :5:18: error: return type '@TypeOf(null)' not allowed +// :8:18: error: return type '@TypeOf(undefined)' not allowed diff --git a/test/cases/compile_errors/function_with_non-extern_non-packed_enum_parameter.zig b/test/cases/compile_errors/function_with_non-extern_non-packed_enum_parameter.zig new file mode 100644 index 0000000000..0cac6e1e0c --- /dev/null +++ b/test/cases/compile_errors/function_with_non-extern_non-packed_enum_parameter.zig @@ -0,0 +1,11 @@ +const Foo = enum { A, B, C }; +export fn entry(foo: Foo) void { _ = foo; } + +// error +// backend=stage2 +// target=native +// +// :2:8: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'C' +// :2:8: note: enum tag type 'u2' is not extern compatible +// :2:8: note: only integers with power of two bits are extern compatible +// :1:13: note: enum declared here diff --git a/test/cases/compile_errors/function_with_non-extern_non-packed_struct_parameter.zig b/test/cases/compile_errors/function_with_non-extern_non-packed_struct_parameter.zig new file mode 100644 index 0000000000..8ae4ef7523 --- /dev/null +++ b/test/cases/compile_errors/function_with_non-extern_non-packed_struct_parameter.zig @@ -0,0 +1,14 @@ +const Foo = struct { + A: i32, + B: f32, + C: bool, +}; +export fn entry(foo: Foo) void { _ = foo; } + +// error +// backend=stage2 +// target=native +// +// :6:8: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'C' +// :6:8: note: only structs with packed or extern layout are extern compatible +// :1:13: note: struct declared here diff --git a/test/cases/compile_errors/function_with_non-extern_non-packed_union_parameter.zig b/test/cases/compile_errors/function_with_non-extern_non-packed_union_parameter.zig new file mode 100644 index 0000000000..a4a96d9172 --- /dev/null +++ b/test/cases/compile_errors/function_with_non-extern_non-packed_union_parameter.zig @@ -0,0 +1,14 @@ +const Foo = union { + A: i32, + B: f32, + C: bool, +}; +export fn entry(foo: Foo) void { _ = foo; } + +// error +// backend=stage2 +// target=native +// +// :6:8: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'C' +// :6:8: note: only unions with packed or extern layout are extern compatible +// :1:13: note: union declared here diff --git a/test/cases/compile_errors/stage1/obj/function_returning_opaque_type.zig b/test/cases/compile_errors/stage1/obj/function_returning_opaque_type.zig deleted file mode 100644 index e2c084b517..0000000000 --- a/test/cases/compile_errors/stage1/obj/function_returning_opaque_type.zig +++ /dev/null @@ -1,19 +0,0 @@ -const FooType = opaque {}; -export fn bar() !FooType { - return error.InvalidValue; -} -export fn bav() !@TypeOf(null) { - return error.InvalidValue; -} -export fn baz() !@TypeOf(undefined) { - return error.InvalidValue; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:18: error: Opaque return type 'FooType' not allowed -// tmp.zig:1:1: note: type declared here -// tmp.zig:5:18: error: Null return type '@Type(.Null)' not allowed -// tmp.zig:8:18: error: Undefined return type '@Type(.Undefined)' not allowed diff --git a/test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_enum_parameter.zig b/test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_enum_parameter.zig deleted file mode 100644 index 8de26c08b8..0000000000 --- a/test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_enum_parameter.zig +++ /dev/null @@ -1,8 +0,0 @@ -const Foo = enum { A, B, C }; -export fn entry(foo: Foo) void { _ = foo; } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:2:22: error: parameter of type 'Foo' not allowed in function with calling convention 'C' diff --git a/test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_struct_parameter.zig b/test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_struct_parameter.zig deleted file mode 100644 index ecb4a36267..0000000000 --- a/test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_struct_parameter.zig +++ /dev/null @@ -1,12 +0,0 @@ -const Foo = struct { - A: i32, - B: f32, - C: bool, -}; -export fn entry(foo: Foo) void { _ = foo; } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'C' diff --git a/test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_union_parameter.zig b/test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_union_parameter.zig deleted file mode 100644 index bfe2dc6cfa..0000000000 --- a/test/cases/compile_errors/stage1/obj/function_with_non-extern_non-packed_union_parameter.zig +++ /dev/null @@ -1,12 +0,0 @@ -const Foo = union { - A: i32, - B: f32, - C: bool, -}; -export fn entry(foo: Foo) void { _ = foo; } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'C' From 1705a21f804a79d5267a198dc28d8a91dc0dc201 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 19 Jul 2022 01:25:10 +0300 Subject: [PATCH 08/18] Sema: more union and enum tag type validation --- src/AstGen.zig | 21 +++-- src/Module.zig | 31 ++++++- src/Sema.zig | 84 +++++++++++-------- .../extern_union_given_enum_tag_type.zig | 4 +- .../non-enum_tag_type_passed_to_union.zig | 4 +- ...teger_tag_type_to_automatic_union_enum.zig | 4 +- .../non-integer_tag_type_to_enum.zig | 13 +++ .../stage2/union_enum_field_missing.zig | 2 +- .../stage2/union_extra_field.zig | 2 +- ...h_expression-missing_enumeration_prong.zig | 2 +- ...ch_on_enum_with_1_field_with_no_prongs.zig | 2 +- .../switching_with_non-exhaustive_enums.zig | 2 +- .../union_with_specified_enum_omits_field.zig | 2 +- test/stage2/cbe.zig | 2 +- 14 files changed, 119 insertions(+), 56 deletions(-) rename test/cases/compile_errors/{stage1/obj => }/extern_union_given_enum_tag_type.zig (72%) rename test/cases/compile_errors/{stage1/obj => }/non-enum_tag_type_passed_to_union.zig (66%) rename test/cases/compile_errors/{stage1/obj => }/non-integer_tag_type_to_automatic_union_enum.zig (66%) create mode 100644 test/cases/compile_errors/non-integer_tag_type_to_enum.zig diff --git a/src/AstGen.zig b/src/AstGen.zig index 9941fe54e8..c4121e0a20 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -4299,7 +4299,7 @@ fn unionDeclInner( members: []const Ast.Node.Index, layout: std.builtin.Type.ContainerLayout, arg_node: Ast.Node.Index, - have_auto_enum: bool, + auto_enum_tok: ?Ast.TokenIndex, ) InnerError!Zir.Inst.Ref { const decl_inst = try gz.reserveInstructionIndex(); @@ -4333,6 +4333,15 @@ fn unionDeclInner( const decl_count = try astgen.scanDecls(&namespace, members); const field_count = @intCast(u32, members.len - decl_count); + if (layout != .Auto and (auto_enum_tok != null or arg_node != 0)) { + const layout_str = if (layout == .Extern) "extern" else "packed"; + if (arg_node != 0) { + return astgen.failNode(arg_node, "{s} union does not support enum tag type", .{layout_str}); + } else { + return astgen.failTok(auto_enum_tok.?, "{s} union does not support enum tag type", .{layout_str}); + } + } + const arg_inst: Zir.Inst.Ref = if (arg_node != 0) try typeExpr(&block_scope, &namespace.base, arg_node) else @@ -4367,7 +4376,7 @@ fn unionDeclInner( if (have_type) { const field_type = try typeExpr(&block_scope, &namespace.base, member.ast.type_expr); wip_members.appendToField(@enumToInt(field_type)); - } else if (arg_inst == .none and !have_auto_enum) { + } else if (arg_inst == .none and auto_enum_tok == null) { return astgen.failNode(member_node, "union field missing type", .{}); } if (have_align) { @@ -4389,7 +4398,7 @@ fn unionDeclInner( }, ); } - if (!have_auto_enum) { + if (auto_enum_tok == null) { return astgen.failNodeNotes( node, "explicitly valued tagged union requires inferred enum tag type", @@ -4425,7 +4434,7 @@ fn unionDeclInner( .body_len = body_len, .fields_len = field_count, .decls_len = decl_count, - .auto_enum_tag = have_auto_enum, + .auto_enum_tag = auto_enum_tok != null, }); wip_members.finishBits(bits_per_field); @@ -4481,9 +4490,7 @@ fn containerDecl( else => unreachable, } else std.builtin.Type.ContainerLayout.Auto; - const have_auto_enum = container_decl.ast.enum_token != null; - - const result = try unionDeclInner(gz, scope, node, container_decl.ast.members, layout, container_decl.ast.arg, have_auto_enum); + const result = try unionDeclInner(gz, scope, node, container_decl.ast.members, layout, container_decl.ast.arg, container_decl.ast.enum_token); return rvalue(gz, rl, result, node); }, .keyword_enum => { diff --git a/src/Module.zig b/src/Module.zig index c9d956edcf..fd8c217bd1 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2626,6 +2626,29 @@ pub const SrcLoc = struct { }; return nodeToSpan(tree, full.ast.bit_range_end); }, + .node_offset_container_tag => |node_off| { + const tree = try src_loc.file_scope.getTree(gpa); + const node_tags = tree.nodes.items(.tag); + const parent_node = src_loc.declRelativeToNodeIndex(node_off); + + switch (node_tags[parent_node]) { + .container_decl_arg, .container_decl_arg_trailing => { + const full = tree.containerDeclArg(parent_node); + return nodeToSpan(tree, full.ast.arg); + }, + .tagged_union_enum_tag, .tagged_union_enum_tag_trailing => { + const full = tree.taggedUnionEnumTag(parent_node); + + return tokensToSpan( + tree, + tree.firstToken(full.ast.arg) - 2, + tree.lastToken(full.ast.arg) + 1, + tree.nodes.items(.main_token)[full.ast.arg], + ); + }, + else => unreachable, + } + }, } } @@ -2935,6 +2958,9 @@ pub const LazySrcLoc = union(enum) { /// The source location points to the host size of a pointer. /// The Decl is determined contextually. node_offset_ptr_hostsize: i32, + /// The source location points to the tag type of an union or an enum. + /// The Decl is determined contextually. + node_offset_container_tag: i32, pub const nodeOffset = if (TracedOffset.want_tracing) nodeOffsetDebug else nodeOffsetRelease; @@ -3008,6 +3034,7 @@ pub const LazySrcLoc = union(enum) { .node_offset_ptr_addrspace, .node_offset_ptr_bitoffset, .node_offset_ptr_hostsize, + .node_offset_container_tag, => .{ .file_scope = decl.getFileScope(), .parent_decl_node = decl.src_node, @@ -4711,7 +4738,7 @@ pub fn scanNamespace( extra_start: usize, decls_len: u32, parent_decl: *Decl, -) SemaError!usize { +) Allocator.Error!usize { const tracy = trace(@src()); defer tracy.end(); @@ -4758,7 +4785,7 @@ const ScanDeclIter = struct { unnamed_test_index: usize = 0, }; -fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!void { +fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Error!void { const tracy = trace(@src()); defer tracy.end(); diff --git a/src/Sema.zig b/src/Sema.zig index 166e5592c8..af44ffcedd 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2344,6 +2344,7 @@ fn zirEnumDecl( extra_index += 1; break :blk LazySrcLoc.nodeOffset(node_offset); } else sema.src; + const tag_ty_src: LazySrcLoc = .{ .node_offset_container_tag = src.node_offset.x }; const tag_type_ref = if (small.has_tag_type) blk: { const tag_type_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]); @@ -2369,8 +2370,10 @@ fn zirEnumDecl( break :blk decls_len; } else 0; + var done = false; + var new_decl_arena = std.heap.ArenaAllocator.init(gpa); - errdefer new_decl_arena.deinit(); + errdefer if (!done) new_decl_arena.deinit(); const new_decl_arena_allocator = new_decl_arena.allocator(); const enum_obj = try new_decl_arena_allocator.create(Module.EnumFull); @@ -2387,7 +2390,7 @@ fn zirEnumDecl( }, small.name_strategy, "enum", inst); const new_decl = mod.declPtr(new_decl_index); new_decl.owns_tv = true; - errdefer mod.abortAnonDecl(new_decl_index); + errdefer if (!done) mod.abortAnonDecl(new_decl_index); enum_obj.* = .{ .owner_decl = new_decl_index, @@ -2406,19 +2409,28 @@ fn zirEnumDecl( &enum_obj.namespace, new_decl, new_decl.name, }); + try new_decl.finalizeNewArena(&new_decl_arena); + const decl_val = try sema.analyzeDeclVal(block, src, new_decl_index); + done = true; + + var decl_arena = new_decl.value_arena.?.promote(gpa); + defer new_decl.value_arena.?.* = decl_arena.state; + const decl_arena_allocator = decl_arena.allocator(); + extra_index = try mod.scanNamespace(&enum_obj.namespace, extra_index, decls_len, new_decl); const body = sema.code.extra[extra_index..][0..body_len]; if (fields_len == 0) { assert(body.len == 0); if (tag_type_ref != .none) { - // TODO better source location - const ty = try sema.resolveType(block, src, tag_type_ref); + const ty = try sema.resolveType(block, tag_ty_src, tag_type_ref); + if (ty.zigTypeTag() != .Int and ty.zigTypeTag() != .ComptimeInt) { + return sema.fail(block, tag_ty_src, "expected integer tag type, found '{}'", .{ty.fmt(sema.mod)}); + } enum_obj.tag_ty = try ty.copy(new_decl_arena_allocator); enum_obj.tag_ty_inferred = false; } - try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl_index); + return decl_val; } extra_index += body.len; @@ -2471,13 +2483,15 @@ fn zirEnumDecl( try wip_captures.finalize(); if (tag_type_ref != .none) { - // TODO better source location - const ty = try sema.resolveType(block, src, tag_type_ref); - enum_obj.tag_ty = try ty.copy(new_decl_arena_allocator); + const ty = try sema.resolveType(block, tag_ty_src, tag_type_ref); + if (ty.zigTypeTag() != .Int and ty.zigTypeTag() != .ComptimeInt) { + return sema.fail(block, tag_ty_src, "expected integer tag type, found '{}'", .{ty.fmt(sema.mod)}); + } + enum_obj.tag_ty = try ty.copy(decl_arena_allocator); enum_obj.tag_ty_inferred = false; } else { const bits = std.math.log2_int_ceil(usize, fields_len); - enum_obj.tag_ty = try Type.Tag.int_unsigned.create(new_decl_arena_allocator, bits); + enum_obj.tag_ty = try Type.Tag.int_unsigned.create(decl_arena_allocator, bits); enum_obj.tag_ty_inferred = true; } } @@ -2488,12 +2502,12 @@ fn zirEnumDecl( } } - try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); + try enum_obj.fields.ensureTotalCapacity(decl_arena_allocator, fields_len); const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| { if (bag != 0) break true; } else false; if (any_values) { - try enum_obj.values.ensureTotalCapacityContext(new_decl_arena_allocator, fields_len, .{ + try enum_obj.values.ensureTotalCapacityContext(decl_arena_allocator, fields_len, .{ .ty = enum_obj.tag_ty, .mod = mod, }); @@ -2518,7 +2532,7 @@ fn zirEnumDecl( extra_index += 1; // This string needs to outlive the ZIR code. - const field_name = try new_decl_arena_allocator.dupe(u8, field_name_zir); + const field_name = try decl_arena_allocator.dupe(u8, field_name_zir); const gop = enum_obj.fields.getOrPutAssumeCapacity(field_name); if (gop.found_existing) { @@ -2542,7 +2556,7 @@ fn zirEnumDecl( // But only resolve the source location if we need to emit a compile error. const tag_val = (try sema.resolveInstConst(block, src, tag_val_ref, "enum tag value must be comptime known")).val; last_tag_val = tag_val; - const copied_tag_val = try tag_val.copy(new_decl_arena_allocator); + const copied_tag_val = try tag_val.copy(decl_arena_allocator); enum_obj.values.putAssumeCapacityNoClobberContext(copied_tag_val, {}, .{ .ty = enum_obj.tag_ty, .mod = mod, @@ -2553,16 +2567,14 @@ fn zirEnumDecl( else Value.zero; last_tag_val = tag_val; - const copied_tag_val = try tag_val.copy(new_decl_arena_allocator); + const copied_tag_val = try tag_val.copy(decl_arena_allocator); enum_obj.values.putAssumeCapacityNoClobberContext(copied_tag_val, {}, .{ .ty = enum_obj.tag_ty, .mod = mod, }); } } - - try new_decl.finalizeNewArena(&new_decl_arena); - return sema.analyzeDeclVal(block, src, new_decl_index); + return decl_val; } fn zirUnionDecl( @@ -8551,11 +8563,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError if (seen_src != null) continue; const field_name = operand_ty.enumFieldName(i); - - const field_src = src; // TODO better source location - try sema.errNote( + try sema.addFieldErrNote( block, - field_src, + operand_ty, + i, msg, "unhandled enumeration value: '{s}'", .{field_name}, @@ -10587,7 +10598,7 @@ fn zirOverflowArithmetic( try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); const dest_ty = lhs_ty; if (dest_ty.scalarType().zigTypeTag() != .Int) { - return sema.fail(block, src, "expected vector of integers or integer type, found '{}'", .{dest_ty.fmt(mod)}); + return sema.fail(block, src, "expected vector of integers or integer tag type, found '{}'", .{dest_ty.fmt(mod)}); } const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs); @@ -25175,7 +25186,7 @@ fn resolveTypeFieldsUnion( } union_obj.status = .field_types_wip; - try semaUnionFields(block, sema.mod, union_obj); + try semaUnionFields(sema.mod, union_obj); union_obj.status = .have_field_types; } @@ -25462,7 +25473,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void struct_obj.have_field_inits = true; } -fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) CompileError!void { +fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { const tracy = trace(@src()); defer tracy.end(); @@ -25567,10 +25578,14 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil var enum_value_map: ?*Module.EnumNumbered.ValueMap = null; var tag_ty_field_names: ?Module.EnumFull.NameMap = null; if (tag_type_ref != .none) { - const provided_ty = try sema.resolveType(&block_scope, src, tag_type_ref); + const tag_ty_src: LazySrcLoc = .{ .node_offset_container_tag = src.node_offset.x }; + const provided_ty = try sema.resolveType(&block_scope, tag_ty_src, tag_type_ref); if (small.auto_enum_tag) { // The provided type is an integer type and we must construct the enum tag type here. int_tag_ty = provided_ty; + if (int_tag_ty.zigTypeTag() != .Int and int_tag_ty.zigTypeTag() != .ComptimeInt) { + return sema.fail(&block_scope, tag_ty_src, "expected integer tag type, found '{}'", .{int_tag_ty.fmt(sema.mod)}); + } union_obj.tag_ty = try sema.generateUnionTagTypeNumbered(&block_scope, fields_len, provided_ty, union_obj); const enum_obj = union_obj.tag_ty.castTag(.enum_numbered).?.data; enum_field_names = &enum_obj.fields; @@ -25579,8 +25594,7 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil // The provided type is the enum tag type. union_obj.tag_ty = try provided_ty.copy(decl_arena_allocator); if (union_obj.tag_ty.zigTypeTag() != .Enum) { - const tag_ty_src = src; // TODO better source location - return sema.fail(block, tag_ty_src, "expected enum tag type, found '{}'", .{union_obj.tag_ty.fmt(sema.mod)}); + return sema.fail(&block_scope, tag_ty_src, "expected enum tag type, found '{}'", .{union_obj.tag_ty.fmt(sema.mod)}); } // The fields of the union must match the enum exactly. // Store a copy of the enum field names so we can check for @@ -25658,7 +25672,7 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil }); } else { const val = if (last_tag_val) |val| - try sema.intAdd(block, src, val, Value.one, int_tag_ty) + try sema.intAdd(&block_scope, src, val, Value.one, int_tag_ty) else Value.zero; last_tag_val = val; @@ -25712,12 +25726,14 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil const enum_has_field = names.orderedRemove(field_name); if (!enum_has_field) { const msg = msg: { - const msg = try sema.errMsg(block, src, "enum '{}' has no field named '{s}'", .{ union_obj.tag_ty.fmt(sema.mod), field_name }); + const tree = try sema.getAstTree(&block_scope); + const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i); + const msg = try sema.errMsg(&block_scope, field_src, "enum '{}' has no field named '{s}'", .{ union_obj.tag_ty.fmt(sema.mod), field_name }); errdefer msg.destroy(sema.gpa); try sema.addDeclaredHereNote(msg, union_obj.tag_ty); break :msg msg; }; - return sema.failWithOwnedErrorMsg(block, msg); + return sema.failWithOwnedErrorMsg(&block_scope, msg); } } @@ -25739,18 +25755,18 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil if (tag_ty_field_names) |names| { if (names.count() > 0) { const msg = msg: { - const msg = try sema.errMsg(block, src, "enum field(s) missing in union", .{}); + const msg = try sema.errMsg(&block_scope, src, "enum field(s) missing in union", .{}); errdefer msg.destroy(sema.gpa); const enum_ty = union_obj.tag_ty; for (names.keys()) |field_name| { const field_index = enum_ty.enumFieldIndex(field_name).?; - try sema.addFieldErrNote(block, enum_ty, field_index, msg, "field '{s}' missing, declared here", .{field_name}); + try sema.addFieldErrNote(&block_scope, enum_ty, field_index, msg, "field '{s}' missing, declared here", .{field_name}); } try sema.addDeclaredHereNote(msg, union_obj.tag_ty); break :msg msg; }; - return sema.failWithOwnedErrorMsg(block, msg); + return sema.failWithOwnedErrorMsg(&block_scope, msg); } } } diff --git a/test/cases/compile_errors/stage1/obj/extern_union_given_enum_tag_type.zig b/test/cases/compile_errors/extern_union_given_enum_tag_type.zig similarity index 72% rename from test/cases/compile_errors/stage1/obj/extern_union_given_enum_tag_type.zig rename to test/cases/compile_errors/extern_union_given_enum_tag_type.zig index 86ac42cccf..6a691eb2e2 100644 --- a/test/cases/compile_errors/stage1/obj/extern_union_given_enum_tag_type.zig +++ b/test/cases/compile_errors/extern_union_given_enum_tag_type.zig @@ -14,7 +14,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:6:30: error: extern union does not support enum tag type +// :6:30: error: extern union does not support enum tag type diff --git a/test/cases/compile_errors/stage1/obj/non-enum_tag_type_passed_to_union.zig b/test/cases/compile_errors/non-enum_tag_type_passed_to_union.zig similarity index 66% rename from test/cases/compile_errors/stage1/obj/non-enum_tag_type_passed_to_union.zig rename to test/cases/compile_errors/non-enum_tag_type_passed_to_union.zig index e188a3f819..addc970425 100644 --- a/test/cases/compile_errors/stage1/obj/non-enum_tag_type_passed_to_union.zig +++ b/test/cases/compile_errors/non-enum_tag_type_passed_to_union.zig @@ -7,7 +7,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:1:19: error: expected enum tag type, found 'u32' +// :1:19: error: expected enum tag type, found 'u32' diff --git a/test/cases/compile_errors/stage1/obj/non-integer_tag_type_to_automatic_union_enum.zig b/test/cases/compile_errors/non-integer_tag_type_to_automatic_union_enum.zig similarity index 66% rename from test/cases/compile_errors/stage1/obj/non-integer_tag_type_to_automatic_union_enum.zig rename to test/cases/compile_errors/non-integer_tag_type_to_automatic_union_enum.zig index 41d5e14163..3483ea8a5c 100644 --- a/test/cases/compile_errors/stage1/obj/non-integer_tag_type_to_automatic_union_enum.zig +++ b/test/cases/compile_errors/non-integer_tag_type_to_automatic_union_enum.zig @@ -7,7 +7,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:1:24: error: expected integer tag type, found 'f32' +// :1:24: error: expected integer tag type, found 'f32' diff --git a/test/cases/compile_errors/non-integer_tag_type_to_enum.zig b/test/cases/compile_errors/non-integer_tag_type_to_enum.zig new file mode 100644 index 0000000000..85ffc5b4b6 --- /dev/null +++ b/test/cases/compile_errors/non-integer_tag_type_to_enum.zig @@ -0,0 +1,13 @@ +const Foo = enum(f32) { + A, +}; +export fn entry() void { + var f: Foo = undefined; + _ = f; +} + +// error +// backend=stage2 +// target=native +// +// :1:18: error: expected integer tag type, found 'f32' diff --git a/test/cases/compile_errors/stage2/union_enum_field_missing.zig b/test/cases/compile_errors/stage2/union_enum_field_missing.zig index 25ec477894..81bc83cf18 100644 --- a/test/cases/compile_errors/stage2/union_enum_field_missing.zig +++ b/test/cases/compile_errors/stage2/union_enum_field_missing.zig @@ -16,6 +16,6 @@ export fn entry() usize { // error // target=native // -// :7:1: error: enum field(s) missing in union +// :7:11: error: enum field(s) missing in union // :4:5: note: field 'c' missing, declared here // :1:11: note: enum declared here diff --git a/test/cases/compile_errors/stage2/union_extra_field.zig b/test/cases/compile_errors/stage2/union_extra_field.zig index e3bc0deb33..0488ce183c 100644 --- a/test/cases/compile_errors/stage2/union_extra_field.zig +++ b/test/cases/compile_errors/stage2/union_extra_field.zig @@ -16,5 +16,5 @@ export fn entry() usize { // error // target=native // -// :6:1: error: enum 'tmp.E' has no field named 'd' +// :10:5: error: enum 'tmp.E' has no field named 'd' // :1:11: note: enum declared here diff --git a/test/cases/compile_errors/switch_expression-missing_enumeration_prong.zig b/test/cases/compile_errors/switch_expression-missing_enumeration_prong.zig index 20b373f6b7..1075b837de 100644 --- a/test/cases/compile_errors/switch_expression-missing_enumeration_prong.zig +++ b/test/cases/compile_errors/switch_expression-missing_enumeration_prong.zig @@ -19,5 +19,5 @@ export fn entry() usize { return @sizeOf(@TypeOf(&f)); } // target=native // // :8:5: error: switch must handle all possibilities -// :8:5: note: unhandled enumeration value: 'Four' +// :5:5: note: unhandled enumeration value: 'Four' // :1:16: note: enum 'tmp.Number' declared here diff --git a/test/cases/compile_errors/switch_on_enum_with_1_field_with_no_prongs.zig b/test/cases/compile_errors/switch_on_enum_with_1_field_with_no_prongs.zig index dbf1cd948f..cf6fc141de 100644 --- a/test/cases/compile_errors/switch_on_enum_with_1_field_with_no_prongs.zig +++ b/test/cases/compile_errors/switch_on_enum_with_1_field_with_no_prongs.zig @@ -10,5 +10,5 @@ export fn entry() void { // target=native // // :5:5: error: switch must handle all possibilities -// :5:5: note: unhandled enumeration value: 'M' +// :1:20: note: unhandled enumeration value: 'M' // :1:13: note: enum 'tmp.Foo' declared here diff --git a/test/cases/compile_errors/switching_with_non-exhaustive_enums.zig b/test/cases/compile_errors/switching_with_non-exhaustive_enums.zig index e33d686d86..435d409dd4 100644 --- a/test/cases/compile_errors/switching_with_non-exhaustive_enums.zig +++ b/test/cases/compile_errors/switching_with_non-exhaustive_enums.zig @@ -35,7 +35,7 @@ pub export fn entry3() void { // target=native // // :12:5: error: switch must handle all possibilities -// :12:5: note: unhandled enumeration value: 'b' +// :3:5: note: unhandled enumeration value: 'b' // :1:11: note: enum 'tmp.E' declared here // :19:5: error: switch on non-exhaustive enum must include 'else' or '_' prong // :26:5: error: '_' prong only allowed when switching on non-exhaustive enums diff --git a/test/cases/compile_errors/union_with_specified_enum_omits_field.zig b/test/cases/compile_errors/union_with_specified_enum_omits_field.zig index 3033feb376..5c2b735875 100644 --- a/test/cases/compile_errors/union_with_specified_enum_omits_field.zig +++ b/test/cases/compile_errors/union_with_specified_enum_omits_field.zig @@ -15,6 +15,6 @@ export fn entry() usize { // backend=stage2 // target=native // -// :6:1: error: enum field(s) missing in union +// :6:17: error: enum field(s) missing in union // :4:5: note: field 'C' missing, declared here // :1:16: note: enum declared here diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index 44667fbda6..cf0f307af3 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -772,7 +772,7 @@ pub fn addCases(ctx: *TestContext) !void { \\} , &.{ ":4:5: error: switch must handle all possibilities", - ":4:5: note: unhandled enumeration value: 'b'", + ":1:21: note: unhandled enumeration value: 'b'", ":1:11: note: enum 'tmp.E' declared here", }); From 9fb8d21a019deab308a63f7959dab2ee05385969 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 19 Jul 2022 12:25:47 +0300 Subject: [PATCH 09/18] AstGen: add error for fields in opaque types --- src/AstGen.zig | 5 ++++- .../{stage1/obj => }/opaque_type_with_field.zig | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) rename test/cases/compile_errors/{stage1/obj => }/opaque_type_with_field.zig (65%) diff --git a/src/AstGen.zig b/src/AstGen.zig index c4121e0a20..af0fac04f1 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -4724,7 +4724,10 @@ fn containerDecl( defer wip_members.deinit(); for (container_decl.ast.members) |member_node| { - _ = try containerMember(gz, &namespace.base, &wip_members, member_node); + const res = try containerMember(gz, &namespace.base, &wip_members, member_node); + if (res == .field) { + return astgen.failNode(member_node, "opaque types cannot have fields", .{}); + } } try gz.setOpaque(decl_inst, .{ diff --git a/test/cases/compile_errors/stage1/obj/opaque_type_with_field.zig b/test/cases/compile_errors/opaque_type_with_field.zig similarity index 65% rename from test/cases/compile_errors/stage1/obj/opaque_type_with_field.zig rename to test/cases/compile_errors/opaque_type_with_field.zig index 0b87091de9..6cfa67e5ec 100644 --- a/test/cases/compile_errors/stage1/obj/opaque_type_with_field.zig +++ b/test/cases/compile_errors/opaque_type_with_field.zig @@ -5,7 +5,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:1:25: error: opaque types cannot have fields +// :1:25: error: opaque types cannot have fields From 83b2d2cd3eb701b43d17404e73933bc00f7be828 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 19 Jul 2022 13:19:23 +0300 Subject: [PATCH 10/18] Sema: better source location for incompatible capture group --- src/Module.zig | 28 +++++++++++++++++++ src/Sema.zig | 23 +++++++++------ ..._prong_with_incompatible_payload_types.zig | 8 +++--- 3 files changed, 46 insertions(+), 13 deletions(-) rename test/cases/compile_errors/{stage1/obj => }/capture_group_on_switch_prong_with_incompatible_payload_types.zig (59%) diff --git a/src/Module.zig b/src/Module.zig index fd8c217bd1..8389dcec19 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2348,6 +2348,26 @@ pub const SrcLoc = struct { } } else unreachable; }, + .node_offset_switch_prong_capture => |node_off| { + const tree = try src_loc.file_scope.getTree(gpa); + const case_node = src_loc.declRelativeToNodeIndex(node_off); + const node_tags = tree.nodes.items(.tag); + const case = switch (node_tags[case_node]) { + .switch_case_one => tree.switchCaseOne(case_node), + .switch_case => tree.switchCase(case_node), + else => unreachable, + }; + const start_tok = case.payload_token.?; + const token_tags = tree.tokens.items(.tag); + const end_tok = switch (token_tags[start_tok]) { + .asterisk => start_tok + 1, + else => start_tok, + }; + const start = tree.tokens.items(.start)[start_tok]; + const end_start = tree.tokens.items(.start)[end_tok]; + const end = end_start + @intCast(u32, tree.tokenSlice(end_tok).len); + return Span{ .start = start, .end = end, .main = start }; + }, .node_offset_fn_type_align => |node_off| { const tree = try src_loc.file_scope.getTree(gpa); const node_datas = tree.nodes.items(.data); @@ -2877,6 +2897,9 @@ pub const LazySrcLoc = union(enum) { /// range nodes. The error applies to all of them. /// The Decl is determined contextually. node_offset_switch_range: i32, + /// The source location points to the capture of a switch_prong. + /// The Decl is determined contextually. + node_offset_switch_prong_capture: i32, /// The source location points to the align expr of a function type /// expression, found by taking this AST node index offset from the containing /// Decl AST node, which points to a function type AST node. Next, navigate to @@ -3017,6 +3040,7 @@ pub const LazySrcLoc = union(enum) { .node_offset_switch_operand, .node_offset_switch_special_prong, .node_offset_switch_range, + .node_offset_switch_prong_capture, .node_offset_fn_type_align, .node_offset_fn_type_addrspace, .node_offset_fn_type_section, @@ -5602,6 +5626,7 @@ pub const SwitchProngSrc = union(enum) { scalar: u32, multi: Multi, range: Multi, + multi_capture: u32, pub const Multi = struct { prong: u32, @@ -5657,6 +5682,9 @@ pub const SwitchProngSrc = union(enum) { .scalar => |i| if (!is_multi and i == scalar_i) return LazySrcLoc.nodeOffset( decl.nodeIndexToRelative(case.ast.values[0]), ), + .multi_capture => |i| if (is_multi and i == multi_i) { + return LazySrcLoc{ .node_offset_switch_prong_capture = decl.nodeIndexToRelative(case_node) }; + }, .multi => |s| if (is_multi and s.prong == multi_i) { var item_i: u32 = 0; for (case.ast.values) |item_node| { diff --git a/src/Sema.zig b/src/Sema.zig index af44ffcedd..d8a6eddbb6 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -8197,7 +8197,6 @@ fn zirSwitchCapture( const switch_info = zir_datas[capture_info.switch_inst].pl_node; const switch_extra = sema.code.extraData(Zir.Inst.SwitchBlock, switch_info.payload_index); const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = switch_info.src_node }; - const switch_src = switch_info.src(); const operand_is_ref = switch_extra.data.bits.is_ref; const cond_inst = Zir.refToIndex(switch_extra.data.operand).?; const cond_info = sema.code.instructions.items(.data)[cond_inst].un_node; @@ -8247,7 +8246,7 @@ fn zirSwitchCapture( const first_field_index = @intCast(u32, enum_ty.enumTagFieldIndex(first_item_val, sema.mod).?); const first_field = union_obj.fields.values()[first_field_index]; - for (items[1..]) |item| { + for (items[1..]) |item, i| { const item_ref = try sema.resolveInst(item); // Previous switch validation ensured this will succeed const item_val = sema.resolveConstValue(block, .unneeded, item_ref, undefined) catch unreachable; @@ -8255,11 +8254,17 @@ fn zirSwitchCapture( const field_index = enum_ty.enumTagFieldIndex(item_val, sema.mod).?; const field = union_obj.fields.values()[field_index]; if (!field.ty.eql(first_field.ty, sema.mod)) { - const first_item_src = switch_src; // TODO better source location - const item_src = switch_src; const msg = msg: { - const msg = try sema.errMsg(block, switch_src, "capture group with incompatible types", .{}); + const raw_capture_src = Module.SwitchProngSrc{ .multi_capture = capture_info.prong_index }; + const capture_src = raw_capture_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), switch_info.src_node, .first); + + const msg = try sema.errMsg(block, capture_src, "capture group with incompatible types", .{}); errdefer msg.destroy(sema.gpa); + + const raw_first_item_src = Module.SwitchProngSrc{ .multi = .{ .prong = capture_info.prong_index, .item = 0 } }; + const first_item_src = raw_first_item_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), switch_info.src_node, .first); + const raw_item_src = Module.SwitchProngSrc{ .multi = .{ .prong = capture_info.prong_index, .item = 1 + @intCast(u32, i) } }; + const item_src = raw_item_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), switch_info.src_node, .first); try sema.errNote(block, first_item_src, msg, "type '{}' here", .{first_field.ty.fmt(sema.mod)}); try sema.errNote(block, item_src, msg, "type '{}' here", .{field.ty.fmt(sema.mod)}); break :msg msg; @@ -21094,17 +21099,17 @@ const InMemoryCoercionResult = union(enum) { } } if (!actual_noalias) { - try sema.errNote(block, src, msg, "regular paramter {d} cannot cast into a noalias paramter", .{index}); + try sema.errNote(block, src, msg, "regular parameter {d} cannot cast into a noalias parameter", .{index}); } else { - try sema.errNote(block, src, msg, "noalias paramter {d} cannot cast into a regular paramter", .{index}); + try sema.errNote(block, src, msg, "noalias parameter {d} cannot cast into a regular parameter", .{index}); } break; }, .fn_param_comptime => |param| { if (param.wanted) { - try sema.errNote(block, src, msg, "non-comptime paramter {d} cannot cast into a comptime paramter", .{param.index}); + try sema.errNote(block, src, msg, "non-comptime parameter {d} cannot cast into a comptime parameter", .{param.index}); } else { - try sema.errNote(block, src, msg, "comptime paramter {d} cannot cast into a non-comptime paramter", .{param.index}); + try sema.errNote(block, src, msg, "comptime parameter {d} cannot cast into a non-comptime parameter", .{param.index}); } break; }, diff --git a/test/cases/compile_errors/stage1/obj/capture_group_on_switch_prong_with_incompatible_payload_types.zig b/test/cases/compile_errors/capture_group_on_switch_prong_with_incompatible_payload_types.zig similarity index 59% rename from test/cases/compile_errors/stage1/obj/capture_group_on_switch_prong_with_incompatible_payload_types.zig rename to test/cases/compile_errors/capture_group_on_switch_prong_with_incompatible_payload_types.zig index ce2068d6c7..cff9a58bc6 100644 --- a/test/cases/compile_errors/stage1/obj/capture_group_on_switch_prong_with_incompatible_payload_types.zig +++ b/test/cases/compile_errors/capture_group_on_switch_prong_with_incompatible_payload_types.zig @@ -13,9 +13,9 @@ comptime { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:8:20: error: capture group with incompatible types -// tmp.zig:8:9: note: type 'usize' here -// tmp.zig:8:13: note: type 'isize' here +// :8:20: error: capture group with incompatible types +// :8:10: note: type 'usize' here +// :8:14: note: type 'isize' here From 79ef0cdf306c48d3cc447ce14530db4021f8c5de Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 19 Jul 2022 20:20:57 +0300 Subject: [PATCH 11/18] Sema: better function parameter source location --- src/Module.zig | 85 ++++++++++ src/Sema.zig | 158 ++++++++++++------ .../array_in_c_exported_function.zig | 16 ++ ...xport_function_with_comptime_parameter.zig | 9 + .../export_generic_function.zig | 10 ++ ...xtern_function_with_comptime_parameter.zig | 20 +++ .../function_parameter_is_opaque.zig | 9 +- ...h_non-extern_non-packed_enum_parameter.zig | 6 +- ...non-extern_non-packed_struct_parameter.zig | 4 +- ..._non-extern_non-packed_union_parameter.zig | 4 +- .../obj/array_in_c_exported_function.zig | 14 -- ...xport_function_with_comptime_parameter.zig | 9 - .../stage1/obj/export_generic_function.zig | 10 -- ...xtern_function_with_comptime_parameter.zig | 11 -- 14 files changed, 256 insertions(+), 109 deletions(-) create mode 100644 test/cases/compile_errors/array_in_c_exported_function.zig create mode 100644 test/cases/compile_errors/export_function_with_comptime_parameter.zig create mode 100644 test/cases/compile_errors/export_generic_function.zig create mode 100644 test/cases/compile_errors/extern_function_with_comptime_parameter.zig delete mode 100644 test/cases/compile_errors/stage1/obj/array_in_c_exported_function.zig delete mode 100644 test/cases/compile_errors/stage1/obj/export_function_with_comptime_parameter.zig delete mode 100644 test/cases/compile_errors/stage1/obj/export_generic_function.zig delete mode 100644 test/cases/compile_errors/stage1/obj/extern_function_with_comptime_parameter.zig diff --git a/src/Module.zig b/src/Module.zig index 8389dcec19..a1081517c0 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2482,6 +2482,41 @@ pub const SrcLoc = struct { }; return nodeToSpan(tree, full.ast.return_type); }, + .node_offset_param => |node_off| { + const tree = try src_loc.file_scope.getTree(gpa); + const token_tags = tree.tokens.items(.tag); + const node = src_loc.declRelativeToNodeIndex(node_off); + + var first_tok = tree.firstToken(node); + while (true) switch (token_tags[first_tok - 1]) { + .colon, .identifier, .keyword_comptime, .keyword_noalias => first_tok -= 1, + else => break, + }; + return tokensToSpan( + tree, + first_tok, + tree.lastToken(node), + first_tok, + ); + }, + .token_offset_param => |token_off| { + const tree = try src_loc.file_scope.getTree(gpa); + const token_tags = tree.tokens.items(.tag); + const main_token = tree.nodes.items(.main_token)[src_loc.parent_decl_node]; + const tok_index = @bitCast(Ast.TokenIndex, token_off + @bitCast(i32, main_token)); + + var first_tok = tok_index; + while (true) switch (token_tags[first_tok - 1]) { + .colon, .identifier, .keyword_comptime, .keyword_noalias => first_tok -= 1, + else => break, + }; + return tokensToSpan( + tree, + first_tok, + tok_index, + first_tok, + ); + }, .node_offset_anyframe_type => |node_off| { const tree = try src_loc.file_scope.getTree(gpa); @@ -2930,6 +2965,8 @@ pub const LazySrcLoc = union(enum) { /// the return type node. /// The Decl is determined contextually. node_offset_fn_type_ret_ty: i32, + node_offset_param: i32, + token_offset_param: i32, /// The source location points to the type expression of an `anyframe->T` /// expression, found by taking this AST node index offset from the containing /// Decl AST node, which points to a `anyframe->T` expression AST node. Next, navigate @@ -3046,6 +3083,8 @@ pub const LazySrcLoc = union(enum) { .node_offset_fn_type_section, .node_offset_fn_type_cc, .node_offset_fn_type_ret_ty, + .node_offset_param, + .token_offset_param, .node_offset_anyframe_type, .node_offset_lib_name, .node_offset_array_type_len, @@ -5826,6 +5865,52 @@ fn queryFieldSrc( unreachable; } +pub fn paramSrc( + func_node_offset: i32, + gpa: Allocator, + decl: *Decl, + param_i: usize, +) LazySrcLoc { + @setCold(true); + const tree = decl.getFileScope().getTree(gpa) catch |err| { + // In this case we emit a warning + a less precise source location. + log.warn("unable to load {s}: {s}", .{ + decl.getFileScope().sub_file_path, @errorName(err), + }); + return LazySrcLoc.nodeOffset(0); + }; + const node_datas = tree.nodes.items(.data); + const node_tags = tree.nodes.items(.tag); + const node = decl.relativeToNodeIndex(func_node_offset); + var params: [1]Ast.Node.Index = undefined; + const full = switch (node_tags[node]) { + .fn_proto_simple => tree.fnProtoSimple(¶ms, node), + .fn_proto_multi => tree.fnProtoMulti(node), + .fn_proto_one => tree.fnProtoOne(¶ms, node), + .fn_proto => tree.fnProto(node), + .fn_decl => switch (node_tags[node_datas[node].lhs]) { + .fn_proto_simple => tree.fnProtoSimple(¶ms, node_datas[node].lhs), + .fn_proto_multi => tree.fnProtoMulti(node_datas[node].lhs), + .fn_proto_one => tree.fnProtoOne(¶ms, node_datas[node].lhs), + .fn_proto => tree.fnProto(node_datas[node].lhs), + else => unreachable, + }, + else => unreachable, + }; + var it = full.iterate(tree); + while (true) { + if (it.param_i == param_i) { + const param = it.next().?; + if (param.anytype_ellipsis3) |some| { + const main_token = tree.nodes.items(.main_token)[decl.src_node]; + return .{ .token_offset_param = @bitCast(i32, some) - @bitCast(i32, main_token) }; + } + return .{ .node_offset_param = decl.nodeIndexToRelative(param.type_expr) }; + } + _ = it.next(); + } +} + /// Called from `performAllTheWork`, after all AstGen workers have finished, /// and before the main semantic analysis loop begins. pub fn processOutdatedAndDeletedDecls(mod: *Module) !void { diff --git a/src/Sema.zig b/src/Sema.zig index d8a6eddbb6..2001067bbf 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7233,6 +7233,7 @@ fn funcCommon( opt_lib_name: ?[]const u8, noalias_bits: u32, ) CompileError!Air.Inst.Ref { + const fn_src = LazySrcLoc.nodeOffset(src_node_offset); const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset }; const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = src_node_offset }; @@ -7241,10 +7242,6 @@ fn funcCommon( address_space == null or section == .generic or cc == null; - // Check for generic params. - for (block.params.items) |param| { - if (param.ty.tag() == .generic_poison) is_generic = true; - } var destroy_fn_on_error = false; const new_func: *Module.Fn = new_func: { @@ -7304,55 +7301,35 @@ fn funcCommon( const param_types = try sema.arena.alloc(Type, block.params.items.len); const comptime_params = try sema.arena.alloc(bool, block.params.items.len); for (block.params.items) |param, i| { - const param_src = LazySrcLoc.nodeOffset(src_node_offset); // TODO better soruce location param_types[i] = param.ty; - const requires_comptime = try sema.typeRequiresComptime(block, param_src, param.ty); - comptime_params[i] = param.is_comptime or requires_comptime; - is_generic = is_generic or comptime_params[i] or param.ty.tag() == .generic_poison; - if (is_extern and is_generic) { - // TODO add note: function is generic because of this parameter - return sema.fail(block, param_src, "extern function cannot be generic", .{}); - } - if (!param.ty.isValidParamType()) { - const opaque_str = if (param.ty.zigTypeTag() == .Opaque) "opaque " else ""; - const msg = msg: { - const msg = try sema.errMsg(block, param_src, "parameter of {s}type '{}' not allowed", .{ - opaque_str, param.ty.fmt(sema.mod), - }); - errdefer msg.destroy(sema.gpa); - - try sema.addDeclaredHereNote(msg, param.ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } - if (!Type.fnCallingConventionAllowsZigTypes(cc_workaround) and !(try sema.validateExternType(param.ty, .param_ty))) { - const msg = msg: { - const msg = try sema.errMsg(block, param_src, "parameter of type '{}' not allowed in function with calling convention '{s}'", .{ - param.ty.fmt(sema.mod), @tagName(cc_workaround), - }); - errdefer msg.destroy(sema.gpa); - - const src_decl = sema.mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotExtern(block, param_src, msg, param_src.toSrcLoc(src_decl), param.ty, .param_ty); - - try sema.addDeclaredHereNote(msg, param.ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } - if (requires_comptime and !param.is_comptime) { - const msg = msg: { - const msg = try sema.errMsg(block, param_src, "parametter of type '{}' must be declared comptime", .{ - param.ty.fmt(sema.mod), - }); - errdefer msg.destroy(sema.gpa); - - try sema.addDeclaredHereNote(msg, param.ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } + sema.analyzeParameter( + block, + fn_src, + .unneeded, + param, + comptime_params, + i, + &is_generic, + is_extern, + cc_workaround, + ) catch |err| switch (err) { + error.NeededSourceLocation => { + const decl = sema.mod.declPtr(block.src_decl); + try sema.analyzeParameter( + block, + fn_src, + Module.paramSrc(src_node_offset, sema.gpa, decl, i), + param, + comptime_params, + i, + &is_generic, + is_extern, + cc_workaround, + ); + return error.AnalysisFail; + }, + else => |e| return e, + }; } const ret_poison = if (!is_generic) rp: { @@ -7542,6 +7519,79 @@ fn funcCommon( return sema.addConstant(fn_ty, Value.initPayload(&fn_payload.base)); } +fn analyzeParameter( + sema: *Sema, + block: *Block, + func_src: LazySrcLoc, + param_src: LazySrcLoc, + param: Block.Param, + comptime_params: []bool, + i: usize, + is_generic: *bool, + is_extern: bool, + cc: std.builtin.CallingConvention, +) !void { + const requires_comptime = try sema.typeRequiresComptime(block, param_src, param.ty); + comptime_params[i] = param.is_comptime or requires_comptime; + const this_generic = comptime_params[i] or param.ty.tag() == .generic_poison; + is_generic.* = is_generic.* or this_generic; + if (is_extern and this_generic) { + // TODO this check should exist somewhere for notes. + if (param_src == .unneeded) return error.NeededSourceLocation; + const msg = msg: { + const msg = try sema.errMsg(block, func_src, "extern function cannot be generic", .{}); + errdefer msg.destroy(sema.gpa); + + try sema.errNote(block, param_src, msg, "function is generic because of this parameter", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + if (this_generic and !Type.fnCallingConventionAllowsZigTypes(cc)) { + return sema.fail(block, param_src, "generic parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)}); + } + if (!param.ty.isValidParamType()) { + const opaque_str = if (param.ty.zigTypeTag() == .Opaque) "opaque " else ""; + const msg = msg: { + const msg = try sema.errMsg(block, param_src, "parameter of {s}type '{}' not allowed", .{ + opaque_str, param.ty.fmt(sema.mod), + }); + errdefer msg.destroy(sema.gpa); + + try sema.addDeclaredHereNote(msg, param.ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + if (!Type.fnCallingConventionAllowsZigTypes(cc) and !(try sema.validateExternType(param.ty, .param_ty))) { + const msg = msg: { + const msg = try sema.errMsg(block, param_src, "parameter of type '{}' not allowed in function with calling convention '{s}'", .{ + param.ty.fmt(sema.mod), @tagName(cc), + }); + errdefer msg.destroy(sema.gpa); + + const src_decl = sema.mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsNotExtern(block, param_src, msg, param_src.toSrcLoc(src_decl), param.ty, .param_ty); + + try sema.addDeclaredHereNote(msg, param.ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } + if (requires_comptime and !param.is_comptime) { + const msg = msg: { + const msg = try sema.errMsg(block, param_src, "parametter of type '{}' must be declared comptime", .{ + param.ty.fmt(sema.mod), + }); + errdefer msg.destroy(sema.gpa); + + try sema.addDeclaredHereNote(msg, param.ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } +} + fn zirParam( sema: *Sema, block: *Block, @@ -18570,9 +18620,9 @@ fn explainWhyTypeIsNotExtern( .Union => try mod.errNoteNonLazy(src_loc, msg, "only unions with packed or extern layout are extern compatible", .{}), .Array => { if (position == .ret_ty) { - try mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a return type", .{}); + return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a return type", .{}); } else if (position == .param_ty) { - try mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a parameter type", .{}); + return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a parameter type", .{}); } try sema.explainWhyTypeIsNotExtern(block, src, msg, src_loc, ty.elemType2(), position); }, diff --git a/test/cases/compile_errors/array_in_c_exported_function.zig b/test/cases/compile_errors/array_in_c_exported_function.zig new file mode 100644 index 0000000000..e938b6afd4 --- /dev/null +++ b/test/cases/compile_errors/array_in_c_exported_function.zig @@ -0,0 +1,16 @@ +export fn zig_array(x: [10]u8) void { + try std.testing.expect(std.mem.eql(u8, &x, "1234567890")); +} +const std = @import("std"); +export fn zig_return_array() [10]u8 { + return "1234567890".*; +} + +// error +// backend=stage2 +// target=native +// +// :1:21: error: parameter of type '[10]u8' not allowed in function with calling convention 'C' +// :1:21: note: arrays are not allowed as a parameter type +// :5:30: error: return type '[10]u8' not allowed in function with calling convention 'C' +// :5:30: note: arrays are not allowed as a return type diff --git a/test/cases/compile_errors/export_function_with_comptime_parameter.zig b/test/cases/compile_errors/export_function_with_comptime_parameter.zig new file mode 100644 index 0000000000..94c8f6de50 --- /dev/null +++ b/test/cases/compile_errors/export_function_with_comptime_parameter.zig @@ -0,0 +1,9 @@ +export fn foo(comptime x: anytype, y: i32) i32{ + return x + y; +} + +// error +// backend=stage2 +// target=native +// +// :1:15: error: generic parameters not allowed in function with calling convention 'C' diff --git a/test/cases/compile_errors/export_generic_function.zig b/test/cases/compile_errors/export_generic_function.zig new file mode 100644 index 0000000000..4ffbad9df7 --- /dev/null +++ b/test/cases/compile_errors/export_generic_function.zig @@ -0,0 +1,10 @@ +export fn foo(num: anytype) i32 { + _ = num; + return 0; +} + +// error +// backend=stage2 +// target=native +// +// :1:15: error: generic parameters not allowed in function with calling convention 'C' diff --git a/test/cases/compile_errors/extern_function_with_comptime_parameter.zig b/test/cases/compile_errors/extern_function_with_comptime_parameter.zig new file mode 100644 index 0000000000..cac4c7e5e2 --- /dev/null +++ b/test/cases/compile_errors/extern_function_with_comptime_parameter.zig @@ -0,0 +1,20 @@ +extern fn foo(comptime x: i32, y: i32) i32; +fn f() i32 { + return foo(1, 2); +} +pub extern fn entry1(b: u32, comptime a: [2]u8, c: i32) void; +pub extern fn entry2(b: u32, noalias a: anytype, i43) void; +comptime { _ = f; } +comptime { _ = entry1; } +comptime { _ = entry2; } + +// error +// backend=stage2 +// target=native +// +// :5:12: error: extern function cannot be generic +// :5:30: note: function is generic because of this parameter +// :6:12: error: extern function cannot be generic +// :6:30: note: function is generic because of this parameter +// :1:8: error: extern function cannot be generic +// :1:15: note: function is generic because of this parameter diff --git a/test/cases/compile_errors/function_parameter_is_opaque.zig b/test/cases/compile_errors/function_parameter_is_opaque.zig index 31c477e8bc..57c89bd7f4 100644 --- a/test/cases/compile_errors/function_parameter_is_opaque.zig +++ b/test/cases/compile_errors/function_parameter_is_opaque.zig @@ -23,8 +23,9 @@ export fn entry4() void { // backend=stage2 // target=native // -// :3:24: error: parameter of opaque type 'tmp.FooType' not allowed +// :3:28: error: parameter of opaque type 'tmp.FooType' not allowed // :1:17: note: opaque declared here -// :8:24: error: parameter of type '@TypeOf(null)' not allowed -// :12:1: error: parameter of opaque type 'tmp.FooType' not allowed -// :17:1: error: parameter of type '@TypeOf(null)' not allowed +// :8:28: error: parameter of type '@TypeOf(null)' not allowed +// :12:8: error: parameter of opaque type 'tmp.FooType' not allowed +// :1:17: note: opaque declared here +// :17:8: error: parameter of type '@TypeOf(null)' not allowed diff --git a/test/cases/compile_errors/function_with_non-extern_non-packed_enum_parameter.zig b/test/cases/compile_errors/function_with_non-extern_non-packed_enum_parameter.zig index 0cac6e1e0c..572378d093 100644 --- a/test/cases/compile_errors/function_with_non-extern_non-packed_enum_parameter.zig +++ b/test/cases/compile_errors/function_with_non-extern_non-packed_enum_parameter.zig @@ -5,7 +5,7 @@ export fn entry(foo: Foo) void { _ = foo; } // backend=stage2 // target=native // -// :2:8: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'C' -// :2:8: note: enum tag type 'u2' is not extern compatible -// :2:8: note: only integers with power of two bits are extern compatible +// :2:17: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'C' +// :2:17: note: enum tag type 'u2' is not extern compatible +// :2:17: note: only integers with power of two bits are extern compatible // :1:13: note: enum declared here diff --git a/test/cases/compile_errors/function_with_non-extern_non-packed_struct_parameter.zig b/test/cases/compile_errors/function_with_non-extern_non-packed_struct_parameter.zig index 8ae4ef7523..0007a2014e 100644 --- a/test/cases/compile_errors/function_with_non-extern_non-packed_struct_parameter.zig +++ b/test/cases/compile_errors/function_with_non-extern_non-packed_struct_parameter.zig @@ -9,6 +9,6 @@ export fn entry(foo: Foo) void { _ = foo; } // backend=stage2 // target=native // -// :6:8: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'C' -// :6:8: note: only structs with packed or extern layout are extern compatible +// :6:17: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'C' +// :6:17: note: only structs with packed or extern layout are extern compatible // :1:13: note: struct declared here diff --git a/test/cases/compile_errors/function_with_non-extern_non-packed_union_parameter.zig b/test/cases/compile_errors/function_with_non-extern_non-packed_union_parameter.zig index a4a96d9172..001d235e18 100644 --- a/test/cases/compile_errors/function_with_non-extern_non-packed_union_parameter.zig +++ b/test/cases/compile_errors/function_with_non-extern_non-packed_union_parameter.zig @@ -9,6 +9,6 @@ export fn entry(foo: Foo) void { _ = foo; } // backend=stage2 // target=native // -// :6:8: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'C' -// :6:8: note: only unions with packed or extern layout are extern compatible +// :6:17: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'C' +// :6:17: note: only unions with packed or extern layout are extern compatible // :1:13: note: union declared here diff --git a/test/cases/compile_errors/stage1/obj/array_in_c_exported_function.zig b/test/cases/compile_errors/stage1/obj/array_in_c_exported_function.zig deleted file mode 100644 index 5137755474..0000000000 --- a/test/cases/compile_errors/stage1/obj/array_in_c_exported_function.zig +++ /dev/null @@ -1,14 +0,0 @@ -export fn zig_array(x: [10]u8) void { - try std.testing.expect(std.mem.eql(u8, &x, "1234567890")); -} -const std = @import("std"); -export fn zig_return_array() [10]u8 { - return "1234567890".*; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:1:24: error: parameter of type '[10]u8' not allowed in function with calling convention 'C' -// tmp.zig:5:30: error: return type '[10]u8' not allowed in function with calling convention 'C' diff --git a/test/cases/compile_errors/stage1/obj/export_function_with_comptime_parameter.zig b/test/cases/compile_errors/stage1/obj/export_function_with_comptime_parameter.zig deleted file mode 100644 index ccfda1ba67..0000000000 --- a/test/cases/compile_errors/stage1/obj/export_function_with_comptime_parameter.zig +++ /dev/null @@ -1,9 +0,0 @@ -export fn foo(comptime x: i32, y: i32) i32{ - return x + y; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:1:15: error: comptime parameter not allowed in function with calling convention 'C' diff --git a/test/cases/compile_errors/stage1/obj/export_generic_function.zig b/test/cases/compile_errors/stage1/obj/export_generic_function.zig deleted file mode 100644 index 9f6bcd4cf4..0000000000 --- a/test/cases/compile_errors/stage1/obj/export_generic_function.zig +++ /dev/null @@ -1,10 +0,0 @@ -export fn foo(num: anytype) i32 { - _ = num; - return 0; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:1:15: error: parameter of type 'anytype' not allowed in function with calling convention 'C' diff --git a/test/cases/compile_errors/stage1/obj/extern_function_with_comptime_parameter.zig b/test/cases/compile_errors/stage1/obj/extern_function_with_comptime_parameter.zig deleted file mode 100644 index 93e26b9543..0000000000 --- a/test/cases/compile_errors/stage1/obj/extern_function_with_comptime_parameter.zig +++ /dev/null @@ -1,11 +0,0 @@ -extern fn foo(comptime x: i32, y: i32) i32; -fn f() i32 { - return foo(1, 2); -} -export fn entry() usize { return @sizeOf(@TypeOf(f)); } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:1:15: error: comptime parameter not allowed in function with calling convention 'C' From 821e4063f9f32f71cce263265fdbacc632bd5af9 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 20 Jul 2022 00:02:20 +0300 Subject: [PATCH 12/18] Sema: better source location for function call args --- src/Module.zig | 25 ++ src/Sema.zig | 321 ++++++++++++------ ...ction_passing_array_instead_of_pointer.zig | 2 +- ..._bit_offset_pointer_to_regular_pointer.zig | 6 +- ...ted_pointer_to_null-terminated_pointer.zig | 7 +- ...for_function_parameter_incompatibility.zig | 6 +- .../function_parameter_is_opaque.zig | 1 - ..._instance_with_non-constant_expression.zig | 13 + ...licit_cast_from_array_to_mutable_slice.zig | 4 +- ...mplicitly_increasing_pointer_alignment.zig | 5 +- .../pass_const_ptr_to_mutable_ptr_fn.zig | 7 +- ..._instance_with_non-constant_expression.zig | 12 - ...ointer_coerced_to_pointer_to_opaque_{}.zig | 12 - .../type_checking_function_pointers.zig | 6 +- .../obj => }/unreachable_parameter.zig | 4 +- ...use_anyopaque_as_return_type_of_fn_ptr.zig | 4 +- ...ializer_for_union_payload_of_type_type.zig | 5 +- ...ointer_coerced_to_pointer_to_opaque_{}.zig | 14 + 18 files changed, 301 insertions(+), 153 deletions(-) rename test/cases/compile_errors/{stage1/obj => }/disallow_coercion_from_non-null-terminated_pointer_to_null-terminated_pointer.zig (54%) create mode 100644 test/cases/compile_errors/generic_function_instance_with_non-constant_expression.zig rename test/cases/compile_errors/{stage1/obj => }/implicit_cast_from_array_to_mutable_slice.zig (58%) rename test/cases/compile_errors/{stage1/obj => }/implicitly_increasing_pointer_alignment.zig (57%) rename test/cases/compile_errors/{stage1/obj => }/pass_const_ptr_to_mutable_ptr_fn.zig (52%) delete mode 100644 test/cases/compile_errors/stage1/obj/generic_function_instance_with_non-constant_expression.zig delete mode 100644 test/cases/compile_errors/stage1/obj/wrong_pointer_coerced_to_pointer_to_opaque_{}.zig rename test/cases/compile_errors/{stage1/obj => }/unreachable_parameter.zig (53%) rename test/cases/compile_errors/{stage1/obj => }/use_anyopaque_as_return_type_of_fn_ptr.zig (58%) rename test/cases/compile_errors/{stage1/obj => }/wrong_initializer_for_union_payload_of_type_type.zig (64%) create mode 100644 test/cases/compile_errors/wrong_pointer_coerced_to_pointer_to_opaque_{}.zig diff --git a/src/Module.zig b/src/Module.zig index a1081517c0..5f441790b7 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -5911,6 +5911,31 @@ pub fn paramSrc( } } +pub fn argSrc( + call_node_offset: i32, + gpa: Allocator, + decl: *Decl, + arg_i: usize, +) LazySrcLoc { + @setCold(true); + const tree = decl.getFileScope().getTree(gpa) catch |err| { + // In this case we emit a warning + a less precise source location. + log.warn("unable to load {s}: {s}", .{ + decl.getFileScope().sub_file_path, @errorName(err), + }); + return LazySrcLoc.nodeOffset(0); + }; + const node_tags = tree.nodes.items(.tag); + const node = decl.relativeToNodeIndex(call_node_offset); + var args: [1]Ast.Node.Index = undefined; + const full = switch (node_tags[node]) { + .call_one, .call_one_comma, .async_call_one, .async_call_one_comma => tree.callOne(&args, node), + .call, .call_comma, .async_call, .async_call_comma => tree.callFull(node), + else => unreachable, + }; + return LazySrcLoc.nodeOffset(decl.nodeIndexToRelative(full.ast.params[arg_i])); +} + /// Called from `performAllTheWork`, after all AstGen workers have finished, /// and before the main semantic analysis loop begins. pub fn processOutdatedAndDeletedDecls(mod: *Module) !void { diff --git a/src/Sema.zig b/src/Sema.zig index 2001067bbf..6a64dc78d8 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5613,84 +5613,39 @@ fn analyzeCall( // which means its parameter type expressions must be resolved in order and used // to successively coerce the arguments. const fn_info = sema.code.getFnInfo(module_fn.zir_body_inst); - const zir_tags = sema.code.instructions.items(.tag); var arg_i: usize = 0; - for (fn_info.param_body) |inst| switch (zir_tags[inst]) { - .param, .param_comptime => { - // Evaluate the parameter type expression now that previous ones have - // been mapped, and coerce the corresponding argument to it. - const pl_tok = sema.code.instructions.items(.data)[inst].pl_tok; - const param_src = pl_tok.src(); - const extra = sema.code.extraData(Zir.Inst.Param, pl_tok.payload_index); - const param_body = sema.code.extra[extra.end..][0..extra.data.body_len]; - const param_ty_inst = try sema.resolveBody(&child_block, param_body, inst); - const param_ty = try sema.analyzeAsType(&child_block, param_src, param_ty_inst); - new_fn_info.param_types[arg_i] = param_ty; - const arg_src = call_src; // TODO: better source location - const casted_arg = try sema.coerce(&child_block, param_ty, uncasted_args[arg_i], arg_src); - try sema.inst_map.putNoClobber(gpa, inst, casted_arg); - - if (is_comptime_call) { - // TODO explain why function is being called at comptime - const arg_val = try sema.resolveConstMaybeUndefVal(&child_block, arg_src, casted_arg, "argument to function being called at comptime must be comptime known"); - switch (arg_val.tag()) { - .generic_poison, .generic_poison_type => { - // This function is currently evaluated as part of an as-of-yet unresolvable - // parameter or return type. - return error.GenericPoison; - }, - else => { - // Needed so that lazy values do not trigger - // assertion due to type not being resolved - // when the hash function is called. - try sema.resolveLazyValue(&child_block, arg_src, arg_val); - }, - } - should_memoize = should_memoize and !arg_val.canMutateComptimeVarState(); - memoized_call_key.args[arg_i] = .{ - .ty = param_ty, - .val = arg_val, - }; - } - - arg_i += 1; - continue; - }, - .param_anytype, .param_anytype_comptime => { - // No coercion needed. - const uncasted_arg = uncasted_args[arg_i]; - new_fn_info.param_types[arg_i] = sema.typeOf(uncasted_arg); - try sema.inst_map.putNoClobber(gpa, inst, uncasted_arg); - - if (is_comptime_call) { - const arg_src = call_src; // TODO: better source location - // TODO explain why function is being called at comptime - const arg_val = try sema.resolveConstMaybeUndefVal(&child_block, arg_src, uncasted_arg, "argument to function being called at comptime must be comptime known"); - switch (arg_val.tag()) { - .generic_poison, .generic_poison_type => { - // This function is currently evaluated as part of an as-of-yet unresolvable - // parameter or return type. - return error.GenericPoison; - }, - else => { - // Needed so that lazy values do not trigger - // assertion due to type not being resolved - // when the hash function is called. - try sema.resolveLazyValue(&child_block, arg_src, arg_val); - }, - } - should_memoize = should_memoize and !arg_val.canMutateComptimeVarState(); - memoized_call_key.args[arg_i] = .{ - .ty = sema.typeOf(uncasted_arg), - .val = arg_val, - }; - } - - arg_i += 1; - continue; - }, - else => continue, - }; + for (fn_info.param_body) |inst| { + sema.analyzeInlineCallArg( + &child_block, + .unneeded, + inst, + new_fn_info, + &arg_i, + uncasted_args, + is_comptime_call, + &should_memoize, + memoized_call_key, + ) catch |err| switch (err) { + error.NeededSourceLocation => { + const decl = sema.mod.declPtr(block.src_decl); + try sema.analyzeInlineCallArg( + // Intentionally use the wrong block here since we know it's + // going to fail and `argSrc` is relative to `block.src_decl`. + block, + Module.argSrc(call_src.node_offset.x, sema.gpa, decl, arg_i), + inst, + new_fn_info, + &arg_i, + uncasted_args, + is_comptime_call, + &should_memoize, + memoized_call_key, + ); + return error.AnalysisFail; + }, + else => |e| return e, + }; + } // In case it is a generic function with an expression for the return type that depends // on parameters, we must now do the same for the return type as we just did with @@ -5746,6 +5701,7 @@ fn analyzeCall( if (!is_comptime_call) { try sema.emitDbgInline(block, parent_func.?, module_fn, new_func_resolved_ty, .dbg_inline_begin); + const zir_tags = sema.code.instructions.items(.tag); for (fn_info.param_body) |param| switch (zir_tags[param]) { .param, .param_comptime => { const inst_data = sema.code.instructions.items(.data)[param].pl_tok; @@ -5826,11 +5782,26 @@ fn analyzeCall( const args = try sema.arena.alloc(Air.Inst.Ref, uncasted_args.len); for (uncasted_args) |uncasted_arg, i| { - const arg_src = call_src; // TODO: better source location if (i < fn_params_len) { const param_ty = func_ty.fnParamType(i); - try sema.resolveTypeFully(block, arg_src, param_ty); - args[i] = try sema.coerce(block, param_ty, uncasted_arg, arg_src); + args[i] = sema.analyzeCallArg( + block, + .unneeded, + param_ty, + uncasted_arg, + ) catch |err| switch (err) { + error.NeededSourceLocation => { + const decl = sema.mod.declPtr(block.src_decl); + _ = try sema.analyzeCallArg( + block, + Module.argSrc(call_src.node_offset.x, sema.gpa, decl, i), + param_ty, + uncasted_arg, + ); + return error.AnalysisFail; + }, + else => |e| return e, + }; } else { args[i] = uncasted_arg; } @@ -5862,6 +5833,136 @@ fn analyzeCall( return result; } +fn analyzeInlineCallArg( + sema: *Sema, + block: *Block, + arg_src: LazySrcLoc, + inst: Zir.Inst.Index, + new_fn_info: Type.Payload.Function.Data, + arg_i: *usize, + uncasted_args: []const Air.Inst.Ref, + is_comptime_call: bool, + should_memoize: *bool, + memoized_call_key: Module.MemoizedCall.Key, +) !void { + const zir_tags = sema.code.instructions.items(.tag); + switch (zir_tags[inst]) { + .param, .param_comptime => { + // Evaluate the parameter type expression now that previous ones have + // been mapped, and coerce the corresponding argument to it. + const pl_tok = sema.code.instructions.items(.data)[inst].pl_tok; + const param_src = pl_tok.src(); + const extra = sema.code.extraData(Zir.Inst.Param, pl_tok.payload_index); + const param_body = sema.code.extra[extra.end..][0..extra.data.body_len]; + const param_ty_inst = try sema.resolveBody(block, param_body, inst); + const param_ty = try sema.analyzeAsType(block, param_src, param_ty_inst); + new_fn_info.param_types[arg_i.*] = param_ty; + const uncasted_arg = uncasted_args[arg_i.*]; + if (try sema.typeRequiresComptime(block, arg_src, param_ty)) { + _ = try sema.resolveConstMaybeUndefVal(block, arg_src, uncasted_arg, "argument to parameter with comptime only type must be comptime known"); + } + const casted_arg = try sema.coerce(block, param_ty, uncasted_arg, arg_src); + try sema.inst_map.putNoClobber(sema.gpa, inst, casted_arg); + + if (is_comptime_call) { + // TODO explain why function is being called at comptime + const arg_val = try sema.resolveConstMaybeUndefVal(block, arg_src, casted_arg, "argument to function being called at comptime must be comptime known"); + switch (arg_val.tag()) { + .generic_poison, .generic_poison_type => { + // This function is currently evaluated as part of an as-of-yet unresolvable + // parameter or return type. + return error.GenericPoison; + }, + else => { + // Needed so that lazy values do not trigger + // assertion due to type not being resolved + // when the hash function is called. + try sema.resolveLazyValue(block, arg_src, arg_val); + }, + } + should_memoize.* = should_memoize.* and !arg_val.canMutateComptimeVarState(); + memoized_call_key.args[arg_i.*] = .{ + .ty = param_ty, + .val = arg_val, + }; + } + + arg_i.* += 1; + }, + .param_anytype, .param_anytype_comptime => { + // No coercion needed. + const uncasted_arg = uncasted_args[arg_i.*]; + new_fn_info.param_types[arg_i.*] = sema.typeOf(uncasted_arg); + try sema.inst_map.putNoClobber(sema.gpa, inst, uncasted_arg); + + if (is_comptime_call) { + // TODO explain why function is being called at comptime + const arg_val = try sema.resolveConstMaybeUndefVal(block, arg_src, uncasted_arg, "argument to function being called at comptime must be comptime known"); + switch (arg_val.tag()) { + .generic_poison, .generic_poison_type => { + // This function is currently evaluated as part of an as-of-yet unresolvable + // parameter or return type. + return error.GenericPoison; + }, + else => { + // Needed so that lazy values do not trigger + // assertion due to type not being resolved + // when the hash function is called. + try sema.resolveLazyValue(block, arg_src, arg_val); + }, + } + should_memoize.* = should_memoize.* and !arg_val.canMutateComptimeVarState(); + memoized_call_key.args[arg_i.*] = .{ + .ty = sema.typeOf(uncasted_arg), + .val = arg_val, + }; + } + + arg_i.* += 1; + }, + else => {}, + } +} + +fn analyzeCallArg( + sema: *Sema, + block: *Block, + arg_src: LazySrcLoc, + param_ty: Type, + uncasted_arg: Air.Inst.Ref, +) !Air.Inst.Ref { + try sema.resolveTypeFully(block, arg_src, param_ty); + return sema.coerce(block, param_ty, uncasted_arg, arg_src); +} + +fn analyzeGenericCallArg( + sema: *Sema, + block: *Block, + arg_src: LazySrcLoc, + uncasted_arg: Air.Inst.Ref, + comptime_arg: TypedValue, + runtime_args: []Air.Inst.Ref, + new_fn_info: Type.Payload.Function.Data, + runtime_i: *u32, +) !void { + const is_runtime = comptime_arg.val.tag() == .generic_poison and + comptime_arg.ty.hasRuntimeBits() and + !(try sema.typeRequiresComptime(block, arg_src, comptime_arg.ty)); + if (is_runtime) { + const param_ty = new_fn_info.param_types[runtime_i.*]; + const casted_arg = try sema.coerce(block, param_ty, uncasted_arg, arg_src); + try sema.queueFullTypeResolution(param_ty); + runtime_args[runtime_i.*] = casted_arg; + runtime_i.* += 1; + } +} + +fn analyzeGenericCallArgVal(sema: *Sema, block: *Block, arg_src: LazySrcLoc, uncasted_arg: Air.Inst.Ref) !Value { + const arg_val = try sema.resolveValue(block, arg_src, uncasted_arg, "parameter is comptime"); + try sema.resolveLazyValue(block, arg_src, arg_val); + return arg_val; +} + fn instantiateGenericCall( sema: *Sema, block: *Block, @@ -5927,10 +6028,16 @@ fn instantiateGenericCall( } if (is_comptime) { - const arg_src = call_src; // TODO better source location const arg_ty = sema.typeOf(uncasted_args[i]); - const arg_val = try sema.resolveValue(block, arg_src, uncasted_args[i], "parameter is comptime"); - try sema.resolveLazyValue(block, arg_src, arg_val); + const arg_val = sema.analyzeGenericCallArgVal(block, .unneeded, uncasted_args[i]) catch |err| switch (err) { + error.NeededSourceLocation => { + const decl = sema.mod.declPtr(block.src_decl); + const arg_src = Module.argSrc(call_src.node_offset.x, sema.gpa, decl, i); + _ = try sema.analyzeGenericCallArgVal(block, arg_src, uncasted_args[i]); + return error.AnalysisFail; + }, + else => |e| return e, + }; arg_val.hash(arg_ty, &hasher, mod); if (is_anytype) { arg_ty.hashWithHasher(&hasher, mod); @@ -6086,19 +6193,18 @@ fn instantiateGenericCall( }, else => continue, } - const arg_src = call_src; // TODO: better source location const arg = uncasted_args[arg_i]; if (is_comptime) { - if (try sema.resolveMaybeUndefVal(block, arg_src, arg)) |arg_val| { + if (try sema.resolveMaybeUndefVal(block, .unneeded, arg)) |arg_val| { const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val); child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); } else { - return sema.failWithNeededComptime(block, arg_src, "parameter is comptime"); + return sema.failWithNeededComptime(block, .unneeded, undefined); } } else if (is_anytype) { const arg_ty = sema.typeOf(arg); - if (try sema.typeRequiresComptime(block, arg_src, arg_ty)) { - const arg_val = try sema.resolveConstValue(block, arg_src, arg, "type of anytype parameter requires comptime"); + if (try sema.typeRequiresComptime(block, .unneeded, arg_ty)) { + const arg_val = try sema.resolveConstValue(block, .unneeded, arg, undefined); const child_arg = try child_sema.addConstant(arg_ty, arg_val); child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); } else { @@ -6156,8 +6262,7 @@ fn instantiateGenericCall( const copied_arg_ty = try child_sema.typeOf(arg).copy(new_decl_arena_allocator); anytype_args[arg_i] = is_anytype; - const arg_src = call_src; // TODO: better source location - if (try sema.typeRequiresComptime(block, arg_src, copied_arg_ty)) { + if (try sema.typeRequiresComptime(block, .unneeded, copied_arg_ty)) { is_comptime = true; } @@ -6236,18 +6341,30 @@ fn instantiateGenericCall( .param_comptime, .param_anytype_comptime, .param, .param_anytype => {}, else => continue, } - const arg_src = call_src; // TODO: better source location - const is_runtime = comptime_args[total_i].val.tag() == .generic_poison and - comptime_args[total_i].ty.hasRuntimeBits() and - !(try sema.typeRequiresComptime(block, arg_src, comptime_args[total_i].ty)); - if (is_runtime) { - const param_ty = new_fn_info.param_types[runtime_i]; - const uncasted_arg = uncasted_args[total_i]; - const casted_arg = try sema.coerce(block, param_ty, uncasted_arg, arg_src); - try sema.queueFullTypeResolution(param_ty); - runtime_args[runtime_i] = casted_arg; - runtime_i += 1; - } + sema.analyzeGenericCallArg( + block, + .unneeded, + uncasted_args[total_i], + comptime_args[total_i], + runtime_args, + new_fn_info, + &runtime_i, + ) catch |err| switch (err) { + error.NeededSourceLocation => { + const decl = sema.mod.declPtr(block.src_decl); + _ = try sema.analyzeGenericCallArg( + block, + Module.argSrc(call_src.node_offset.x, sema.gpa, decl, total_i), + uncasted_args[total_i], + comptime_args[total_i], + runtime_args, + new_fn_info, + &runtime_i, + ); + return error.AnalysisFail; + }, + else => |e| return e, + }; total_i += 1; } diff --git a/test/cases/compile_errors/calling_var_args_extern_function_passing_array_instead_of_pointer.zig b/test/cases/compile_errors/calling_var_args_extern_function_passing_array_instead_of_pointer.zig index fe8037d287..846a5ff81a 100644 --- a/test/cases/compile_errors/calling_var_args_extern_function_passing_array_instead_of_pointer.zig +++ b/test/cases/compile_errors/calling_var_args_extern_function_passing_array_instead_of_pointer.zig @@ -7,4 +7,4 @@ pub extern fn foo(format: *const u8, ...) void; // backend=stage2 // target=native // -// :2:8: error: expected type '*const u8', found '[5:0]u8' +// :2:16: error: expected type '*const u8', found '[5:0]u8' diff --git a/test/cases/compile_errors/casting_bit_offset_pointer_to_regular_pointer.zig b/test/cases/compile_errors/casting_bit_offset_pointer_to_regular_pointer.zig index f25a4a656b..132627557e 100644 --- a/test/cases/compile_errors/casting_bit_offset_pointer_to_regular_pointer.zig +++ b/test/cases/compile_errors/casting_bit_offset_pointer_to_regular_pointer.zig @@ -18,6 +18,6 @@ export fn entry() usize { return @sizeOf(@TypeOf(&foo)); } // backend=stage2 // target=native // -// :8:15: error: expected type '*const u3', found '*align(0:3:1) const u3' -// :8:15: note: pointer host size '1' cannot cast into pointer host size '0' -// :8:15: note: pointer bit offset '3' cannot cast into pointer bit offset '0' +// :8:16: error: expected type '*const u3', found '*align(0:3:1) const u3' +// :8:16: note: pointer host size '1' cannot cast into pointer host size '0' +// :8:16: note: pointer bit offset '3' cannot cast into pointer bit offset '0' diff --git a/test/cases/compile_errors/stage1/obj/disallow_coercion_from_non-null-terminated_pointer_to_null-terminated_pointer.zig b/test/cases/compile_errors/disallow_coercion_from_non-null-terminated_pointer_to_null-terminated_pointer.zig similarity index 54% rename from test/cases/compile_errors/stage1/obj/disallow_coercion_from_non-null-terminated_pointer_to_null-terminated_pointer.zig rename to test/cases/compile_errors/disallow_coercion_from_non-null-terminated_pointer_to_null-terminated_pointer.zig index 1d0a6216dd..0074c26de7 100644 --- a/test/cases/compile_errors/stage1/obj/disallow_coercion_from_non-null-terminated_pointer_to_null-terminated_pointer.zig +++ b/test/cases/compile_errors/disallow_coercion_from_non-null-terminated_pointer_to_null-terminated_pointer.zig @@ -1,12 +1,13 @@ extern fn puts(s: [*:0]const u8) c_int; -pub fn main() void { +pub export fn entry() void { const no_zero_array = [_]u8{'h', 'e', 'l', 'l', 'o'}; const no_zero_ptr: [*]const u8 = &no_zero_array; _ = puts(no_zero_ptr); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:5:14: error: expected type '[*:0]const u8', found '[*]const u8' +// :5:14: error: expected type '[*:0]const u8', found '[*]const u8' +// :5:14: note: destination pointer requires '0' sentinel diff --git a/test/cases/compile_errors/error_note_for_function_parameter_incompatibility.zig b/test/cases/compile_errors/error_note_for_function_parameter_incompatibility.zig index c43df47dfd..76543697a4 100644 --- a/test/cases/compile_errors/error_note_for_function_parameter_incompatibility.zig +++ b/test/cases/compile_errors/error_note_for_function_parameter_incompatibility.zig @@ -8,6 +8,6 @@ export fn entry() void { // backend=stage2 // target=native // -// :4:17: error: expected type '*const fn(i32) void', found '*const fn(bool) void' -// :4:17: note: pointer type child 'fn(bool) void' cannot cast into pointer type child 'fn(i32) void' -// :4:17: note: parameter 0 'bool' cannot cast into 'i32' +// :4:18: error: expected type '*const fn(i32) void', found '*const fn(bool) void' +// :4:18: note: pointer type child 'fn(bool) void' cannot cast into pointer type child 'fn(i32) void' +// :4:18: note: parameter 0 'bool' cannot cast into 'i32' diff --git a/test/cases/compile_errors/function_parameter_is_opaque.zig b/test/cases/compile_errors/function_parameter_is_opaque.zig index 57c89bd7f4..1f92274577 100644 --- a/test/cases/compile_errors/function_parameter_is_opaque.zig +++ b/test/cases/compile_errors/function_parameter_is_opaque.zig @@ -27,5 +27,4 @@ export fn entry4() void { // :1:17: note: opaque declared here // :8:28: error: parameter of type '@TypeOf(null)' not allowed // :12:8: error: parameter of opaque type 'tmp.FooType' not allowed -// :1:17: note: opaque declared here // :17:8: error: parameter of type '@TypeOf(null)' not allowed diff --git a/test/cases/compile_errors/generic_function_instance_with_non-constant_expression.zig b/test/cases/compile_errors/generic_function_instance_with_non-constant_expression.zig new file mode 100644 index 0000000000..1317c4376a --- /dev/null +++ b/test/cases/compile_errors/generic_function_instance_with_non-constant_expression.zig @@ -0,0 +1,13 @@ +fn foo(comptime x: i32, y: i32) i32 { return x + y; } +fn test1(a: i32, b: i32) i32 { + return foo(a, b); +} + +export fn entry() usize { return @sizeOf(@TypeOf(&test1)); } + +// error +// backend=stage2 +// target=native +// +// :3:16: error: unable to resolve comptime value +// :3:16: note: parameter is comptime diff --git a/test/cases/compile_errors/stage1/obj/implicit_cast_from_array_to_mutable_slice.zig b/test/cases/compile_errors/implicit_cast_from_array_to_mutable_slice.zig similarity index 58% rename from test/cases/compile_errors/stage1/obj/implicit_cast_from_array_to_mutable_slice.zig rename to test/cases/compile_errors/implicit_cast_from_array_to_mutable_slice.zig index 012555928c..035fb68d9e 100644 --- a/test/cases/compile_errors/stage1/obj/implicit_cast_from_array_to_mutable_slice.zig +++ b/test/cases/compile_errors/implicit_cast_from_array_to_mutable_slice.zig @@ -5,7 +5,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:9: error: expected type '[]i32', found '[10]i32' +// :4:9: error: array literal requires address-of operator (&) to coerce to slice type '[]i32' diff --git a/test/cases/compile_errors/stage1/obj/implicitly_increasing_pointer_alignment.zig b/test/cases/compile_errors/implicitly_increasing_pointer_alignment.zig similarity index 57% rename from test/cases/compile_errors/stage1/obj/implicitly_increasing_pointer_alignment.zig rename to test/cases/compile_errors/implicitly_increasing_pointer_alignment.zig index 3743eef0b2..b9a9fb2faf 100644 --- a/test/cases/compile_errors/stage1/obj/implicitly_increasing_pointer_alignment.zig +++ b/test/cases/compile_errors/implicitly_increasing_pointer_alignment.zig @@ -13,7 +13,8 @@ fn bar(x: *u32) void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:8:13: error: expected type '*u32', found '*align(1) u32' +// :8:9: error: expected type '*u32', found '*align(1) u32' +// :8:9: note: pointer alignment '1' cannot cast into pointer alignment '4' diff --git a/test/cases/compile_errors/stage1/obj/pass_const_ptr_to_mutable_ptr_fn.zig b/test/cases/compile_errors/pass_const_ptr_to_mutable_ptr_fn.zig similarity index 52% rename from test/cases/compile_errors/stage1/obj/pass_const_ptr_to_mutable_ptr_fn.zig rename to test/cases/compile_errors/pass_const_ptr_to_mutable_ptr_fn.zig index 3209bb7321..39c55870f1 100644 --- a/test/cases/compile_errors/stage1/obj/pass_const_ptr_to_mutable_ptr_fn.zig +++ b/test/cases/compile_errors/pass_const_ptr_to_mutable_ptr_fn.zig @@ -8,10 +8,11 @@ fn ptrEql(a: *[]const u8, b: *[]const u8) bool { return true; } -export fn entry() usize { return @sizeOf(@TypeOf(foo)); } +export fn entry() usize { return @sizeOf(@TypeOf(&foo)); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:4:19: error: expected type '*[]const u8', found '*const []const u8' +// :4:19: error: expected type '*[]const u8', found '*const []const u8' +// :4:19: note: cast discards const qualifier diff --git a/test/cases/compile_errors/stage1/obj/generic_function_instance_with_non-constant_expression.zig b/test/cases/compile_errors/stage1/obj/generic_function_instance_with_non-constant_expression.zig deleted file mode 100644 index 9cdd653766..0000000000 --- a/test/cases/compile_errors/stage1/obj/generic_function_instance_with_non-constant_expression.zig +++ /dev/null @@ -1,12 +0,0 @@ -fn foo(comptime x: i32, y: i32) i32 { return x + y; } -fn test1(a: i32, b: i32) i32 { - return foo(a, b); -} - -export fn entry() usize { return @sizeOf(@TypeOf(test1)); } - -// error -// backend=stage1 -// target=native -// -// tmp.zig:3:16: error: runtime value cannot be passed to comptime arg diff --git a/test/cases/compile_errors/stage1/obj/wrong_pointer_coerced_to_pointer_to_opaque_{}.zig b/test/cases/compile_errors/stage1/obj/wrong_pointer_coerced_to_pointer_to_opaque_{}.zig deleted file mode 100644 index 3f6b0e750b..0000000000 --- a/test/cases/compile_errors/stage1/obj/wrong_pointer_coerced_to_pointer_to_opaque_{}.zig +++ /dev/null @@ -1,12 +0,0 @@ -const Derp = opaque {}; -extern fn bar(d: *Derp) void; -export fn foo() void { - var x = @as(u8, 1); - bar(@ptrCast(*anyopaque, &x)); -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:5:9: error: expected type '*Derp', found '*anyopaque' diff --git a/test/cases/compile_errors/type_checking_function_pointers.zig b/test/cases/compile_errors/type_checking_function_pointers.zig index e6c7a7a1f7..f113309170 100644 --- a/test/cases/compile_errors/type_checking_function_pointers.zig +++ b/test/cases/compile_errors/type_checking_function_pointers.zig @@ -10,6 +10,6 @@ export fn entry() void { // backend=stage2 // target=native // -// :6:6: error: expected type '*const fn(*const u8) void', found '*const fn(u8) void' -// :6:6: note: pointer type child 'fn(u8) void' cannot cast into pointer type child 'fn(*const u8) void' -// :6:6: note: parameter 0 'u8' cannot cast into '*const u8' +// :6:7: error: expected type '*const fn(*const u8) void', found '*const fn(u8) void' +// :6:7: note: pointer type child 'fn(u8) void' cannot cast into pointer type child 'fn(*const u8) void' +// :6:7: note: parameter 0 'u8' cannot cast into '*const u8' diff --git a/test/cases/compile_errors/stage1/obj/unreachable_parameter.zig b/test/cases/compile_errors/unreachable_parameter.zig similarity index 53% rename from test/cases/compile_errors/stage1/obj/unreachable_parameter.zig rename to test/cases/compile_errors/unreachable_parameter.zig index 296194f7e3..3164974432 100644 --- a/test/cases/compile_errors/stage1/obj/unreachable_parameter.zig +++ b/test/cases/compile_errors/unreachable_parameter.zig @@ -2,7 +2,7 @@ fn f(a: noreturn) void { _ = a; } export fn entry() void { f(); } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:1:9: error: parameter of type 'noreturn' not allowed +// :1:6: error: parameter of type 'noreturn' not allowed diff --git a/test/cases/compile_errors/stage1/obj/use_anyopaque_as_return_type_of_fn_ptr.zig b/test/cases/compile_errors/use_anyopaque_as_return_type_of_fn_ptr.zig similarity index 58% rename from test/cases/compile_errors/stage1/obj/use_anyopaque_as_return_type_of_fn_ptr.zig rename to test/cases/compile_errors/use_anyopaque_as_return_type_of_fn_ptr.zig index ef2787ca75..beccd50c7f 100644 --- a/test/cases/compile_errors/stage1/obj/use_anyopaque_as_return_type_of_fn_ptr.zig +++ b/test/cases/compile_errors/use_anyopaque_as_return_type_of_fn_ptr.zig @@ -4,7 +4,7 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:2:20: error: return type cannot be opaque +// :2:20: error: opaque return type 'anyopaque' not allowed diff --git a/test/cases/compile_errors/stage1/obj/wrong_initializer_for_union_payload_of_type_type.zig b/test/cases/compile_errors/wrong_initializer_for_union_payload_of_type_type.zig similarity index 64% rename from test/cases/compile_errors/stage1/obj/wrong_initializer_for_union_payload_of_type_type.zig rename to test/cases/compile_errors/wrong_initializer_for_union_payload_of_type_type.zig index 2e7b8f2e40..dd69531b01 100644 --- a/test/cases/compile_errors/stage1/obj/wrong_initializer_for_union_payload_of_type_type.zig +++ b/test/cases/compile_errors/wrong_initializer_for_union_payload_of_type_type.zig @@ -10,7 +10,8 @@ export fn entry() void { } // error -// backend=stage1 +// backend=stage2 // target=native // -// tmp.zig:9:8: error: use of undefined value here causes undefined behavior +// :9:14: error: expected type 'type', found 'tmp.U' +// :1:11: note: union declared here diff --git a/test/cases/compile_errors/wrong_pointer_coerced_to_pointer_to_opaque_{}.zig b/test/cases/compile_errors/wrong_pointer_coerced_to_pointer_to_opaque_{}.zig new file mode 100644 index 0000000000..e22c285cb0 --- /dev/null +++ b/test/cases/compile_errors/wrong_pointer_coerced_to_pointer_to_opaque_{}.zig @@ -0,0 +1,14 @@ +const Derp = opaque {}; +extern fn bar(d: *Derp) void; +export fn foo() void { + var x = @as(u8, 1); + bar(@ptrCast(*anyopaque, &x)); +} + +// error +// backend=stage2 +// target=native +// +// :5:9: error: expected type '*tmp.Derp', found '*anyopaque' +// :5:9: note: pointer type child 'anyopaque' cannot cast into pointer type child 'tmp.Derp' +// :1:14: note: opaque declared here From 794beafb9c2c687d993a0933be258a2ccdf0be4f Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 20 Jul 2022 12:30:11 +0300 Subject: [PATCH 13/18] Sema: validate extern struct field types earlier `validateExternType` does not require the type to be resolved so we can check it earlier. Only doing it in `resolveTypeFully` lead to worse or missing compile errors. --- src/Sema.zig | 108 +++++++++++++----- test/behavior/align.zig | 4 +- test/behavior/bugs/1310.zig | 4 +- ...edding_opaque_type_in_struct_and_union.zig | 38 ++++++ ...mpatible_but_inferred_integer_tag_type.zig | 6 +- ...non-extern-compatible_integer_tag_type.zig | 6 +- ...invalid_optional_type_in_extern_struct.zig | 4 +- ...edding_opaque_type_in_struct_and_union.zig | 29 ----- 8 files changed, 129 insertions(+), 70 deletions(-) create mode 100644 test/cases/compile_errors/directly_embedding_opaque_type_in_struct_and_union.zig delete mode 100644 test/cases/compile_errors/stage1/obj/directly_embedding_opaque_type_in_struct_and_union.zig diff --git a/src/Sema.zig b/src/Sema.zig index 6a64dc78d8..47429b9dbf 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -14559,13 +14559,23 @@ fn zirStructInitAnon( var runtime_src: ?LazySrcLoc = null; var extra_index = extra.end; for (types) |*field_ty, i| { + const init_src = src; // TODO better source location const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index); extra_index = item.end; names[i] = sema.code.nullTerminatedString(item.data.field_name); const init = try sema.resolveInst(item.data.init); field_ty.* = sema.typeOf(init); - const init_src = src; // TODO better source location + if (types[i].zigTypeTag() == .Opaque) { + const msg = msg: { + const msg = try sema.errMsg(block, init_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); + errdefer msg.destroy(sema.gpa); + + try sema.addDeclaredHereNote(msg, types[i]); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } if (try sema.resolveMaybeUndefVal(block, init_src, init)) |init_val| { values[i] = init_val; } else { @@ -14742,9 +14752,19 @@ fn zirArrayInitAnon( const opt_runtime_src = rs: { var runtime_src: ?LazySrcLoc = null; for (operands) |operand, i| { + const operand_src = src; // TODO better source location const elem = try sema.resolveInst(operand); types[i] = sema.typeOf(elem); - const operand_src = src; // TODO better source location + if (types[i].zigTypeTag() == .Opaque) { + const msg = msg: { + const msg = try sema.errMsg(block, operand_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); + errdefer msg.destroy(sema.gpa); + + try sema.addDeclaredHereNote(msg, types[i]); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(block, msg); + } if (try sema.resolveMaybeUndefVal(block, operand_src, elem)) |val| { values[i] = val; } else { @@ -18641,6 +18661,8 @@ const ExternPosition = enum { other, }; +/// Returns true if `ty` is allowed in extern types. +/// Does *NOT* require `ty` to be resolved in any way. fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) CompileError!bool { switch (ty.zigTypeTag()) { .Type, @@ -25214,20 +25236,6 @@ fn resolveStructFully( struct_obj.status = .fully_resolved_wip; for (struct_obj.fields.values()) |field| { try sema.resolveTypeFully(block, src, field.ty); - - if (struct_obj.layout == .Extern and !(try sema.validateExternType(field.ty, .other))) { - const msg = msg: { - const msg = try sema.errMsg(block, src, "extern structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)}); - errdefer msg.destroy(sema.gpa); - - const src_decl = sema.mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotExtern(block, src, msg, src.toSrcLoc(src_decl), field.ty, .other); - - try sema.addDeclaredHereNote(msg, field.ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } } struct_obj.status = .fully_resolved; } @@ -25261,20 +25269,6 @@ fn resolveUnionFully( union_obj.status = .fully_resolved_wip; for (union_obj.fields.values()) |field| { try sema.resolveTypeFully(block, src, field.ty); - - if (union_obj.layout == .Extern and !(try sema.validateExternType(field.ty, .union_field))) { - const msg = msg: { - const msg = try sema.errMsg(block, src, "extern unions cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)}); - errdefer msg.destroy(sema.gpa); - - const src_decl = sema.mod.declPtr(block.src_decl); - try sema.explainWhyTypeIsNotExtern(block, src, msg, src.toSrcLoc(src_decl), field.ty, .union_field); - - try sema.addDeclaredHereNote(msg, field.ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(block, msg); - } } union_obj.status = .fully_resolved; } @@ -25612,6 +25606,33 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void const field = &struct_obj.fields.values()[i]; field.ty = try field_ty.copy(decl_arena_allocator); + if (struct_obj.layout == .Extern and !(try sema.validateExternType(field.ty, .other))) { + const msg = msg: { + const tree = try sema.getAstTree(&block_scope); + const fields_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, i); + const msg = try sema.errMsg(&block_scope, fields_src, "extern structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)}); + errdefer msg.destroy(sema.gpa); + + try sema.explainWhyTypeIsNotExtern(&block_scope, fields_src, msg, fields_src.toSrcLoc(decl), field.ty, .other); + + try sema.addDeclaredHereNote(msg, field.ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(&block_scope, msg); + } + if (field_ty.zigTypeTag() == .Opaque) { + const msg = msg: { + const tree = try sema.getAstTree(&block_scope); + const field_src = enumFieldSrcLoc(decl, tree.*, struct_obj.node_offset, i); + const msg = try sema.errMsg(&block_scope, field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); + errdefer msg.destroy(sema.gpa); + + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(&block_scope, msg); + } + if (zir_field.align_body_len > 0) { const body = zir.extra[extra_index..][0..zir_field.align_body_len]; extra_index += body.len; @@ -25909,6 +25930,33 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { } } + if (union_obj.layout == .Extern and !(try sema.validateExternType(field_ty, .union_field))) { + const msg = msg: { + const tree = try sema.getAstTree(&block_scope); + const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i); + const msg = try sema.errMsg(&block_scope, field_src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)}); + errdefer msg.destroy(sema.gpa); + + try sema.explainWhyTypeIsNotExtern(&block_scope, field_src, msg, field_src.toSrcLoc(decl), field_ty, .union_field); + + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(&block_scope, msg); + } + if (field_ty.zigTypeTag() == .Opaque) { + const msg = msg: { + const tree = try sema.getAstTree(&block_scope); + const field_src = enumFieldSrcLoc(decl, tree.*, union_obj.node_offset, field_i); + const msg = try sema.errMsg(&block_scope, field_src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{}); + errdefer msg.destroy(sema.gpa); + + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(&block_scope, msg); + } + gop.value_ptr.* = .{ .ty = try field_ty.copy(decl_arena_allocator), .abi_align = 0, diff --git a/test/behavior/align.zig b/test/behavior/align.zig index ad9a6d9616..d09e97c05b 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -299,7 +299,7 @@ test "implicitly decreasing fn alignment" { try testImplicitlyDecreaseFnAlign(alignedBig, 5678); } -fn testImplicitlyDecreaseFnAlign(ptr: *align(1) const fn () i32, answer: i32) !void { +fn testImplicitlyDecreaseFnAlign(ptr: *const fn () align(1) i32, answer: i32) !void { try expect(ptr() == answer); } @@ -325,7 +325,7 @@ test "@alignCast functions" { fn fnExpectsOnly1(ptr: *const fn () align(1) i32) i32 { return fnExpects4(@alignCast(4, ptr)); } -fn fnExpects4(ptr: *align(4) const fn () i32) i32 { +fn fnExpects4(ptr: *const fn () align(4) i32) i32 { return ptr(); } fn simple4() align(4) i32 { diff --git a/test/behavior/bugs/1310.zig b/test/behavior/bugs/1310.zig index 1f19ec20c2..0f574942d3 100644 --- a/test/behavior/bugs/1310.zig +++ b/test/behavior/bugs/1310.zig @@ -4,7 +4,7 @@ const builtin = @import("builtin"); pub const VM = ?[*]const struct_InvocationTable_; pub const struct_InvocationTable_ = extern struct { - GetVM: ?fn (?[*]VM) callconv(.C) c_int, + GetVM: ?*const fn (?[*]VM) callconv(.C) c_int, }; pub const struct_VM_ = extern struct { @@ -23,5 +23,7 @@ fn agent_callback(_vm: [*]VM, options: [*]u8) callconv(.C) i32 { } test "fixed" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; try expect(agent_callback(undefined, undefined) == 11); } diff --git a/test/cases/compile_errors/directly_embedding_opaque_type_in_struct_and_union.zig b/test/cases/compile_errors/directly_embedding_opaque_type_in_struct_and_union.zig new file mode 100644 index 0000000000..5cfce8e61e --- /dev/null +++ b/test/cases/compile_errors/directly_embedding_opaque_type_in_struct_and_union.zig @@ -0,0 +1,38 @@ +const O = opaque {}; +const Foo = struct { + o: O, +}; +const Bar = union { + One: i32, + Two: O, +}; +export fn a() void { + var foo: Foo = undefined; + _ = foo; +} +export fn b() void { + var bar: Bar = undefined; + _ = bar; +} +export fn c() void { + const baz = &@as(opaque {}, undefined); + const qux = .{baz.*}; + _ = qux; +} +export fn d() void { + const baz = &@as(opaque {}, undefined); + const qux = .{ .a = baz.* }; + _ = qux; +} + +// error +// backend=stage2 +// target=native +// +// :3:5: error: opaque types have unknown size and therefore cannot be directly embedded in structs +// :1:11: note: opaque declared here +// :7:5: error: opaque types have unknown size and therefore cannot be directly embedded in unions +// :19:18: error: opaque types have unknown size and therefore cannot be directly embedded in structs +// :18:22: note: opaque declared here +// :24:18: error: opaque types have unknown size and therefore cannot be directly embedded in structs +// :23:22: note: opaque declared here diff --git a/test/cases/compile_errors/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig b/test/cases/compile_errors/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig index 1740574389..d12bea66d7 100644 --- a/test/cases/compile_errors/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig +++ b/test/cases/compile_errors/extern_struct_with_extern-compatible_but_inferred_integer_tag_type.zig @@ -39,7 +39,7 @@ export fn entry() void { // backend=stage2 // target=native // -// :33:8: error: extern structs cannot contain fields of type 'tmp.E' -// :33:8: note: enum tag type 'u9' is not extern compatible -// :33:8: note: only integers with power of two bits are extern compatible +// :31:5: error: extern structs cannot contain fields of type 'tmp.E' +// :31:5: note: enum tag type 'u9' is not extern compatible +// :31:5: note: only integers with power of two bits are extern compatible // :1:15: note: enum declared here diff --git a/test/cases/compile_errors/extern_struct_with_non-extern-compatible_integer_tag_type.zig b/test/cases/compile_errors/extern_struct_with_non-extern-compatible_integer_tag_type.zig index 5fb36b707d..61073e9803 100644 --- a/test/cases/compile_errors/extern_struct_with_non-extern-compatible_integer_tag_type.zig +++ b/test/cases/compile_errors/extern_struct_with_non-extern-compatible_integer_tag_type.zig @@ -11,7 +11,7 @@ export fn entry() void { // backend=stage2 // target=native // -// :5:8: error: extern structs cannot contain fields of type 'tmp.E' -// :5:8: note: enum tag type 'u31' is not extern compatible -// :5:8: note: only integers with power of two bits are extern compatible +// :3:5: error: extern structs cannot contain fields of type 'tmp.E' +// :3:5: note: enum tag type 'u31' is not extern compatible +// :3:5: note: only integers with power of two bits are extern compatible // :1:15: note: enum declared here diff --git a/test/cases/compile_errors/invalid_optional_type_in_extern_struct.zig b/test/cases/compile_errors/invalid_optional_type_in_extern_struct.zig index 9a6c77363c..3dc9e64765 100644 --- a/test/cases/compile_errors/invalid_optional_type_in_extern_struct.zig +++ b/test/cases/compile_errors/invalid_optional_type_in_extern_struct.zig @@ -7,5 +7,5 @@ export fn testf(fluff: *stroo) void { _ = fluff; } // backend=stage2 // target=native // -// :4:8: error: extern structs cannot contain fields of type '?[*c]u8' -// :4:8: note: only pointer like optionals are extern compatible +// :2:5: error: extern structs cannot contain fields of type '?[*c]u8' +// :2:5: note: only pointer like optionals are extern compatible diff --git a/test/cases/compile_errors/stage1/obj/directly_embedding_opaque_type_in_struct_and_union.zig b/test/cases/compile_errors/stage1/obj/directly_embedding_opaque_type_in_struct_and_union.zig deleted file mode 100644 index 8b8d84ad2a..0000000000 --- a/test/cases/compile_errors/stage1/obj/directly_embedding_opaque_type_in_struct_and_union.zig +++ /dev/null @@ -1,29 +0,0 @@ -const O = opaque {}; -const Foo = struct { - o: O, -}; -const Bar = union { - One: i32, - Two: O, -}; -export fn a() void { - var foo: Foo = undefined; - _ = foo; -} -export fn b() void { - var bar: Bar = undefined; - _ = bar; -} -export fn c() void { - var baz: *opaque {} = undefined; - const qux = .{baz.*}; - _ = qux; -} - -// error -// backend=stage1 -// target=native -// -// tmp.zig:3:5: error: opaque types have unknown size and therefore cannot be directly embedded in structs -// tmp.zig:7:5: error: opaque types have unknown size and therefore cannot be directly embedded in unions -// tmp.zig:19:22: error: opaque types have unknown size and therefore cannot be directly embedded in structs From 644baa0f5bde6e51304dee46aa9e7eae1e46c863 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 20 Jul 2022 16:51:17 -0700 Subject: [PATCH 14/18] constrain target/backend of new test cases avoids testing stuff that doesn't work yet. --- .../compile_errors/endless_loop_in_function_evaluation.zig | 2 +- test/cases/compile_errors/function_ptr_alignment.zig | 2 +- .../implicit_cast_from_array_to_mutable_slice.zig | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/cases/compile_errors/endless_loop_in_function_evaluation.zig b/test/cases/compile_errors/endless_loop_in_function_evaluation.zig index 7616bfa5e7..d63f896f45 100644 --- a/test/cases/compile_errors/endless_loop_in_function_evaluation.zig +++ b/test/cases/compile_errors/endless_loop_in_function_evaluation.zig @@ -7,7 +7,7 @@ export fn entry() usize { return @sizeOf(@TypeOf(&seventh_fib_number)); } // error // backend=stage2 -// target=native +// target=x86_64-linux // // :3:21: error: evaluation exceeded 1000 backwards branches // :3:21: note: use @setEvalBranchQuota() to raise the branch limit from 1000 diff --git a/test/cases/compile_errors/function_ptr_alignment.zig b/test/cases/compile_errors/function_ptr_alignment.zig index e242e666f2..ca0f3ee2ac 100644 --- a/test/cases/compile_errors/function_ptr_alignment.zig +++ b/test/cases/compile_errors/function_ptr_alignment.zig @@ -19,7 +19,7 @@ comptime { // error // backend=stage2 -// target=native +// target=x86_64-linux // // :2:19: error: function pointer alignment disagrees with function alignment // :16:19: error: function pointer alignment disagrees with function alignment diff --git a/test/cases/compile_errors/implicit_cast_from_array_to_mutable_slice.zig b/test/cases/compile_errors/implicit_cast_from_array_to_mutable_slice.zig index 035fb68d9e..e7ae5d7277 100644 --- a/test/cases/compile_errors/implicit_cast_from_array_to_mutable_slice.zig +++ b/test/cases/compile_errors/implicit_cast_from_array_to_mutable_slice.zig @@ -5,7 +5,7 @@ export fn entry() void { } // error -// backend=stage2 +// backend=llvm // target=native // // :4:9: error: array literal requires address-of operator (&) to coerce to slice type '[]i32' From b9469345761bbac5dcb95f2ba662d4ddd7fc567e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 21 Jul 2022 12:20:50 -0700 Subject: [PATCH 15/18] build.zig: 32 MiB stack size This gives more headroom for comptime recursion, especially for debug builds of Zig. --- build.zig | 3 +++ .../compile_errors/endless_loop_in_function_evaluation.zig | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/build.zig b/build.zig index 31347f8e7e..cd29b5b4fd 100644 --- a/build.zig +++ b/build.zig @@ -11,6 +11,7 @@ const InstallDirectoryOptions = std.build.InstallDirectoryOptions; const assert = std.debug.assert; const zig_version = std.builtin.Version{ .major = 0, .minor = 10, .patch = 0 }; +const stack_size = 32 * 1024 * 1024; pub fn build(b: *Builder) !void { b.setPreferredReleaseMode(.ReleaseFast); @@ -41,6 +42,7 @@ pub fn build(b: *Builder) !void { const toolchain_step = b.step("test-toolchain", "Run the tests for the toolchain"); var test_cases = b.addTest("src/test.zig"); + test_cases.stack_size = stack_size; test_cases.setBuildMode(mode); test_cases.addPackagePath("test_cases", "test/cases.zig"); test_cases.single_threaded = single_threaded; @@ -141,6 +143,7 @@ pub fn build(b: *Builder) !void { }; const exe = b.addExecutable("zig", main_file); + exe.stack_size = stack_size; exe.strip = strip; exe.build_id = b.option(bool, "build-id", "Include a build id note") orelse false; exe.install(); diff --git a/test/cases/compile_errors/endless_loop_in_function_evaluation.zig b/test/cases/compile_errors/endless_loop_in_function_evaluation.zig index d63f896f45..7616bfa5e7 100644 --- a/test/cases/compile_errors/endless_loop_in_function_evaluation.zig +++ b/test/cases/compile_errors/endless_loop_in_function_evaluation.zig @@ -7,7 +7,7 @@ export fn entry() usize { return @sizeOf(@TypeOf(&seventh_fib_number)); } // error // backend=stage2 -// target=x86_64-linux +// target=native // // :3:21: error: evaluation exceeded 1000 backwards branches // :3:21: note: use @setEvalBranchQuota() to raise the branch limit from 1000 From fc6e111b76764ae00e2c868ad46f39235837e239 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 21 Jul 2022 13:07:27 -0700 Subject: [PATCH 16/18] Sema: improve compile error for bad function alignment * Integrate more declaratively with src/target.zig * Only trigger the check when a function body is found, do not trigger for function types. --- src/Sema.zig | 9 ++++++--- src/target.zig | 8 ++++++++ ...align_n_expr_function_pointers_is_a_compile_error.zig | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 47429b9dbf..63e4c46f9b 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -18010,6 +18010,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const section_src: LazySrcLoc = .{ .node_offset_fn_type_section = inst_data.src_node }; const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = inst_data.src_node }; const ret_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = inst_data.src_node }; + const has_body = extra.data.body_len != 0; var extra_index: usize = extra.end; @@ -18019,8 +18020,11 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A break :blk lib_name; } else null; - if ((extra.data.bits.has_align_body or extra.data.bits.has_align_ref) and sema.mod.getTarget().cpu.arch.isWasm()) { - return sema.fail(block, align_src, "'align' is not allowed on functions in wasm", .{}); + if (has_body and + (extra.data.bits.has_align_body or extra.data.bits.has_align_ref) and + !target_util.supportsFunctionAlignment(target)) + { + return sema.fail(block, align_src, "target does not support function alignment", .{}); } const @"align": ?u32 = if (extra.data.bits.has_align_body) blk: { @@ -18162,7 +18166,6 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A } else 0; var src_locs: Zir.Inst.Func.SrcLocs = undefined; - const has_body = extra.data.body_len != 0; if (has_body) { extra_index += extra.data.body_len; src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; diff --git a/src/target.zig b/src/target.zig index ea8b3efc45..5202fb15fc 100644 --- a/src/target.zig +++ b/src/target.zig @@ -744,6 +744,7 @@ pub fn llvmMachineAbi(target: std.Target) ?[:0]const u8 { } } +/// This function returns 1 if function alignment is not observable or settable. pub fn defaultFunctionAlignment(target: std.Target) u32 { return switch (target.cpu.arch) { .arm, .armeb => 4, @@ -753,3 +754,10 @@ pub fn defaultFunctionAlignment(target: std.Target) u32 { else => 1, }; } + +pub fn supportsFunctionAlignment(target: std.Target) bool { + return switch (target.cpu.arch) { + .wasm32, .wasm64 => false, + else => true, + }; +} diff --git a/test/cases/compile_errors/align_n_expr_function_pointers_is_a_compile_error.zig b/test/cases/compile_errors/align_n_expr_function_pointers_is_a_compile_error.zig index ca6a3a6eee..4e2f1ca023 100644 --- a/test/cases/compile_errors/align_n_expr_function_pointers_is_a_compile_error.zig +++ b/test/cases/compile_errors/align_n_expr_function_pointers_is_a_compile_error.zig @@ -6,4 +6,4 @@ export fn foo() align(1) void { // backend=stage2 // target=wasm32-freestanding-none // -// :1:23: error: 'align' is not allowed on functions in wasm \ No newline at end of file +// :1:23: error: target does not support function alignment From 25f3be32db148c2816547c75462285ae8f5e99eb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 21 Jul 2022 15:16:59 -0700 Subject: [PATCH 17/18] Sema: fix fn pointer align disagrees with fn align error Check the specified function alignment rather than the effective function alignment. --- src/Sema.zig | 6 ++++-- test/cases/compile_errors/function_ptr_alignment.zig | 9 ++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 63e4c46f9b..ce14b2ae45 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -14209,8 +14209,10 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air if (inst_data.size != .One) { return sema.fail(block, elem_ty_src, "function pointers must be single pointers", .{}); } - const fn_align = elem_ty.abiAlignment(target); - if (inst_data.flags.has_align and abi_align != 0 and abi_align != fn_align) { + const fn_align = elem_ty.fnInfo().alignment; + if (inst_data.flags.has_align and abi_align != 0 and fn_align != 0 and + abi_align != fn_align) + { return sema.fail(block, align_src, "function pointer alignment disagrees with function alignment", .{}); } } else if (inst_data.size == .Many and elem_ty.zigTypeTag() == .Opaque) { diff --git a/test/cases/compile_errors/function_ptr_alignment.zig b/test/cases/compile_errors/function_ptr_alignment.zig index ca0f3ee2ac..fabee524c7 100644 --- a/test/cases/compile_errors/function_ptr_alignment.zig +++ b/test/cases/compile_errors/function_ptr_alignment.zig @@ -16,10 +16,13 @@ comptime { var a: *align(2) fn () void = undefined; _ = a; } +comptime { + var a: *align(1) fn () align(2) void = undefined; + _ = a; +} // error // backend=stage2 -// target=x86_64-linux +// target=native // -// :2:19: error: function pointer alignment disagrees with function alignment -// :16:19: error: function pointer alignment disagrees with function alignment +// :20:19: error: function pointer alignment disagrees with function alignment From b749487f48cad2dd779d7fa6d0afcafc975ba26c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 21 Jul 2022 22:05:46 -0700 Subject: [PATCH 18/18] add new compile error note to old test case --- test/cases/x86_64-macos/assert_function.8.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/test/cases/x86_64-macos/assert_function.8.zig b/test/cases/x86_64-macos/assert_function.8.zig index 801cd2e3be..7cfa795330 100644 --- a/test/cases/x86_64-macos/assert_function.8.zig +++ b/test/cases/x86_64-macos/assert_function.8.zig @@ -17,3 +17,4 @@ pub fn assert(ok: bool) void { // error // // :5:21: error: unable to resolve comptime value +// :5:21: note: condition in comptime branch must be comptime known