| author | |
| committer | |
| log | b3d463c9e6ab41d55849e6cfbbc83d6a1f171ef9 |
| tree | 7d7076d0933c119ee2a0d58c868d8b06bd400089 |
| parent | 3e2defd36c0bf90a0604a7618f57beaa4077139c |
| parent | 75275a1514b6954bed09c4c14a325e883a129c7b |
| signature |
Stage2: implement remaining runtime safety checks33 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, |
| 662 | 662 | ||
| 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, |
| 1061 | 1066 | ||
| 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) |
| 1578 | 1578 | ||
| 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); | ||
| 1583 | 1582 | ||
| 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 | } |
| 6950 | 6949 | ||
| 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 value | 6951 | 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 | } |
| 6955 | 6958 | ||
| 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 | } |
| 9708 | 9711 | ||
| 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(); |
| 9713 | 9716 | ||
| ... | @@ -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 proven | 9734 | // 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, something | 9736 | 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 | } |
| 9737 | 9742 | ||
| 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; |
| 10242 | 10247 | ||
| 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 | else | 10283 | 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); | ||
| 10269 | 10296 | ||
| 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( |
| 10347 | 10375 | ||
| 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; |
| 10352 | 10380 | 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 instead | 16020 | // 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 operand | 16021 | // 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 | } |
| 16943 | 16999 | ||
| 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 | } |
| 17235 | 17291 | ||
| 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 | }; |
| 20118 | 20179 | ||
| 20119 | fn addSafetyCheck( | 20180 | fn 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 | }; |
| 20412 | 20476 | ||
| 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 | } |
| 22102 | 22165 | ||
| ... | @@ -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 | } |
| 24574 | 24655 | ||
| ... | @@ -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_sentinel | 25813 | // 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 | } |
| 25774 | 25880 | ||
| 25775 | // requirement: start <= end | 25881 | // 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", .{}), |
| 779 | 779 | ||
| 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: on | 784 | // 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", .{}), |
| 770 | 770 | ||
| 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: on | 775 | // 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", .{}), |
| 695 | 695 | ||
| 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: on | 700 | // 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"), |
| 707 | 707 | ||
| 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: on | 712 | // 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)}), |
| 1625 | 1626 | ||
| 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", .{}), |
| 777 | 777 | ||
| 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: on | 782 | // 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: on | 1957 | // 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( |
| 3250 | 3252 | ||
| 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 | else | 3257 | 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 of | 206 | /// 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 compilation | 208 | /// 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), |
| 4182 | 4186 | ||
| 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), |
| 4185 | 4191 | ||
| ... | @@ -7882,6 +7888,87 @@ pub const FuncGen = struct { | ... | @@ -7882,6 +7888,87 @@ pub const FuncGen = struct { |
| 7882 | } | 7888 | } |
| 7883 | } | 7889 | } |
| 7884 | 7890 | ||
| 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; |
| 7887 | 7974 |
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" { |
| 531 | test "switch prongs with cases with identical payload types" { | 531 | test "switch prongs with cases with identical payload types" { |
| 532 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 532 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 533 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 533 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 534 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 534 | 535 | ||
| 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 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { | 3 | pub 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 | } |
| 8 | const Foo = enum { | 10 | const 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 | } |
| 20 | fn baz(_: Foo) void {} | 22 | fn baz(_: Foo) void {} |
| 23 | |||
| 21 | // run | 24 | // run |
| 22 | // backend=stage1 | 25 | // backend=llvm |
| 23 | // target=native | 26 | // 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 |
| 10 | 10 | ||
| 11 | const E = enum(u32) { | 11 | const E = enum(u32) { |
| 12 | X = 1, | 12 | X = 1, |
| 13 | Y = 2, | ||
| 13 | }; | 14 | }; |
| 14 | 15 | ||
| 15 | pub fn main() !void { | 16 | pub fn main() !void { |
| ... | @@ -21,5 +22,5 @@ pub fn main() !void { | ... | @@ -21,5 +22,5 @@ pub fn main() !void { |
| 21 | } | 22 | } |
| 22 | 23 | ||
| 23 | // run | 24 | // run |
| 24 | // backend=stage1 | 25 | // backend=llvm |
| 25 | // target=native | 26 | // 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 |
| 10 | 10 | ||
| 11 | const U = union(enum(u32)) { | 11 | const U = union(enum(u32)) { |
| 12 | X: u8, | 12 | X: u8, |
| 13 | Y: i8, | ||
| 13 | }; | 14 | }; |
| 14 | 15 | ||
| 15 | pub fn main() !void { | 16 | pub fn main() !void { |
| ... | @@ -22,5 +23,5 @@ pub fn main() !void { | ... | @@ -22,5 +23,5 @@ pub fn main() !void { |
| 22 | } | 23 | } |
| 23 | 24 | ||
| 24 | // run | 25 | // run |
| 25 | // backend=stage1 | 26 | // backend=llvm |
| 26 | // target=native | 27 | // target=native |
test/cases/safety/cast []u8 to bigger slice of wrong size.zig	+6-4| ... | @@ -1,9 +1,11 @@ | ... | @@ -1,9 +1,11 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { | 3 | pub 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 | } |
| 8 | 10 | ||
| 9 | pub fn main() !void { | 11 | pub 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 | // run | 19 | // 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"); |
| 2 | 2 | ||
| 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { | 3 | pub 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 | } |
| 18 | 18 | ||
| 19 | // run | 19 | // run |
| 20 | // backend=stage1 | 20 | // backend=llvm |
| 21 | // target=native | 21 | // target=native |
test/cases/safety/pointer casting null to non-optional pointer.zig	+7-3| ... | @@ -1,16 +1,20 @@ | ... | @@ -1,16 +1,20 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { | 3 | pub 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 | |||
| 8 | pub fn main() !void { | 11 | pub 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 | // run | 18 | // run |
| 15 | // backend=stage1 | 19 | // backend=llvm |
| 16 | // target=native | 20 | // 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"); |
| 2 | 2 | ||
| 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { | 3 | pub 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 | } |
| 10 | 10 | ||
| 11 | pub fn main() !void { | 11 | pub 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 | } |
| 18 | 18 | ||
| 19 | // run | 19 | // run |
| 20 | // backend=stage1 | 20 | // backend=llvm |
| 21 | // target=native | 21 | // 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 | } |
| 18 | 18 | ||
| 19 | // run | 19 | // run |
| 20 | // backend=stage1 | 20 | // backend=llvm |
| 21 | // target=native | 21 | // 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 | } |
| 18 | 18 | ||
| 19 | // run | 19 | // run |
| 20 | // backend=stage1 | 20 | // backend=llvm |
| 21 | // target=native | 21 | // target=native |
test/cases/safety/signed integer division overflow - vectors.zig	+6-4| ... | @@ -1,9 +1,11 @@ | ... | @@ -1,9 +1,11 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { | 3 | pub 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 | } |
| 8 | 10 | ||
| 9 | pub fn main() !void { | 11 | pub 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 | // run | 21 | // 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 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { | 3 | pub 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 | } |
| 8 | 10 | ||
| 9 | pub fn main() !void { | 11 | pub 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 | // run | 19 | // 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"); |
| 2 | 2 | ||
| 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { | 3 | pub 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 | } |
| 10 | 10 | ||
| 11 | pub fn main() !void { | 11 | pub 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 | } |
| 17 | 17 | ||
| 18 | // run | 18 | // run |
| 19 | // backend=stage1 | 19 | // backend=llvm |
| 20 | // target=native | 20 | // 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"); |
| 2 | 2 | ||
| 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { | 3 | pub 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 | } |
| 10 | 10 | ||
| 11 | pub fn main() !void { | 11 | pub 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 | } |
| 17 | 17 | ||
| 18 | // run | 18 | // run |
| 19 | // backend=stage1 | 19 | // backend=llvm |
| 20 | // target=native | 20 | // 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"); |
| 2 | 2 | ||
| 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { | 3 | pub 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 | } |
| 10 | pub fn main() !void { | 10 | pub 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 | // run | 17 | // run |
| 18 | // backend=stage1 | 18 | // backend=llvm |
| 19 | // target=native | 19 | // target=native |
test/cases/safety/slice with sentinel out of bounds - runtime len.zig	 created+22| ... | @@ -0,0 +1,22 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub 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 | |||
| 11 | pub 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"); |
| 2 | 2 | ||
| 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { | 3 | pub 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 | } |
| 18 | 18 | ||
| 19 | // run | 19 | // run |
| 20 | // backend=stage1 | 20 | // backend=llvm |
| 21 | // target=native | 21 | // target=native |
test/cases/safety/slicing null C pointer - runtime len.zig	 created+20| ... | @@ -0,0 +1,20 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub 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 | |||
| 11 | pub 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 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | 2 | ||
| 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { | 3 | pub 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 | } |
| 8 | 10 | ||
| 9 | pub fn main() !void { | 11 | pub 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 | // run | 17 | // 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"); |
| 2 | 2 | ||
| 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { | 3 | pub 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 |
| 10 | 10 | ||
| 11 | const E = enum(u32) { | 11 | const E = enum(u32) { |
| 12 | X = 1, | 12 | X = 1, |
| 13 | Y = 2, | ||
| 13 | }; | 14 | }; |
| 14 | 15 | ||
| 15 | pub fn main() !void { | 16 | pub 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 | } |
| 23 | 24 | ||
| 24 | // run | 25 | // run |
| 25 | // backend=stage1 | 26 | // backend=llvm |
| 26 | // target=native | 27 | // 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"); |
| 2 | 2 | ||
| 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { | 3 | pub 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 |
| 10 | 10 | ||
| 11 | const U = union(enum(u32)) { | 11 | const U = union(enum(u32)) { |
| 12 | X: u8, | 12 | X: u8, |
| 13 | Y: i8, | ||
| 13 | }; | 14 | }; |
| 14 | 15 | ||
| 15 | pub fn main() !void { | 16 | pub 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 | } |
| 23 | 24 | ||
| 24 | // run | 25 | // run |
| 25 | // backend=stage1 | 26 | // backend=llvm |
| 26 | // target=native | 27 | // target=native |