| 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 | 660 | /// Uses the `pl_op` field with payload `AtomicRmw`. Operand is `ptr`. |
| 661 | 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 | 667 | /// Given an enum tag value, returns the tag name. The enum type may be non-exhaustive. |
| 664 | 668 | /// Result type is always `[:0]const u8`. |
| 665 | 669 | /// Uses the `un_op` field. |
| ... | ... | @@ -1057,6 +1061,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 1057 | 1061 | .is_non_err, |
| 1058 | 1062 | .is_err_ptr, |
| 1059 | 1063 | .is_non_err_ptr, |
| 1064 | .is_named_enum_value, | |
| 1060 | 1065 | => return Type.bool, |
| 1061 | 1066 | |
| 1062 | 1067 | .const_ty => return Type.type, |
src/Liveness.zig+2| ... | ... | @@ -291,6 +291,7 @@ pub fn categorizeOperand( |
| 291 | 291 | .is_non_err_ptr, |
| 292 | 292 | .ptrtoint, |
| 293 | 293 | .bool_to_int, |
| 294 | .is_named_enum_value, | |
| 294 | 295 | .tag_name, |
| 295 | 296 | .error_name, |
| 296 | 297 | .sqrt, |
| ... | ... | @@ -858,6 +859,7 @@ fn analyzeInst( |
| 858 | 859 | .bool_to_int, |
| 859 | 860 | .ret, |
| 860 | 861 | .ret_load, |
| 862 | .is_named_enum_value, | |
| 861 | 863 | .tag_name, |
| 862 | 864 | .error_name, |
| 863 | 865 | .sqrt, |
src/Sema.zig+159-53| ... | ... | @@ -1578,8 +1578,7 @@ pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) |
| 1578 | 1578 | |
| 1579 | 1579 | // st.index = 0; |
| 1580 | 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); | |
| 1582 | try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, zero, src, .store); | |
| 1581 | try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, .zero_usize, src, .store); | |
| 1583 | 1582 | |
| 1584 | 1583 | // @errorReturnTrace() = &st; |
| 1585 | 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 | 6948 | } |
| 6950 | 6949 | |
| 6951 | 6950 | try sema.requireRuntimeBlock(block, src, operand_src); |
| 6952 | // TODO insert safety check to make sure the value matches an enum value | |
| 6953 | return block.addTyOp(.intcast, dest_ty, operand); | |
| 6951 | const result = try 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 | 6959 | /// Pointer in, pointer out. |
| ... | ... | @@ -9707,7 +9710,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError |
| 9707 | 9710 | } |
| 9708 | 9711 | |
| 9709 | 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 | 9714 | var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, child_block.wip_capture_scope); |
| 9712 | 9715 | defer wip_captures.deinit(); |
| 9713 | 9716 | |
| ... | ... | @@ -9730,9 +9733,11 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError |
| 9730 | 9733 | } else { |
| 9731 | 9734 | // We still need a terminator in this block, but we have proven |
| 9732 | 9735 | // that it is unreachable. |
| 9733 | // TODO this should be a special safety panic other than unreachable, something | |
| 9734 | // like "panic: switch operand had corrupt value not allowed by the type" | |
| 9735 | try case_block.addUnreachable(src, true); | |
| 9736 | if (case_block.wantSafety()) { | |
| 9737 | _ = try sema.safetyPanic(&case_block, src, .corrupt_switch); | |
| 9738 | } else { | |
| 9739 | _ = try case_block.addNoOp(.unreach); | |
| 9740 | } | |
| 9736 | 9741 | } |
| 9737 | 9742 | |
| 9738 | 9743 | try wip_captures.finalize(); |
| ... | ... | @@ -10241,34 +10246,57 @@ fn zirShl( |
| 10241 | 10246 | } else rhs; |
| 10242 | 10247 | |
| 10243 | 10248 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 10244 | if (block.wantSafety() and air_tag == .shl_exact) { | |
| 10245 | const op_ov_tuple_ty = try sema.overflowArithmeticTupleType(lhs_ty); | |
| 10246 | const op_ov = try block.addInst(.{ | |
| 10247 | .tag = .shl_with_overflow, | |
| 10248 | .data = .{ .ty_pl = .{ | |
| 10249 | .ty = try sema.addType(op_ov_tuple_ty), | |
| 10250 | .payload = try sema.addExtra(Air.Bin{ | |
| 10251 | .lhs = lhs, | |
| 10252 | .rhs = rhs, | |
| 10253 | }), | |
| 10254 | } }, | |
| 10255 | }); | |
| 10256 | const ov_bit = try sema.tupleFieldValByIndex(block, src, op_ov, 1, op_ov_tuple_ty); | |
| 10257 | const any_ov_bit = if (lhs_ty.zigTypeTag() == .Vector) | |
| 10258 | try block.addInst(.{ | |
| 10259 | .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, | |
| 10260 | .data = .{ .reduce = .{ | |
| 10261 | .operand = ov_bit, | |
| 10262 | .operation = .Or, | |
| 10249 | if (block.wantSafety()) { | |
| 10250 | const bit_count = scalar_ty.intInfo(target).bits; | |
| 10251 | if (!std.math.isPowerOfTwo(bit_count)) { | |
| 10252 | const bit_count_val = try Value.Tag.int_u64.create(sema.arena, bit_count); | |
| 10253 | ||
| 10254 | const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: { | |
| 10255 | const bit_count_inst = try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, bit_count_val)); | |
| 10256 | const lt = try block.addCmpVector(rhs, bit_count_inst, .lt, try sema.addType(rhs_ty)); | |
| 10257 | break :ok try block.addInst(.{ | |
| 10258 | .tag = .reduce, | |
| 10259 | .data = .{ .reduce = .{ | |
| 10260 | .operand = lt, | |
| 10261 | .operation = .And, | |
| 10262 | } }, | |
| 10263 | }); | |
| 10264 | } else ok: { | |
| 10265 | const bit_count_inst = try sema.addConstant(rhs_ty, bit_count_val); | |
| 10266 | break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst); | |
| 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 | }) | |
| 10265 | else | |
| 10266 | ov_bit; | |
| 10267 | const zero_ov = try sema.addConstant(Type.@"u1", Value.zero); | |
| 10268 | const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, zero_ov); | |
| 10282 | }); | |
| 10283 | const ov_bit = try sema.tupleFieldValByIndex(block, src, op_ov, 1, op_ov_tuple_ty); | |
| 10284 | const any_ov_bit = if (lhs_ty.zigTypeTag() == .Vector) | |
| 10285 | try block.addInst(.{ | |
| 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); | |
| 10271 | return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty); | |
| 10297 | try sema.addSafetyCheck(block, no_ov, .shl_overflow); | |
| 10298 | return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty); | |
| 10299 | } | |
| 10272 | 10300 | } |
| 10273 | 10301 | return block.addBinOp(air_tag, lhs, new_rhs); |
| 10274 | 10302 | } |
| ... | ... | @@ -10347,20 +10375,43 @@ fn zirShr( |
| 10347 | 10375 | |
| 10348 | 10376 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 10349 | 10377 | const result = try block.addBinOp(air_tag, lhs, rhs); |
| 10350 | if (block.wantSafety() and air_tag == .shr_exact) { | |
| 10351 | const back = try block.addBinOp(.shl, result, rhs); | |
| 10352 | ||
| 10353 | const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: { | |
| 10354 | const eql = try block.addCmpVector(lhs, back, .eq, try sema.addType(rhs_ty)); | |
| 10355 | break :ok try block.addInst(.{ | |
| 10356 | .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce, | |
| 10357 | .data = .{ .reduce = .{ | |
| 10358 | .operand = eql, | |
| 10359 | .operation = .And, | |
| 10360 | } }, | |
| 10361 | }); | |
| 10362 | } else try block.addBinOp(.cmp_eq, lhs, back); | |
| 10363 | try sema.addSafetyCheck(block, ok, .shr_overflow); | |
| 10378 | if (block.wantSafety()) { | |
| 10379 | const bit_count = scalar_ty.intInfo(target).bits; | |
| 10380 | if (!std.math.isPowerOfTwo(bit_count)) { | |
| 10381 | const bit_count_val = try Value.Tag.int_u64.create(sema.arena, bit_count); | |
| 10382 | ||
| 10383 | const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: { | |
| 10384 | const bit_count_inst = try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, bit_count_val)); | |
| 10385 | const lt = try block.addCmpVector(rhs, bit_count_inst, .lt, try sema.addType(rhs_ty)); | |
| 10386 | break :ok try block.addInst(.{ | |
| 10387 | .tag = .reduce, | |
| 10388 | .data = .{ .reduce = .{ | |
| 10389 | .operand = lt, | |
| 10390 | .operation = .And, | |
| 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 | 10416 | return result; |
| 10366 | 10417 | } |
| ... | ... | @@ -15961,6 +16012,11 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 15961 | 16012 | const field_name = enum_ty.enumFieldName(field_index); |
| 15962 | 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 | 16020 | // In case the value is runtime-known, we have an AIR instruction for this instead |
| 15965 | 16021 | // of trying to lower it in Sema because an optimization pass may result in the operand |
| 15966 | 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 | 16998 | } |
| 16943 | 16999 | |
| 16944 | 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 | 17002 | if (!type_res.isAllowzeroPtr()) { |
| 16947 | 17003 | const is_non_zero = try block.addBinOp(.cmp_neq, operand_coerced, .zero_usize); |
| 16948 | 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 | 17290 | } |
| 17235 | 17291 | |
| 17236 | 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 | 17296 | const val_payload = try sema.arena.create(Value.Payload.U64); |
| 17239 | 17297 | val_payload.* = .{ |
| 17240 | 17298 | .base = .{ .tag = .int_u64 }, |
| ... | ... | @@ -17253,7 +17311,7 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A |
| 17253 | 17311 | const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize); |
| 17254 | 17312 | const ok = if (ptr_ty.isSlice()) ok: { |
| 17255 | 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 | 17315 | break :ok try block.addBinOp(.bit_or, len_zero, is_aligned); |
| 17258 | 17316 | } else is_aligned; |
| 17259 | 17317 | try sema.addSafetyCheck(block, ok, .incorrect_alignment); |
| ... | ... | @@ -20114,6 +20172,9 @@ pub const PanicId = enum { |
| 20114 | 20172 | /// TODO make this call `std.builtin.panicInactiveUnionField`. |
| 20115 | 20173 | inactive_union_field, |
| 20116 | 20174 | integer_part_out_of_bounds, |
| 20175 | corrupt_switch, | |
| 20176 | shift_rhs_too_big, | |
| 20177 | invalid_enum_value, | |
| 20117 | 20178 | }; |
| 20118 | 20179 | |
| 20119 | 20180 | fn addSafetyCheck( |
| ... | ... | @@ -20408,6 +20469,9 @@ fn safetyPanic( |
| 20408 | 20469 | .exact_division_remainder => "exact division produced remainder", |
| 20409 | 20470 | .inactive_union_field => "access of inactive union field", |
| 20410 | 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 | 20477 | const msg_inst = msg_inst: { |
| ... | ... | @@ -22096,7 +22160,6 @@ fn coerceExtra( |
| 22096 | 22160 | .ok => {}, |
| 22097 | 22161 | else => break :src_c_ptr, |
| 22098 | 22162 | } |
| 22099 | // TODO add safety check for null pointer | |
| 22100 | 22163 | return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); |
| 22101 | 22164 | } |
| 22102 | 22165 | |
| ... | ... | @@ -24569,6 +24632,24 @@ fn coerceCompatiblePtrs( |
| 24569 | 24632 | return sema.addConstant(dest_ty, val); |
| 24570 | 24633 | } |
| 24571 | 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 | 24653 | return sema.bitCast(block, dest_ty, inst, inst_src); |
| 24573 | 24654 | } |
| 24574 | 24655 | |
| ... | ... | @@ -25708,6 +25789,27 @@ fn analyzeSlice( |
| 25708 | 25789 | const new_ptr_val = opt_new_ptr_val orelse { |
| 25709 | 25790 | const result = try block.addBitCast(return_ty, new_ptr); |
| 25710 | 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 | 25813 | // requirement: result[new_len] == slice_sentinel |
| 25712 | 25814 | try sema.panicSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len); |
| 25713 | 25815 | } |
| ... | ... | @@ -25769,7 +25871,11 @@ fn analyzeSlice( |
| 25769 | 25871 | break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src); |
| 25770 | 25872 | } else null; |
| 25771 | 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 | 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 | 777 | .float_to_int_optimized, |
| 778 | 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 | 782 | .wasm_memory_size => unreachable, |
| 781 | 783 | .wasm_memory_grow => unreachable, |
| 782 | 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 | 768 | .float_to_int_optimized, |
| 769 | 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 | 773 | .wasm_memory_size => unreachable, |
| 772 | 774 | .wasm_memory_grow => unreachable, |
| 773 | 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 | 693 | .float_to_int_optimized, |
| 694 | 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 | 698 | .wasm_memory_size => unreachable, |
| 697 | 699 | .wasm_memory_grow => unreachable, |
| 698 | 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 | 705 | .float_to_int_optimized, |
| 706 | 706 | => @panic("TODO implement optimized float mode"), |
| 707 | 707 | |
| 708 | .is_named_enum_value => @panic("TODO implement is_named_enum_value"), | |
| 709 | ||
| 708 | 710 | .wasm_memory_size => unreachable, |
| 709 | 711 | .wasm_memory_grow => unreachable, |
| 710 | 712 | // zig fmt: on |
src/arch/wasm/CodeGen.zig+1| ... | ... | @@ -1621,6 +1621,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1621 | 1621 | .tag_name, |
| 1622 | 1622 | .err_return_trace, |
| 1623 | 1623 | .set_err_return_trace, |
| 1624 | .is_named_enum_value, | |
| 1624 | 1625 | => |tag| return self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}), |
| 1625 | 1626 | |
| 1626 | 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 | 775 | .float_to_int_optimized, |
| 776 | 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 | 780 | .wasm_memory_size => unreachable, |
| 779 | 781 | .wasm_memory_grow => unreachable, |
| 780 | 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 | 1952 | .reduce_optimized, |
| 1953 | 1953 | .float_to_int_optimized, |
| 1954 | 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 | 1957 | // zig fmt: on |
| 1956 | 1958 | }; |
| 1957 | 1959 | switch (result_value) { |
| ... | ... | @@ -3250,7 +3252,7 @@ fn airIsNull( |
| 3250 | 3252 | |
| 3251 | 3253 | const ty = f.air.typeOf(un_op); |
| 3252 | 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 | 3256 | ty.childType().optionalChild(&opt_buf) |
| 3255 | 3257 | else |
| 3256 | 3258 | ty.optionalChild(&opt_buf); |
src/codegen/llvm.zig+87| ... | ... | @@ -201,6 +201,8 @@ pub const Object = struct { |
| 201 | 201 | /// * it works for functions not all globals. |
| 202 | 202 | /// Therefore, this table keeps track of the mapping. |
| 203 | 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 | 206 | /// Maps Zig types to LLVM types. The table memory itself is backed by the GPA of |
| 205 | 207 | /// the compiler, but the Type/Value memory here is backed by `type_map_arena`. |
| 206 | 208 | /// TODO we need to remove entries from this map in response to incremental compilation |
| ... | ... | @@ -377,6 +379,7 @@ pub const Object = struct { |
| 377 | 379 | .target_data = target_data, |
| 378 | 380 | .target = options.target, |
| 379 | 381 | .decl_map = .{}, |
| 382 | .named_enum_map = .{}, | |
| 380 | 383 | .type_map = .{}, |
| 381 | 384 | .type_map_arena = std.heap.ArenaAllocator.init(gpa), |
| 382 | 385 | .di_type_map = .{}, |
| ... | ... | @@ -396,6 +399,7 @@ pub const Object = struct { |
| 396 | 399 | self.llvm_module.dispose(); |
| 397 | 400 | self.context.dispose(); |
| 398 | 401 | self.decl_map.deinit(gpa); |
| 402 | self.named_enum_map.deinit(gpa); | |
| 399 | 403 | self.type_map.deinit(gpa); |
| 400 | 404 | self.type_map_arena.deinit(); |
| 401 | 405 | self.extern_collisions.deinit(gpa); |
| ... | ... | @@ -4180,6 +4184,8 @@ pub const FuncGen = struct { |
| 4180 | 4184 | .union_init => try self.airUnionInit(inst), |
| 4181 | 4185 | .prefetch => try self.airPrefetch(inst), |
| 4182 | 4186 | |
| 4187 | .is_named_enum_value => try self.airIsNamedEnumValue(inst), | |
| 4188 | ||
| 4183 | 4189 | .reduce => try self.airReduce(inst, false), |
| 4184 | 4190 | .reduce_optimized => try self.airReduce(inst, true), |
| 4185 | 4191 | |
| ... | ... | @@ -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 | 7972 | fn airTagName(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 7886 | 7973 | if (self.liveness.isUnused(inst)) return null; |
| 7887 | 7974 |
src/print_air.zig+1| ... | ... | @@ -170,6 +170,7 @@ const Writer = struct { |
| 170 | 170 | .bool_to_int, |
| 171 | 171 | .ret, |
| 172 | 172 | .ret_load, |
| 173 | .is_named_enum_value, | |
| 173 | 174 | .tag_name, |
| 174 | 175 | .error_name, |
| 175 | 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 | 531 | test "switch prongs with cases with identical payload types" { |
| 532 | 532 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 533 | 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 | 536 | const Union = union(enum) { |
| 536 | 537 | A: usize, |
test/cases/safety/@intToEnum - no matching tag value.zig	+6-3| ... | ... | @@ -1,9 +1,11 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { |
| 4 | _ = message; | |
| 5 | 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 | 10 | const Foo = enum { |
| 9 | 11 | A, |
| ... | ... | @@ -18,6 +20,7 @@ fn bar(a: u2) Foo { |
| 18 | 20 | return @intToEnum(Foo, a); |
| 19 | 21 | } |
| 20 | 22 | fn baz(_: Foo) void {} |
| 23 | ||
| 21 | 24 | // run |
| 22 | // backend=stage1 | |
| 25 | // backend=llvm | |
| 23 | 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 | 10 | |
| 11 | 11 | const E = enum(u32) { |
| 12 | 12 | X = 1, |
| 13 | Y = 2, | |
| 13 | 14 | }; |
| 14 | 15 | |
| 15 | 16 | pub fn main() !void { |
| ... | ... | @@ -21,5 +22,5 @@ pub fn main() !void { |
| 21 | 22 | } |
| 22 | 23 | |
| 23 | 24 | // run |
| 24 | // backend=stage1 | |
| 25 | // backend=llvm | |
| 25 | 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 | 10 | |
| 11 | 11 | const U = union(enum(u32)) { |
| 12 | 12 | X: u8, |
| 13 | Y: i8, | |
| 13 | 14 | }; |
| 14 | 15 | |
| 15 | 16 | pub fn main() !void { |
| ... | ... | @@ -22,5 +23,5 @@ pub fn main() !void { |
| 22 | 23 | } |
| 23 | 24 | |
| 24 | 25 | // run |
| 25 | // backend=stage1 | |
| 26 | // backend=llvm | |
| 26 | 27 | // target=native |
test/cases/safety/cast []u8 to bigger slice of wrong size.zig	+6-4| ... | ... | @@ -1,9 +1,11 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { |
| 4 | _ = message; | |
| 5 | 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 | 11 | pub fn main() !void { |
| ... | ... | @@ -15,5 +17,5 @@ fn widenSlice(slice: []align(1) const u8) []align(1) const i32 { |
| 15 | 17 | return std.mem.bytesAsSlice(i32, slice); |
| 16 | 18 | } |
| 17 | 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 | 2 | |
| 3 | 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { |
| 4 | 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 | 6 | std.process.exit(0); |
| 7 | 7 | } |
| 8 | 8 | std.process.exit(1); |
| ... | ... | @@ -17,5 +17,5 @@ pub fn main() !void { |
| 17 | 17 | } |
| 18 | 18 | |
| 19 | 19 | // run |
| 20 | // backend=stage1 | |
| 20 | // backend=llvm | |
| 21 | 21 | // target=native |
test/cases/safety/pointer casting null to non-optional pointer.zig	+7-3| ... | ... | @@ -1,16 +1,20 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { |
| 4 | _ = message; | |
| 5 | 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 | 11 | pub fn main() !void { |
| 9 | 12 | var c_ptr: [*c]u8 = 0; |
| 10 | 13 | var zig_ptr: *u8 = c_ptr; |
| 11 | 14 | _ = zig_ptr; |
| 12 | 15 | return error.TestFailed; |
| 13 | 16 | } |
| 17 | ||
| 14 | 18 | // run |
| 15 | // backend=stage1 | |
| 19 | // backend=llvm | |
| 16 | 20 | // target=native |
test/cases/safety/pointer slice sentinel mismatch.zig	+3-3| ... | ... | @@ -2,14 +2,14 @@ const std = @import("std"); |
| 2 | 2 | |
| 3 | 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { |
| 4 | 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 | 6 | std.process.exit(0); |
| 7 | 7 | } |
| 8 | 8 | std.process.exit(1); |
| 9 | 9 | } |
| 10 | 10 | |
| 11 | 11 | pub fn main() !void { |
| 12 | var buf: [4]u8 = undefined; | |
| 12 | var buf: [4]u8 = .{ 1, 2, 3, 4 }; | |
| 13 | 13 | const ptr: [*]u8 = &buf; |
| 14 | 14 | const slice = ptr[0..3 :0]; |
| 15 | 15 | _ = slice; |
| ... | ... | @@ -17,5 +17,5 @@ pub fn main() !void { |
| 17 | 17 | } |
| 18 | 18 | |
| 19 | 19 | // run |
| 20 | // backend=stage1 | |
| 20 | // backend=llvm | |
| 21 | 21 | // target=native |
test/cases/safety/shift left by huge amount.zig	+1-1| ... | ... | @@ -17,5 +17,5 @@ pub fn main() !void { |
| 17 | 17 | } |
| 18 | 18 | |
| 19 | 19 | // run |
| 20 | // backend=stage1 | |
| 20 | // backend=llvm | |
| 21 | 21 | // target=native |
test/cases/safety/shift right by huge amount.zig	+1-1| ... | ... | @@ -17,5 +17,5 @@ pub fn main() !void { |
| 17 | 17 | } |
| 18 | 18 | |
| 19 | 19 | // run |
| 20 | // backend=stage1 | |
| 20 | // backend=llvm | |
| 21 | 21 | // target=native |
test/cases/safety/signed integer division overflow - vectors.zig	+6-4| ... | ... | @@ -1,9 +1,11 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { |
| 4 | _ = message; | |
| 5 | 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 | 11 | pub fn main() !void { |
| ... | ... | @@ -17,5 +19,5 @@ fn div(a: @Vector(4, i16), b: @Vector(4, i16)) @Vector(4, i16) { |
| 17 | 19 | return @divTrunc(a, b); |
| 18 | 20 | } |
| 19 | 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 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { |
| 4 | _ = message; | |
| 5 | 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 | 11 | pub fn main() !void { |
| ... | ... | @@ -15,5 +17,5 @@ fn div(a: i16, b: i16) i16 { |
| 15 | 17 | return @divTrunc(a, b); |
| 16 | 18 | } |
| 17 | 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 | 2 | |
| 3 | 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { |
| 4 | 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 | 6 | std.process.exit(0); |
| 7 | 7 | } |
| 8 | 8 | std.process.exit(1); |
| 9 | 9 | } |
| 10 | 10 | |
| 11 | 11 | pub fn main() !void { |
| 12 | var buf: [4]f32 = undefined; | |
| 12 | var buf: [4]f32 = .{ 1, 2, 3, 4 }; | |
| 13 | 13 | const slice = buf[0..3 :1.2]; |
| 14 | 14 | _ = slice; |
| 15 | 15 | return error.TestFailed; |
| 16 | 16 | } |
| 17 | 17 | |
| 18 | 18 | // run |
| 19 | // backend=stage1 | |
| 19 | // backend=llvm | |
| 20 | 20 | // target=native |
test/cases/safety/slice sentinel mismatch - optional pointers.zig	+3-3| ... | ... | @@ -2,19 +2,19 @@ const std = @import("std"); |
| 2 | 2 | |
| 3 | 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { |
| 4 | 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 | 6 | std.process.exit(0); |
| 7 | 7 | } |
| 8 | 8 | std.process.exit(1); |
| 9 | 9 | } |
| 10 | 10 | |
| 11 | 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 | 13 | const slice = buf[0..3 :null]; |
| 14 | 14 | _ = slice; |
| 15 | 15 | return error.TestFailed; |
| 16 | 16 | } |
| 17 | 17 | |
| 18 | 18 | // run |
| 19 | // backend=stage1 | |
| 19 | // backend=llvm | |
| 20 | 20 | // target=native |
test/cases/safety/slice slice sentinel mismatch.zig	+3-3| ... | ... | @@ -2,18 +2,18 @@ const std = @import("std"); |
| 2 | 2 | |
| 3 | 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { |
| 4 | 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 | 6 | std.process.exit(0); |
| 7 | 7 | } |
| 8 | 8 | std.process.exit(1); |
| 9 | 9 | } |
| 10 | 10 | pub fn main() !void { |
| 11 | var buf: [4]u8 = undefined; | |
| 11 | var buf: [4]u8 = .{ 1, 2, 3, 4 }; | |
| 12 | 12 | const slice = buf[0..]; |
| 13 | 13 | const slice2 = slice[0..3 :0]; |
| 14 | 14 | _ = slice2; |
| 15 | 15 | return error.TestFailed; |
| 16 | 16 | } |
| 17 | 17 | // run |
| 18 | // backend=stage1 | |
| 18 | // backend=llvm | |
| 19 | 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 | 2 | |
| 3 | 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { |
| 4 | 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 | 6 | std.process.exit(0); |
| 7 | 7 | } |
| 8 | 8 | std.process.exit(1); |
| ... | ... | @@ -17,5 +17,5 @@ pub fn main() !void { |
| 17 | 17 | } |
| 18 | 18 | |
| 19 | 19 | // run |
| 20 | // backend=stage1 | |
| 20 | // backend=llvm | |
| 21 | 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 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { |
| 4 | _ = message; | |
| 5 | 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 | 11 | pub fn main() !void { |
| ... | ... | @@ -13,5 +15,5 @@ pub fn main() !void { |
| 13 | 15 | return error.TestFailed; |
| 14 | 16 | } |
| 15 | 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 | 2 | |
| 3 | 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { |
| 4 | 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 | 6 | std.process.exit(0); |
| 7 | 7 | } |
| 8 | 8 | std.process.exit(1); |
| ... | ... | @@ -10,17 +10,18 @@ pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noretur |
| 10 | 10 | |
| 11 | 11 | const E = enum(u32) { |
| 12 | 12 | X = 1, |
| 13 | Y = 2, | |
| 13 | 14 | }; |
| 14 | 15 | |
| 15 | 16 | pub fn main() !void { |
| 16 | 17 | var e: E = undefined; |
| 17 | 18 | @memset(@ptrCast([*]u8, &e), 0x55, @sizeOf(E)); |
| 18 | 19 | switch (e) { |
| 19 | .X => @breakpoint(), | |
| 20 | .X, .Y => @breakpoint(), | |
| 20 | 21 | } |
| 21 | 22 | return error.TestFailed; |
| 22 | 23 | } |
| 23 | 24 | |
| 24 | 25 | // run |
| 25 | // backend=stage1 | |
| 26 | // backend=llvm | |
| 26 | 27 | // target=native |
test/cases/safety/switch on corrupted union value.zig	+4-3| ... | ... | @@ -2,7 +2,7 @@ const std = @import("std"); |
| 2 | 2 | |
| 3 | 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { |
| 4 | 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 | 6 | std.process.exit(0); |
| 7 | 7 | } |
| 8 | 8 | std.process.exit(1); |
| ... | ... | @@ -10,17 +10,18 @@ pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noretur |
| 10 | 10 | |
| 11 | 11 | const U = union(enum(u32)) { |
| 12 | 12 | X: u8, |
| 13 | Y: i8, | |
| 13 | 14 | }; |
| 14 | 15 | |
| 15 | 16 | pub fn main() !void { |
| 16 | 17 | var u: U = undefined; |
| 17 | 18 | @memset(@ptrCast([*]u8, &u), 0x55, @sizeOf(U)); |
| 18 | 19 | switch (u) { |
| 19 | .X => @breakpoint(), | |
| 20 | .X, .Y => @breakpoint(), | |
| 20 | 21 | } |
| 21 | 22 | return error.TestFailed; |
| 22 | 23 | } |
| 23 | 24 | |
| 24 | 25 | // run |
| 25 | // backend=stage1 | |
| 26 | // backend=llvm | |
| 26 | 27 | // target=native |