diff --git a/src/analyze.cpp b/src/analyze.cpp index 9fe656ff77..040483a05e 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -359,28 +359,6 @@ uint64_t type_size(CodeGen *g, ZigType *type_entry) { return LLVMABISizeOfType(g->target_data_ref, type_entry->type_ref); } -uint64_t type_size_store(CodeGen *g, ZigType *type_entry) { - assert(type_is_complete(type_entry)); - - if (!type_has_bits(type_entry)) - return 0; - - if (type_entry->id == ZigTypeIdStruct && type_entry->data.structure.layout == ContainerLayoutPacked) { - uint64_t size_in_bits = type_size_bits(g, type_entry); - return (size_in_bits + 7) / 8; - } else if (type_entry->id == ZigTypeIdArray) { - ZigType *child_type = type_entry->data.array.child_type; - if (child_type->id == ZigTypeIdStruct && - child_type->data.structure.layout == ContainerLayoutPacked) - { - uint64_t size_in_bits = type_size_bits(g, type_entry); - return (size_in_bits + 7) / 8; - } - } - - return LLVMStoreSizeOfType(g->target_data_ref, type_entry->type_ref); -} - uint64_t type_size_bits(CodeGen *g, ZigType *type_entry) { assert(type_is_complete(type_entry)); diff --git a/src/analyze.hpp b/src/analyze.hpp index 956ef47309..39c1450678 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -19,7 +19,6 @@ ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const); ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_const, bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count); uint64_t type_size(CodeGen *g, ZigType *type_entry); -uint64_t type_size_store(CodeGen *g, ZigType *type_entry); uint64_t type_size_bits(CodeGen *g, ZigType *type_entry); ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits); ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type); diff --git a/src/codegen.cpp b/src/codegen.cpp index 86923a0bde..dbe0291299 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3070,7 +3070,10 @@ static LLVMValueRef ir_render_bit_cast(CodeGen *g, IrExecutable *executable, { ZigType *wanted_type = instruction->base.value.type; LLVMValueRef value = ir_llvm_value(g, instruction->value); - return LLVMBuildBitCast(g->builder, value, wanted_type->type_ref, ""); + // We either bitcast the value directly or bitcast the pointer which does a pointer cast + LLVMTypeRef wanted_type_ref = handle_is_ptr(wanted_type) ? + LLVMPointerType(wanted_type->type_ref, 0) : wanted_type->type_ref; + return LLVMBuildBitCast(g->builder, value, wanted_type_ref, ""); } static LLVMValueRef ir_render_widen_or_shorten(CodeGen *g, IrExecutable *executable, diff --git a/src/ir.cpp b/src/ir.cpp index 0eb1f2d533..177133788f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -198,10 +198,11 @@ static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *c result = &array_val->data.x_array.data.s_none.elements[const_val->data.x_ptr.data.base_array.elem_index]; break; } - case ConstPtrSpecialBaseStruct: - result = &const_val->data.x_ptr.data.base_struct.struct_val->data.x_struct.fields[ - const_val->data.x_ptr.data.base_struct.field_index]; + case ConstPtrSpecialBaseStruct: { + ConstExprValue *struct_val = const_val->data.x_ptr.data.base_struct.struct_val; + result = &struct_val->data.x_struct.fields[const_val->data.x_ptr.data.base_struct.field_index]; break; + } case ConstPtrSpecialBaseErrorUnionCode: result = const_val->data.x_ptr.data.base_err_union_code.err_union_val->data.x_err_union.error_set; break; @@ -230,20 +231,55 @@ static bool is_opt_err_set(ZigType *ty) { (ty->id == ZigTypeIdOptional && ty->data.maybe.child_type->id == ZigTypeIdErrorSet); } +// This function returns true when you can change the type of a ConstExprValue and the +// value remains meaningful. static bool types_have_same_zig_comptime_repr(ZigType *a, ZigType *b) { if (a == b) return true; - if (a->id == b->id) - return true; - if (get_codegen_ptr_type(a) != nullptr && get_codegen_ptr_type(b) != nullptr) return true; if (is_opt_err_set(a) && is_opt_err_set(b)) return true; - return false; + if (a->id != b->id) + return false; + + switch (a->id) { + case ZigTypeIdInvalid: + case ZigTypeIdUnreachable: + zig_unreachable(); + case ZigTypeIdMetaType: + case ZigTypeIdVoid: + case ZigTypeIdBool: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdPointer: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdNamespace: + case ZigTypeIdBoundFn: + case ZigTypeIdErrorSet: + case ZigTypeIdOpaque: + return true; + case ZigTypeIdFloat: + return a->data.floating.bit_count == b->data.floating.bit_count; + case ZigTypeIdInt: + return a->data.integral.is_signed == b->data.integral.is_signed; + case ZigTypeIdArray: + case ZigTypeIdStruct: + case ZigTypeIdOptional: + case ZigTypeIdErrorUnion: + case ZigTypeIdEnum: + case ZigTypeIdUnion: + case ZigTypeIdFn: + case ZigTypeIdArgTuple: + case ZigTypeIdPromise: + case ZigTypeIdVector: + return false; + } + zig_unreachable(); } static bool ir_should_inline(IrExecutable *exec, Scope *scope) { @@ -14421,12 +14457,11 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source if ((err = type_resolve(codegen, out_val->type, ResolveStatusSizeKnown))) return ErrorSemanticAnalyzeFail; - // We don't need to read the padding bytes, so we look at type_size_store bytes - size_t src_size = type_size_store(codegen, pointee->type); - size_t dst_size = type_size_store(codegen, out_val->type); + size_t src_size = type_size(codegen, pointee->type); + size_t dst_size = type_size(codegen, out_val->type); if (dst_size <= src_size) { - if (types_have_same_zig_comptime_repr(pointee->type, out_val->type)) { + if (src_size == dst_size && types_have_same_zig_comptime_repr(pointee->type, out_val->type)) { copy_const_val(out_val, pointee, ptr_val->data.x_ptr.mut == ConstPtrMutComptimeConst); return ErrorNone; } @@ -20885,7 +20920,68 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue case ZigTypeIdVector: return buf_write_value_bytes_array(codegen, buf, val, val->type->data.vector.len); case ZigTypeIdStruct: - zig_panic("TODO buf_write_value_bytes struct type"); + switch (val->type->data.structure.layout) { + case ContainerLayoutAuto: + zig_unreachable(); + case ContainerLayoutExtern: + zig_panic("TODO buf_write_value_bytes extern struct"); + case ContainerLayoutPacked: { + size_t src_field_count = val->type->data.structure.src_field_count; + size_t gen_field_count = val->type->data.structure.gen_field_count; + size_t gen_i = 0; + size_t src_i = 0; + size_t offset = 0; + bool is_big_endian = codegen->is_big_endian; + uint8_t child_buf_prealloc[16]; + size_t child_buf_len = 16; + uint8_t *child_buf = child_buf_prealloc; + while (gen_i < gen_field_count) { + LLVMTypeRef gen_llvm_int_type = LLVMStructGetTypeAtIndex(val->type->type_ref, + (unsigned)gen_i); + size_t big_int_bit_count = LLVMGetIntTypeWidth(gen_llvm_int_type); + size_t big_int_byte_count = big_int_bit_count / 8; + if (big_int_byte_count > child_buf_len) { + child_buf = allocate_nonzero(big_int_byte_count); + child_buf_len = big_int_byte_count; + } + BigInt big_int; + bigint_init_unsigned(&big_int, 0); + size_t used_bits = 0; + while (src_i < src_field_count) { + TypeStructField *field = &val->type->data.structure.fields[src_i]; + assert(field->gen_index != SIZE_MAX); + if (field->gen_index != gen_i) + break; + uint32_t packed_bits_size = type_size_bits(codegen, field->type_entry); + buf_write_value_bytes(codegen, child_buf, &val->data.x_struct.fields[src_i]); + BigInt child_val; + bigint_read_twos_complement(&child_val, child_buf, packed_bits_size, is_big_endian, + false); + if (is_big_endian) { + BigInt shift_amt; + bigint_init_unsigned(&shift_amt, packed_bits_size); + BigInt shifted; + bigint_shl(&shifted, &big_int, &shift_amt); + bigint_or(&big_int, &shifted, &child_val); + } else { + BigInt shift_amt; + bigint_init_unsigned(&shift_amt, used_bits); + BigInt child_val_shifted; + bigint_shl(&child_val_shifted, &child_val, &shift_amt); + BigInt tmp; + bigint_or(&tmp, &big_int, &child_val_shifted); + big_int = tmp; + used_bits += packed_bits_size; + } + src_i += 1; + } + bigint_write_twos_complement(&big_int, buf + offset, big_int_bit_count, is_big_endian); + offset += big_int_byte_count; + gen_i += 1; + } + } + } + return; case ZigTypeIdOptional: zig_panic("TODO buf_write_value_bytes maybe type"); case ZigTypeIdErrorUnion: @@ -21012,8 +21108,62 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou } return ErrorNone; } - case ContainerLayoutPacked: - zig_panic("TODO buf_read_value_bytes packed struct"); + case ContainerLayoutPacked: { + size_t src_field_count = val->type->data.structure.src_field_count; + val->data.x_struct.fields = create_const_vals(src_field_count); + size_t gen_field_count = val->type->data.structure.gen_field_count; + size_t gen_i = 0; + size_t src_i = 0; + size_t offset = 0; + bool is_big_endian = codegen->is_big_endian; + uint8_t child_buf_prealloc[16]; + size_t child_buf_len = 16; + uint8_t *child_buf = child_buf_prealloc; + while (gen_i < gen_field_count) { + LLVMTypeRef gen_llvm_int_type = LLVMStructGetTypeAtIndex(val->type->type_ref, + (unsigned)gen_i); + size_t big_int_bit_count = LLVMGetIntTypeWidth(gen_llvm_int_type); + size_t big_int_byte_count = big_int_bit_count / 8; + if (big_int_byte_count > child_buf_len) { + child_buf = allocate_nonzero(big_int_byte_count); + child_buf_len = big_int_byte_count; + } + BigInt big_int; + bigint_read_twos_complement(&big_int, buf + offset, big_int_bit_count, is_big_endian, false); + while (src_i < src_field_count) { + TypeStructField *field = &val->type->data.structure.fields[src_i]; + assert(field->gen_index != SIZE_MAX); + if (field->gen_index != gen_i) + break; + ConstExprValue *field_val = &val->data.x_struct.fields[src_i]; + field_val->special = ConstValSpecialStatic; + field_val->type = field->type_entry; + uint32_t packed_bits_size = type_size_bits(codegen, field->type_entry); + + BigInt child_val; + if (is_big_endian) { + zig_panic("TODO buf_read_value_bytes packed struct big endian"); + } else { + BigInt packed_bits_size_bi; + bigint_init_unsigned(&packed_bits_size_bi, packed_bits_size); + bigint_truncate(&child_val, &big_int, packed_bits_size, false); + BigInt tmp; + bigint_shr(&tmp, &big_int, &packed_bits_size_bi); + big_int = tmp; + } + + bigint_write_twos_complement(&child_val, child_buf, big_int_bit_count, is_big_endian); + if ((err = buf_read_value_bytes(ira, codegen, source_node, child_buf, field_val))) { + return err; + } + + src_i += 1; + } + offset += big_int_byte_count; + gen_i += 1; + } + return ErrorNone; + } } zig_unreachable(); case ZigTypeIdOptional: diff --git a/test/compile_errors.zig b/test/compile_errors.zig index a9c748bcda..07e677b8ed 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -318,12 +318,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "reading past end of pointer casted array", \\comptime { \\ const array = "aoeu"; - \\ const slice = array[2..]; + \\ const slice = array[1..]; \\ const int_ptr = @ptrCast(*const u24, slice.ptr); \\ const deref = int_ptr.*; \\} , - ".tmp_source.zig:5:26: error: attempt to read 3 bytes from [4]u8 at index 2 which is 2 bytes", + ".tmp_source.zig:5:26: error: attempt to read 4 bytes from [4]u8 at index 1 which is 3 bytes", ); cases.add( diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index df311637fa..708bb91ac0 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -11,6 +11,7 @@ comptime { _ = @import("behavior/bswap.zig"); _ = @import("behavior/bugs/1076.zig"); _ = @import("behavior/bugs/1111.zig"); + _ = @import("behavior/bugs/1120.zig"); _ = @import("behavior/bugs/1277.zig"); _ = @import("behavior/bugs/1322.zig"); _ = @import("behavior/bugs/1381.zig"); diff --git a/test/stage1/behavior/bitcast.zig b/test/stage1/behavior/bitcast.zig index 9607d2e3ef..e498905f4e 100644 --- a/test/stage1/behavior/bitcast.zig +++ b/test/stage1/behavior/bitcast.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const builtin = @import("builtin"); const expect = std.testing.expect; const maxInt = std.math.maxInt; @@ -34,3 +35,34 @@ test "@bitCast extern enum to its integer type" { SOCK.testBitCastExternEnum(); comptime SOCK.testBitCastExternEnum(); } + +test "@bitCast packed structs at runtime and comptime" { + const Full = packed struct { + number: u16, + }; + const Divided = packed struct { + half1: u8, + quarter3: u4, + quarter4: u4, + }; + const S = struct { + fn doTheTest() void { + var full = Full{ .number = 0x1234 }; + var two_halves = @bitCast(Divided, full); + switch (builtin.endian) { + builtin.Endian.Big => { + expect(two_halves.half1 == 0x12); + expect(two_halves.quarter3 == 0x3); + expect(two_halves.quarter4 == 0x4); + }, + builtin.Endian.Little => { + expect(two_halves.half1 == 0x34); + expect(two_halves.quarter3 == 0x2); + expect(two_halves.quarter4 == 0x1); + }, + } + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} diff --git a/test/stage1/behavior/bugs/1120.zig b/test/stage1/behavior/bugs/1120.zig new file mode 100644 index 0000000000..dda46e8e1c --- /dev/null +++ b/test/stage1/behavior/bugs/1120.zig @@ -0,0 +1,23 @@ +const std = @import("std"); +const expect = std.testing.expect; + +const A = packed struct { + a: u2, + b: u6, +}; +const B = packed struct { + q: u8, + a: u2, + b: u6, +}; +test "bug 1120" { + var a = A{ .a = 2, .b = 2 }; + var b = B{ .q = 22, .a = 3, .b = 2 }; + var t: usize = 0; + const ptr = switch (t) { + 0 => &a.a, + 1 => &b.a, + else => unreachable, + }; + expect(ptr.* == 2); +}