parent
9e905ab364
commit
56908dcb9d
|
@ -1129,6 +1129,7 @@ enum BuiltinFnId {
|
|||
BuiltinFnIdCmpExchange,
|
||||
BuiltinFnIdFence,
|
||||
BuiltinFnIdDivExact,
|
||||
BuiltinFnIdTruncate,
|
||||
};
|
||||
|
||||
struct BuiltinFnEntry {
|
||||
|
|
|
@ -4686,6 +4686,44 @@ static TypeTableEntry *analyze_div_exact(CodeGen *g, ImportTableEntry *import,
|
|||
}
|
||||
}
|
||||
|
||||
static TypeTableEntry *analyze_truncate(CodeGen *g, ImportTableEntry *import,
|
||||
BlockContext *context, AstNode *node)
|
||||
{
|
||||
assert(node->type == NodeTypeFnCallExpr);
|
||||
|
||||
AstNode **op1 = &node->data.fn_call_expr.params.at(0);
|
||||
AstNode **op2 = &node->data.fn_call_expr.params.at(1);
|
||||
|
||||
TypeTableEntry *dest_type = analyze_type_expr(g, import, context, *op1);
|
||||
TypeTableEntry *src_type = analyze_expression(g, import, context, nullptr, *op2);
|
||||
|
||||
if (dest_type->id == TypeTableEntryIdInvalid || src_type->id == TypeTableEntryIdInvalid) {
|
||||
return g->builtin_types.entry_invalid;
|
||||
} else if (dest_type->id != TypeTableEntryIdInt) {
|
||||
add_node_error(g, *op1,
|
||||
buf_sprintf("expected integer type, got '%s'", buf_ptr(&dest_type->name)));
|
||||
return g->builtin_types.entry_invalid;
|
||||
} else if (src_type->id != TypeTableEntryIdInt) {
|
||||
add_node_error(g, *op2,
|
||||
buf_sprintf("expected integer type, got '%s'", buf_ptr(&src_type->name)));
|
||||
return g->builtin_types.entry_invalid;
|
||||
} else if (src_type->data.integral.is_signed != dest_type->data.integral.is_signed) {
|
||||
const char *sign_str = dest_type->data.integral.is_signed ? "signed" : "unsigned";
|
||||
add_node_error(g, *op2,
|
||||
buf_sprintf("expected %s integer type, got '%s'", sign_str, buf_ptr(&src_type->name)));
|
||||
return g->builtin_types.entry_invalid;
|
||||
} else if (src_type->data.integral.bit_count <= dest_type->data.integral.bit_count) {
|
||||
add_node_error(g, *op2,
|
||||
buf_sprintf("type '%s' has same or fewer bits than destination type '%s'",
|
||||
buf_ptr(&src_type->name), buf_ptr(&dest_type->name)));
|
||||
return g->builtin_types.entry_invalid;
|
||||
}
|
||||
|
||||
// TODO const expr eval
|
||||
|
||||
return dest_type;
|
||||
}
|
||||
|
||||
static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
|
||||
TypeTableEntry *expected_type, AstNode *node)
|
||||
{
|
||||
|
@ -5028,6 +5066,8 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
|
|||
return analyze_fence(g, import, context, node);
|
||||
case BuiltinFnIdDivExact:
|
||||
return analyze_div_exact(g, import, context, node);
|
||||
case BuiltinFnIdTruncate:
|
||||
return analyze_truncate(g, import, context, node);
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
|
|
@ -473,6 +473,18 @@ static LLVMValueRef gen_div_exact(CodeGen *g, AstNode *node) {
|
|||
return gen_div(g, node, op1_val, op2_val, get_expr_type(op1_node), true);
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_truncate(CodeGen *g, AstNode *node) {
|
||||
assert(node->type == NodeTypeFnCallExpr);
|
||||
|
||||
TypeTableEntry *dest_type = get_type_for_type_node(node->data.fn_call_expr.params.at(0));
|
||||
AstNode *src_node = node->data.fn_call_expr.params.at(1);
|
||||
|
||||
LLVMValueRef src_val = gen_expr(g, src_node);
|
||||
|
||||
set_debug_source_node(g, node);
|
||||
return LLVMBuildTrunc(g->builder, src_val, dest_type->type_ref, "");
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_shl_with_overflow(CodeGen *g, AstNode *node) {
|
||||
assert(node->type == NodeTypeFnCallExpr);
|
||||
|
||||
|
@ -661,6 +673,8 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
|
|||
return gen_fence(g, node);
|
||||
case BuiltinFnIdDivExact:
|
||||
return gen_div_exact(g, node);
|
||||
case BuiltinFnIdTruncate:
|
||||
return gen_truncate(g, node);
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
@ -729,6 +743,24 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, AstNode *source_node, TypeT
|
|||
zig_unreachable();
|
||||
}
|
||||
|
||||
if (actual_bits >= wanted_bits && actual_type->id == TypeTableEntryIdInt &&
|
||||
!wanted_type->data.integral.is_signed && actual_type->data.integral.is_signed &&
|
||||
want_debug_safety(g, source_node))
|
||||
{
|
||||
set_debug_source_node(g, source_node);
|
||||
LLVMValueRef zero = LLVMConstNull(actual_type->type_ref);
|
||||
LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntSGE, expr_val, zero, "");
|
||||
|
||||
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SignCastOk");
|
||||
LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SignCastFail");
|
||||
LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, fail_block);
|
||||
gen_debug_safety_crash(g);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, ok_block);
|
||||
}
|
||||
|
||||
if (actual_bits == wanted_bits) {
|
||||
return expr_val;
|
||||
} else if (actual_bits < wanted_bits) {
|
||||
|
@ -752,7 +784,26 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, AstNode *source_node, TypeT
|
|||
return LLVMBuildFPTrunc(g->builder, expr_val, wanted_type->type_ref, "");
|
||||
} else if (actual_type->id == TypeTableEntryIdInt) {
|
||||
set_debug_source_node(g, source_node);
|
||||
return LLVMBuildTrunc(g->builder, expr_val, wanted_type->type_ref, "");
|
||||
LLVMValueRef trunc_val = LLVMBuildTrunc(g->builder, expr_val, wanted_type->type_ref, "");
|
||||
if (!want_debug_safety(g, source_node)) {
|
||||
return trunc_val;
|
||||
}
|
||||
LLVMValueRef orig_val;
|
||||
if (actual_type->data.integral.is_signed) {
|
||||
orig_val = LLVMBuildSExt(g->builder, trunc_val, actual_type->type_ref, "");
|
||||
} else {
|
||||
orig_val = LLVMBuildZExt(g->builder, trunc_val, actual_type->type_ref, "");
|
||||
}
|
||||
LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, expr_val, orig_val, "");
|
||||
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "CastShortenOk");
|
||||
LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "CastShortenFail");
|
||||
LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, fail_block);
|
||||
gen_debug_safety_crash(g);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, ok_block);
|
||||
return trunc_val;
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
|
@ -4568,6 +4619,7 @@ static void define_builtin_fns(CodeGen *g) {
|
|||
create_builtin_fn_with_arg_count(g, BuiltinFnIdCmpExchange, "cmpxchg", 5);
|
||||
create_builtin_fn_with_arg_count(g, BuiltinFnIdFence, "fence", 1);
|
||||
create_builtin_fn_with_arg_count(g, BuiltinFnIdDivExact, "div_exact", 2);
|
||||
create_builtin_fn_with_arg_count(g, BuiltinFnIdTruncate, "truncate", 2);
|
||||
}
|
||||
|
||||
static void init(CodeGen *g, Buf *source_path) {
|
||||
|
|
|
@ -827,6 +827,7 @@ static bool eval_fn_call_builtin(EvalFn *ef, AstNode *node, ConstExprValue *out_
|
|||
case BuiltinFnIdErrName:
|
||||
case BuiltinFnIdEmbedFile:
|
||||
case BuiltinFnIdCmpExchange:
|
||||
case BuiltinFnIdTruncate:
|
||||
zig_panic("TODO");
|
||||
case BuiltinFnIdBreakpoint:
|
||||
case BuiltinFnIdInvalid:
|
||||
|
|
|
@ -15,7 +15,7 @@ pub struct Rand {
|
|||
var i : isize = 1;
|
||||
var prev_value: u64w = seed;
|
||||
while (i < ARRAY_SIZE; i += 1) {
|
||||
r.array[i] = u32((prev_value ^ (prev_value << 30)) * 0x6c078965 + u64w(i));
|
||||
r.array[i] = @truncate(u32, (prev_value ^ (prev_value << 30)) * 0x6c078965 + u64w(i));
|
||||
prev_value = r.array[i];
|
||||
}
|
||||
return r;
|
||||
|
|
|
@ -1368,6 +1368,20 @@ fn add(x: i8w, y: i32) {
|
|||
}
|
||||
)SOURCE", 1, ".tmp_source.zig:3:17: error: incompatible types: 'i8w' and 'i32'");
|
||||
|
||||
add_compile_fail_case("truncate sign mismatch", R"SOURCE(
|
||||
fn f() {
|
||||
const x: u32 = 10;
|
||||
@truncate(i8, x);
|
||||
}
|
||||
)SOURCE", 1, ".tmp_source.zig:4:19: error: expected signed integer type, got 'u32'");
|
||||
|
||||
add_compile_fail_case("truncate same bit count", R"SOURCE(
|
||||
fn f() {
|
||||
const x: i8 = 10;
|
||||
@truncate(i8, x);
|
||||
}
|
||||
)SOURCE", 1, ".tmp_source.zig:4:19: error: type 'i8' has same or fewer bits than destination type 'i8'");
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1476,6 +1490,26 @@ fn widen_slice(slice: []u8) -> []i32 {
|
|||
}
|
||||
)SOURCE");
|
||||
|
||||
add_debug_safety_case("value does not fit in shortening cast", R"SOURCE(
|
||||
pub fn main(args: [][]u8) -> %void {
|
||||
shorten_cast(200);
|
||||
}
|
||||
#static_eval_enable(false)
|
||||
fn shorten_cast(x: i32) -> i8 {
|
||||
i8(x)
|
||||
}
|
||||
)SOURCE");
|
||||
|
||||
add_debug_safety_case("signed integer not fitting in cast to unsigned integer", R"SOURCE(
|
||||
pub fn main(args: [][]u8) -> %void {
|
||||
unsigned_cast(-10);
|
||||
}
|
||||
#static_eval_enable(false)
|
||||
fn unsigned_cast(x: i32) -> u32 {
|
||||
u32(x)
|
||||
}
|
||||
)SOURCE");
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -1631,3 +1631,12 @@ struct SillyStruct {
|
|||
const here_is_a_null_literal = SillyStruct {
|
||||
.context = null,
|
||||
};
|
||||
|
||||
#attribute("test")
|
||||
fn truncate() {
|
||||
assert(test_truncate(0x10fd) == 0xfd);
|
||||
}
|
||||
#static_eval_enable(false)
|
||||
fn test_truncate(x: u32) -> u8 {
|
||||
@truncate(u8, x)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue