From 34be5784a3f19658d15d1fb24bb07800cdb025c4 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sat, 26 Nov 2022 22:40:43 +0200 Subject: [PATCH 1/9] parser: disallow defer and variable declaration as else branch Closes #13658 --- lib/std/zig/parse.zig | 28 +++++++++++++++------------- lib/std/zig/parser_test.zig | 24 ++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 77ed67b3d2..2ae0221d8a 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -950,13 +950,15 @@ const Parser = struct { /// / LabeledStatement /// / SwitchExpr /// / AssignExpr SEMICOLON - fn parseStatement(p: *Parser) Error!Node.Index { + fn parseStatement(p: *Parser, allow_defer_var: bool) Error!Node.Index { const comptime_token = p.eatToken(.keyword_comptime); - const var_decl = try p.parseVarDecl(); - if (var_decl != 0) { - try p.expectSemicolon(.expected_semi_after_decl, true); - return var_decl; + if (allow_defer_var) { + const var_decl = try p.parseVarDecl(); + if (var_decl != 0) { + try p.expectSemicolon(.expected_semi_after_decl, true); + return var_decl; + } } if (comptime_token) |token| { @@ -993,7 +995,7 @@ const Parser = struct { }, }); }, - .keyword_defer => return p.addNode(.{ + .keyword_defer => if (allow_defer_var) return p.addNode(.{ .tag = .@"defer", .main_token = p.nextToken(), .data = .{ @@ -1001,7 +1003,7 @@ const Parser = struct { .rhs = try p.expectBlockExprStatement(), }, }), - .keyword_errdefer => return p.addNode(.{ + .keyword_errdefer => if (allow_defer_var) return p.addNode(.{ .tag = .@"errdefer", .main_token = p.nextToken(), .data = .{ @@ -1040,8 +1042,8 @@ const Parser = struct { return null_node; } - fn expectStatement(p: *Parser) !Node.Index { - const statement = try p.parseStatement(); + fn expectStatement(p: *Parser, allow_defer_var: bool) !Node.Index { + const statement = try p.parseStatement(allow_defer_var); if (statement == 0) { return p.fail(.expected_statement); } @@ -1053,7 +1055,7 @@ const Parser = struct { /// statement, returns 0. fn expectStatementRecoverable(p: *Parser) Error!Node.Index { while (true) { - return p.expectStatement() catch |err| switch (err) { + return p.expectStatement(true) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.ParseError => { p.findNextStmt(); // Try to skip to the next statement. @@ -1114,7 +1116,7 @@ const Parser = struct { }); }; _ = try p.parsePayload(); - const else_expr = try p.expectStatement(); + const else_expr = try p.expectStatement(false); return p.addNode(.{ .tag = .@"if", .main_token = if_token, @@ -1226,7 +1228,7 @@ const Parser = struct { .lhs = array_expr, .rhs = try p.addExtra(Node.If{ .then_expr = then_expr, - .else_expr = try p.expectStatement(), + .else_expr = try p.expectStatement(false), }), }, }); @@ -1309,7 +1311,7 @@ const Parser = struct { } }; _ = try p.parsePayload(); - const else_expr = try p.expectStatement(); + const else_expr = try p.expectStatement(false); return p.addNode(.{ .tag = .@"while", .main_token = while_token, diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index e554c51f70..d2f7a15994 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -4233,6 +4233,30 @@ test "zig fmt: remove newlines surrounding doc comment within container decl" { ); } +test "zig fmt: invalid else branch statement" { + try testError( + \\comptime { + \\ if (true) {} else var a = 0; + \\ if (true) {} else defer {} + \\} + \\comptime { + \\ while (true) {} else var a = 0; + \\ while (true) {} else defer {} + \\} + \\comptime { + \\ for ("") |_| {} else var a = 0; + \\ for ("") |_| {} else defer {} + \\} + , &[_]Error{ + .expected_statement, + .expected_statement, + .expected_statement, + .expected_statement, + .expected_statement, + .expected_statement, + }); +} + test "zig fmt: anytype struct field" { try testError( \\pub const Pointer = struct { From 6f9c7e33b956686ebfd4690c7f85a602d0ac9ffe Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 27 Nov 2022 15:37:48 +0200 Subject: [PATCH 2/9] llvm: implement `union_init` for packed unions Closes #13664 --- src/codegen/llvm.zig | 15 +++++++++++++++ test/behavior.zig | 1 + test/behavior/bugs/13664.zig | 27 +++++++++++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 test/behavior/bugs/13664.zig diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 02fa34fe87..bbbcbdd754 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -9228,6 +9228,21 @@ pub const FuncGen = struct { const target = self.dg.module.getTarget(); const layout = union_ty.unionGetLayout(target); const union_obj = union_ty.cast(Type.Payload.Union).?.data; + + if (union_obj.layout == .Packed) { + const big_bits = union_ty.bitSize(target); + const int_llvm_ty = self.dg.context.intType(@intCast(c_uint, big_bits)); + const field = union_obj.fields.values()[extra.field_index]; + const non_int_val = try self.resolveInst(extra.init); + const ty_bit_size = @intCast(u16, field.ty.bitSize(target)); + const small_int_ty = self.dg.context.intType(ty_bit_size); + const small_int_val = if (field.ty.isPtrAtRuntime()) + self.builder.buildPtrToInt(non_int_val, small_int_ty, "") + else + self.builder.buildBitCast(non_int_val, small_int_ty, ""); + return self.builder.buildZExtOrBitCast(small_int_val, int_llvm_ty, ""); + } + const tag_int = blk: { const tag_ty = union_ty.unionTagTypeHypothetical(); const union_field_name = union_obj.fields.keys()[extra.field_index]; diff --git a/test/behavior.zig b/test/behavior.zig index c45c819762..04c6d4c13d 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -116,6 +116,7 @@ test { _ = @import("behavior/bugs/13171.zig"); _ = @import("behavior/bugs/13285.zig"); _ = @import("behavior/bugs/13435.zig"); + _ = @import("behavior/bugs/13664.zig"); _ = @import("behavior/byteswap.zig"); _ = @import("behavior/byval_arg_var.zig"); _ = @import("behavior/call.zig"); diff --git a/test/behavior/bugs/13664.zig b/test/behavior/bugs/13664.zig new file mode 100644 index 0000000000..48b2533b60 --- /dev/null +++ b/test/behavior/bugs/13664.zig @@ -0,0 +1,27 @@ +const std = @import("std"); +const builtin = @import("builtin"); + +const Fields = packed struct { + timestamp: u50, + random_bits: u13, +}; +const ID = packed union { + value: u63, + fields: Fields, +}; +fn value() i64 { + return 1341; +} +test { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const timestamp: i64 = value(); + const id = ID{ .fields = Fields{ + .timestamp = @intCast(u50, timestamp), + .random_bits = 420, + } }; + try std.testing.expect((ID{ .value = id.value }).fields.timestamp == timestamp); +} From 6337c04244d9c27cc6535340347d4c127f4742eb Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 29 Nov 2022 14:29:50 +0200 Subject: [PATCH 3/9] Sema: improve panic for slice start index being greater than end index Closes #13689 --- lib/std/builtin.zig | 11 +++- src/Sema.zig | 64 ++++++++++++++++--- ...ice start index greater than end index.zig | 23 +++++++ 3 files changed, 86 insertions(+), 12 deletions(-) create mode 100644 test/cases/safety/slice start index greater than end index.zig diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 1f7d48ccb9..09833988ae 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -863,10 +863,9 @@ pub fn panicOutOfBounds(index: usize, len: usize) noreturn { std.debug.panicExtra(null, @returnAddress(), "index out of bounds: index {d}, len {d}", .{ index, len }); } -pub noinline fn returnError(st: *StackTrace) void { +pub fn panicStartGreaterThanEnd(start: usize, end: usize) noreturn { @setCold(true); - @setRuntimeSafety(false); - addErrRetTraceAddr(st, @returnAddress()); + std.debug.panicExtra(null, @returnAddress(), "start index {d} is larger than end index {d}", .{ start, end }); } pub const panic_messages = struct { @@ -889,6 +888,12 @@ pub const panic_messages = struct { pub const invalid_enum_value = "invalid enum value"; }; +pub noinline fn returnError(st: *StackTrace) void { + @setCold(true); + @setRuntimeSafety(false); + addErrRetTraceAddr(st, @returnAddress()); +} + pub inline fn addErrRetTraceAddr(st: *StackTrace, addr: usize) void { if (st.index < st.instruction_addresses.len) st.instruction_addresses[st.index] = addr; diff --git a/src/Sema.zig b/src/Sema.zig index b8b8f3738a..d35c4e3881 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -12437,7 +12437,7 @@ fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. else try sema.resolveInst(.zero); - return sema.analyzeArithmetic(block, .sub, lhs, rhs, src, lhs_src, rhs_src); + return sema.analyzeArithmetic(block, .sub, lhs, rhs, src, lhs_src, rhs_src, true); } fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -12460,7 +12460,7 @@ fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! else try sema.resolveInst(.zero); - return sema.analyzeArithmetic(block, .subwrap, lhs, rhs, src, lhs_src, rhs_src); + return sema.analyzeArithmetic(block, .subwrap, lhs, rhs, src, lhs_src, rhs_src, true); } fn zirArithmetic( @@ -12480,7 +12480,7 @@ fn zirArithmetic( const lhs = try sema.resolveInst(extra.lhs); const rhs = try sema.resolveInst(extra.rhs); - return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src); + return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src, true); } fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -13776,6 +13776,7 @@ fn analyzeArithmetic( src: LazySrcLoc, lhs_src: LazySrcLoc, rhs_src: LazySrcLoc, + want_safety: bool, ) CompileError!Air.Inst.Ref { const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); @@ -14204,7 +14205,7 @@ fn analyzeArithmetic( }; try sema.requireRuntimeBlock(block, src, rs.src); - if (block.wantSafety()) { + if (block.wantSafety() and want_safety) { if (scalar_tag == .Int) { const maybe_op_ov: ?Air.Inst.Tag = switch (rs.air_tag) { .add => .add_with_overflow, @@ -22334,6 +22335,47 @@ fn panicIndexOutOfBounds( try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); } +fn panicStartLargerThanEnd( + sema: *Sema, + parent_block: *Block, + src: LazySrcLoc, + start: Air.Inst.Ref, + end: Air.Inst.Ref, +) !void { + assert(!parent_block.is_comptime); + const ok = try parent_block.addBinOp(.cmp_lte, start, end); + const gpa = sema.gpa; + + var fail_block: Block = .{ + .parent = parent_block, + .sema = sema, + .src_decl = parent_block.src_decl, + .namespace = parent_block.namespace, + .wip_capture_scope = parent_block.wip_capture_scope, + .instructions = .{}, + .inlining = parent_block.inlining, + .is_comptime = false, + }; + + defer fail_block.instructions.deinit(gpa); + + { + const this_feature_is_implemented_in_the_backend = + sema.mod.comp.bin_file.options.use_llvm; + + if (!this_feature_is_implemented_in_the_backend) { + // TODO implement this feature in all the backends and then delete this branch + _ = try fail_block.addNoOp(.breakpoint); + _ = try fail_block.addNoOp(.unreach); + } else { + const panic_fn = try sema.getBuiltin("panicStartGreaterThanEnd"); + const args: [2]Air.Inst.Ref = .{ start, end }; + _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args, null); + } + } + try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); +} + fn panicSentinelMismatch( sema: *Sema, parent_block: *Block, @@ -28028,7 +28070,11 @@ fn analyzeSlice( } } - const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src); + if (block.wantSafety() and !block.is_comptime) { + // requirement: start <= end + try sema.panicStartLargerThanEnd(block, src, start, end); + } + const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false); const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len); const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data; @@ -28063,10 +28109,10 @@ fn analyzeSlice( const actual_len = if (slice_ty.sentinel() == null) slice_len_inst else - try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src); + try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true); const actual_end = if (slice_sentinel != null) - try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src) + try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true) else end; @@ -28131,11 +28177,11 @@ fn analyzeSlice( if (slice_ty.sentinel() == null) break :blk slice_len_inst; // we have to add one because slice lengths don't include the sentinel - break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src); + break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true); } else null; if (opt_len_inst) |len_inst| { const actual_end = if (slice_sentinel != null) - try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src) + try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true) else end; try sema.panicIndexOutOfBounds(block, src, actual_end, len_inst, .cmp_lte); diff --git a/test/cases/safety/slice start index greater than end index.zig b/test/cases/safety/slice start index greater than end index.zig new file mode 100644 index 0000000000..ef0485ae60 --- /dev/null +++ b/test/cases/safety/slice start index greater than end index.zig @@ -0,0 +1,23 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "start index 10 is larger than end index 1")) { + std.process.exit(0); + } + std.process.exit(1); +} + +pub fn main() !void { + var a: usize = 1; + var b: usize = 10; + var buf: [16]u8 = undefined; + + const slice = buf[b..a]; + _ = slice; + return error.TestFailed; +} + +// run +// backend=llvm +// target=native From 17ff002bc0ac55850e647fc3a70a43d1d874f6ab Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 29 Nov 2022 14:53:54 +0200 Subject: [PATCH 4/9] Sema: improve safety panic for access of inactive union field --- doc/langref.html.in | 2 +- lib/std/builtin.zig | 5 + src/Sema.zig | 119 ++++++------------- test/cases/safety/bad union field access.zig | 2 +- 4 files changed, 45 insertions(+), 83 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 8974ab06cf..dcb15af9f6 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -3803,7 +3803,7 @@ test "switch on non-exhaustive enum" { {#link|Accessing the non-active field|Wrong Union Field Access#} is safety-checked {#link|Undefined Behavior#}:

