authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-11-29 19:59:55-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-11-29 19:59:55-05:00
logb8473ae7d333ea2750e55e712722d446076e99d9
treefb83bd2e26fb33d7f244abfe03d966b98e9a1b8d
parent648579b33060888316649b0d42cd03dd52ecf589
parentb2b1d421c35ba602ddfadf94190d956de3293c62
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #13693 from Vexu/safety

Safety panic improvements & some bug fixes

17 files changed, 280 insertions(+), 101 deletions(-)

doc/langref.html.in+1-1
......@@ -3803,7 +3803,7 @@ test "switch on non-exhaustive enum" {
38033803 {#link|Accessing the non-active field|Wrong Union Field Access#} is
38043804 safety-checked {#link|Undefined Behavior#}:
38053805 </p>
3806 {#code_begin|test_err|inactive union field#}
3806 {#code_begin|test_err|access of union field 'float' while field 'int' is active#}
38073807const Payload = union {
38083808 int: i64,
38093809 float: f64,
lib/std/builtin.zig+17-3
......@@ -863,10 +863,14 @@ pub fn panicOutOfBounds(index: usize, len: usize) noreturn {
863863 std.debug.panicExtra(null, @returnAddress(), "index out of bounds: index {d}, len {d}", .{ index, len });
864864}
865865
866pub noinline fn returnError(st: *StackTrace) void {
866pub fn panicStartGreaterThanEnd(start: usize, end: usize) noreturn {
867867 @setCold(true);
868 @setRuntimeSafety(false);
869 addErrRetTraceAddr(st, @returnAddress());
868 std.debug.panicExtra(null, @returnAddress(), "start index {d} is larger than end index {d}", .{ start, end });
869}
870
871pub fn panicInactiveUnionField(active: anytype, wanted: @TypeOf(active)) noreturn {
872 @setCold(true);
873 std.debug.panicExtra(null, @returnAddress(), "access of union field '{s}' while field '{s}' is active", .{ @tagName(wanted), @tagName(active) });
870874}
871875
872876pub const panic_messages = struct {
......@@ -887,8 +891,18 @@ pub const panic_messages = struct {
887891 pub const corrupt_switch = "switch on corrupt value";
888892 pub const shift_rhs_too_big = "shift amount is greater than the type size";
889893 pub const invalid_enum_value = "invalid enum value";
894 pub const sentinel_mismatch = "sentinel mismatch";
895 pub const unwrap_error = "attempt to unwrap error";
896 pub const index_out_of_bounds = "index out of bounds";
897 pub const start_index_greater_than_end = "start index is larger than end index";
890898};
891899
900pub noinline fn returnError(st: *StackTrace) void {
901 @setCold(true);
902 @setRuntimeSafety(false);
903 addErrRetTraceAddr(st, @returnAddress());
904}
905
892906pub inline fn addErrRetTraceAddr(st: *StackTrace, addr: usize) void {
893907 if (st.index < st.instruction_addresses.len)
894908 st.instruction_addresses[st.index] = addr;
lib/std/zig/parse.zig+15-13
......@@ -950,13 +950,15 @@ const Parser = struct {
950950 /// / LabeledStatement
951951 /// / SwitchExpr
952952 /// / AssignExpr SEMICOLON
953 fn parseStatement(p: *Parser) Error!Node.Index {
953 fn parseStatement(p: *Parser, allow_defer_var: bool) Error!Node.Index {
954954 const comptime_token = p.eatToken(.keyword_comptime);
955955
956 const var_decl = try p.parseVarDecl();
957 if (var_decl != 0) {
958 try p.expectSemicolon(.expected_semi_after_decl, true);
959 return var_decl;
956 if (allow_defer_var) {
957 const var_decl = try p.parseVarDecl();
958 if (var_decl != 0) {
959 try p.expectSemicolon(.expected_semi_after_decl, true);
960 return var_decl;
961 }
960962 }
961963
962964 if (comptime_token) |token| {
......@@ -993,7 +995,7 @@ const Parser = struct {
993995 },
994996 });
995997 },
996 .keyword_defer => return p.addNode(.{
998 .keyword_defer => if (allow_defer_var) return p.addNode(.{
997999 .tag = .@"defer",
9981000 .main_token = p.nextToken(),
9991001 .data = .{
......@@ -1001,7 +1003,7 @@ const Parser = struct {
10011003 .rhs = try p.expectBlockExprStatement(),
10021004 },
10031005 }),
1004 .keyword_errdefer => return p.addNode(.{
1006 .keyword_errdefer => if (allow_defer_var) return p.addNode(.{
10051007 .tag = .@"errdefer",
10061008 .main_token = p.nextToken(),
10071009 .data = .{
......@@ -1040,8 +1042,8 @@ const Parser = struct {
10401042 return null_node;
10411043 }
10421044
1043 fn expectStatement(p: *Parser) !Node.Index {
1044 const statement = try p.parseStatement();
1045 fn expectStatement(p: *Parser, allow_defer_var: bool) !Node.Index {
1046 const statement = try p.parseStatement(allow_defer_var);
10451047 if (statement == 0) {
10461048 return p.fail(.expected_statement);
10471049 }
......@@ -1053,7 +1055,7 @@ const Parser = struct {
10531055 /// statement, returns 0.
10541056 fn expectStatementRecoverable(p: *Parser) Error!Node.Index {
10551057 while (true) {
1056 return p.expectStatement() catch |err| switch (err) {
1058 return p.expectStatement(true) catch |err| switch (err) {
10571059 error.OutOfMemory => return error.OutOfMemory,
10581060 error.ParseError => {
10591061 p.findNextStmt(); // Try to skip to the next statement.
......@@ -1114,7 +1116,7 @@ const Parser = struct {
11141116 });
11151117 };
11161118 _ = try p.parsePayload();
1117 const else_expr = try p.expectStatement();
1119 const else_expr = try p.expectStatement(false);
11181120 return p.addNode(.{
11191121 .tag = .@"if",
11201122 .main_token = if_token,
......@@ -1226,7 +1228,7 @@ const Parser = struct {
12261228 .lhs = array_expr,
12271229 .rhs = try p.addExtra(Node.If{
12281230 .then_expr = then_expr,
1229 .else_expr = try p.expectStatement(),
1231 .else_expr = try p.expectStatement(false),
12301232 }),
12311233 },
12321234 });
......@@ -1309,7 +1311,7 @@ const Parser = struct {
13091311 }
13101312 };
13111313 _ = try p.parsePayload();
1312 const else_expr = try p.expectStatement();
1314 const else_expr = try p.expectStatement(false);
13131315 return p.addNode(.{
13141316 .tag = .@"while",
13151317 .main_token = while_token,
lib/std/zig/parser_test.zig+24
......@@ -4233,6 +4233,30 @@ test "zig fmt: remove newlines surrounding doc comment within container decl" {
42334233 );
42344234}
42354235
4236test "zig fmt: invalid else branch statement" {
4237 try testError(
4238 \\comptime {
4239 \\ if (true) {} else var a = 0;
4240 \\ if (true) {} else defer {}
4241 \\}
4242 \\comptime {
4243 \\ while (true) {} else var a = 0;
4244 \\ while (true) {} else defer {}
4245 \\}
4246 \\comptime {
4247 \\ for ("") |_| {} else var a = 0;
4248 \\ for ("") |_| {} else defer {}
4249 \\}
4250 , &[_]Error{
4251 .expected_statement,
4252 .expected_statement,
4253 .expected_statement,
4254 .expected_statement,
4255 .expected_statement,
4256 .expected_statement,
4257 });
4258}
4259
42364260test "zig fmt: anytype struct field" {
42374261 try testError(
42384262 \\pub const Pointer = struct {
src/AstGen.zig+1
......@@ -5070,6 +5070,7 @@ fn containerDecl(
50705070 try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len);
50715071 astgen.extra.appendSliceAssumeCapacity(decls_slice);
50725072
5073 block_scope.unstack();
50735074 try gz.addNamespaceCaptures(&namespace);
50745075 return rvalue(gz, ri, indexToRef(decl_inst), node);
50755076 },
src/Compilation.zig+5
......@@ -101,6 +101,7 @@ debug_compile_errors: bool,
101101job_queued_compiler_rt_lib: bool = false,
102102job_queued_compiler_rt_obj: bool = false,
103103alloc_failure_occurred: bool = false,
104formatted_panics: bool = false,
104105
105106c_source_files: []const CSourceFile,
106107clang_argv: []const []const u8,
......@@ -937,6 +938,7 @@ pub const InitOptions = struct {
937938 use_stage1: ?bool = null,
938939 single_threaded: ?bool = null,
939940 strip: ?bool = null,
941 formatted_panics: ?bool = null,
940942 rdynamic: bool = false,
941943 function_sections: bool = false,
942944 no_builtin: bool = false,
......@@ -1457,6 +1459,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
14571459 .Debug => @as(u8, 0),
14581460 else => @as(u8, 3),
14591461 };
1462 const formatted_panics = options.formatted_panics orelse (options.optimize_mode == .Debug);
14601463
14611464 // We put everything into the cache hash that *cannot be modified
14621465 // during an incremental update*. For example, one cannot change the
......@@ -1551,6 +1554,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
15511554 hash.addOptionalBytes(options.test_name_prefix);
15521555 hash.add(options.skip_linker_dependencies);
15531556 hash.add(options.parent_compilation_link_libc);
1557 hash.add(formatted_panics);
15541558
15551559 // In the case of incremental cache mode, this `zig_cache_artifact_directory`
15561560 // 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 {
19571961 .owned_link_dir = owned_link_dir,
19581962 .color = options.color,
19591963 .reference_trace = options.reference_trace,
1964 .formatted_panics = formatted_panics,
19601965 .time_report = options.time_report,
19611966 .stack_report = options.stack_report,
19621967 .unwind_tables = unwind_tables,
src/Sema.zig+108-83
......@@ -667,9 +667,9 @@ pub const Block = struct {
667667 return result_index;
668668 }
669669
670 fn addUnreachable(block: *Block, src: LazySrcLoc, safety_check: bool) !void {
670 fn addUnreachable(block: *Block, safety_check: bool) !void {
671671 if (safety_check and block.wantSafety()) {
672 _ = try block.sema.safetyPanic(block, src, .unreach);
672 try block.sema.safetyPanic(block, .unreach);
673673 } else {
674674 _ = try block.addNoOp(.unreach);
675675 }
......@@ -5003,7 +5003,8 @@ fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index, force_comptime: bo
50035003 if (block.is_comptime or force_comptime) {
50045004 return sema.fail(block, src, "encountered @panic at comptime", .{});
50055005 }
5006 return sema.panicWithMsg(block, src, msg_inst);
5006 try sema.panicWithMsg(block, src, msg_inst);
5007 return always_noreturn;
50075008}
50085009
50095010fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -5390,7 +5391,8 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
53905391 const container_namespace = container_ty.getNamespace().?;
53915392
53925393 const maybe_index = try sema.lookupInNamespace(block, operand_src, container_namespace, decl_name, false);
5393 break :index_blk maybe_index.?; // AstGen would produce error in case of unidentified name
5394 break :index_blk maybe_index orelse
5395 return sema.failWithBadMemberAccess(block, container_ty, operand_src, decl_name);
53945396 } else try sema.lookupIdentifier(block, operand_src, decl_name);
53955397 const options = sema.resolveExportOptions(block, .unneeded, extra.options) catch |err| switch (err) {
53965398 error.NeededSourceLocation => {
......@@ -7962,7 +7964,7 @@ fn analyzeErrUnionPayload(
79627964 if (safety_check and block.wantSafety() and
79637965 !err_union_ty.errorUnionSet().errorSetIsEmpty())
79647966 {
7965 try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err);
7967 try sema.panicUnwrapError(block, operand, .unwrap_errunion_err, .is_non_err);
79667968 }
79677969
79687970 return block.addTyOp(.unwrap_errunion_payload, payload_ty, operand);
......@@ -8047,7 +8049,7 @@ fn analyzeErrUnionPayloadPtr(
80478049 if (safety_check and block.wantSafety() and
80488050 !err_union_ty.errorUnionSet().errorSetIsEmpty())
80498051 {
8050 try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr);
8052 try sema.panicUnwrapError(block, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr);
80518053 }
80528054
80538055 const air_tag: Air.Inst.Tag = if (initializing)
......@@ -8709,6 +8711,9 @@ fn analyzeParameter(
87098711 });
87108712 errdefer msg.destroy(sema.gpa);
87118713
8714 const src_decl = sema.mod.declPtr(block.src_decl);
8715 try sema.explainWhyTypeIsComptime(block, param_src, msg, param_src.toSrcLoc(src_decl), param.ty);
8716
87128717 try sema.addDeclaredHereNote(msg, param.ty);
87138718 break :msg msg;
87148719 };
......@@ -9539,7 +9544,7 @@ fn zirSwitchCapture(
95399544 .ErrorSet => if (block.switch_else_err_ty) |some| {
95409545 return sema.bitCast(block, some, operand, operand_src);
95419546 } else {
9542 try block.addUnreachable(operand_src, false);
9547 try block.addUnreachable(false);
95439548 return Air.Inst.Ref.unreachable_value;
95449549 },
95459550 else => return operand,
......@@ -10972,7 +10977,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
1097210977 // that it is unreachable.
1097310978 if (case_block.wantSafety()) {
1097410979 try sema.zirDbgStmt(&case_block, cond_dbg_node_index);
10975 _ = try sema.safetyPanic(&case_block, src, .corrupt_switch);
10980 try sema.safetyPanic(&case_block, .corrupt_switch);
1097610981 } else {
1097710982 _ = try case_block.addNoOp(.unreach);
1097810983 }
......@@ -11301,6 +11306,11 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op
1130111306 const inst_data = sema.code.instructions.items(.data)[inst].@"unreachable";
1130211307 const src = inst_data.src();
1130311308
11309 if (!sema.mod.comp.formatted_panics) {
11310 try sema.safetyPanic(block, .unwrap_error);
11311 return true;
11312 }
11313
1130411314 const panic_fn = try sema.getBuiltin("panicUnwrapError");
1130511315 const err_return_trace = try sema.getErrorReturnTrace(block);
1130611316 const args: [2]Air.Inst.Ref = .{ err_return_trace, operand };
......@@ -12437,7 +12447,7 @@ fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
1243712447 else
1243812448 try sema.resolveInst(.zero);
1243912449
12440 return sema.analyzeArithmetic(block, .sub, lhs, rhs, src, lhs_src, rhs_src);
12450 return sema.analyzeArithmetic(block, .sub, lhs, rhs, src, lhs_src, rhs_src, true);
1244112451}
1244212452
1244312453fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -12460,7 +12470,7 @@ fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
1246012470 else
1246112471 try sema.resolveInst(.zero);
1246212472
12463 return sema.analyzeArithmetic(block, .subwrap, lhs, rhs, src, lhs_src, rhs_src);
12473 return sema.analyzeArithmetic(block, .subwrap, lhs, rhs, src, lhs_src, rhs_src, true);
1246412474}
1246512475
1246612476fn zirArithmetic(
......@@ -12480,7 +12490,7 @@ fn zirArithmetic(
1248012490 const lhs = try sema.resolveInst(extra.lhs);
1248112491 const rhs = try sema.resolveInst(extra.rhs);
1248212492
12483 return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src);
12493 return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src, true);
1248412494}
1248512495
1248612496fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -13776,6 +13786,7 @@ fn analyzeArithmetic(
1377613786 src: LazySrcLoc,
1377713787 lhs_src: LazySrcLoc,
1377813788 rhs_src: LazySrcLoc,
13789 want_safety: bool,
1377913790) CompileError!Air.Inst.Ref {
1378013791 const lhs_ty = sema.typeOf(lhs);
1378113792 const rhs_ty = sema.typeOf(rhs);
......@@ -14204,7 +14215,7 @@ fn analyzeArithmetic(
1420414215 };
1420514216
1420614217 try sema.requireRuntimeBlock(block, src, rs.src);
14207 if (block.wantSafety()) {
14218 if (block.wantSafety() and want_safety) {
1420814219 if (scalar_tag == .Int) {
1420914220 const maybe_op_ov: ?Air.Inst.Tag = switch (rs.air_tag) {
1421014221 .add => .add_with_overflow,
......@@ -16509,7 +16520,7 @@ fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
1650916520 return sema.fail(block, src, "reached unreachable code", .{});
1651016521 }
1651116522 // TODO Add compile error for @optimizeFor occurring too late in a scope.
16512 try block.addUnreachable(src, true);
16523 try block.addUnreachable(true);
1651316524 return always_noreturn;
1651416525}
1651516526
......@@ -17603,11 +17614,11 @@ fn zirFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
1760317614 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1760417615 const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data;
1760517616 const ty_src = inst_data.src();
17606 const field_src = inst_data.src();
17617 const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
1760717618 const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type);
1760817619 if (aggregate_ty.tag() == .var_args_param) return sema.addType(aggregate_ty);
1760917620 const field_name = sema.code.nullTerminatedString(extra.name_start);
17610 return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src);
17621 return sema.fieldType(block, aggregate_ty, field_name, field_name_src, ty_src);
1761117622}
1761217623
1761317624fn fieldType(
......@@ -22119,12 +22130,15 @@ pub const PanicId = enum {
2211922130 shr_overflow,
2212022131 divide_by_zero,
2212122132 exact_division_remainder,
22122 /// TODO make this call `std.builtin.panicInactiveUnionField`.
2212322133 inactive_union_field,
2212422134 integer_part_out_of_bounds,
2212522135 corrupt_switch,
2212622136 shift_rhs_too_big,
2212722137 invalid_enum_value,
22138 sentinel_mismatch,
22139 unwrap_error,
22140 index_out_of_bounds,
22141 start_index_greater_than_end,
2212822142};
2212922143
2213022144fn addSafetyCheck(
......@@ -22149,12 +22163,7 @@ fn addSafetyCheck(
2214922163
2215022164 defer fail_block.instructions.deinit(gpa);
2215122165
22152 // This function doesn't actually need a src location but if
22153 // the panic function interface ever changes passing `.unneeded` here
22154 // will cause confusing panics.
22155 const src = sema.src;
22156 _ = try sema.safetyPanic(&fail_block, src, panic_id);
22157
22166 try sema.safetyPanic(&fail_block, panic_id);
2215822167 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
2215922168}
2216022169
......@@ -22218,7 +22227,7 @@ fn panicWithMsg(
2221822227 block: *Block,
2221922228 src: LazySrcLoc,
2222022229 msg_inst: Air.Inst.Ref,
22221) !Zir.Inst.Index {
22230) !void {
2222222231 const mod = sema.mod;
2222322232 const arena = sema.arena;
2222422233
......@@ -22229,7 +22238,7 @@ fn panicWithMsg(
2222922238 // TODO implement this feature in all the backends and then delete this branch
2223022239 _ = try block.addNoOp(.breakpoint);
2223122240 _ = try block.addNoOp(.unreach);
22232 return always_noreturn;
22241 return;
2223322242 }
2223422243 const panic_fn = try sema.getBuiltin("panic");
2223522244 const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace");
......@@ -22245,19 +22254,20 @@ fn panicWithMsg(
2224522254 );
2224622255 const args: [3]Air.Inst.Ref = .{ msg_inst, null_stack_trace, .null_value };
2224722256 _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, &args, null);
22248 return always_noreturn;
2224922257}
2225022258
2225122259fn panicUnwrapError(
2225222260 sema: *Sema,
2225322261 parent_block: *Block,
22254 src: LazySrcLoc,
2225522262 operand: Air.Inst.Ref,
2225622263 unwrap_err_tag: Air.Inst.Tag,
2225722264 is_non_err_tag: Air.Inst.Tag,
2225822265) !void {
2225922266 assert(!parent_block.is_comptime);
2226022267 const ok = try parent_block.addUnOp(is_non_err_tag, operand);
22268 if (!sema.mod.comp.formatted_panics) {
22269 return sema.addSafetyCheck(parent_block, ok, .unwrap_error);
22270 }
2226122271 const gpa = sema.gpa;
2226222272
2226322273 var fail_block: Block = .{
......@@ -22286,7 +22296,7 @@ fn panicUnwrapError(
2228622296 const err = try fail_block.addTyOp(unwrap_err_tag, Type.anyerror, operand);
2228722297 const err_return_trace = try sema.getErrorReturnTrace(&fail_block);
2228822298 const args: [2]Air.Inst.Ref = .{ err_return_trace, err };
22289 _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args, null);
22299 _ = try sema.analyzeCall(&fail_block, panic_fn, sema.src, sema.src, .auto, false, &args, null);
2229022300 }
2229122301 }
2229222302 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
......@@ -22295,49 +22305,49 @@ fn panicUnwrapError(
2229522305fn panicIndexOutOfBounds(
2229622306 sema: *Sema,
2229722307 parent_block: *Block,
22298 src: LazySrcLoc,
2229922308 index: Air.Inst.Ref,
2230022309 len: Air.Inst.Ref,
2230122310 cmp_op: Air.Inst.Tag,
2230222311) !void {
2230322312 assert(!parent_block.is_comptime);
2230422313 const ok = try parent_block.addBinOp(cmp_op, index, len);
22305 const gpa = sema.gpa;
22306
22307 var fail_block: Block = .{
22308 .parent = parent_block,
22309 .sema = sema,
22310 .src_decl = parent_block.src_decl,
22311 .namespace = parent_block.namespace,
22312 .wip_capture_scope = parent_block.wip_capture_scope,
22313 .instructions = .{},
22314 .inlining = parent_block.inlining,
22315 .is_comptime = false,
22316 };
22317
22318 defer fail_block.instructions.deinit(gpa);
22314 if (!sema.mod.comp.formatted_panics) {
22315 return sema.addSafetyCheck(parent_block, ok, .index_out_of_bounds);
22316 }
22317 try sema.safetyCheckFormatted(parent_block, ok, "panicOutOfBounds", &.{ index, len });
22318}
2231922319
22320 {
22321 const this_feature_is_implemented_in_the_backend =
22322 sema.mod.comp.bin_file.options.use_llvm;
22320fn panicStartLargerThanEnd(
22321 sema: *Sema,
22322 parent_block: *Block,
22323 start: Air.Inst.Ref,
22324 end: Air.Inst.Ref,
22325) !void {
22326 assert(!parent_block.is_comptime);
22327 const ok = try parent_block.addBinOp(.cmp_lte, start, end);
22328 if (!sema.mod.comp.formatted_panics) {
22329 return sema.addSafetyCheck(parent_block, ok, .start_index_greater_than_end);
22330 }
22331 try sema.safetyCheckFormatted(parent_block, ok, "panicStartGreaterThanEnd", &.{ start, end });
22332}
2232322333
22324 if (!this_feature_is_implemented_in_the_backend) {
22325 // TODO implement this feature in all the backends and then delete this branch
22326 _ = try fail_block.addNoOp(.breakpoint);
22327 _ = try fail_block.addNoOp(.unreach);
22328 } else {
22329 const panic_fn = try sema.getBuiltin("panicOutOfBounds");
22330 const args: [2]Air.Inst.Ref = .{ index, len };
22331 _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args, null);
22332 }
22334fn panicInactiveUnionField(
22335 sema: *Sema,
22336 parent_block: *Block,
22337 active_tag: Air.Inst.Ref,
22338 wanted_tag: Air.Inst.Ref,
22339) !void {
22340 assert(!parent_block.is_comptime);
22341 const ok = try parent_block.addBinOp(.cmp_eq, active_tag, wanted_tag);
22342 if (!sema.mod.comp.formatted_panics) {
22343 return sema.addSafetyCheck(parent_block, ok, .inactive_union_field);
2233322344 }
22334 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
22345 try sema.safetyCheckFormatted(parent_block, ok, "panicInactiveUnionField", &.{ active_tag, wanted_tag });
2233522346}
2233622347
2233722348fn panicSentinelMismatch(
2233822349 sema: *Sema,
2233922350 parent_block: *Block,
22340 src: LazySrcLoc,
2234122351 maybe_sentinel: ?Value,
2234222352 sentinel_ty: Type,
2234322353 ptr: Air.Inst.Ref,
......@@ -22371,9 +22381,24 @@ fn panicSentinelMismatch(
2237122381 else {
2237222382 const panic_fn = try sema.getBuiltin("checkNonScalarSentinel");
2237322383 const args: [2]Air.Inst.Ref = .{ expected_sentinel, actual_sentinel };
22374 _ = try sema.analyzeCall(parent_block, panic_fn, src, src, .auto, false, &args, null);
22384 _ = try sema.analyzeCall(parent_block, panic_fn, sema.src, sema.src, .auto, false, &args, null);
2237522385 return;
2237622386 };
22387
22388 if (!sema.mod.comp.formatted_panics) {
22389 return sema.addSafetyCheck(parent_block, ok, .sentinel_mismatch);
22390 }
22391 try sema.safetyCheckFormatted(parent_block, ok, "panicSentinelMismatch", &.{ expected_sentinel, actual_sentinel });
22392}
22393
22394fn safetyCheckFormatted(
22395 sema: *Sema,
22396 parent_block: *Block,
22397 ok: Air.Inst.Ref,
22398 func: []const u8,
22399 args: []const Air.Inst.Ref,
22400) CompileError!void {
22401 assert(sema.mod.comp.formatted_panics);
2237722402 const gpa = sema.gpa;
2237822403
2237922404 var fail_block: Block = .{
......@@ -22398,9 +22423,8 @@ fn panicSentinelMismatch(
2239822423 _ = try fail_block.addNoOp(.breakpoint);
2239922424 _ = try fail_block.addNoOp(.unreach);
2240022425 } else {
22401 const panic_fn = try sema.getBuiltin("panicSentinelMismatch");
22402 const args: [2]Air.Inst.Ref = .{ expected_sentinel, actual_sentinel };
22403 _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args, null);
22426 const panic_fn = try sema.getBuiltin(func);
22427 _ = try sema.analyzeCall(&fail_block, panic_fn, sema.src, sema.src, .auto, false, args, null);
2240422428 }
2240522429 }
2240622430 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
......@@ -22409,19 +22433,18 @@ fn panicSentinelMismatch(
2240922433fn safetyPanic(
2241022434 sema: *Sema,
2241122435 block: *Block,
22412 src: LazySrcLoc,
2241322436 panic_id: PanicId,
22414) CompileError!Zir.Inst.Index {
22437) CompileError!void {
2241522438 const panic_messages_ty = try sema.getBuiltinType("panic_messages");
2241622439 const msg_decl_index = (try sema.namespaceLookup(
2241722440 block,
22418 src,
22441 sema.src,
2241922442 panic_messages_ty.getNamespace().?,
2242022443 @tagName(panic_id),
2242122444 )).?;
2242222445
22423 const msg_inst = try sema.analyzeDeclVal(block, src, msg_decl_index);
22424 return sema.panicWithMsg(block, src, msg_inst);
22446 const msg_inst = try sema.analyzeDeclVal(block, sema.src, msg_decl_index);
22447 try sema.panicWithMsg(block, sema.src, msg_inst);
2242522448}
2242622449
2242722450fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void {
......@@ -23423,8 +23446,7 @@ fn unionFieldPtr(
2342323446 // TODO would it be better if get_union_tag supported pointers to unions?
2342423447 const union_val = try block.addTyOp(.load, union_ty, union_ptr);
2342523448 const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_val);
23426 const ok = try block.addBinOp(.cmp_eq, active_tag, wanted_tag);
23427 try sema.addSafetyCheck(block, ok, .inactive_union_field);
23449 try sema.panicInactiveUnionField(block, active_tag, wanted_tag);
2342823450 }
2342923451 if (field.ty.zigTypeTag() == .NoReturn) {
2343023452 _ = try block.addNoOp(.unreach);
......@@ -23495,8 +23517,7 @@ fn unionFieldVal(
2349523517 const wanted_tag_val = try Value.Tag.enum_field_index.create(sema.arena, enum_field_index);
2349623518 const wanted_tag = try sema.addConstant(union_obj.tag_ty, wanted_tag_val);
2349723519 const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_byval);
23498 const ok = try block.addBinOp(.cmp_eq, active_tag, wanted_tag);
23499 try sema.addSafetyCheck(block, ok, .inactive_union_field);
23520 try sema.panicInactiveUnionField(block, active_tag, wanted_tag);
2350023521 }
2350123522 if (field.ty.zigTypeTag() == .NoReturn) {
2350223523 _ = try block.addNoOp(.unreach);
......@@ -23807,7 +23828,7 @@ fn elemValArray(
2380723828 if (maybe_index_val == null) {
2380823829 const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
2380923830 const cmp_op: Air.Inst.Tag = if (array_sent != null) .cmp_lte else .cmp_lt;
23810 try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
23831 try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op);
2381123832 }
2381223833 }
2381323834 return block.addBinOp(.array_elem_val, array, elem_index);
......@@ -23868,7 +23889,7 @@ fn elemPtrArray(
2386823889 if (block.wantSafety() and offset == null) {
2386923890 const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
2387023891 const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;
23871 try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
23892 try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op);
2387223893 }
2387323894
2387423895 return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty);
......@@ -23924,7 +23945,7 @@ fn elemValSlice(
2392423945 else
2392523946 try block.addTyOp(.slice_len, Type.usize, slice);
2392623947 const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
23927 try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
23948 try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op);
2392823949 }
2392923950 try sema.queueFullTypeResolution(sema.typeOf(slice));
2393023951 return block.addBinOp(.slice_elem_val, slice, elem_index);
......@@ -23983,7 +24004,7 @@ fn elemPtrSlice(
2398324004 break :len try block.addTyOp(.slice_len, Type.usize, slice);
2398424005 };
2398524006 const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
23986 try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
24007 try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op);
2398724008 }
2398824009 return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty);
2398924010}
......@@ -28028,7 +28049,11 @@ fn analyzeSlice(
2802828049 }
2802928050 }
2803028051
28031 const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src);
28052 if (block.wantSafety() and !block.is_comptime) {
28053 // requirement: start <= end
28054 try sema.panicStartLargerThanEnd(block, start, end);
28055 }
28056 const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false);
2803228057 const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);
2803328058
2803428059 const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data;
......@@ -28063,18 +28088,18 @@ fn analyzeSlice(
2806328088 const actual_len = if (slice_ty.sentinel() == null)
2806428089 slice_len_inst
2806528090 else
28066 try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src);
28091 try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true);
2806728092
2806828093 const actual_end = if (slice_sentinel != null)
28069 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src)
28094 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true)
2807028095 else
2807128096 end;
2807228097
28073 try sema.panicIndexOutOfBounds(block, src, actual_end, actual_len, .cmp_lte);
28098 try sema.panicIndexOutOfBounds(block, actual_end, actual_len, .cmp_lte);
2807428099 }
2807528100
2807628101 // requirement: result[new_len] == slice_sentinel
28077 try sema.panicSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len);
28102 try sema.panicSentinelMismatch(block, slice_sentinel, elem_ty, result, new_len);
2807828103 }
2807928104 return result;
2808028105 };
......@@ -28131,18 +28156,18 @@ fn analyzeSlice(
2813128156 if (slice_ty.sentinel() == null) break :blk slice_len_inst;
2813228157
2813328158 // we have to add one because slice lengths don't include the sentinel
28134 break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src);
28159 break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true);
2813528160 } else null;
2813628161 if (opt_len_inst) |len_inst| {
2813728162 const actual_end = if (slice_sentinel != null)
28138 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src)
28163 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true)
2813928164 else
2814028165 end;
28141 try sema.panicIndexOutOfBounds(block, src, actual_end, len_inst, .cmp_lte);
28166 try sema.panicIndexOutOfBounds(block, actual_end, len_inst, .cmp_lte);
2814228167 }
2814328168
2814428169 // requirement: start <= end
28145 try sema.panicIndexOutOfBounds(block, src, start, end, .cmp_lte);
28170 try sema.panicIndexOutOfBounds(block, start, end, .cmp_lte);
2814628171 }
2814728172 const result = try block.addInst(.{
2814828173 .tag = .slice,
......@@ -28156,7 +28181,7 @@ fn analyzeSlice(
2815628181 });
2815728182 if (block.wantSafety()) {
2815828183 // requirement: result[new_len] == slice_sentinel
28159 try sema.panicSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len);
28184 try sema.panicSentinelMismatch(block, slice_sentinel, elem_ty, result, new_len);
2816028185 }
2816128186 return result;
2816228187}
src/codegen/llvm.zig+15
......@@ -9228,6 +9228,21 @@ pub const FuncGen = struct {
92289228 const target = self.dg.module.getTarget();
92299229 const layout = union_ty.unionGetLayout(target);
92309230 const union_obj = union_ty.cast(Type.Payload.Union).?.data;
9231
9232 if (union_obj.layout == .Packed) {
9233 const big_bits = union_ty.bitSize(target);
9234 const int_llvm_ty = self.dg.context.intType(@intCast(c_uint, big_bits));
9235 const field = union_obj.fields.values()[extra.field_index];
9236 const non_int_val = try self.resolveInst(extra.init);
9237 const ty_bit_size = @intCast(u16, field.ty.bitSize(target));
9238 const small_int_ty = self.dg.context.intType(ty_bit_size);
9239 const small_int_val = if (field.ty.isPtrAtRuntime())
9240 self.builder.buildPtrToInt(non_int_val, small_int_ty, "")
9241 else
9242 self.builder.buildBitCast(non_int_val, small_int_ty, "");
9243 return self.builder.buildZExtOrBitCast(small_int_val, int_llvm_ty, "");
9244 }
9245
92319246 const tag_int = blk: {
92329247 const tag_ty = union_ty.unionTagTypeHypothetical();
92339248 const union_field_name = union_obj.fields.keys()[extra.field_index];
src/main.zig+8
......@@ -406,6 +406,8 @@ const usage_build_generic =
406406 \\ -fno-function-sections All functions go into same section
407407 \\ -fstrip Omit debug symbols
408408 \\ -fno-strip Keep debug symbols
409 \\ -fformatted-panics Enable formatted safety panics
410 \\ -fno-formatted-panics Disable formatted safety panics
409411 \\ -ofmt=[mode] Override target object format
410412 \\ elf Executable and Linking Format
411413 \\ c C source code
......@@ -632,6 +634,7 @@ fn buildOutputType(
632634 var have_version = false;
633635 var compatibility_version: ?std.builtin.Version = null;
634636 var strip: ?bool = null;
637 var formatted_panics: ?bool = null;
635638 var function_sections = false;
636639 var no_builtin = false;
637640 var watch = false;
......@@ -1242,6 +1245,10 @@ fn buildOutputType(
12421245 strip = true;
12431246 } else if (mem.eql(u8, arg, "-fno-strip")) {
12441247 strip = false;
1248 } else if (mem.eql(u8, arg, "-fformatted-panics")) {
1249 formatted_panics = true;
1250 } else if (mem.eql(u8, arg, "-fno-formatted-panics")) {
1251 formatted_panics = false;
12451252 } else if (mem.eql(u8, arg, "-fsingle-threaded")) {
12461253 single_threaded = true;
12471254 } else if (mem.eql(u8, arg, "-fno-single-threaded")) {
......@@ -2938,6 +2945,7 @@ fn buildOutputType(
29382945 .stack_size_override = stack_size_override,
29392946 .image_base_override = image_base_override,
29402947 .strip = strip,
2948 .formatted_panics = formatted_panics,
29412949 .single_threaded = single_threaded,
29422950 .function_sections = function_sections,
29432951 .no_builtin = no_builtin,
test/behavior.zig+1
......@@ -116,6 +116,7 @@ test {
116116 _ = @import("behavior/bugs/13171.zig");
117117 _ = @import("behavior/bugs/13285.zig");
118118 _ = @import("behavior/bugs/13435.zig");
119 _ = @import("behavior/bugs/13664.zig");
119120 _ = @import("behavior/byteswap.zig");
120121 _ = @import("behavior/byval_arg_var.zig");
121122 _ = @import("behavior/call.zig");
test/behavior/basic.zig+11
......@@ -1127,3 +1127,14 @@ test "pointer to zero sized global is mutable" {
11271127 };
11281128 try expect(@TypeOf(&S.thing) == *S.Thing);
11291129}
1130
1131test "returning an opaque type from a function" {
1132 const S = struct {
1133 fn foo(comptime a: u32) type {
1134 return opaque {
1135 const b = a;
1136 };
1137 }
1138 };
1139 try expect(S.foo(123).b == 123);
1140}
test/behavior/bugs/13664.zig created+27
......@@ -0,0 +1,27 @@
1const std = @import("std");
2const builtin = @import("builtin");
3
4const Fields = packed struct {
5 timestamp: u50,
6 random_bits: u13,
7};
8const ID = packed union {
9 value: u63,
10 fields: Fields,
11};
12fn value() i64 {
13 return 1341;
14}
15test {
16 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
17 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
18 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
19 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
20
21 const timestamp: i64 = value();
22 const id = ID{ .fields = Fields{
23 .timestamp = @intCast(u50, timestamp),
24 .random_bits = 420,
25 } };
26 try std.testing.expect((ID{ .value = id.value }).fields.timestamp == timestamp);
27}
test/cases/compile_errors/comptime_parameter_not_declared_as_such.zig+1
......@@ -21,4 +21,5 @@ pub export fn entry1() void {
2121// target=native
2222//
2323// :3:6: error: parameter of type '*const fn(anytype) void' must be declared comptime
24// :3:6: note: function is generic
2425// :10:34: error: parameter of type 'comptime_int' must be declared comptime
test/cases/compile_errors/invalid_field_in_struct_value_expression.zig+12
......@@ -12,9 +12,21 @@ export fn f() void {
1212 _ = a;
1313}
1414
15const Object = struct {
16 field_1: u32,
17 field_2: u32,
18};
19fn dump(_: Object) void {}
20pub export fn entry() void {
21 dump(.{ .field_1 = 123, .field_3 = 456 });
22}
23
24
1525// error
1626// backend=stage2
1727// target=native
1828//
1929// :10:10: error: no field named 'foo' in struct 'tmp.A'
2030// :1:11: note: struct declared here
31// :21:30: error: no field named 'field_3' in struct 'tmp.Object'
32// :15:16: note: struct declared here
test/cases/compile_errors/missing_member_in_namespace_export.zig created+10
......@@ -0,0 +1,10 @@
1const S = struct {};
2comptime {
3 @export(S.foo, .{ .name = "foo" });
4}
5
6// error
7// target=native
8//
9// :3:14: error: struct 'tmp.S' has no member named 'foo'
10// :1:11: note: struct declared here
test/cases/safety/bad union field access.zig +1-1
......@@ -2,7 +2,7 @@ const std = @import("std");
22
33pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
44 _ = stack_trace;
5 if (std.mem.eql(u8, message, "access of inactive union field")) {
5 if (std.mem.eql(u8, message, "access of union field 'float' while field 'int' is active")) {
66 std.process.exit(0);
77 }
88 std.process.exit(1);
test/cases/safety/slice start index greater than end index.zig created+23
......@@ -0,0 +1,23 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "start index 10 is larger than end index 1")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10
11pub fn main() !void {
12 var a: usize = 1;
13 var b: usize = 10;
14 var buf: [16]u8 = undefined;
15
16 const slice = buf[b..a];
17 _ = slice;
18 return error.TestFailed;
19}
20
21// run
22// backend=llvm
23// target=native