Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Air.zig
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,10 @@ pub const Inst = struct {
/// Uses the `pl_op` field with payload `AtomicRmw`. Operand is `ptr`.
atomic_rmw,

/// Returns true if enum tag value has a name.
/// Uses the `un_op` field.
is_named_enum_value,

/// Given an enum tag value, returns the tag name. The enum type may be non-exhaustive.
/// Result type is always `[:0]const u8`.
/// Uses the `un_op` field.
Expand Down Expand Up @@ -1057,6 +1061,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
.is_non_err,
.is_err_ptr,
.is_non_err_ptr,
.is_named_enum_value,
=> return Type.bool,

.const_ty => return Type.type,
Expand Down
2 changes: 2 additions & 0 deletions src/Liveness.zig
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ pub fn categorizeOperand(
.is_non_err_ptr,
.ptrtoint,
.bool_to_int,
.is_named_enum_value,
.tag_name,
.error_name,
.sqrt,
Expand Down Expand Up @@ -858,6 +859,7 @@ fn analyzeInst(
.bool_to_int,
.ret,
.ret_load,
.is_named_enum_value,
.tag_name,
.error_name,
.sqrt,
Expand Down
212 changes: 159 additions & 53 deletions src/Sema.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1577,8 +1577,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);
Expand Down Expand Up @@ -6934,8 +6933,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.
Expand Down Expand Up @@ -9692,7 +9695,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();

Expand All @@ -9715,9 +9718,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();
Expand Down Expand Up @@ -10226,34 +10231,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);
}
Expand Down Expand Up @@ -10332,20 +10360,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;
}
Expand Down Expand Up @@ -15840,6 +15891,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.
Expand Down Expand Up @@ -16821,7 +16877,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);
Expand Down Expand Up @@ -17113,7 +17169,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 },
Expand All @@ -17132,7 +17190,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);
Expand Down Expand Up @@ -19970,6 +20028,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(
Expand Down Expand Up @@ -20265,6 +20326,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: {
Expand Down Expand Up @@ -21953,7 +22017,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);
}

Expand Down Expand Up @@ -24426,6 +24489,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);
}

Expand Down Expand Up @@ -25565,6 +25646,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);
}
Expand Down Expand Up @@ -25626,7 +25728,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
Expand Down
2 changes: 2 additions & 0 deletions src/arch/aarch64/CodeGen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.float_to_int_optimized,
=> return self.fail("TODO implement optimized float mode", .{}),

.is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}),

.wasm_memory_size => unreachable,
.wasm_memory_grow => unreachable,
// zig fmt: on
Expand Down
2 changes: 2 additions & 0 deletions src/arch/arm/CodeGen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.float_to_int_optimized,
=> return self.fail("TODO implement optimized float mode", .{}),

.is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}),

.wasm_memory_size => unreachable,
.wasm_memory_grow => unreachable,
// zig fmt: on
Expand Down
2 changes: 2 additions & 0 deletions src/arch/riscv64/CodeGen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.float_to_int_optimized,
=> return self.fail("TODO implement optimized float mode", .{}),

.is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}),

.wasm_memory_size => unreachable,
.wasm_memory_grow => unreachable,
// zig fmt: on
Expand Down
2 changes: 2 additions & 0 deletions src/arch/sparc64/CodeGen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.float_to_int_optimized,
=> @panic("TODO implement optimized float mode"),

.is_named_enum_value => @panic("TODO implement is_named_enum_value"),

.wasm_memory_size => unreachable,
.wasm_memory_grow => unreachable,
// zig fmt: on
Expand Down
Loading