- {#code_begin|test_err|inactive union field#} + {#code_begin|test_err|access of union field 'float' while field 'int' is active#} const Payload = union { int: i64, float: f64, diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 09833988ae..fcdf43bd31 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -868,6 +868,11 @@ pub fn panicStartGreaterThanEnd(start: usize, end: usize) noreturn { std.debug.panicExtra(null, @returnAddress(), "start index {d} is larger than end index {d}", .{ start, end }); } +pub fn panicInactiveUnionField(active: anytype, wanted: @TypeOf(active)) noreturn { + @setCold(true); + std.debug.panicExtra(null, @returnAddress(), "access of union field '{s}' while field '{s}' is active", .{ @tagName(wanted), @tagName(active) }); +} + pub const panic_messages = struct { pub const unreach = "reached unreachable code"; pub const unwrap_null = "attempt to use null value"; diff --git a/src/Sema.zig b/src/Sema.zig index d35c4e3881..396c5912fa 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -22120,7 +22120,6 @@ pub const PanicId = enum { shr_overflow, divide_by_zero, exact_division_remainder, - /// TODO make this call `std.builtin.panicInactiveUnionField`. inactive_union_field, integer_part_out_of_bounds, corrupt_switch, @@ -22296,90 +22295,40 @@ fn panicUnwrapError( fn panicIndexOutOfBounds( sema: *Sema, parent_block: *Block, - src: LazySrcLoc, index: Air.Inst.Ref, len: Air.Inst.Ref, cmp_op: Air.Inst.Tag, ) !void { assert(!parent_block.is_comptime); const ok = try parent_block.addBinOp(cmp_op, index, len); - const gpa = sema.gpa; - - var fail_block: Block = .{ - .parent = parent_block, - .sema = sema, - .src_decl = parent_block.src_decl, - .namespace = parent_block.namespace, - .wip_capture_scope = parent_block.wip_capture_scope, - .instructions = .{}, - .inlining = parent_block.inlining, - .is_comptime = false, - }; - - defer fail_block.instructions.deinit(gpa); - - { - const this_feature_is_implemented_in_the_backend = - sema.mod.comp.bin_file.options.use_llvm; - - if (!this_feature_is_implemented_in_the_backend) { - // TODO implement this feature in all the backends and then delete this branch - _ = try fail_block.addNoOp(.breakpoint); - _ = try fail_block.addNoOp(.unreach); - } else { - const panic_fn = try sema.getBuiltin("panicOutOfBounds"); - const args: [2]Air.Inst.Ref = .{ index, len }; - _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args, null); - } - } - try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); + try sema.safetyPanicFormatted(parent_block, ok, "panicOutOfBounds", &.{ index, len }); } fn panicStartLargerThanEnd( sema: *Sema, parent_block: *Block, - src: LazySrcLoc, start: Air.Inst.Ref, end: Air.Inst.Ref, ) !void { assert(!parent_block.is_comptime); const ok = try parent_block.addBinOp(.cmp_lte, start, end); - const gpa = sema.gpa; + try sema.safetyPanicFormatted(parent_block, ok, "panicStartGreaterThanEnd", &.{ start, end }); +} - var fail_block: Block = .{ - .parent = parent_block, - .sema = sema, - .src_decl = parent_block.src_decl, - .namespace = parent_block.namespace, - .wip_capture_scope = parent_block.wip_capture_scope, - .instructions = .{}, - .inlining = parent_block.inlining, - .is_comptime = false, - }; - - defer fail_block.instructions.deinit(gpa); - - { - const this_feature_is_implemented_in_the_backend = - sema.mod.comp.bin_file.options.use_llvm; - - if (!this_feature_is_implemented_in_the_backend) { - // TODO implement this feature in all the backends and then delete this branch - _ = try fail_block.addNoOp(.breakpoint); - _ = try fail_block.addNoOp(.unreach); - } else { - const panic_fn = try sema.getBuiltin("panicStartGreaterThanEnd"); - const args: [2]Air.Inst.Ref = .{ start, end }; - _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args, null); - } - } - try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); +fn panicInactiveUnionField( + sema: *Sema, + parent_block: *Block, + active_tag: Air.Inst.Ref, + wanted_tag: Air.Inst.Ref, +) !void { + assert(!parent_block.is_comptime); + const ok = try parent_block.addBinOp(.cmp_eq, active_tag, wanted_tag); + try sema.safetyPanicFormatted(parent_block, ok, "panicInactiveUnionField", &.{ active_tag, wanted_tag }); } fn panicSentinelMismatch( sema: *Sema, parent_block: *Block, - src: LazySrcLoc, maybe_sentinel: ?Value, sentinel_ty: Type, ptr: Air.Inst.Ref, @@ -22413,9 +22362,20 @@ fn panicSentinelMismatch( else { const panic_fn = try sema.getBuiltin("checkNonScalarSentinel"); const args: [2]Air.Inst.Ref = .{ expected_sentinel, actual_sentinel }; - _ = try sema.analyzeCall(parent_block, panic_fn, src, src, .auto, false, &args, null); + _ = try sema.analyzeCall(parent_block, panic_fn, sema.src, sema.src, .auto, false, &args, null); return; }; + + try sema.safetyPanicFormatted(parent_block, ok, "panicSentinelMismatch", &.{ expected_sentinel, actual_sentinel }); +} + +fn safetyPanicFormatted( + sema: *Sema, + parent_block: *Block, + ok: Air.Inst.Ref, + func: []const u8, + args: []const Air.Inst.Ref, +) CompileError!void { const gpa = sema.gpa; var fail_block: Block = .{ @@ -22440,9 +22400,8 @@ fn panicSentinelMismatch( _ = try fail_block.addNoOp(.breakpoint); _ = try fail_block.addNoOp(.unreach); } else { - const panic_fn = try sema.getBuiltin("panicSentinelMismatch"); - const args: [2]Air.Inst.Ref = .{ expected_sentinel, actual_sentinel }; - _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args, null); + const panic_fn = try sema.getBuiltin(func); + _ = try sema.analyzeCall(&fail_block, panic_fn, sema.src, sema.src, .auto, false, args, null); } } try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); @@ -23465,8 +23424,7 @@ fn unionFieldPtr( // TODO would it be better if get_union_tag supported pointers to unions? const union_val = try block.addTyOp(.load, union_ty, union_ptr); const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_val); - const ok = try block.addBinOp(.cmp_eq, active_tag, wanted_tag); - try sema.addSafetyCheck(block, ok, .inactive_union_field); + try sema.panicInactiveUnionField(block, active_tag, wanted_tag); } if (field.ty.zigTypeTag() == .NoReturn) { _ = try block.addNoOp(.unreach); @@ -23537,8 +23495,7 @@ fn unionFieldVal( const wanted_tag_val = try Value.Tag.enum_field_index.create(sema.arena, enum_field_index); const wanted_tag = try sema.addConstant(union_obj.tag_ty, wanted_tag_val); const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_byval); - const ok = try block.addBinOp(.cmp_eq, active_tag, wanted_tag); - try sema.addSafetyCheck(block, ok, .inactive_union_field); + try sema.panicInactiveUnionField(block, active_tag, wanted_tag); } if (field.ty.zigTypeTag() == .NoReturn) { _ = try block.addNoOp(.unreach); @@ -23849,7 +23806,7 @@ fn elemValArray( if (maybe_index_val == null) { const len_inst = try sema.addIntUnsigned(Type.usize, array_len); const cmp_op: Air.Inst.Tag = if (array_sent != null) .cmp_lte else .cmp_lt; - try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op); + try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op); } } return block.addBinOp(.array_elem_val, array, elem_index); @@ -23910,7 +23867,7 @@ fn elemPtrArray( if (block.wantSafety() and offset == null) { const len_inst = try sema.addIntUnsigned(Type.usize, array_len); const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt; - try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op); + try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op); } return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty); @@ -23966,7 +23923,7 @@ fn elemValSlice( else try block.addTyOp(.slice_len, Type.usize, slice); const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; - try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op); + try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op); } try sema.queueFullTypeResolution(sema.typeOf(slice)); return block.addBinOp(.slice_elem_val, slice, elem_index); @@ -24025,7 +23982,7 @@ fn elemPtrSlice( break :len try block.addTyOp(.slice_len, Type.usize, slice); }; const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; - try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op); + try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op); } return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty); } @@ -28072,7 +28029,7 @@ fn analyzeSlice( if (block.wantSafety() and !block.is_comptime) { // requirement: start <= end - try sema.panicStartLargerThanEnd(block, src, start, end); + try sema.panicStartLargerThanEnd(block, start, end); } const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false); const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len); @@ -28116,11 +28073,11 @@ fn analyzeSlice( else end; - try sema.panicIndexOutOfBounds(block, src, actual_end, actual_len, .cmp_lte); + try sema.panicIndexOutOfBounds(block, actual_end, actual_len, .cmp_lte); } // requirement: result[new_len] == slice_sentinel - try sema.panicSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len); + try sema.panicSentinelMismatch(block, slice_sentinel, elem_ty, result, new_len); } return result; }; @@ -28184,11 +28141,11 @@ fn analyzeSlice( try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true) else end; - try sema.panicIndexOutOfBounds(block, src, actual_end, len_inst, .cmp_lte); + try sema.panicIndexOutOfBounds(block, actual_end, len_inst, .cmp_lte); } // requirement: start <= end - try sema.panicIndexOutOfBounds(block, src, start, end, .cmp_lte); + try sema.panicIndexOutOfBounds(block, start, end, .cmp_lte); } const result = try block.addInst(.{ .tag = .slice, @@ -28202,7 +28159,7 @@ fn analyzeSlice( }); if (block.wantSafety()) { // requirement: result[new_len] == slice_sentinel - try sema.panicSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len); + try sema.panicSentinelMismatch(block, slice_sentinel, elem_ty, result, new_len); } return result; } diff --git a/test/cases/safety/bad union field access.zig b/test/cases/safety/bad union field access.zig index 0badf380fb..fbe7718c66 100644 --- a/test/cases/safety/bad union field access.zig +++ b/test/cases/safety/bad union field access.zig @@ -2,7 +2,7 @@ const std = @import("std"); pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { _ = stack_trace; - if (std.mem.eql(u8, message, "access of inactive union field")) { + if (std.mem.eql(u8, message, "access of union field 'float' while field 'int' is active")) { std.process.exit(0); } std.process.exit(1); From ed734299269d50083db27d68598ced7df42b8631 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 29 Nov 2022 15:09:21 +0200 Subject: [PATCH 5/9] Sema: explain why parameter must be declared comptime Closes #13692 --- src/Sema.zig | 3 +++ .../compile_errors/comptime_parameter_not_declared_as_such.zig | 1 + 2 files changed, 4 insertions(+) diff --git a/src/Sema.zig b/src/Sema.zig index 396c5912fa..6c87f7d4b9 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -8709,6 +8709,9 @@ fn analyzeParameter( }); errdefer msg.destroy(sema.gpa); + const src_decl = sema.mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsComptime(block, param_src, msg, param_src.toSrcLoc(src_decl), param.ty); + try sema.addDeclaredHereNote(msg, param.ty); break :msg msg; }; diff --git a/test/cases/compile_errors/comptime_parameter_not_declared_as_such.zig b/test/cases/compile_errors/comptime_parameter_not_declared_as_such.zig index 008d14f2fc..6758454ccd 100644 --- a/test/cases/compile_errors/comptime_parameter_not_declared_as_such.zig +++ b/test/cases/compile_errors/comptime_parameter_not_declared_as_such.zig @@ -21,4 +21,5 @@ pub export fn entry1() void { // target=native // // :3:6: error: parameter of type '*const fn(anytype) void' must be declared comptime +// :3:6: note: function is generic // :10:34: error: parameter of type 'comptime_int' must be declared comptime From e60db701d13a50798222bbef2d4560245f975671 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 29 Nov 2022 15:57:34 +0200 Subject: [PATCH 6/9] Sema: add option to disable formatted panics Closes #13174 --- lib/std/builtin.zig | 4 +++ src/Compilation.zig | 5 +++ src/Sema.zig | 76 ++++++++++++++++++++++++++++----------------- src/main.zig | 8 +++++ 4 files changed, 64 insertions(+), 29 deletions(-) diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index fcdf43bd31..8c8d0b37b7 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -891,6 +891,10 @@ pub const panic_messages = struct { pub const corrupt_switch = "switch on corrupt value"; pub const shift_rhs_too_big = "shift amount is greater than the type size"; pub const invalid_enum_value = "invalid enum value"; + pub const sentinel_mismatch = "sentinel mismatch"; + pub const unwrap_error = "attempt to unwrap error"; + pub const index_out_of_bounds = "index out of bounds"; + pub const start_index_greater_than_end = "start index is larger than end index"; }; pub noinline fn returnError(st: *StackTrace) void { diff --git a/src/Compilation.zig b/src/Compilation.zig index 795eb493e2..dcd9ca5cf6 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -101,6 +101,7 @@ debug_compile_errors: bool, job_queued_compiler_rt_lib: bool = false, job_queued_compiler_rt_obj: bool = false, alloc_failure_occurred: bool = false, +formatted_panics: bool = false, c_source_files: []const CSourceFile, clang_argv: []const []const u8, @@ -937,6 +938,7 @@ pub const InitOptions = struct { use_stage1: ?bool = null, single_threaded: ?bool = null, strip: ?bool = null, + formatted_panics: ?bool = null, rdynamic: bool = false, function_sections: bool = false, no_builtin: bool = false, @@ -1457,6 +1459,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .Debug => @as(u8, 0), else => @as(u8, 3), }; + const formatted_panics = options.formatted_panics orelse (options.optimize_mode == .Debug); // We put everything into the cache hash that *cannot be modified // during an incremental update*. For example, one cannot change the @@ -1551,6 +1554,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { hash.addOptionalBytes(options.test_name_prefix); hash.add(options.skip_linker_dependencies); hash.add(options.parent_compilation_link_libc); + hash.add(formatted_panics); // In the case of incremental cache mode, this `zig_cache_artifact_directory` // is computed based on a hash of non-linker inputs, and it is where all @@ -1957,6 +1961,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .owned_link_dir = owned_link_dir, .color = options.color, .reference_trace = options.reference_trace, + .formatted_panics = formatted_panics, .time_report = options.time_report, .stack_report = options.stack_report, .unwind_tables = unwind_tables, diff --git a/src/Sema.zig b/src/Sema.zig index 6c87f7d4b9..686d9c11cb 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -667,9 +667,9 @@ pub const Block = struct { return result_index; } - fn addUnreachable(block: *Block, src: LazySrcLoc, safety_check: bool) !void { + fn addUnreachable(block: *Block, safety_check: bool) !void { if (safety_check and block.wantSafety()) { - _ = try block.sema.safetyPanic(block, src, .unreach); + try block.sema.safetyPanic(block, .unreach); } else { _ = try block.addNoOp(.unreach); } @@ -5003,7 +5003,8 @@ 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", .{}); } - return sema.panicWithMsg(block, src, msg_inst); + try sema.panicWithMsg(block, src, msg_inst); + return always_noreturn; } fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -7962,7 +7963,7 @@ fn analyzeErrUnionPayload( if (safety_check and block.wantSafety() and !err_union_ty.errorUnionSet().errorSetIsEmpty()) { - try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err); + try sema.panicUnwrapError(block, operand, .unwrap_errunion_err, .is_non_err); } return block.addTyOp(.unwrap_errunion_payload, payload_ty, operand); @@ -8047,7 +8048,7 @@ fn analyzeErrUnionPayloadPtr( if (safety_check and block.wantSafety() and !err_union_ty.errorUnionSet().errorSetIsEmpty()) { - try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr); + try sema.panicUnwrapError(block, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr); } const air_tag: Air.Inst.Tag = if (initializing) @@ -9542,7 +9543,7 @@ fn zirSwitchCapture( .ErrorSet => if (block.switch_else_err_ty) |some| { return sema.bitCast(block, some, operand, operand_src); } else { - try block.addUnreachable(operand_src, false); + try block.addUnreachable(false); return Air.Inst.Ref.unreachable_value; }, else => return operand, @@ -10975,7 +10976,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError // that it is unreachable. if (case_block.wantSafety()) { try sema.zirDbgStmt(&case_block, cond_dbg_node_index); - _ = try sema.safetyPanic(&case_block, src, .corrupt_switch); + try sema.safetyPanic(&case_block, .corrupt_switch); } else { _ = try case_block.addNoOp(.unreach); } @@ -11304,6 +11305,11 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op const inst_data = sema.code.instructions.items(.data)[inst].@"unreachable"; const src = inst_data.src(); + if (!sema.mod.comp.formatted_panics) { + try sema.safetyPanic(block, .unwrap_error); + return true; + } + const panic_fn = try sema.getBuiltin("panicUnwrapError"); const err_return_trace = try sema.getErrorReturnTrace(block); const args: [2]Air.Inst.Ref = .{ err_return_trace, operand }; @@ -16513,7 +16519,7 @@ fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError return sema.fail(block, src, "reached unreachable code", .{}); } // TODO Add compile error for @optimizeFor occurring too late in a scope. - try block.addUnreachable(src, true); + try block.addUnreachable(true); return always_noreturn; } @@ -22128,6 +22134,10 @@ pub const PanicId = enum { corrupt_switch, shift_rhs_too_big, invalid_enum_value, + sentinel_mismatch, + unwrap_error, + index_out_of_bounds, + start_index_greater_than_end, }; fn addSafetyCheck( @@ -22152,12 +22162,7 @@ fn addSafetyCheck( defer fail_block.instructions.deinit(gpa); - // This function doesn't actually need a src location but if - // the panic function interface ever changes passing `.unneeded` here - // will cause confusing panics. - const src = sema.src; - _ = try sema.safetyPanic(&fail_block, src, panic_id); - + try sema.safetyPanic(&fail_block, panic_id); try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); } @@ -22221,7 +22226,7 @@ fn panicWithMsg( block: *Block, src: LazySrcLoc, msg_inst: Air.Inst.Ref, -) !Zir.Inst.Index { +) !void { const mod = sema.mod; const arena = sema.arena; @@ -22232,7 +22237,7 @@ fn panicWithMsg( // TODO implement this feature in all the backends and then delete this branch _ = try block.addNoOp(.breakpoint); _ = try block.addNoOp(.unreach); - return always_noreturn; + return; } const panic_fn = try sema.getBuiltin("panic"); const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace"); @@ -22248,19 +22253,20 @@ fn panicWithMsg( ); const args: [3]Air.Inst.Ref = .{ msg_inst, null_stack_trace, .null_value }; _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, &args, null); - return always_noreturn; } fn panicUnwrapError( sema: *Sema, parent_block: *Block, - src: LazySrcLoc, operand: Air.Inst.Ref, unwrap_err_tag: Air.Inst.Tag, is_non_err_tag: Air.Inst.Tag, ) !void { assert(!parent_block.is_comptime); const ok = try parent_block.addUnOp(is_non_err_tag, operand); + if (!sema.mod.comp.formatted_panics) { + return sema.addSafetyCheck(parent_block, ok, .unwrap_error); + } const gpa = sema.gpa; var fail_block: Block = .{ @@ -22289,7 +22295,7 @@ fn panicUnwrapError( const err = try fail_block.addTyOp(unwrap_err_tag, Type.anyerror, operand); const err_return_trace = try sema.getErrorReturnTrace(&fail_block); const args: [2]Air.Inst.Ref = .{ err_return_trace, err }; - _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args, null); + _ = try sema.analyzeCall(&fail_block, panic_fn, sema.src, sema.src, .auto, false, &args, null); } } try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); @@ -22304,7 +22310,10 @@ fn panicIndexOutOfBounds( ) !void { assert(!parent_block.is_comptime); const ok = try parent_block.addBinOp(cmp_op, index, len); - try sema.safetyPanicFormatted(parent_block, ok, "panicOutOfBounds", &.{ index, len }); + if (!sema.mod.comp.formatted_panics) { + return sema.addSafetyCheck(parent_block, ok, .index_out_of_bounds); + } + try sema.safetyCheckFormatted(parent_block, ok, "panicOutOfBounds", &.{ index, len }); } fn panicStartLargerThanEnd( @@ -22315,7 +22324,10 @@ fn panicStartLargerThanEnd( ) !void { assert(!parent_block.is_comptime); const ok = try parent_block.addBinOp(.cmp_lte, start, end); - try sema.safetyPanicFormatted(parent_block, ok, "panicStartGreaterThanEnd", &.{ start, end }); + if (!sema.mod.comp.formatted_panics) { + return sema.addSafetyCheck(parent_block, ok, .start_index_greater_than_end); + } + try sema.safetyCheckFormatted(parent_block, ok, "panicStartGreaterThanEnd", &.{ start, end }); } fn panicInactiveUnionField( @@ -22326,7 +22338,10 @@ fn panicInactiveUnionField( ) !void { assert(!parent_block.is_comptime); const ok = try parent_block.addBinOp(.cmp_eq, active_tag, wanted_tag); - try sema.safetyPanicFormatted(parent_block, ok, "panicInactiveUnionField", &.{ active_tag, wanted_tag }); + if (!sema.mod.comp.formatted_panics) { + return sema.addSafetyCheck(parent_block, ok, .inactive_union_field); + } + try sema.safetyCheckFormatted(parent_block, ok, "panicInactiveUnionField", &.{ active_tag, wanted_tag }); } fn panicSentinelMismatch( @@ -22369,16 +22384,20 @@ fn panicSentinelMismatch( return; }; - try sema.safetyPanicFormatted(parent_block, ok, "panicSentinelMismatch", &.{ expected_sentinel, actual_sentinel }); + if (!sema.mod.comp.formatted_panics) { + return sema.addSafetyCheck(parent_block, ok, .sentinel_mismatch); + } + try sema.safetyCheckFormatted(parent_block, ok, "panicSentinelMismatch", &.{ expected_sentinel, actual_sentinel }); } -fn safetyPanicFormatted( +fn safetyCheckFormatted( sema: *Sema, parent_block: *Block, ok: Air.Inst.Ref, func: []const u8, args: []const Air.Inst.Ref, ) CompileError!void { + assert(sema.mod.comp.formatted_panics); const gpa = sema.gpa; var fail_block: Block = .{ @@ -22413,19 +22432,18 @@ fn safetyPanicFormatted( fn safetyPanic( sema: *Sema, block: *Block, - src: LazySrcLoc, panic_id: PanicId, -) CompileError!Zir.Inst.Index { +) CompileError!void { const panic_messages_ty = try sema.getBuiltinType("panic_messages"); const msg_decl_index = (try sema.namespaceLookup( block, - src, + sema.src, panic_messages_ty.getNamespace().?, @tagName(panic_id), )).?; - const msg_inst = try sema.analyzeDeclVal(block, src, msg_decl_index); - return sema.panicWithMsg(block, src, msg_inst); + const msg_inst = try sema.analyzeDeclVal(block, sema.src, msg_decl_index); + try sema.panicWithMsg(block, sema.src, msg_inst); } fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void { diff --git a/src/main.zig b/src/main.zig index c372462365..e3721d1101 100644 --- a/src/main.zig +++ b/src/main.zig @@ -406,6 +406,8 @@ const usage_build_generic = \\ -fno-function-sections All functions go into same section \\ -fstrip Omit debug symbols \\ -fno-strip Keep debug symbols + \\ -fformatted-panics Enable formatted safety panics + \\ -fno-formatted-panics Disable formatted safety panics \\ -ofmt=[mode] Override target object format \\ elf Executable and Linking Format \\ c C source code @@ -632,6 +634,7 @@ fn buildOutputType( var have_version = false; var compatibility_version: ?std.builtin.Version = null; var strip: ?bool = null; + var formatted_panics: ?bool = null; var function_sections = false; var no_builtin = false; var watch = false; @@ -1242,6 +1245,10 @@ fn buildOutputType( strip = true; } else if (mem.eql(u8, arg, "-fno-strip")) { strip = false; + } else if (mem.eql(u8, arg, "-fformatted-panics")) { + formatted_panics = true; + } else if (mem.eql(u8, arg, "-fno-formatted-panics")) { + formatted_panics = false; } else if (mem.eql(u8, arg, "-fsingle-threaded")) { single_threaded = true; } else if (mem.eql(u8, arg, "-fno-single-threaded")) { @@ -2938,6 +2945,7 @@ fn buildOutputType( .stack_size_override = stack_size_override, .image_base_override = image_base_override, .strip = strip, + .formatted_panics = formatted_panics, .single_threaded = single_threaded, .function_sections = function_sections, .no_builtin = no_builtin, From 6f5a438946dce2b201acf5b7bbe42977bc990cc4 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 29 Nov 2022 18:40:28 +0200 Subject: [PATCH 7/9] AstGen: unstack block scope when creating opaque type Closes #13697 --- src/AstGen.zig | 1 + test/behavior/basic.zig | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/src/AstGen.zig b/src/AstGen.zig index e6f83dc00b..6836b4f1cc 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -5070,6 +5070,7 @@ fn containerDecl( try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len); astgen.extra.appendSliceAssumeCapacity(decls_slice); + block_scope.unstack(); try gz.addNamespaceCaptures(&namespace); return rvalue(gz, ri, indexToRef(decl_inst), node); }, diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 7351891959..3fc1cda04d 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -1127,3 +1127,14 @@ test "pointer to zero sized global is mutable" { }; try expect(@TypeOf(&S.thing) == *S.Thing); } + +test "returning an opaque type from a function" { + const S = struct { + fn foo(comptime a: u32) type { + return opaque { + const b = a; + }; + } + }; + try expect(S.foo(123).b == 123); +} From 4b0ef6a409a11946a999e277ed2a3f4b6120d0f5 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 29 Nov 2022 18:49:27 +0200 Subject: [PATCH 8/9] Sema: make non-existent field error point to field name Closes #13698 --- src/Sema.zig | 4 ++-- .../invalid_field_in_struct_value_expression.zig | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 686d9c11cb..00d323a219 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -17613,11 +17613,11 @@ fn zirFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data; const ty_src = inst_data.src(); - const field_src = inst_data.src(); + const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type); if (aggregate_ty.tag() == .var_args_param) return sema.addType(aggregate_ty); const field_name = sema.code.nullTerminatedString(extra.name_start); - return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src); + return sema.fieldType(block, aggregate_ty, field_name, field_name_src, ty_src); } fn fieldType( diff --git a/test/cases/compile_errors/invalid_field_in_struct_value_expression.zig b/test/cases/compile_errors/invalid_field_in_struct_value_expression.zig index fc9667910b..97f440da3b 100644 --- a/test/cases/compile_errors/invalid_field_in_struct_value_expression.zig +++ b/test/cases/compile_errors/invalid_field_in_struct_value_expression.zig @@ -12,9 +12,21 @@ export fn f() void { _ = a; } +const Object = struct { + field_1: u32, + field_2: u32, +}; +fn dump(_: Object) void {} +pub export fn entry() void { + dump(.{ .field_1 = 123, .field_3 = 456 }); +} + + // error // backend=stage2 // target=native // // :10:10: error: no field named 'foo' in struct 'tmp.A' // :1:11: note: struct declared here +// :21:30: error: no field named 'field_3' in struct 'tmp.Object' +// :15:16: note: struct declared here From b2b1d421c35ba602ddfadf94190d956de3293c62 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 29 Nov 2022 18:59:58 +0200 Subject: [PATCH 9/9] Sema: add missing failWithBadMemberAccess to zirExport The assumption that AstGen would error only holds when exporting a identifier not a namespace member. --- src/Sema.zig | 3 ++- .../missing_member_in_namespace_export.zig | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 test/cases/compile_errors/missing_member_in_namespace_export.zig diff --git a/src/Sema.zig b/src/Sema.zig index 00d323a219..eb465d25a3 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5391,7 +5391,8 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void const container_namespace = container_ty.getNamespace().?; const maybe_index = try sema.lookupInNamespace(block, operand_src, container_namespace, decl_name, false); - break :index_blk maybe_index.?; // AstGen would produce error in case of unidentified name + break :index_blk maybe_index orelse + return sema.failWithBadMemberAccess(block, container_ty, operand_src, decl_name); } else try sema.lookupIdentifier(block, operand_src, decl_name); const options = sema.resolveExportOptions(block, .unneeded, extra.options) catch |err| switch (err) { error.NeededSourceLocation => { diff --git a/test/cases/compile_errors/missing_member_in_namespace_export.zig b/test/cases/compile_errors/missing_member_in_namespace_export.zig new file mode 100644 index 0000000000..413cf35319 --- /dev/null +++ b/test/cases/compile_errors/missing_member_in_namespace_export.zig @@ -0,0 +1,10 @@ +const S = struct {}; +comptime { + @export(S.foo, .{ .name = "foo" }); +} + +// error +// target=native +// +// :3:14: error: struct 'tmp.S' has no member named 'foo' +// :1:11: note: struct declared here