authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-08-06 14:59:10-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-08-06 14:59:10-07:00
logb3d463c9e6ab41d55849e6cfbbc83d6a1f171ef9
tree7d7076d0933c119ee2a0d58c868d8b06bd400089
parent3e2defd36c0bf90a0604a7618f57beaa4077139c
parent75275a1514b6954bed09c4c14a325e883a129c7b
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #12337 from Vexu/stage2-safety

Stage2: implement remaining runtime safety checks

33 files changed, 378 insertions(+), 102 deletions(-)

src/Air.zig+5
...@@ -660,6 +660,10 @@ pub const Inst = struct {...@@ -660,6 +660,10 @@ pub const Inst = struct {
660 /// Uses the `pl_op` field with payload `AtomicRmw`. Operand is `ptr`.660 /// Uses the `pl_op` field with payload `AtomicRmw`. Operand is `ptr`.
661 atomic_rmw,661 atomic_rmw,
662662
663 /// Returns true if enum tag value has a name.
664 /// Uses the `un_op` field.
665 is_named_enum_value,
666
663 /// Given an enum tag value, returns the tag name. The enum type may be non-exhaustive.667 /// Given an enum tag value, returns the tag name. The enum type may be non-exhaustive.
664 /// Result type is always `[:0]const u8`.668 /// Result type is always `[:0]const u8`.
665 /// Uses the `un_op` field.669 /// Uses the `un_op` field.
...@@ -1057,6 +1061,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -1057,6 +1061,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
1057 .is_non_err,1061 .is_non_err,
1058 .is_err_ptr,1062 .is_err_ptr,
1059 .is_non_err_ptr,1063 .is_non_err_ptr,
1064 .is_named_enum_value,
1060 => return Type.bool,1065 => return Type.bool,
10611066
1062 .const_ty => return Type.type,1067 .const_ty => return Type.type,
src/Liveness.zig+2
...@@ -291,6 +291,7 @@ pub fn categorizeOperand(...@@ -291,6 +291,7 @@ pub fn categorizeOperand(
291 .is_non_err_ptr,291 .is_non_err_ptr,
292 .ptrtoint,292 .ptrtoint,
293 .bool_to_int,293 .bool_to_int,
294 .is_named_enum_value,
294 .tag_name,295 .tag_name,
295 .error_name,296 .error_name,
296 .sqrt,297 .sqrt,
...@@ -858,6 +859,7 @@ fn analyzeInst(...@@ -858,6 +859,7 @@ fn analyzeInst(
858 .bool_to_int,859 .bool_to_int,
859 .ret,860 .ret,
860 .ret_load,861 .ret_load,
862 .is_named_enum_value,
861 .tag_name,863 .tag_name,
862 .error_name,864 .error_name,
863 .sqrt,865 .sqrt,
src/Sema.zig+159-53
...@@ -1578,8 +1578,7 @@ pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize)...@@ -1578,8 +1578,7 @@ pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize)
15781578
1579 // st.index = 0;1579 // st.index = 0;
1580 const index_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, "index", src, true);1580 const index_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, "index", src, true);
1581 const zero = try sema.addConstant(Type.usize, Value.zero);1581 try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, .zero_usize, src, .store);
1582 try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, zero, src, .store);
15831582
1584 // @errorReturnTrace() = &st;1583 // @errorReturnTrace() = &st;
1585 _ = try err_trace_block.addUnOp(.set_err_return_trace, st_ptr);1584 _ = try err_trace_block.addUnOp(.set_err_return_trace, st_ptr);
...@@ -6949,8 +6948,12 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A...@@ -6949,8 +6948,12 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
6949 }6948 }
69506949
6951 try sema.requireRuntimeBlock(block, src, operand_src);6950 try sema.requireRuntimeBlock(block, src, operand_src);
6952 // TODO insert safety check to make sure the value matches an enum value6951 const result = try block.addTyOp(.intcast, dest_ty, operand);
6953 return block.addTyOp(.intcast, dest_ty, operand);6952 if (block.wantSafety() and !dest_ty.isNonexhaustiveEnum() and sema.mod.comp.bin_file.options.use_llvm) {
6953 const ok = try block.addUnOp(.is_named_enum_value, result);
6954 try sema.addSafetyCheck(block, ok, .invalid_enum_value);
6955 }
6956 return result;
6954}6957}
69556958
6956/// Pointer in, pointer out.6959/// Pointer in, pointer out.
...@@ -9707,7 +9710,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9707,7 +9710,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9707 }9710 }
97089711
9709 var final_else_body: []const Air.Inst.Index = &.{};9712 var final_else_body: []const Air.Inst.Index = &.{};
9710 if (special.body.len != 0 or !is_first) {9713 if (special.body.len != 0 or !is_first or case_block.wantSafety()) {
9711 var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, child_block.wip_capture_scope);9714 var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, child_block.wip_capture_scope);
9712 defer wip_captures.deinit();9715 defer wip_captures.deinit();
97139716
...@@ -9730,9 +9733,11 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9730,9 +9733,11 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9730 } else {9733 } else {
9731 // We still need a terminator in this block, but we have proven9734 // We still need a terminator in this block, but we have proven
9732 // that it is unreachable.9735 // that it is unreachable.
9733 // TODO this should be a special safety panic other than unreachable, something9736 if (case_block.wantSafety()) {
9734 // like "panic: switch operand had corrupt value not allowed by the type"9737 _ = try sema.safetyPanic(&case_block, src, .corrupt_switch);
9735 try case_block.addUnreachable(src, true);9738 } else {
9739 _ = try case_block.addNoOp(.unreach);
9740 }
9736 }9741 }
97379742
9738 try wip_captures.finalize();9743 try wip_captures.finalize();
...@@ -10241,34 +10246,57 @@ fn zirShl(...@@ -10241,34 +10246,57 @@ fn zirShl(
10241 } else rhs;10246 } else rhs;
1024210247
10243 try sema.requireRuntimeBlock(block, src, runtime_src);10248 try sema.requireRuntimeBlock(block, src, runtime_src);
10244 if (block.wantSafety() and air_tag == .shl_exact) {10249 if (block.wantSafety()) {
10245 const op_ov_tuple_ty = try sema.overflowArithmeticTupleType(lhs_ty);10250 const bit_count = scalar_ty.intInfo(target).bits;
10246 const op_ov = try block.addInst(.{10251 if (!std.math.isPowerOfTwo(bit_count)) {
10247 .tag = .shl_with_overflow,10252 const bit_count_val = try Value.Tag.int_u64.create(sema.arena, bit_count);
10248 .data = .{ .ty_pl = .{10253
10249 .ty = try sema.addType(op_ov_tuple_ty),10254 const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: {
10250 .payload = try sema.addExtra(Air.Bin{10255 const bit_count_inst = try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, bit_count_val));
10251 .lhs = lhs,10256 const lt = try block.addCmpVector(rhs, bit_count_inst, .lt, try sema.addType(rhs_ty));
10252 .rhs = rhs,10257 break :ok try block.addInst(.{
10253 }),10258 .tag = .reduce,
10254 } },10259 .data = .{ .reduce = .{
10255 });10260 .operand = lt,
10256 const ov_bit = try sema.tupleFieldValByIndex(block, src, op_ov, 1, op_ov_tuple_ty);10261 .operation = .And,
10257 const any_ov_bit = if (lhs_ty.zigTypeTag() == .Vector)10262 } },
10258 try block.addInst(.{10263 });
10259 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,10264 } else ok: {
10260 .data = .{ .reduce = .{10265 const bit_count_inst = try sema.addConstant(rhs_ty, bit_count_val);
10261 .operand = ov_bit,10266 break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst);
10262 .operation = .Or,10267 };
10268 try sema.addSafetyCheck(block, ok, .shift_rhs_too_big);
10269 }
10270
10271 if (air_tag == .shl_exact) {
10272 const op_ov_tuple_ty = try sema.overflowArithmeticTupleType(lhs_ty);
10273 const op_ov = try block.addInst(.{
10274 .tag = .shl_with_overflow,
10275 .data = .{ .ty_pl = .{
10276 .ty = try sema.addType(op_ov_tuple_ty),
10277 .payload = try sema.addExtra(Air.Bin{
10278 .lhs = lhs,
10279 .rhs = rhs,
10280 }),
10263 } },10281 } },
10264 })10282 });
10265 else10283 const ov_bit = try sema.tupleFieldValByIndex(block, src, op_ov, 1, op_ov_tuple_ty);
10266 ov_bit;10284 const any_ov_bit = if (lhs_ty.zigTypeTag() == .Vector)
10267 const zero_ov = try sema.addConstant(Type.@"u1", Value.zero);10285 try block.addInst(.{
10268 const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, zero_ov);10286 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
10287 .data = .{ .reduce = .{
10288 .operand = ov_bit,
10289 .operation = .Or,
10290 } },
10291 })
10292 else
10293 ov_bit;
10294 const zero_ov = try sema.addConstant(Type.@"u1", Value.zero);
10295 const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, zero_ov);
1026910296
10270 try sema.addSafetyCheck(block, no_ov, .shl_overflow);10297 try sema.addSafetyCheck(block, no_ov, .shl_overflow);
10271 return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty);10298 return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty);
10299 }
10272 }10300 }
10273 return block.addBinOp(air_tag, lhs, new_rhs);10301 return block.addBinOp(air_tag, lhs, new_rhs);
10274}10302}
...@@ -10347,20 +10375,43 @@ fn zirShr(...@@ -10347,20 +10375,43 @@ fn zirShr(
1034710375
10348 try sema.requireRuntimeBlock(block, src, runtime_src);10376 try sema.requireRuntimeBlock(block, src, runtime_src);
10349 const result = try block.addBinOp(air_tag, lhs, rhs);10377 const result = try block.addBinOp(air_tag, lhs, rhs);
10350 if (block.wantSafety() and air_tag == .shr_exact) {10378 if (block.wantSafety()) {
10351 const back = try block.addBinOp(.shl, result, rhs);10379 const bit_count = scalar_ty.intInfo(target).bits;
1035210380 if (!std.math.isPowerOfTwo(bit_count)) {
10353 const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: {10381 const bit_count_val = try Value.Tag.int_u64.create(sema.arena, bit_count);
10354 const eql = try block.addCmpVector(lhs, back, .eq, try sema.addType(rhs_ty));10382
10355 break :ok try block.addInst(.{10383 const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: {
10356 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,10384 const bit_count_inst = try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, bit_count_val));
10357 .data = .{ .reduce = .{10385 const lt = try block.addCmpVector(rhs, bit_count_inst, .lt, try sema.addType(rhs_ty));
10358 .operand = eql,10386 break :ok try block.addInst(.{
10359 .operation = .And,10387 .tag = .reduce,
10360 } },10388 .data = .{ .reduce = .{
10361 });10389 .operand = lt,
10362 } else try block.addBinOp(.cmp_eq, lhs, back);10390 .operation = .And,
10363 try sema.addSafetyCheck(block, ok, .shr_overflow);10391 } },
10392 });
10393 } else ok: {
10394 const bit_count_inst = try sema.addConstant(rhs_ty, bit_count_val);
10395 break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst);
10396 };
10397 try sema.addSafetyCheck(block, ok, .shift_rhs_too_big);
10398 }
10399
10400 if (air_tag == .shr_exact) {
10401 const back = try block.addBinOp(.shl, result, rhs);
10402
10403 const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: {
10404 const eql = try block.addCmpVector(lhs, back, .eq, try sema.addType(rhs_ty));
10405 break :ok try block.addInst(.{
10406 .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
10407 .data = .{ .reduce = .{
10408 .operand = eql,
10409 .operation = .And,
10410 } },
10411 });
10412 } else try block.addBinOp(.cmp_eq, lhs, back);
10413 try sema.addSafetyCheck(block, ok, .shr_overflow);
10414 }
10364 }10415 }
10365 return result;10416 return result;
10366}10417}
...@@ -15961,6 +16012,11 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -15961,6 +16012,11 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
15961 const field_name = enum_ty.enumFieldName(field_index);16012 const field_name = enum_ty.enumFieldName(field_index);
15962 return sema.addStrLit(block, field_name);16013 return sema.addStrLit(block, field_name);
15963 }16014 }
16015 try sema.requireRuntimeBlock(block, src, operand_src);
16016 if (block.wantSafety() and sema.mod.comp.bin_file.options.use_llvm) {
16017 const ok = try block.addUnOp(.is_named_enum_value, casted_operand);
16018 try sema.addSafetyCheck(block, ok, .invalid_enum_value);
16019 }
15964 // In case the value is runtime-known, we have an AIR instruction for this instead16020 // In case the value is runtime-known, we have an AIR instruction for this instead
15965 // of trying to lower it in Sema because an optimization pass may result in the operand16021 // of trying to lower it in Sema because an optimization pass may result in the operand
15966 // being comptime-known, which would let us elide the `tag_name` AIR instruction.16022 // being comptime-known, which would let us elide the `tag_name` AIR instruction.
...@@ -16942,7 +16998,7 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -16942,7 +16998,7 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
16942 }16998 }
1694316999
16944 try sema.requireRuntimeBlock(block, src, operand_src);17000 try sema.requireRuntimeBlock(block, src, operand_src);
16945 if (block.wantSafety()) {17001 if (block.wantSafety() and try sema.typeHasRuntimeBits(block, sema.src, type_res.elemType2())) {
16946 if (!type_res.isAllowzeroPtr()) {17002 if (!type_res.isAllowzeroPtr()) {
16947 const is_non_zero = try block.addBinOp(.cmp_neq, operand_coerced, .zero_usize);17003 const is_non_zero = try block.addBinOp(.cmp_neq, operand_coerced, .zero_usize);
16948 try sema.addSafetyCheck(block, is_non_zero, .cast_to_null);17004 try sema.addSafetyCheck(block, is_non_zero, .cast_to_null);
...@@ -17234,7 +17290,9 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A...@@ -17234,7 +17290,9 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
17234 }17290 }
1723517291
17236 try sema.requireRuntimeBlock(block, inst_data.src(), ptr_src);17292 try sema.requireRuntimeBlock(block, inst_data.src(), ptr_src);
17237 if (block.wantSafety() and dest_align > 1) {17293 if (block.wantSafety() and dest_align > 1 and
17294 try sema.typeHasRuntimeBits(block, sema.src, dest_ty.elemType2()))
17295 {
17238 const val_payload = try sema.arena.create(Value.Payload.U64);17296 const val_payload = try sema.arena.create(Value.Payload.U64);
17239 val_payload.* = .{17297 val_payload.* = .{
17240 .base = .{ .tag = .int_u64 },17298 .base = .{ .tag = .int_u64 },
...@@ -17253,7 +17311,7 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A...@@ -17253,7 +17311,7 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
17253 const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize);17311 const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize);
17254 const ok = if (ptr_ty.isSlice()) ok: {17312 const ok = if (ptr_ty.isSlice()) ok: {
17255 const len = try sema.analyzeSliceLen(block, ptr_src, ptr);17313 const len = try sema.analyzeSliceLen(block, ptr_src, ptr);
17256 const len_zero = try block.addBinOp(.cmp_eq, len, try sema.addConstant(Type.usize, Value.zero));17314 const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize);
17257 break :ok try block.addBinOp(.bit_or, len_zero, is_aligned);17315 break :ok try block.addBinOp(.bit_or, len_zero, is_aligned);
17258 } else is_aligned;17316 } else is_aligned;
17259 try sema.addSafetyCheck(block, ok, .incorrect_alignment);17317 try sema.addSafetyCheck(block, ok, .incorrect_alignment);
...@@ -20114,6 +20172,9 @@ pub const PanicId = enum {...@@ -20114,6 +20172,9 @@ pub const PanicId = enum {
20114 /// TODO make this call `std.builtin.panicInactiveUnionField`.20172 /// TODO make this call `std.builtin.panicInactiveUnionField`.
20115 inactive_union_field,20173 inactive_union_field,
20116 integer_part_out_of_bounds,20174 integer_part_out_of_bounds,
20175 corrupt_switch,
20176 shift_rhs_too_big,
20177 invalid_enum_value,
20117};20178};
2011820179
20119fn addSafetyCheck(20180fn addSafetyCheck(
...@@ -20408,6 +20469,9 @@ fn safetyPanic(...@@ -20408,6 +20469,9 @@ fn safetyPanic(
20408 .exact_division_remainder => "exact division produced remainder",20469 .exact_division_remainder => "exact division produced remainder",
20409 .inactive_union_field => "access of inactive union field",20470 .inactive_union_field => "access of inactive union field",
20410 .integer_part_out_of_bounds => "integer part of floating point value out of bounds",20471 .integer_part_out_of_bounds => "integer part of floating point value out of bounds",
20472 .corrupt_switch => "switch on corrupt value",
20473 .shift_rhs_too_big => "shift amount is greater than the type size",
20474 .invalid_enum_value => "invalid enum value",
20411 };20475 };
2041220476
20413 const msg_inst = msg_inst: {20477 const msg_inst = msg_inst: {
...@@ -22096,7 +22160,6 @@ fn coerceExtra(...@@ -22096,7 +22160,6 @@ fn coerceExtra(
22096 .ok => {},22160 .ok => {},
22097 else => break :src_c_ptr,22161 else => break :src_c_ptr,
22098 }22162 }
22099 // TODO add safety check for null pointer
22100 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);22163 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
22101 }22164 }
2210222165
...@@ -24569,6 +24632,24 @@ fn coerceCompatiblePtrs(...@@ -24569,6 +24632,24 @@ fn coerceCompatiblePtrs(
24569 return sema.addConstant(dest_ty, val);24632 return sema.addConstant(dest_ty, val);
24570 }24633 }
24571 try sema.requireRuntimeBlock(block, inst_src, null);24634 try sema.requireRuntimeBlock(block, inst_src, null);
24635 const inst_ty = sema.typeOf(inst);
24636 const inst_allows_zero = (inst_ty.zigTypeTag() == .Pointer and inst_ty.ptrAllowsZero()) or true;
24637 if (block.wantSafety() and inst_allows_zero and !dest_ty.ptrAllowsZero() and
24638 try sema.typeHasRuntimeBits(block, sema.src, dest_ty.elemType2()))
24639 {
24640 const actual_ptr = if (inst_ty.isSlice())
24641 try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty)
24642 else
24643 inst;
24644 const ptr_int = try block.addUnOp(.ptrtoint, actual_ptr);
24645 const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize);
24646 const ok = if (inst_ty.isSlice()) ok: {
24647 const len = try sema.analyzeSliceLen(block, inst_src, inst);
24648 const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize);
24649 break :ok try block.addBinOp(.bit_or, len_zero, is_non_zero);
24650 } else is_non_zero;
24651 try sema.addSafetyCheck(block, ok, .cast_to_null);
24652 }
24572 return sema.bitCast(block, dest_ty, inst, inst_src);24653 return sema.bitCast(block, dest_ty, inst, inst_src);
24573}24654}
2457424655
...@@ -25708,6 +25789,27 @@ fn analyzeSlice(...@@ -25708,6 +25789,27 @@ fn analyzeSlice(
25708 const new_ptr_val = opt_new_ptr_val orelse {25789 const new_ptr_val = opt_new_ptr_val orelse {
25709 const result = try block.addBitCast(return_ty, new_ptr);25790 const result = try block.addBitCast(return_ty, new_ptr);
25710 if (block.wantSafety()) {25791 if (block.wantSafety()) {
25792 // requirement: slicing C ptr is non-null
25793 if (ptr_ptr_child_ty.isCPtr()) {
25794 const is_non_null = try sema.analyzeIsNull(block, ptr_src, ptr, true);
25795 try sema.addSafetyCheck(block, is_non_null, .unwrap_null);
25796 }
25797
25798 if (slice_ty.isSlice()) {
25799 const slice_len_inst = try block.addTyOp(.slice_len, Type.usize, ptr_or_slice);
25800 const actual_len = if (slice_ty.sentinel() == null)
25801 slice_len_inst
25802 else
25803 try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src);
25804
25805 const actual_end = if (slice_sentinel != null)
25806 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src)
25807 else
25808 end;
25809
25810 try sema.panicIndexOutOfBounds(block, src, actual_end, actual_len, .cmp_lte);
25811 }
25812
25711 // requirement: result[new_len] == slice_sentinel25813 // requirement: result[new_len] == slice_sentinel
25712 try sema.panicSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len);25814 try sema.panicSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len);
25713 }25815 }
...@@ -25769,7 +25871,11 @@ fn analyzeSlice(...@@ -25769,7 +25871,11 @@ fn analyzeSlice(
25769 break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src);25871 break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src);
25770 } else null;25872 } else null;
25771 if (opt_len_inst) |len_inst| {25873 if (opt_len_inst) |len_inst| {
25772 try sema.panicIndexOutOfBounds(block, src, end, len_inst, .cmp_lte);25874 const actual_end = if (slice_sentinel != null)
25875 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src)
25876 else
25877 end;
25878 try sema.panicIndexOutOfBounds(block, src, actual_end, len_inst, .cmp_lte);
25773 }25879 }
2577425880
25775 // requirement: start <= end25881 // requirement: start <= end
src/arch/aarch64/CodeGen.zig+2
...@@ -777,6 +777,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -777,6 +777,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
777 .float_to_int_optimized,777 .float_to_int_optimized,
778 => return self.fail("TODO implement optimized float mode", .{}),778 => return self.fail("TODO implement optimized float mode", .{}),
779779
780 .is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}),
781
780 .wasm_memory_size => unreachable,782 .wasm_memory_size => unreachable,
781 .wasm_memory_grow => unreachable,783 .wasm_memory_grow => unreachable,
782 // zig fmt: on784 // zig fmt: on
src/arch/arm/CodeGen.zig+2
...@@ -768,6 +768,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -768,6 +768,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
768 .float_to_int_optimized,768 .float_to_int_optimized,
769 => return self.fail("TODO implement optimized float mode", .{}),769 => return self.fail("TODO implement optimized float mode", .{}),
770770
771 .is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}),
772
771 .wasm_memory_size => unreachable,773 .wasm_memory_size => unreachable,
772 .wasm_memory_grow => unreachable,774 .wasm_memory_grow => unreachable,
773 // zig fmt: on775 // zig fmt: on
src/arch/riscv64/CodeGen.zig+2
...@@ -693,6 +693,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -693,6 +693,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
693 .float_to_int_optimized,693 .float_to_int_optimized,
694 => return self.fail("TODO implement optimized float mode", .{}),694 => return self.fail("TODO implement optimized float mode", .{}),
695695
696 .is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}),
697
696 .wasm_memory_size => unreachable,698 .wasm_memory_size => unreachable,
697 .wasm_memory_grow => unreachable,699 .wasm_memory_grow => unreachable,
698 // zig fmt: on700 // zig fmt: on
src/arch/sparc64/CodeGen.zig+2
...@@ -705,6 +705,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -705,6 +705,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
705 .float_to_int_optimized,705 .float_to_int_optimized,
706 => @panic("TODO implement optimized float mode"),706 => @panic("TODO implement optimized float mode"),
707707
708 .is_named_enum_value => @panic("TODO implement is_named_enum_value"),
709
708 .wasm_memory_size => unreachable,710 .wasm_memory_size => unreachable,
709 .wasm_memory_grow => unreachable,711 .wasm_memory_grow => unreachable,
710 // zig fmt: on712 // zig fmt: on
src/arch/wasm/CodeGen.zig+1
...@@ -1621,6 +1621,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {...@@ -1621,6 +1621,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
1621 .tag_name,1621 .tag_name,
1622 .err_return_trace,1622 .err_return_trace,
1623 .set_err_return_trace,1623 .set_err_return_trace,
1624 .is_named_enum_value,
1624 => |tag| return self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),1625 => |tag| return self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),
16251626
1626 .add_optimized,1627 .add_optimized,
src/arch/x86_64/CodeGen.zig+2
...@@ -775,6 +775,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -775,6 +775,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
775 .float_to_int_optimized,775 .float_to_int_optimized,
776 => return self.fail("TODO implement optimized float mode", .{}),776 => return self.fail("TODO implement optimized float mode", .{}),
777777
778 .is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}),
779
778 .wasm_memory_size => unreachable,780 .wasm_memory_size => unreachable,
779 .wasm_memory_grow => unreachable,781 .wasm_memory_grow => unreachable,
780 // zig fmt: on782 // zig fmt: on
src/codegen/c.zig+3-1
...@@ -1952,6 +1952,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -1952,6 +1952,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
1952 .reduce_optimized,1952 .reduce_optimized,
1953 .float_to_int_optimized,1953 .float_to_int_optimized,
1954 => return f.fail("TODO implement optimized float mode", .{}),1954 => return f.fail("TODO implement optimized float mode", .{}),
1955
1956 .is_named_enum_value => return f.fail("TODO: C backend: implement is_named_enum_value", .{}),
1955 // zig fmt: on1957 // zig fmt: on
1956 };1958 };
1957 switch (result_value) {1959 switch (result_value) {
...@@ -3250,7 +3252,7 @@ fn airIsNull(...@@ -3250,7 +3252,7 @@ fn airIsNull(
32503252
3251 const ty = f.air.typeOf(un_op);3253 const ty = f.air.typeOf(un_op);
3252 var opt_buf: Type.Payload.ElemType = undefined;3254 var opt_buf: Type.Payload.ElemType = undefined;
3253 const payload_ty = if (ty.zigTypeTag() == .Pointer)3255 const payload_ty = if (deref_suffix[0] != 0)
3254 ty.childType().optionalChild(&opt_buf)3256 ty.childType().optionalChild(&opt_buf)
3255 else3257 else
3256 ty.optionalChild(&opt_buf);3258 ty.optionalChild(&opt_buf);
src/codegen/llvm.zig+87
...@@ -201,6 +201,8 @@ pub const Object = struct {...@@ -201,6 +201,8 @@ pub const Object = struct {
201 /// * it works for functions not all globals.201 /// * it works for functions not all globals.
202 /// Therefore, this table keeps track of the mapping.202 /// Therefore, this table keeps track of the mapping.
203 decl_map: std.AutoHashMapUnmanaged(Module.Decl.Index, *const llvm.Value),203 decl_map: std.AutoHashMapUnmanaged(Module.Decl.Index, *const llvm.Value),
204 /// Serves the same purpose as `decl_map` but only used for the `is_named_enum_value` instruction.
205 named_enum_map: std.AutoHashMapUnmanaged(Module.Decl.Index, *const llvm.Value),
204 /// Maps Zig types to LLVM types. The table memory itself is backed by the GPA of206 /// Maps Zig types to LLVM types. The table memory itself is backed by the GPA of
205 /// the compiler, but the Type/Value memory here is backed by `type_map_arena`.207 /// the compiler, but the Type/Value memory here is backed by `type_map_arena`.
206 /// TODO we need to remove entries from this map in response to incremental compilation208 /// TODO we need to remove entries from this map in response to incremental compilation
...@@ -377,6 +379,7 @@ pub const Object = struct {...@@ -377,6 +379,7 @@ pub const Object = struct {
377 .target_data = target_data,379 .target_data = target_data,
378 .target = options.target,380 .target = options.target,
379 .decl_map = .{},381 .decl_map = .{},
382 .named_enum_map = .{},
380 .type_map = .{},383 .type_map = .{},
381 .type_map_arena = std.heap.ArenaAllocator.init(gpa),384 .type_map_arena = std.heap.ArenaAllocator.init(gpa),
382 .di_type_map = .{},385 .di_type_map = .{},
...@@ -396,6 +399,7 @@ pub const Object = struct {...@@ -396,6 +399,7 @@ pub const Object = struct {
396 self.llvm_module.dispose();399 self.llvm_module.dispose();
397 self.context.dispose();400 self.context.dispose();
398 self.decl_map.deinit(gpa);401 self.decl_map.deinit(gpa);
402 self.named_enum_map.deinit(gpa);
399 self.type_map.deinit(gpa);403 self.type_map.deinit(gpa);
400 self.type_map_arena.deinit();404 self.type_map_arena.deinit();
401 self.extern_collisions.deinit(gpa);405 self.extern_collisions.deinit(gpa);
...@@ -4180,6 +4184,8 @@ pub const FuncGen = struct {...@@ -4180,6 +4184,8 @@ pub const FuncGen = struct {
4180 .union_init => try self.airUnionInit(inst),4184 .union_init => try self.airUnionInit(inst),
4181 .prefetch => try self.airPrefetch(inst),4185 .prefetch => try self.airPrefetch(inst),
41824186
4187 .is_named_enum_value => try self.airIsNamedEnumValue(inst),
4188
4183 .reduce => try self.airReduce(inst, false),4189 .reduce => try self.airReduce(inst, false),
4184 .reduce_optimized => try self.airReduce(inst, true),4190 .reduce_optimized => try self.airReduce(inst, true),
41854191
...@@ -7882,6 +7888,87 @@ pub const FuncGen = struct {...@@ -7882,6 +7888,87 @@ pub const FuncGen = struct {
7882 }7888 }
7883 }7889 }
78847890
7891 fn airIsNamedEnumValue(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
7892 if (self.liveness.isUnused(inst)) return null;
7893
7894 const un_op = self.air.instructions.items(.data)[inst].un_op;
7895 const operand = try self.resolveInst(un_op);
7896 const enum_ty = self.air.typeOf(un_op);
7897
7898 const llvm_fn = try self.getIsNamedEnumValueFunction(enum_ty);
7899 const params = [_]*const llvm.Value{operand};
7900 return self.builder.buildCall(llvm_fn, &params, params.len, .Fast, .Auto, "");
7901 }
7902
7903 fn getIsNamedEnumValueFunction(self: *FuncGen, enum_ty: Type) !*const llvm.Value {
7904 const enum_decl = enum_ty.getOwnerDecl();
7905
7906 // TODO: detect when the type changes and re-emit this function.
7907 const gop = try self.dg.object.named_enum_map.getOrPut(self.dg.gpa, enum_decl);
7908 if (gop.found_existing) return gop.value_ptr.*;
7909 errdefer assert(self.dg.object.named_enum_map.remove(enum_decl));
7910
7911 var arena_allocator = std.heap.ArenaAllocator.init(self.gpa);
7912 defer arena_allocator.deinit();
7913 const arena = arena_allocator.allocator();
7914
7915 const mod = self.dg.module;
7916 const llvm_fn_name = try std.fmt.allocPrintZ(arena, "__zig_is_named_enum_value_{s}", .{
7917 try mod.declPtr(enum_decl).getFullyQualifiedName(mod),
7918 });
7919
7920 var int_tag_type_buffer: Type.Payload.Bits = undefined;
7921 const int_tag_ty = enum_ty.intTagType(&int_tag_type_buffer);
7922 const param_types = [_]*const llvm.Type{try self.dg.lowerType(int_tag_ty)};
7923
7924 const llvm_ret_ty = try self.dg.lowerType(Type.bool);
7925 const fn_type = llvm.functionType(llvm_ret_ty, &param_types, param_types.len, .False);
7926 const fn_val = self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type);
7927 fn_val.setLinkage(.Internal);
7928 fn_val.setFunctionCallConv(.Fast);
7929 self.dg.addCommonFnAttributes(fn_val);
7930 gop.value_ptr.* = fn_val;
7931
7932 const prev_block = self.builder.getInsertBlock();
7933 const prev_debug_location = self.builder.getCurrentDebugLocation2();
7934 defer {
7935 self.builder.positionBuilderAtEnd(prev_block);
7936 if (self.di_scope != null) {
7937 self.builder.setCurrentDebugLocation2(prev_debug_location);
7938 }
7939 }
7940
7941 const entry_block = self.dg.context.appendBasicBlock(fn_val, "Entry");
7942 self.builder.positionBuilderAtEnd(entry_block);
7943 self.builder.clearCurrentDebugLocation();
7944
7945 const fields = enum_ty.enumFields();
7946 const named_block = self.dg.context.appendBasicBlock(fn_val, "Named");
7947 const unnamed_block = self.dg.context.appendBasicBlock(fn_val, "Unnamed");
7948 const tag_int_value = fn_val.getParam(0);
7949 const switch_instr = self.builder.buildSwitch(tag_int_value, unnamed_block, @intCast(c_uint, fields.count()));
7950
7951 for (fields.keys()) |_, field_index| {
7952 const this_tag_int_value = int: {
7953 var tag_val_payload: Value.Payload.U32 = .{
7954 .base = .{ .tag = .enum_field_index },
7955 .data = @intCast(u32, field_index),
7956 };
7957 break :int try self.dg.lowerValue(.{
7958 .ty = enum_ty,
7959 .val = Value.initPayload(&tag_val_payload.base),
7960 });
7961 };
7962 switch_instr.addCase(this_tag_int_value, named_block);
7963 }
7964 self.builder.positionBuilderAtEnd(named_block);
7965 _ = self.builder.buildRet(self.dg.context.intType(1).constInt(1, .False));
7966
7967 self.builder.positionBuilderAtEnd(unnamed_block);
7968 _ = self.builder.buildRet(self.dg.context.intType(1).constInt(0, .False));
7969 return fn_val;
7970 }
7971
7885 fn airTagName(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {7972 fn airTagName(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
7886 if (self.liveness.isUnused(inst)) return null;7973 if (self.liveness.isUnused(inst)) return null;
78877974
src/print_air.zig+1
...@@ -170,6 +170,7 @@ const Writer = struct {...@@ -170,6 +170,7 @@ const Writer = struct {
170 .bool_to_int,170 .bool_to_int,
171 .ret,171 .ret,
172 .ret_load,172 .ret_load,
173 .is_named_enum_value,
173 .tag_name,174 .tag_name,
174 .error_name,175 .error_name,
175 .sqrt,176 .sqrt,
test/behavior/switch.zig+1
...@@ -531,6 +531,7 @@ test "switch with null and T peer types and inferred result location type" {...@@ -531,6 +531,7 @@ test "switch with null and T peer types and inferred result location type" {
531test "switch prongs with cases with identical payload types" {531test "switch prongs with cases with identical payload types" {
532 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO532 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
533 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO533 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
534 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
534535
535 const Union = union(enum) {536 const Union = union(enum) {
536 A: usize,537 A: usize,
test/cases/safety/@intToEnum - no matching tag value.zig +6-3
...@@ -1,9 +1,11 @@...@@ -1,9 +1,11 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = message;
5 _ = stack_trace;4 _ = stack_trace;
6 std.process.exit(0);5 if (std.mem.eql(u8, message, "invalid enum value")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
7}9}
8const Foo = enum {10const Foo = enum {
9 A,11 A,
...@@ -18,6 +20,7 @@ fn bar(a: u2) Foo {...@@ -18,6 +20,7 @@ fn bar(a: u2) Foo {
18 return @intToEnum(Foo, a);20 return @intToEnum(Foo, a);
19}21}
20fn baz(_: Foo) void {}22fn baz(_: Foo) void {}
23
21// run24// run
22// backend=stage125// backend=llvm
23// target=native26// target=native
test/cases/safety/@tagName on corrupted enum value.zig +2-1
...@@ -10,6 +10,7 @@ pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noretur...@@ -10,6 +10,7 @@ pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noretur
1010
11const E = enum(u32) {11const E = enum(u32) {
12 X = 1,12 X = 1,
13 Y = 2,
13};14};
1415
15pub fn main() !void {16pub fn main() !void {
...@@ -21,5 +22,5 @@ pub fn main() !void {...@@ -21,5 +22,5 @@ pub fn main() !void {
21}22}
2223
23// run24// run
24// backend=stage125// backend=llvm
25// target=native26// target=native
test/cases/safety/@tagName on corrupted union value.zig +2-1
...@@ -10,6 +10,7 @@ pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noretur...@@ -10,6 +10,7 @@ pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noretur
1010
11const U = union(enum(u32)) {11const U = union(enum(u32)) {
12 X: u8,12 X: u8,
13 Y: i8,
13};14};
1415
15pub fn main() !void {16pub fn main() !void {
...@@ -22,5 +23,5 @@ pub fn main() !void {...@@ -22,5 +23,5 @@ pub fn main() !void {
22}23}
2324
24// run25// run
25// backend=stage126// backend=llvm
26// target=native27// target=native
test/cases/safety/cast []u8 to bigger slice of wrong size.zig +6-4
...@@ -1,9 +1,11 @@...@@ -1,9 +1,11 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = message;
5 _ = stack_trace;4 _ = stack_trace;
6 std.process.exit(0);5 if (std.mem.eql(u8, message, "exact division produced remainder")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
7}9}
810
9pub fn main() !void {11pub fn main() !void {
...@@ -15,5 +17,5 @@ fn widenSlice(slice: []align(1) const u8) []align(1) const i32 {...@@ -15,5 +17,5 @@ fn widenSlice(slice: []align(1) const u8) []align(1) const i32 {
15 return std.mem.bytesAsSlice(i32, slice);17 return std.mem.bytesAsSlice(i32, slice);
16}18}
17// run19// run
18// backend=stage1
19// target=native
\ No newline at end of file
20// backend=llvm
21// target=native
test/cases/safety/empty slice with sentinel out of bounds.zig +2-2
...@@ -2,7 +2,7 @@ const std = @import("std");...@@ -2,7 +2,7 @@ const std = @import("std");
22
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = stack_trace;4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "index out of bounds")) {5 if (std.mem.eql(u8, message, "attempt to index out of bound: index 1, len 0")) {
6 std.process.exit(0);6 std.process.exit(0);
7 }7 }
8 std.process.exit(1);8 std.process.exit(1);
...@@ -17,5 +17,5 @@ pub fn main() !void {...@@ -17,5 +17,5 @@ pub fn main() !void {
17}17}
1818
19// run19// run
20// backend=stage120// backend=llvm
21// target=native21// target=native
test/cases/safety/pointer casting null to non-optional pointer.zig +7-3
...@@ -1,16 +1,20 @@...@@ -1,16 +1,20 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = message;
5 _ = stack_trace;4 _ = stack_trace;
6 std.process.exit(0);5 if (std.mem.eql(u8, message, "cast causes pointer to be null")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
7}9}
10
8pub fn main() !void {11pub fn main() !void {
9 var c_ptr: [*c]u8 = 0;12 var c_ptr: [*c]u8 = 0;
10 var zig_ptr: *u8 = c_ptr;13 var zig_ptr: *u8 = c_ptr;
11 _ = zig_ptr;14 _ = zig_ptr;
12 return error.TestFailed;15 return error.TestFailed;
13}16}
17
14// run18// run
15// backend=stage119// backend=llvm
16// target=native20// target=native
test/cases/safety/pointer slice sentinel mismatch.zig +3-3
...@@ -2,14 +2,14 @@ const std = @import("std");...@@ -2,14 +2,14 @@ const std = @import("std");
22
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = stack_trace;4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "sentinel mismatch")) {5 if (std.mem.eql(u8, message, "sentinel mismatch: expected 0, found 4")) {
6 std.process.exit(0);6 std.process.exit(0);
7 }7 }
8 std.process.exit(1);8 std.process.exit(1);
9}9}
1010
11pub fn main() !void {11pub fn main() !void {
12 var buf: [4]u8 = undefined;12 var buf: [4]u8 = .{ 1, 2, 3, 4 };
13 const ptr: [*]u8 = &buf;13 const ptr: [*]u8 = &buf;
14 const slice = ptr[0..3 :0];14 const slice = ptr[0..3 :0];
15 _ = slice;15 _ = slice;
...@@ -17,5 +17,5 @@ pub fn main() !void {...@@ -17,5 +17,5 @@ pub fn main() !void {
17}17}
1818
19// run19// run
20// backend=stage120// backend=llvm
21// target=native21// target=native
test/cases/safety/shift left by huge amount.zig +1-1
...@@ -17,5 +17,5 @@ pub fn main() !void {...@@ -17,5 +17,5 @@ pub fn main() !void {
17}17}
1818
19// run19// run
20// backend=stage120// backend=llvm
21// target=native21// target=native
test/cases/safety/shift right by huge amount.zig +1-1
...@@ -17,5 +17,5 @@ pub fn main() !void {...@@ -17,5 +17,5 @@ pub fn main() !void {
17}17}
1818
19// run19// run
20// backend=stage120// backend=llvm
21// target=native21// target=native
test/cases/safety/signed integer division overflow - vectors.zig +6-4
...@@ -1,9 +1,11 @@...@@ -1,9 +1,11 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = message;
5 _ = stack_trace;4 _ = stack_trace;
6 std.process.exit(0);5 if (std.mem.eql(u8, message, "integer overflow")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
7}9}
810
9pub fn main() !void {11pub fn main() !void {
...@@ -17,5 +19,5 @@ fn div(a: @Vector(4, i16), b: @Vector(4, i16)) @Vector(4, i16) {...@@ -17,5 +19,5 @@ fn div(a: @Vector(4, i16), b: @Vector(4, i16)) @Vector(4, i16) {
17 return @divTrunc(a, b);19 return @divTrunc(a, b);
18}20}
19// run21// run
20// backend=stage1
21// target=native
\ No newline at end of file
22// backend=llvm
23// target=native
test/cases/safety/signed integer division overflow.zig +6-4
...@@ -1,9 +1,11 @@...@@ -1,9 +1,11 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = message;
5 _ = stack_trace;4 _ = stack_trace;
6 std.process.exit(0);5 if (std.mem.eql(u8, message, "integer overflow")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
7}9}
810
9pub fn main() !void {11pub fn main() !void {
...@@ -15,5 +17,5 @@ fn div(a: i16, b: i16) i16 {...@@ -15,5 +17,5 @@ fn div(a: i16, b: i16) i16 {
15 return @divTrunc(a, b);17 return @divTrunc(a, b);
16}18}
17// run19// run
18// backend=stage1
19// target=native
\ No newline at end of file
20// backend=llvm
21// target=native
test/cases/safety/slice sentinel mismatch - floats.zig +3-3
...@@ -2,19 +2,19 @@ const std = @import("std");...@@ -2,19 +2,19 @@ const std = @import("std");
22
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = stack_trace;4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "sentinel mismatch")) {5 if (std.mem.eql(u8, message, "sentinel mismatch: expected 1.20000004e+00, found 4.0e+00")) {
6 std.process.exit(0);6 std.process.exit(0);
7 }7 }
8 std.process.exit(1);8 std.process.exit(1);
9}9}
1010
11pub fn main() !void {11pub fn main() !void {
12 var buf: [4]f32 = undefined;12 var buf: [4]f32 = .{ 1, 2, 3, 4 };
13 const slice = buf[0..3 :1.2];13 const slice = buf[0..3 :1.2];
14 _ = slice;14 _ = slice;
15 return error.TestFailed;15 return error.TestFailed;
16}16}
1717
18// run18// run
19// backend=stage119// backend=llvm
20// target=native20// target=native
test/cases/safety/slice sentinel mismatch - optional pointers.zig +3-3
...@@ -2,19 +2,19 @@ const std = @import("std");...@@ -2,19 +2,19 @@ const std = @import("std");
22
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = stack_trace;4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "sentinel mismatch")) {5 if (std.mem.eql(u8, message, "sentinel mismatch: expected null, found i32@10")) {
6 std.process.exit(0);6 std.process.exit(0);
7 }7 }
8 std.process.exit(1);8 std.process.exit(1);
9}9}
1010
11pub fn main() !void {11pub fn main() !void {
12 var buf: [4]?*i32 = undefined;12 var buf: [4]?*i32 = .{ @intToPtr(*i32, 4), @intToPtr(*i32, 8), @intToPtr(*i32, 12), @intToPtr(*i32, 16) };
13 const slice = buf[0..3 :null];13 const slice = buf[0..3 :null];
14 _ = slice;14 _ = slice;
15 return error.TestFailed;15 return error.TestFailed;
16}16}
1717
18// run18// run
19// backend=stage119// backend=llvm
20// target=native20// target=native
test/cases/safety/slice slice sentinel mismatch.zig +3-3
...@@ -2,18 +2,18 @@ const std = @import("std");...@@ -2,18 +2,18 @@ const std = @import("std");
22
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = stack_trace;4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "sentinel mismatch")) {5 if (std.mem.eql(u8, message, "sentinel mismatch: expected 0, found 4")) {
6 std.process.exit(0);6 std.process.exit(0);
7 }7 }
8 std.process.exit(1);8 std.process.exit(1);
9}9}
10pub fn main() !void {10pub fn main() !void {
11 var buf: [4]u8 = undefined;11 var buf: [4]u8 = .{ 1, 2, 3, 4 };
12 const slice = buf[0..];12 const slice = buf[0..];
13 const slice2 = slice[0..3 :0];13 const slice2 = slice[0..3 :0];
14 _ = slice2;14 _ = slice2;
15 return error.TestFailed;15 return error.TestFailed;
16}16}
17// run17// run
18// backend=stage118// backend=llvm
19// target=native19// target=native
test/cases/safety/slice with sentinel out of bounds - runtime len.zig created+22
...@@ -0,0 +1,22 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "attempt to index out of bound: index 5, len 4")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10
11pub fn main() !void {
12 var buf = [4]u8{ 'a', 'b', 'c', 0 };
13 const input: []u8 = &buf;
14 var len: usize = 4;
15 const slice = input[0..len :0];
16 _ = slice;
17 return error.TestFailed;
18}
19
20// run
21// backend=llvm
22// target=native
test/cases/safety/slice with sentinel out of bounds.zig +2-2
...@@ -2,7 +2,7 @@ const std = @import("std");...@@ -2,7 +2,7 @@ const std = @import("std");
22
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = stack_trace;4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "index out of bounds")) {5 if (std.mem.eql(u8, message, "attempt to index out of bound: index 5, len 4")) {
6 std.process.exit(0);6 std.process.exit(0);
7 }7 }
8 std.process.exit(1);8 std.process.exit(1);
...@@ -17,5 +17,5 @@ pub fn main() !void {...@@ -17,5 +17,5 @@ pub fn main() !void {
17}17}
1818
19// run19// run
20// backend=stage120// backend=llvm
21// target=native21// target=native
test/cases/safety/slicing null C pointer - runtime len.zig created+20
...@@ -0,0 +1,20 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "attempt to use null value")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10
11pub fn main() !void {
12 var ptr: [*c]const u32 = null;
13 var len: usize = 3;
14 var slice = ptr[0..len];
15 _ = slice;
16 return error.TestFailed;
17}
18// run
19// backend=llvm
20// target=native
test/cases/safety/slicing null C pointer.zig +6-4
...@@ -1,9 +1,11 @@...@@ -1,9 +1,11 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = message;
5 _ = stack_trace;4 _ = stack_trace;
6 std.process.exit(0);5 if (std.mem.eql(u8, message, "attempt to use null value")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
7}9}
810
9pub fn main() !void {11pub fn main() !void {
...@@ -13,5 +15,5 @@ pub fn main() !void {...@@ -13,5 +15,5 @@ pub fn main() !void {
13 return error.TestFailed;15 return error.TestFailed;
14}16}
15// run17// run
16// backend=stage1
17// target=native
\ No newline at end of file
18// backend=llvm
19// target=native
test/cases/safety/switch on corrupted enum value.zig +4-3
...@@ -2,7 +2,7 @@ const std = @import("std");...@@ -2,7 +2,7 @@ const std = @import("std");
22
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = stack_trace;4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "reached unreachable code")) {5 if (std.mem.eql(u8, message, "switch on corrupt value")) {
6 std.process.exit(0);6 std.process.exit(0);
7 }7 }
8 std.process.exit(1);8 std.process.exit(1);
...@@ -10,17 +10,18 @@ pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noretur...@@ -10,17 +10,18 @@ pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noretur
1010
11const E = enum(u32) {11const E = enum(u32) {
12 X = 1,12 X = 1,
13 Y = 2,
13};14};
1415
15pub fn main() !void {16pub fn main() !void {
16 var e: E = undefined;17 var e: E = undefined;
17 @memset(@ptrCast([*]u8, &e), 0x55, @sizeOf(E));18 @memset(@ptrCast([*]u8, &e), 0x55, @sizeOf(E));
18 switch (e) {19 switch (e) {
19 .X => @breakpoint(),20 .X, .Y => @breakpoint(),
20 }21 }
21 return error.TestFailed;22 return error.TestFailed;
22}23}
2324
24// run25// run
25// backend=stage126// backend=llvm
26// target=native27// target=native
test/cases/safety/switch on corrupted union value.zig +4-3
...@@ -2,7 +2,7 @@ const std = @import("std");...@@ -2,7 +2,7 @@ const std = @import("std");
22
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = stack_trace;4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "reached unreachable code")) {5 if (std.mem.eql(u8, message, "switch on corrupt value")) {
6 std.process.exit(0);6 std.process.exit(0);
7 }7 }
8 std.process.exit(1);8 std.process.exit(1);
...@@ -10,17 +10,18 @@ pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noretur...@@ -10,17 +10,18 @@ pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noretur
1010
11const U = union(enum(u32)) {11const U = union(enum(u32)) {
12 X: u8,12 X: u8,
13 Y: i8,
13};14};
1415
15pub fn main() !void {16pub fn main() !void {
16 var u: U = undefined;17 var u: U = undefined;
17 @memset(@ptrCast([*]u8, &u), 0x55, @sizeOf(U));18 @memset(@ptrCast([*]u8, &u), 0x55, @sizeOf(U));
18 switch (u) {19 switch (u) {
19 .X => @breakpoint(),20 .X, .Y => @breakpoint(),
20 }21 }
21 return error.TestFailed;22 return error.TestFailed;
22}23}
2324
24// run25// run
25// backend=stage126// backend=llvm
26// target=native27// target=native