diff --git a/src/all_types.hpp b/src/all_types.hpp index 143e476f13..82603f6077 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -382,6 +382,7 @@ enum CastOp { CastOpIntToFloat, CastOpFloatToInt, CastOpBoolToInt, + CastOpResizeSlice, }; struct AstNodeFnCallExpr { diff --git a/src/analyze.cpp b/src/analyze.cpp index 65075b9ab0..5832998900 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -219,6 +219,23 @@ static bool type_is_complete(TypeTableEntry *type_entry) { zig_unreachable(); } +uint64_t type_size(CodeGen *g, TypeTableEntry *type_entry) { + if (type_has_bits(type_entry)) { + return LLVMStoreSizeOfType(g->target_data_ref, type_entry->type_ref); + } else { + return 0; + } +} + +static bool is_u8(TypeTableEntry *type) { + return type->id == TypeTableEntryIdInt && + !type->data.integral.is_signed && type->data.integral.bit_count == 8; +} + +static bool is_slice(TypeTableEntry *type) { + return type->id == TypeTableEntryIdStruct && type->data.structure.is_slice; +} + TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x) { return get_int_type(g, false, false, bits_needed_for_unsigned(x)); } @@ -4215,8 +4232,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B } // explicit cast from array to slice - if (wanted_type->id == TypeTableEntryIdStruct && - wanted_type->data.structure.is_slice && + if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdArray && types_match_const_cast_only( wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type, @@ -4225,6 +4241,17 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B return resolve_cast(g, context, node, expr_node, wanted_type, CastOpToUnknownSizeArray, true); } + // explicit cast from []T to []u8 + if (is_slice(wanted_type) && + is_u8(wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type) && + is_slice(actual_type) && + (wanted_type->data.structure.fields[0].type_entry->data.pointer.is_const || + !actual_type->data.structure.fields[0].type_entry->data.pointer.is_const)) + { + return resolve_cast(g, context, node, expr_node, wanted_type, CastOpResizeSlice, true); + } + + // explicit cast from pointer to another pointer if ((actual_type->id == TypeTableEntryIdPointer || actual_type->id == TypeTableEntryIdFn) && (wanted_type->id == TypeTableEntryIdPointer || wanted_type->id == TypeTableEntryIdFn)) @@ -4757,12 +4784,7 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry buf_sprintf("no size available for type '%s'", buf_ptr(&type_entry->name))); return g->builtin_types.entry_invalid; } else { - uint64_t size_in_bytes; - if (type_has_bits(type_entry)) { - size_in_bytes = LLVMStoreSizeOfType(g->target_data_ref, type_entry->type_ref); - } else { - size_in_bytes = 0; - } + uint64_t size_in_bytes = type_size(g, type_entry); return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type, size_in_bytes); } } diff --git a/src/analyze.hpp b/src/analyze.hpp index d2928dacba..44fc297f8d 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -18,6 +18,7 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool BlockContext *new_block_context(AstNode *node, BlockContext *parent); Expr *get_resolved_expr(AstNode *node); bool is_node_void_expr(AstNode *node); +uint64_t type_size(CodeGen *g, TypeTableEntry *type_entry); TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, bool is_wrapping, int size_in_bits); TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, bool is_wrapping, int size_in_bits); TypeTableEntry **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type); diff --git a/src/codegen.cpp b/src/codegen.cpp index a6c5cc919c..4d86e85df3 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -860,6 +860,43 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) { actual_type->data.array.len, false); LLVMBuildStore(g->builder, len_val, len_ptr); + return cast_expr->tmp_ptr; + } + case CastOpResizeSlice: + { + assert(cast_expr->tmp_ptr); + assert(wanted_type->id == TypeTableEntryIdStruct); + assert(wanted_type->data.structure.is_slice); + assert(actual_type->id == TypeTableEntryIdStruct); + assert(actual_type->data.structure.is_slice); + + TypeTableEntry *actual_pointer_type = actual_type->data.structure.fields[0].type_entry; + TypeTableEntry *actual_child_type = actual_pointer_type->data.pointer.child_type; + + set_debug_source_node(g, node); + + int actual_ptr_index = actual_type->data.structure.fields[0].gen_index; + int actual_len_index = actual_type->data.structure.fields[1].gen_index; + int wanted_ptr_index = wanted_type->data.structure.fields[0].gen_index; + int wanted_len_index = wanted_type->data.structure.fields[1].gen_index; + + LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, expr_val, actual_len_index, ""); + LLVMValueRef src_len = LLVMBuildLoad(g->builder, src_len_ptr, ""); + LLVMValueRef src_size = LLVMConstInt(g->builtin_types.entry_isize->type_ref, + type_size(g, actual_child_type), false); + LLVMValueRef new_len = LLVMBuildMul(g->builder, src_len, src_size, ""); + LLVMValueRef dest_len_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, + wanted_len_index, ""); + LLVMBuildStore(g->builder, new_len, dest_len_ptr); + + LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, expr_val, actual_ptr_index, ""); + LLVMValueRef src_ptr = LLVMBuildLoad(g->builder, src_ptr_ptr, ""); + LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, src_ptr, + wanted_type->data.structure.fields[0].type_entry->type_ref, ""); + LLVMValueRef dest_ptr_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, + wanted_ptr_index, ""); + LLVMBuildStore(g->builder, src_ptr_casted, dest_ptr_ptr); + return cast_expr->tmp_ptr; } case CastOpIntToFloat: diff --git a/src/eval.cpp b/src/eval.cpp index 8b7984b46c..56bf6a8577 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -600,6 +600,7 @@ void eval_const_expr_implicit_cast(CastOp cast_op, break; case CastOpPtrToInt: case CastOpIntToPtr: + case CastOpResizeSlice: // can't do it break; case CastOpToUnknownSizeArray: diff --git a/test/self_hosted.zig b/test/self_hosted.zig index d8e4563bf2..ac8ef41917 100644 --- a/test/self_hosted.zig +++ b/test/self_hosted.zig @@ -1580,3 +1580,17 @@ struct GenNode(T: type) { next: ?&GenNode(T), fn get_val(n: &const GenNode(T)) -> T { n.value } } + +#attribute("test") +fn cast_slice_to_u8_slice() { + assert(@sizeof(i32) == 4); + var big_thing_array = []i32{1, 2, 3, 4}; + const big_thing_slice: []i32 = big_thing_array; + const bytes = ([]u8)(big_thing_slice); + assert(bytes.len == 4 * 4); + bytes[4] = 0; + bytes[5] = 0; + bytes[6] = 0; + bytes[7] = 0; + assert(big_thing_slice[1] == 0); +}