aboutsummaryrefslogtreecommitdiff
path: root/src/Sema.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2022-08-06 14:59:10 -0700
committerGitHub <noreply@github.com>2022-08-06 14:59:10 -0700
commitb3d463c9e6ab41d55849e6cfbbc83d6a1f171ef9 (patch)
tree7d7076d0933c119ee2a0d58c868d8b06bd400089 /src/Sema.zig
parent3e2defd36c0bf90a0604a7618f57beaa4077139c (diff)
parent75275a1514b6954bed09c4c14a325e883a129c7b (diff)
downloadzig-b3d463c9e6ab41d55849e6cfbbc83d6a1f171ef9.tar.gz
zig-b3d463c9e6ab41d55849e6cfbbc83d6a1f171ef9.zip
Merge pull request #12337 from Vexu/stage2-safety
Stage2: implement remaining runtime safety checks
Diffstat (limited to 'src/Sema.zig')
-rw-r--r--src/Sema.zig212
1 files changed, 159 insertions, 53 deletions
diff --git a/src/Sema.zig b/src/Sema.zig
index bd75e54e87..2f3c931e16 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -1578,8 +1578,7 @@ pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize)
// st.index = 0;
const index_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, "index", src, true);
- const zero = try sema.addConstant(Type.usize, Value.zero);
- try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, zero, src, .store);
+ try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, .zero_usize, src, .store);
// @errorReturnTrace() = &st;
_ = 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
}
try sema.requireRuntimeBlock(block, src, operand_src);
- // TODO insert safety check to make sure the value matches an enum value
- return block.addTyOp(.intcast, dest_ty, operand);
+ const result = try block.addTyOp(.intcast, dest_ty, operand);
+ if (block.wantSafety() and !dest_ty.isNonexhaustiveEnum() and sema.mod.comp.bin_file.options.use_llvm) {
+ const ok = try block.addUnOp(.is_named_enum_value, result);
+ try sema.addSafetyCheck(block, ok, .invalid_enum_value);
+ }
+ return result;
}
/// Pointer in, pointer out.
@@ -9707,7 +9710,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
}
var final_else_body: []const Air.Inst.Index = &.{};
- if (special.body.len != 0 or !is_first) {
+ if (special.body.len != 0 or !is_first or case_block.wantSafety()) {
var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, child_block.wip_capture_scope);
defer wip_captures.deinit();
@@ -9730,9 +9733,11 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
} else {
// We still need a terminator in this block, but we have proven
// that it is unreachable.
- // TODO this should be a special safety panic other than unreachable, something
- // like "panic: switch operand had corrupt value not allowed by the type"
- try case_block.addUnreachable(src, true);
+ if (case_block.wantSafety()) {
+ _ = try sema.safetyPanic(&case_block, src, .corrupt_switch);
+ } else {
+ _ = try case_block.addNoOp(.unreach);
+ }
}
try wip_captures.finalize();
@@ -10241,34 +10246,57 @@ fn zirShl(
} else rhs;
try sema.requireRuntimeBlock(block, src, runtime_src);
- if (block.wantSafety() and air_tag == .shl_exact) {
- const op_ov_tuple_ty = try sema.overflowArithmeticTupleType(lhs_ty);
- const op_ov = try block.addInst(.{
- .tag = .shl_with_overflow,
- .data = .{ .ty_pl = .{
- .ty = try sema.addType(op_ov_tuple_ty),
- .payload = try sema.addExtra(Air.Bin{
- .lhs = lhs,
- .rhs = rhs,
- }),
- } },
- });
- const ov_bit = try sema.tupleFieldValByIndex(block, src, op_ov, 1, op_ov_tuple_ty);
- const any_ov_bit = if (lhs_ty.zigTypeTag() == .Vector)
- try block.addInst(.{
- .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
- .data = .{ .reduce = .{
- .operand = ov_bit,
- .operation = .Or,
+ if (block.wantSafety()) {
+ const bit_count = scalar_ty.intInfo(target).bits;
+ if (!std.math.isPowerOfTwo(bit_count)) {
+ const bit_count_val = try Value.Tag.int_u64.create(sema.arena, bit_count);
+
+ const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: {
+ const bit_count_inst = try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, bit_count_val));
+ const lt = try block.addCmpVector(rhs, bit_count_inst, .lt, try sema.addType(rhs_ty));
+ break :ok try block.addInst(.{
+ .tag = .reduce,
+ .data = .{ .reduce = .{
+ .operand = lt,
+ .operation = .And,
+ } },
+ });
+ } else ok: {
+ const bit_count_inst = try sema.addConstant(rhs_ty, bit_count_val);
+ break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst);
+ };
+ try sema.addSafetyCheck(block, ok, .shift_rhs_too_big);
+ }
+
+ if (air_tag == .shl_exact) {
+ const op_ov_tuple_ty = try sema.overflowArithmeticTupleType(lhs_ty);
+ const op_ov = try block.addInst(.{
+ .tag = .shl_with_overflow,
+ .data = .{ .ty_pl = .{
+ .ty = try sema.addType(op_ov_tuple_ty),
+ .payload = try sema.addExtra(Air.Bin{
+ .lhs = lhs,
+ .rhs = rhs,
+ }),
} },
- })
- else
- ov_bit;
- const zero_ov = try sema.addConstant(Type.@"u1", Value.zero);
- const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, zero_ov);
+ });
+ const ov_bit = try sema.tupleFieldValByIndex(block, src, op_ov, 1, op_ov_tuple_ty);
+ const any_ov_bit = if (lhs_ty.zigTypeTag() == .Vector)
+ try block.addInst(.{
+ .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
+ .data = .{ .reduce = .{
+ .operand = ov_bit,
+ .operation = .Or,
+ } },
+ })
+ else
+ ov_bit;
+ const zero_ov = try sema.addConstant(Type.@"u1", Value.zero);
+ const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, zero_ov);
- try sema.addSafetyCheck(block, no_ov, .shl_overflow);
- return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty);
+ try sema.addSafetyCheck(block, no_ov, .shl_overflow);
+ return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty);
+ }
}
return block.addBinOp(air_tag, lhs, new_rhs);
}
@@ -10347,20 +10375,43 @@ fn zirShr(
try sema.requireRuntimeBlock(block, src, runtime_src);
const result = try block.addBinOp(air_tag, lhs, rhs);
- if (block.wantSafety() and air_tag == .shr_exact) {
- const back = try block.addBinOp(.shl, result, rhs);
-
- const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: {
- const eql = try block.addCmpVector(lhs, back, .eq, try sema.addType(rhs_ty));
- break :ok try block.addInst(.{
- .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
- .data = .{ .reduce = .{
- .operand = eql,
- .operation = .And,
- } },
- });
- } else try block.addBinOp(.cmp_eq, lhs, back);
- try sema.addSafetyCheck(block, ok, .shr_overflow);
+ if (block.wantSafety()) {
+ const bit_count = scalar_ty.intInfo(target).bits;
+ if (!std.math.isPowerOfTwo(bit_count)) {
+ const bit_count_val = try Value.Tag.int_u64.create(sema.arena, bit_count);
+
+ const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: {
+ const bit_count_inst = try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, bit_count_val));
+ const lt = try block.addCmpVector(rhs, bit_count_inst, .lt, try sema.addType(rhs_ty));
+ break :ok try block.addInst(.{
+ .tag = .reduce,
+ .data = .{ .reduce = .{
+ .operand = lt,
+ .operation = .And,
+ } },
+ });
+ } else ok: {
+ const bit_count_inst = try sema.addConstant(rhs_ty, bit_count_val);
+ break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst);
+ };
+ try sema.addSafetyCheck(block, ok, .shift_rhs_too_big);
+ }
+
+ if (air_tag == .shr_exact) {
+ const back = try block.addBinOp(.shl, result, rhs);
+
+ const ok = if (rhs_ty.zigTypeTag() == .Vector) ok: {
+ const eql = try block.addCmpVector(lhs, back, .eq, try sema.addType(rhs_ty));
+ break :ok try block.addInst(.{
+ .tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
+ .data = .{ .reduce = .{
+ .operand = eql,
+ .operation = .And,
+ } },
+ });
+ } else try block.addBinOp(.cmp_eq, lhs, back);
+ try sema.addSafetyCheck(block, ok, .shr_overflow);
+ }
}
return result;
}
@@ -15961,6 +16012,11 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
const field_name = enum_ty.enumFieldName(field_index);
return sema.addStrLit(block, field_name);
}
+ try sema.requireRuntimeBlock(block, src, operand_src);
+ if (block.wantSafety() and sema.mod.comp.bin_file.options.use_llvm) {
+ const ok = try block.addUnOp(.is_named_enum_value, casted_operand);
+ try sema.addSafetyCheck(block, ok, .invalid_enum_value);
+ }
// In case the value is runtime-known, we have an AIR instruction for this instead
// of trying to lower it in Sema because an optimization pass may result in the operand
// 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
}
try sema.requireRuntimeBlock(block, src, operand_src);
- if (block.wantSafety()) {
+ if (block.wantSafety() and try sema.typeHasRuntimeBits(block, sema.src, type_res.elemType2())) {
if (!type_res.isAllowzeroPtr()) {
const is_non_zero = try block.addBinOp(.cmp_neq, operand_coerced, .zero_usize);
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
}
try sema.requireRuntimeBlock(block, inst_data.src(), ptr_src);
- if (block.wantSafety() and dest_align > 1) {
+ if (block.wantSafety() and dest_align > 1 and
+ try sema.typeHasRuntimeBits(block, sema.src, dest_ty.elemType2()))
+ {
const val_payload = try sema.arena.create(Value.Payload.U64);
val_payload.* = .{
.base = .{ .tag = .int_u64 },
@@ -17253,7 +17311,7 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize);
const ok = if (ptr_ty.isSlice()) ok: {
const len = try sema.analyzeSliceLen(block, ptr_src, ptr);
- const len_zero = try block.addBinOp(.cmp_eq, len, try sema.addConstant(Type.usize, Value.zero));
+ const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize);
break :ok try block.addBinOp(.bit_or, len_zero, is_aligned);
} else is_aligned;
try sema.addSafetyCheck(block, ok, .incorrect_alignment);
@@ -20114,6 +20172,9 @@ pub const PanicId = enum {
/// TODO make this call `std.builtin.panicInactiveUnionField`.
inactive_union_field,
integer_part_out_of_bounds,
+ corrupt_switch,
+ shift_rhs_too_big,
+ invalid_enum_value,
};
fn addSafetyCheck(
@@ -20408,6 +20469,9 @@ fn safetyPanic(
.exact_division_remainder => "exact division produced remainder",
.inactive_union_field => "access of inactive union field",
.integer_part_out_of_bounds => "integer part of floating point value out of bounds",
+ .corrupt_switch => "switch on corrupt value",
+ .shift_rhs_too_big => "shift amount is greater than the type size",
+ .invalid_enum_value => "invalid enum value",
};
const msg_inst = msg_inst: {
@@ -22096,7 +22160,6 @@ fn coerceExtra(
.ok => {},
else => break :src_c_ptr,
}
- // TODO add safety check for null pointer
return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
}
@@ -24569,6 +24632,24 @@ fn coerceCompatiblePtrs(
return sema.addConstant(dest_ty, val);
}
try sema.requireRuntimeBlock(block, inst_src, null);
+ const inst_ty = sema.typeOf(inst);
+ const inst_allows_zero = (inst_ty.zigTypeTag() == .Pointer and inst_ty.ptrAllowsZero()) or true;
+ if (block.wantSafety() and inst_allows_zero and !dest_ty.ptrAllowsZero() and
+ try sema.typeHasRuntimeBits(block, sema.src, dest_ty.elemType2()))
+ {
+ const actual_ptr = if (inst_ty.isSlice())
+ try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty)
+ else
+ inst;
+ const ptr_int = try block.addUnOp(.ptrtoint, actual_ptr);
+ const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize);
+ const ok = if (inst_ty.isSlice()) ok: {
+ const len = try sema.analyzeSliceLen(block, inst_src, inst);
+ const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize);
+ break :ok try block.addBinOp(.bit_or, len_zero, is_non_zero);
+ } else is_non_zero;
+ try sema.addSafetyCheck(block, ok, .cast_to_null);
+ }
return sema.bitCast(block, dest_ty, inst, inst_src);
}
@@ -25708,6 +25789,27 @@ fn analyzeSlice(
const new_ptr_val = opt_new_ptr_val orelse {
const result = try block.addBitCast(return_ty, new_ptr);
if (block.wantSafety()) {
+ // requirement: slicing C ptr is non-null
+ if (ptr_ptr_child_ty.isCPtr()) {
+ const is_non_null = try sema.analyzeIsNull(block, ptr_src, ptr, true);
+ try sema.addSafetyCheck(block, is_non_null, .unwrap_null);
+ }
+
+ if (slice_ty.isSlice()) {
+ const slice_len_inst = try block.addTyOp(.slice_len, Type.usize, ptr_or_slice);
+ const actual_len = if (slice_ty.sentinel() == null)
+ slice_len_inst
+ else
+ try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src);
+
+ const actual_end = if (slice_sentinel != null)
+ try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src)
+ else
+ end;
+
+ try sema.panicIndexOutOfBounds(block, src, actual_end, actual_len, .cmp_lte);
+ }
+
// requirement: result[new_len] == slice_sentinel
try sema.panicSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len);
}
@@ -25769,7 +25871,11 @@ fn analyzeSlice(
break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src);
} else null;
if (opt_len_inst) |len_inst| {
- try sema.panicIndexOutOfBounds(block, src, end, len_inst, .cmp_lte);
+ const actual_end = if (slice_sentinel != null)
+ try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src)
+ else
+ end;
+ try sema.panicIndexOutOfBounds(block, src, actual_end, len_inst, .cmp_lte);
}
// requirement: start <= end