Skip to content

Commit f330eeb

Browse files
committed
fix using the result of @intcast to u0
closes #1817
1 parent 7843c96 commit f330eeb

File tree

7 files changed

+104
-17
lines changed

7 files changed

+104
-17
lines changed

src/all_types.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2239,6 +2239,7 @@ enum IrInstructionId {
22392239
IrInstructionIdCheckRuntimeScope,
22402240
IrInstructionIdVectorToArray,
22412241
IrInstructionIdArrayToVector,
2242+
IrInstructionIdAssertZero,
22422243
};
22432244

22442245
struct IrInstruction {
@@ -3381,6 +3382,12 @@ struct IrInstructionVectorToArray {
33813382
LLVMValueRef tmp_ptr;
33823383
};
33833384

3385+
struct IrInstructionAssertZero {
3386+
IrInstruction base;
3387+
3388+
IrInstruction *target;
3389+
};
3390+
33843391
static const size_t slice_ptr_index = 0;
33853392
static const size_t slice_len_index = 1;
33863393

src/codegen.cpp

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1651,10 +1651,25 @@ static void add_bounds_check(CodeGen *g, LLVMValueRef target_val,
16511651
LLVMPositionBuilderAtEnd(g->builder, ok_block);
16521652
}
16531653

1654+
static LLVMValueRef gen_assert_zero(CodeGen *g, LLVMValueRef expr_val, ZigType *int_type) {
1655+
LLVMValueRef zero = LLVMConstNull(int_type->type_ref);
1656+
LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, expr_val, zero, "");
1657+
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenOk");
1658+
LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenFail");
1659+
LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
1660+
1661+
LLVMPositionBuilderAtEnd(g->builder, fail_block);
1662+
gen_safety_crash(g, PanicMsgIdCastTruncatedData);
1663+
1664+
LLVMPositionBuilderAtEnd(g->builder, ok_block);
1665+
return nullptr;
1666+
}
1667+
16541668
static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, ZigType *actual_type,
16551669
ZigType *wanted_type, LLVMValueRef expr_val)
16561670
{
16571671
assert(actual_type->id == wanted_type->id);
1672+
assert(expr_val != nullptr);
16581673

16591674
uint64_t actual_bits;
16601675
uint64_t wanted_bits;
@@ -1707,17 +1722,7 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, Z
17071722
if (!want_runtime_safety)
17081723
return nullptr;
17091724

1710-
LLVMValueRef zero = LLVMConstNull(actual_type->type_ref);
1711-
LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, expr_val, zero, "");
1712-
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenOk");
1713-
LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenFail");
1714-
LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
1715-
1716-
LLVMPositionBuilderAtEnd(g->builder, fail_block);
1717-
gen_safety_crash(g, PanicMsgIdCastTruncatedData);
1718-
1719-
LLVMPositionBuilderAtEnd(g->builder, ok_block);
1720-
return nullptr;
1725+
return gen_assert_zero(g, expr_val, actual_type);
17211726
}
17221727
LLVMValueRef trunc_val = LLVMBuildTrunc(g->builder, expr_val, wanted_type->type_ref, "");
17231728
if (!want_runtime_safety) {
@@ -5209,6 +5214,17 @@ static LLVMValueRef ir_render_array_to_vector(CodeGen *g, IrExecutable *executab
52095214
return gen_load_untyped(g, casted_ptr, 0, false, "");
52105215
}
52115216

5217+
static LLVMValueRef ir_render_assert_zero(CodeGen *g, IrExecutable *executable,
5218+
IrInstructionAssertZero *instruction)
5219+
{
5220+
LLVMValueRef target = ir_llvm_value(g, instruction->target);
5221+
ZigType *int_type = instruction->target->value.type;
5222+
if (ir_want_runtime_safety(g, &instruction->base)) {
5223+
return gen_assert_zero(g, target, int_type);
5224+
}
5225+
return nullptr;
5226+
}
5227+
52125228
static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
52135229
AstNode *source_node = instruction->source_node;
52145230
Scope *scope = instruction->scope;
@@ -5458,6 +5474,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
54585474
return ir_render_array_to_vector(g, executable, (IrInstructionArrayToVector *)instruction);
54595475
case IrInstructionIdVectorToArray:
54605476
return ir_render_vector_to_array(g, executable, (IrInstructionVectorToArray *)instruction);
5477+
case IrInstructionIdAssertZero:
5478+
return ir_render_assert_zero(g, executable, (IrInstructionAssertZero *)instruction);
54615479
}
54625480
zig_unreachable();
54635481
}

src/ir.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayToVector *)
908908
return IrInstructionIdArrayToVector;
909909
}
910910

911+
static constexpr IrInstructionId ir_instruction_id(IrInstructionAssertZero *) {
912+
return IrInstructionIdAssertZero;
913+
}
914+
911915
template<typename T>
912916
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
913917
T *special_instruction = allocate<T>(1);
@@ -2858,6 +2862,19 @@ static IrInstruction *ir_build_array_to_vector(IrAnalyze *ira, IrInstruction *so
28582862
return &instruction->base;
28592863
}
28602864

