authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-15 18:05:50-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-15 18:05:50-05:00
log7293e012d7956b892380517e914108ffadc6941b
tree420049c2174484e2c536d2e36b5b1bb906b921b3
parent567c9b688effdb64e3995df09af4b45105515c2c
signaturelock-open Commit is signed but in an unrecognized format.

breaking: fix @sizeOf to be alloc size rather than store size

* Fixes breaches of the guarantee that `@sizeOf(T) >= @alignOf(T)` * Fixes std.mem.secureZero for integers where this guarantee previously was breached * Fixes std.mem.Allocator for integers where this guarantee previously was breached Closes #1851 Closes #1864

8 files changed, 112 insertions(+), 51 deletions(-)

doc/langref.html.in+6-1
......@@ -6299,10 +6299,15 @@ pub const FloatMode = enum {
62996299 <pre>{#syntax#}@sizeOf(comptime T: type) comptime_int{#endsyntax#}</pre>
63006300 <p>
63016301 This function returns the number of bytes it takes to store {#syntax#}T{#endsyntax#} in memory.
6302 The result is a target-specific compile time constant.
63026303 </p>
63036304 <p>
6304 The result is a target-specific compile time constant.
6305 This size may contain padding bytes. If there were two consecutive T in memory, this would be the offset
6306 in bytes between element at index 0 and the element at index 1. For {#link|integer|Integers#},
6307 consider whether you want to use {#syntax#}@sizeOf(T){#endsyntax#} or
6308 {#syntax#}@typeInfo(T).Int.bits{#endsyntax#}.
63056309 </p>
6310 {#see_also|@typeInfo#}
63066311 {#header_close#}
63076312
63086313 {#header_open|@sliceToBytes#}
src/analyze.cpp+32-5
......@@ -356,6 +356,28 @@ uint64_t type_size(CodeGen *g, ZigType *type_entry) {
356356 }
357357 }
358358
359 return LLVMABISizeOfType(g->target_data_ref, type_entry->type_ref);
360}
361
362uint64_t type_size_store(CodeGen *g, ZigType *type_entry) {
363 assert(type_is_complete(type_entry));
364
365 if (!type_has_bits(type_entry))
366 return 0;
367
368 if (type_entry->id == ZigTypeIdStruct && type_entry->data.structure.layout == ContainerLayoutPacked) {
369 uint64_t size_in_bits = type_size_bits(g, type_entry);
370 return (size_in_bits + 7) / 8;
371 } else if (type_entry->id == ZigTypeIdArray) {
372 ZigType *child_type = type_entry->data.array.child_type;
373 if (child_type->id == ZigTypeIdStruct &&
374 child_type->data.structure.layout == ContainerLayoutPacked)
375 {
376 uint64_t size_in_bits = type_size_bits(g, type_entry);
377 return (size_in_bits + 7) / 8;
378 }
379 }
380
359381 return LLVMStoreSizeOfType(g->target_data_ref, type_entry->type_ref);
360382}
361383
......@@ -6230,14 +6252,19 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
62306252 case ZigTypeIdStruct:
62316253 {
62326254 if (is_slice(type_entry)) {
6233 ConstPtrValue *ptr = &const_val->data.x_struct.fields[slice_ptr_index].data.x_ptr;
6234 assert(ptr->special == ConstPtrSpecialBaseArray);
6235 ConstExprValue *array = ptr->data.base_array.array_val;
6236 size_t start = ptr->data.base_array.elem_index;
6237
62386255 ConstExprValue *len_val = &const_val->data.x_struct.fields[slice_len_index];
62396256 size_t len = bigint_as_unsigned(&len_val->data.x_bigint);
62406257
6258 ConstExprValue *ptr_val = &const_val->data.x_struct.fields[slice_ptr_index];
6259 if (ptr_val->special == ConstValSpecialUndef) {
6260 assert(len == 0);
6261 buf_appendf(buf, "((%s)(undefined))[0..0]", buf_ptr(&type_entry->name));
6262 return;
6263 }
6264 assert(ptr_val->data.x_ptr.special == ConstPtrSpecialBaseArray);
6265 ConstExprValue *array = ptr_val->data.x_ptr.data.base_array.array_val;
6266 size_t start = ptr_val->data.x_ptr.data.base_array.elem_index;
6267
62416268 render_const_val_array(g, buf, &type_entry->name, array, start, len);
62426269 } else {
62436270 buf_appendf(buf, "(struct %s constant)", buf_ptr(&type_entry->name));
src/analyze.hpp+1
......@@ -19,6 +19,7 @@ ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const);
1919ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_const,
2020 bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count);
2121uint64_t type_size(CodeGen *g, ZigType *type_entry);
22uint64_t type_size_store(CodeGen *g, ZigType *type_entry);
2223uint64_t type_size_bits(CodeGen *g, ZigType *type_entry);
2324ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits);
2425ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type);
src/ir.cpp+20-8
......@@ -14331,15 +14331,15 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source
1433114331 if ((err = type_resolve(codegen, out_val->type, ResolveStatusSizeKnown)))
1433214332 return ErrorSemanticAnalyzeFail;
1433314333
14334 size_t src_size = type_size(codegen, pointee->type);
14335 size_t dst_size = type_size(codegen, out_val->type);
14336
14337 if (src_size == dst_size && types_have_same_zig_comptime_repr(pointee->type, out_val->type)) {
14338 copy_const_val(out_val, pointee, ptr_val->data.x_ptr.mut == ConstPtrMutComptimeConst);
14339 return ErrorNone;
14340 }
14334 // We don't need to read the padding bytes, so we look at type_size_store bytes
14335 size_t src_size = type_size_store(codegen, pointee->type);
14336 size_t dst_size = type_size_store(codegen, out_val->type);
1434114337
1434214338 if (dst_size <= src_size) {
14339 if (types_have_same_zig_comptime_repr(pointee->type, out_val->type)) {
14340 copy_const_val(out_val, pointee, ptr_val->data.x_ptr.mut == ConstPtrMutComptimeConst);
14341 return ErrorNone;
14342 }
1434314343 Buf buf = BUF_INIT;
1434414344 buf_resize(&buf, src_size);
1434514345 buf_write_value_bytes(codegen, (uint8_t*)buf_ptr(&buf), pointee);
......@@ -15798,6 +15798,8 @@ static IrInstruction *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructio
1579815798static IrInstruction *ir_analyze_instruction_to_ptr_type(IrAnalyze *ira,
1579915799 IrInstructionToPtrType *to_ptr_type_instruction)
1580015800{
15801 Error err;
15802
1580115803 IrInstruction *value = to_ptr_type_instruction->value->child;
1580215804 ZigType *type_entry = value->value.type;
1580315805 if (type_is_invalid(type_entry))
......@@ -15813,7 +15815,17 @@ static IrInstruction *ir_analyze_instruction_to_ptr_type(IrAnalyze *ira,
1581315815 ptr_type = get_pointer_to_type(ira->codegen,
1581415816 type_entry->data.pointer.child_type->data.array.child_type, type_entry->data.pointer.is_const);
1581515817 } else if (is_slice(type_entry)) {
15816 ptr_type = adjust_ptr_len(ira->codegen, type_entry->data.structure.fields[0].type_entry, PtrLenSingle);
15818 ZigType *slice_ptr_type = type_entry->data.structure.fields[0].type_entry;
15819 ptr_type = adjust_ptr_len(ira->codegen, slice_ptr_type, PtrLenSingle);
15820 // If the pointer is over-aligned, we may have to reduce it based on the alignment of the element type.
15821 if (slice_ptr_type->data.pointer.explicit_alignment != 0) {
15822 ZigType *elem_type = slice_ptr_type->data.pointer.child_type;
15823 if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusAlignmentKnown)))
15824 return ira->codegen->invalid_instruction;
15825 uint32_t elem_align = get_abi_alignment(ira->codegen, elem_type);
15826 uint32_t reduced_align = min(elem_align, slice_ptr_type->data.pointer.explicit_alignment);
15827 ptr_type = adjust_ptr_align(ira->codegen, ptr_type, reduced_align);
15828 }
1581715829 } else if (type_entry->id == ZigTypeIdArgTuple) {
1581815830 ConstExprValue *arg_tuple_val = ir_resolve_const(ira, value, UndefBad);
1581915831 if (!arg_tuple_val)
std/io.zig+5-8
......@@ -935,8 +935,6 @@ pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type {
935935 };
936936}
937937
938
939
940938pub const BufferedAtomicFile = struct {
941939 atomic_file: os.AtomicFile,
942940 file_stream: os.File.OutStream,
......@@ -978,7 +976,6 @@ pub const BufferedAtomicFile = struct {
978976 }
979977};
980978
981
982979pub fn readLine(buf: *std.Buffer) ![]u8 {
983980 var stdin = try getStdIn();
984981 var stdin_stream = stdin.inStream();
......@@ -1073,13 +1070,13 @@ pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime E
10731070 else => in_stream,
10741071 } };
10751072 }
1076
1073
10771074 pub fn alignToByte(self: *Self) void {
1078 if(!is_packed) return;
1075 if (!is_packed) return;
10791076 self.in_stream.alignToByte();
10801077 }
10811078
1082 //@BUG: inferred error issue. See: #1386
1079 //@BUG: inferred error issue. See: #1386
10831080 fn deserializeInt(self: *Self, comptime T: type) (Error || error{EndOfStream})!T {
10841081 comptime assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T));
10851082
......@@ -1088,7 +1085,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime E
10881085
10891086 const U = @IntType(false, t_bit_count);
10901087 const Log2U = math.Log2Int(U);
1091 const int_size = @sizeOf(U);
1088 const int_size = (U.bit_count + 7) / 8;
10921089
10931090 if (is_packed) {
10941091 const result = try self.in_stream.readBitsNoEof(U, t_bit_count);
......@@ -1301,7 +1298,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, com
13011298
13021299 const U = @IntType(false, t_bit_count);
13031300 const Log2U = math.Log2Int(U);
1304 const int_size = @sizeOf(U);
1301 const int_size = (U.bit_count + 7) / 8;
13051302
13061303 const u_value = @bitCast(U, value);
13071304
std/mem.zig+20-29
......@@ -423,8 +423,7 @@ pub fn readVarInt(comptime ReturnType: type, bytes: []const u8, endian: builtin.
423423/// This function cannot fail and cannot cause undefined behavior.
424424/// Assumes the endianness of memory is native. This means the function can
425425/// simply pointer cast memory.
426pub fn readIntNative(comptime T: type, bytes: *const [@sizeOf(T)]u8) T {
427 comptime assert(T.bit_count % 8 == 0);
426pub fn readIntNative(comptime T: type, bytes: *const [@divExact(T.bit_count, 8)]u8) T {
428427 return @ptrCast(*align(1) const T, bytes).*;
429428}
430429
......@@ -432,7 +431,7 @@ pub fn readIntNative(comptime T: type, bytes: *const [@sizeOf(T)]u8) T {
432431/// The bit count of T must be evenly divisible by 8.
433432/// This function cannot fail and cannot cause undefined behavior.
434433/// Assumes the endianness of memory is foreign, so it must byte-swap.
435pub fn readIntForeign(comptime T: type, bytes: *const [@sizeOf(T)]u8) T {
434pub fn readIntForeign(comptime T: type, bytes: *const [@divExact(T.bit_count, 8)]u8) T {
436435 return @bswap(T, readIntNative(T, bytes));
437436}
438437
......@@ -446,22 +445,20 @@ pub const readIntBig = switch (builtin.endian) {
446445 builtin.Endian.Big => readIntNative,
447446};
448447
449/// Asserts that bytes.len >= @sizeOf(T). Reads the integer starting from index 0
448/// Asserts that bytes.len >= T.bit_count / 8. Reads the integer starting from index 0
450449/// and ignores extra bytes.
451/// Note that @sizeOf(u24) is 3.
452450/// The bit count of T must be evenly divisible by 8.
453451/// Assumes the endianness of memory is native. This means the function can
454452/// simply pointer cast memory.
455453pub fn readIntSliceNative(comptime T: type, bytes: []const u8) T {
456 assert(@sizeOf(u24) == 3);
457 assert(bytes.len >= @sizeOf(T));
454 const n = @divExact(T.bit_count, 8);
455 assert(bytes.len >= n);
458456 // TODO https://github.com/ziglang/zig/issues/863
459 return readIntNative(T, @ptrCast(*const [@sizeOf(T)]u8, bytes.ptr));
457 return readIntNative(T, @ptrCast(*const [n]u8, bytes.ptr));
460458}
461459
462/// Asserts that bytes.len >= @sizeOf(T). Reads the integer starting from index 0
460/// Asserts that bytes.len >= T.bit_count / 8. Reads the integer starting from index 0
463461/// and ignores extra bytes.
464/// Note that @sizeOf(u24) is 3.
465462/// The bit count of T must be evenly divisible by 8.
466463/// Assumes the endianness of memory is foreign, so it must byte-swap.
467464pub fn readIntSliceForeign(comptime T: type, bytes: []const u8) T {
......@@ -481,7 +478,7 @@ pub const readIntSliceBig = switch (builtin.endian) {
481478/// Reads an integer from memory with bit count specified by T.
482479/// The bit count of T must be evenly divisible by 8.
483480/// This function cannot fail and cannot cause undefined behavior.
484pub fn readInt(comptime T: type, bytes: *const [@sizeOf(T)]u8, endian: builtin.Endian) T {
481pub fn readInt(comptime T: type, bytes: *const [@divExact(T.bit_count, 8)]u8, endian: builtin.Endian) T {
485482 if (endian == builtin.endian) {
486483 return readIntNative(T, bytes);
487484 } else {
......@@ -489,15 +486,14 @@ pub fn readInt(comptime T: type, bytes: *const [@sizeOf(T)]u8, endian: builtin.E
489486 }
490487}
491488
492/// Asserts that bytes.len >= @sizeOf(T). Reads the integer starting from index 0
489/// Asserts that bytes.len >= T.bit_count / 8. Reads the integer starting from index 0
493490/// and ignores extra bytes.
494/// Note that @sizeOf(u24) is 3.
495491/// The bit count of T must be evenly divisible by 8.
496492pub fn readIntSlice(comptime T: type, bytes: []const u8, endian: builtin.Endian) T {
497 assert(@sizeOf(u24) == 3);
498 assert(bytes.len >= @sizeOf(T));
493 const n = @divExact(T.bit_count, 8);
494 assert(bytes.len >= n);
499495 // TODO https://github.com/ziglang/zig/issues/863
500 return readInt(T, @ptrCast(*const [@sizeOf(T)]u8, bytes.ptr), endian);
496 return readInt(T, @ptrCast(*const [n]u8, bytes.ptr), endian);
501497}
502498
503499test "comptime read/write int" {
......@@ -540,7 +536,7 @@ test "readIntBig and readIntLittle" {
540536/// accepts any integer bit width.
541537/// This function stores in native endian, which means it is implemented as a simple
542538/// memory store.
543pub fn writeIntNative(comptime T: type, buf: *[@sizeOf(T)]u8, value: T) void {
539pub fn writeIntNative(comptime T: type, buf: *[(T.bit_count + 7) / 8]u8, value: T) void {
544540 @ptrCast(*align(1) T, buf).* = value;
545541}
546542
......@@ -548,7 +544,7 @@ pub fn writeIntNative(comptime T: type, buf: *[@sizeOf(T)]u8, value: T) void {
548544/// This function always succeeds, has defined behavior for all inputs, but
549545/// the integer bit width must be divisible by 8.
550546/// This function stores in foreign endian, which means it does a @bswap first.
551pub fn writeIntForeign(comptime T: type, buf: *[@sizeOf(T)]u8, value: T) void {
547pub fn writeIntForeign(comptime T: type, buf: *[@divExact(T.bit_count, 8)]u8, value: T) void {
552548 writeIntNative(T, buf, @bswap(T, value));
553549}
554550
......@@ -565,8 +561,7 @@ pub const writeIntBig = switch (builtin.endian) {
565561/// Writes an integer to memory, storing it in twos-complement.
566562/// This function always succeeds, has defined behavior for all inputs, but
567563/// the integer bit width must be divisible by 8.
568pub fn writeInt(comptime T: type, buffer: *[@sizeOf(T)]u8, value: T, endian: builtin.Endian) void {
569 comptime assert(T.bit_count % 8 == 0);
564pub fn writeInt(comptime T: type, buffer: *[@divExact(T.bit_count, 8)]u8, value: T, endian: builtin.Endian) void {
570565 if (endian == builtin.endian) {
571566 return writeIntNative(T, buffer, value);
572567 } else {
......@@ -575,15 +570,13 @@ pub fn writeInt(comptime T: type, buffer: *[@sizeOf(T)]u8, value: T, endian: bui
575570}
576571
577572/// Writes a twos-complement little-endian integer to memory.
578/// Asserts that buf.len >= @sizeOf(T). Note that @sizeOf(u24) is 3.
573/// Asserts that buf.len >= T.bit_count / 8.
579574/// The bit count of T must be divisible by 8.
580575/// Any extra bytes in buffer after writing the integer are set to zero. To
581576/// avoid the branch to check for extra buffer bytes, use writeIntLittle
582577/// instead.
583578pub fn writeIntSliceLittle(comptime T: type, buffer: []u8, value: T) void {
584 comptime assert(@sizeOf(u24) == 3);
585 comptime assert(T.bit_count % 8 == 0);
586 assert(buffer.len >= @sizeOf(T));
579 assert(buffer.len >= @divExact(T.bit_count, 8));
587580
588581 // TODO I want to call writeIntLittle here but comptime eval facilities aren't good enough
589582 const uint = @IntType(false, T.bit_count);
......@@ -595,14 +588,12 @@ pub fn writeIntSliceLittle(comptime T: type, buffer: []u8, value: T) void {
595588}
596589
597590/// Writes a twos-complement big-endian integer to memory.
598/// Asserts that buffer.len >= @sizeOf(T). Note that @sizeOf(u24) is 3.
591/// Asserts that buffer.len >= T.bit_count / 8.
599592/// The bit count of T must be divisible by 8.
600593/// Any extra bytes in buffer before writing the integer are set to zero. To
601594/// avoid the branch to check for extra buffer bytes, use writeIntBig instead.
602595pub fn writeIntSliceBig(comptime T: type, buffer: []u8, value: T) void {
603 comptime assert(@sizeOf(u24) == 3);
604 comptime assert(T.bit_count % 8 == 0);
605 assert(buffer.len >= @sizeOf(T));
596 assert(buffer.len >= @divExact(T.bit_count, 8));
606597
607598 // TODO I want to call writeIntBig here but comptime eval facilities aren't good enough
608599 const uint = @IntType(false, T.bit_count);
......@@ -626,7 +617,7 @@ pub const writeIntSliceForeign = switch (builtin.endian) {
626617};
627618
628619/// Writes a twos-complement integer to memory, with the specified endianness.
629/// Asserts that buf.len >= @sizeOf(T). Note that @sizeOf(u24) is 3.
620/// Asserts that buf.len >= T.bit_count / 8.
630621/// The bit count of T must be evenly divisible by 8.
631622/// Any extra bytes in buffer not part of the integer are set to zero, with
632623/// respect to endianness. To avoid the branch to check for extra buffer bytes,
test/stage1/behavior.zig+1
......@@ -17,6 +17,7 @@ comptime {
1717 _ = @import("behavior/bugs/1421.zig");
1818 _ = @import("behavior/bugs/1442.zig");
1919 _ = @import("behavior/bugs/1486.zig");
20 _ = @import("behavior/bugs/1851.zig");
2021 _ = @import("behavior/bugs/394.zig");
2122 _ = @import("behavior/bugs/655.zig");
2223 _ = @import("behavior/bugs/656.zig");
test/stage1/behavior/bugs/1851.zig created+27
......@@ -0,0 +1,27 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "allocation and looping over 3-byte integer" {
5 expect(@sizeOf(u24) == 4);
6 expect(@sizeOf([1]u24) == 4);
7 expect(@alignOf(u24) == 4);
8 expect(@alignOf([1]u24) == 4);
9 var buffer: [100]u8 = undefined;
10 const a = &std.heap.FixedBufferAllocator.init(&buffer).allocator;
11
12 var x = a.alloc(u24, 2) catch unreachable;
13 expect(x.len == 2);
14 x[0] = 0xFFFFFF;
15 x[1] = 0xFFFFFF;
16
17 const bytes = @sliceToBytes(x);
18 expect(@typeOf(bytes) == []align(4) u8);
19 expect(bytes.len == 8);
20
21 for (bytes) |*b| {
22 b.* = 0x00;
23 }
24
25 expect(x[0] == 0x00);
26 expect(x[1] == 0x00);
27}