2865+
static IrInstruction *ir_build_assert_zero(IrAnalyze *ira, IrInstruction *source_instruction,
2866+
IrInstruction *target)
2867+
{
2868+
IrInstructionAssertZero *instruction = ir_build_instruction<IrInstructionAssertZero>(&ira->new_irb,
2869+
source_instruction->scope, source_instruction->source_node);
2870+
instruction->base.value.type = ira->codegen->builtin_types.entry_void;
2871+
instruction->target = target;
2872+
2873+
ir_ref_instruction(target, ira->new_irb.current_basic_block);
2874+
2875+
return &instruction->base;
2876+
}
2877+
28612878
static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
28622879
results[ReturnKindUnconditional] = 0;
28632880
results[ReturnKindError] = 0;
@@ -10395,6 +10412,18 @@ static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction
1039510412
return result;
1039610413
}
1039710414

10415+
// If the destination integer type has no bits, then we can emit a comptime
10416+
// zero. However, we still want to emit a runtime safety check to make sure
10417+
// the target is zero.
10418+
if (!type_has_bits(wanted_type)) {
10419+
assert(wanted_type->id == ZigTypeIdInt);
10420+
assert(type_has_bits(target->value.type));
10421+
ir_build_assert_zero(ira, source_instr, target);
10422+
IrInstruction *result = ir_const_unsigned(ira, source_instr, 0);
10423+
result->value.type = wanted_type;
10424+
return result;
10425+
}
10426+
1039810427
IrInstruction *result = ir_build_widen_or_shorten(&ira->new_irb, source_instr->scope,
1039910428
source_instr->source_node, target);
1040010429
result->value.type = wanted_type;
@@ -21705,6 +21734,7 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio
2170521734
case IrInstructionIdCmpxchgGen:
2170621735
case IrInstructionIdArrayToVector:
2170721736
case IrInstructionIdVectorToArray:
21737+
case IrInstructionIdAssertZero:
2170821738
zig_unreachable();
2170921739

2171021740
case IrInstructionIdReturn:
@@ -22103,6 +22133,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
2210322133
case IrInstructionIdAtomicRmw:
2210422134
case IrInstructionIdCmpxchgGen:
2210522135
case IrInstructionIdCmpxchgSrc:
22136+
case IrInstructionIdAssertZero:
2210622137
return true;
2210722138

2210822139
case IrInstructionIdPhi:

src/ir_print.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,12 @@ static void ir_print_vector_to_array(IrPrint *irp, IrInstructionVectorToArray *i
984984
fprintf(irp->f, ")");
985985
}
986986

987+
static void ir_print_assert_zero(IrPrint *irp, IrInstructionAssertZero *instruction) {
988+
fprintf(irp->f, "AssertZero(");
989+
ir_print_other_instruction(irp, instruction->target);
990+
fprintf(irp->f, ")");
991+
}
992+
987993
static void ir_print_int_to_err(IrPrint *irp, IrInstructionIntToErr *instruction) {
988994
fprintf(irp->f, "inttoerr ");
989995
ir_print_other_instruction(irp, instruction->target);
@@ -1843,6 +1849,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
18431849
case IrInstructionIdVectorToArray:
18441850
ir_print_vector_to_array(irp, (IrInstructionVectorToArray *)instruction);
18451851
break;
1852+
case IrInstructionIdAssertZero:
1853+
ir_print_assert_zero(irp, (IrInstructionAssertZero *)instruction);
1854+
break;
18461855
}
18471856
fprintf(irp->f, "\n");
18481857
}

test/runtime_safety.zig

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,23 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
362362
\\}
363363
);
364364

365+
// @intCast a runtime integer to u0 actually results in a comptime-known value,
366+
// but we still emit a safety check to ensure the integer was 0 and thus
367+
// did not truncate information.
368+
cases.addRuntimeSafety("@intCast to u0",
369+
\\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
370+
\\ @import("std").os.exit(126);
371+
\\}
372+
\\
373+
\\pub fn main() void {
374+
\\ bar(1, 1);
375+
\\}
376+
\\
377+
\\fn bar(one: u1, not_zero: i32) void {
378+
\\ var x = one << @intCast(u0, not_zero);
379+
\\}
380+
);
381+
365382
// This case makes sure that the code compiles and runs. There is not actually a special
366383
// runtime safety check having to do specifically with error return traces across suspend points.
367384
cases.addRuntimeSafety("error return trace across suspend points",

test/stage1/behavior/cast.zig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,3 +471,14 @@ test "@intToEnum passed a comptime_int to an enum with one item" {
471471
const x = @intToEnum(E, 0);
472472
assertOrPanic(x == E.A);
473473
}
474+
475+
test "@intCast to u0 and use the result" {
476+
const S = struct {
477+
fn doTheTest(zero: u1, one: u1, bigzero: i32) void {
478+
assertOrPanic((one << @intCast(u0, bigzero)) == 1);
479+
assertOrPanic((zero << @intCast(u0, bigzero)) == 0);
480+
}
481+
};
482+
S.doTheTest(0, 1, 0);
483+
comptime S.doTheTest(0, 1, 0);
484+
}

test/stage1/behavior/eval.zig

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -697,12 +697,6 @@ test "bit shift a u1" {
697697
assertOrPanic(y == 1);
698698
}
699699

700-
test "@intCast to a u0" {
701-
var x: u8 = 0;
702-
var y: u0 = @intCast(u0, x);
703-
assertOrPanic(y == 0);
704-
}
705-
706700
test "@bytesToslice on a packed struct" {
707701
const F = packed struct {
708702
a: u8,

0 commit comments

Comments
 (0)