From aa60d2a688c965dcccf8e2c42afe5c180daba8fc Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Sun, 16 Jun 2019 02:10:06 +0200 Subject: [PATCH 01/13] fixes resolving aligment of child type in slice --- src/ir.cpp | 2 +- test/stage1/behavior.zig | 1 + test/stage1/behavior/bugs/2689.zig | 7 +++++++ 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 test/stage1/behavior/bugs/2689.zig diff --git a/src/ir.cpp b/src/ir.cpp index 6c1c84da3..0936f78df 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16794,7 +16794,7 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira, case ZigTypeIdPromise: case ZigTypeIdVector: { - if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown))) + if ((err = type_resolve(ira->codegen, child_type, ResolveStatusAlignmentKnown))) return ira->codegen->invalid_instruction; ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, child_type, is_const, is_volatile, PtrLenUnknown, align_bytes, 0, 0, is_allow_zero); diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index f477bb64e..707d46fd8 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -27,6 +27,7 @@ comptime { _ = @import("behavior/bugs/2114.zig"); _ = @import("behavior/bugs/2346.zig"); _ = @import("behavior/bugs/2578.zig"); + _ = @import("behavior/bugs/2689.zig"); _ = @import("behavior/bugs/394.zig"); _ = @import("behavior/bugs/421.zig"); _ = @import("behavior/bugs/529.zig"); diff --git a/test/stage1/behavior/bugs/2689.zig b/test/stage1/behavior/bugs/2689.zig new file mode 100644 index 000000000..d1c1062ad --- /dev/null +++ b/test/stage1/behavior/bugs/2689.zig @@ -0,0 +1,7 @@ +test "slice with alignment" { + const S = packed struct { + a: u8, + }; + + var a: []align(8) S = undefined; +} From f4b8850002d5f617450a9d08b05519f390bd3f21 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 16 Jun 2019 14:14:57 -0400 Subject: [PATCH 02/13] fix type info crash on extern lib name --- src/ir.cpp | 5 +++-- test/stage1/behavior/type_info.zig | 27 +++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 0936f78df..5c09e48b2 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -18613,10 +18613,11 @@ static Error ir_make_type_info_decls(IrAnalyze *ira, IrInstruction *source_instr true, false, PtrLenUnknown, 0, 0, 0, false); fn_decl_fields[6].type = get_optional_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr)); - if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0) { + if (fn_node->is_extern && fn_node->lib_name != nullptr && buf_len(fn_node->lib_name) > 0) { fn_decl_fields[6].data.x_optional = create_const_vals(1); ConstExprValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name); - init_const_slice(ira->codegen, fn_decl_fields[6].data.x_optional, lib_name, 0, buf_len(fn_node->lib_name), true); + init_const_slice(ira->codegen, fn_decl_fields[6].data.x_optional, lib_name, 0, + buf_len(fn_node->lib_name), true); } else { fn_decl_fields[6].data.x_optional = nullptr; } diff --git a/test/stage1/behavior/type_info.zig b/test/stage1/behavior/type_info.zig index f05b02e7a..4ae81aff2 100644 --- a/test/stage1/behavior/type_info.zig +++ b/test/stage1/behavior/type_info.zig @@ -1,7 +1,9 @@ -const expect = @import("std").testing.expect; -const mem = @import("std").mem; -const TypeInfo = @import("builtin").TypeInfo; -const TypeId = @import("builtin").TypeId; +const std = @import("std"); +const expect = std.testing.expect; +const mem = std.mem; +const builtin = @import("builtin"); +const TypeInfo = builtin.TypeInfo; +const TypeId = builtin.TypeId; test "type info: tag type, void info" { testBasic(); @@ -317,3 +319,20 @@ test "type info: TypeId -> TypeInfo impl cast" { _ = passTypeInfo(TypeId.Void); _ = comptime passTypeInfo(TypeId.Void); } + +test "type info: extern fns with and without lib names" { + const S = struct { + extern fn bar1() void; + extern "cool" fn bar2() void; + }; + const info = @typeInfo(S); + comptime { + for (info.Struct.decls) |decl| { + if (std.mem.eql(u8, decl.name, "bar1")) { + expect(decl.data.Fn.lib_name == null); + } else { + std.testing.expectEqual(([]const u8)("cool"), decl.data.Fn.lib_name.?); + } + } + } +} From 4d3356435f453bdc24ac3c6c2da74d98c981db15 Mon Sep 17 00:00:00 2001 From: Shawn Landden Date: Sun, 16 Jun 2019 13:17:33 -0500 Subject: [PATCH 03/13] stage1: check for null in buf_len and buf_ptr follow up for f4b8850002d5 --- src/buffer.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/buffer.hpp b/src/buffer.hpp index 082d584e2..d4a911fc2 100644 --- a/src/buffer.hpp +++ b/src/buffer.hpp @@ -27,11 +27,13 @@ Buf *buf_sprintf(const char *format, ...) Buf *buf_vprintf(const char *format, va_list ap); static inline size_t buf_len(Buf *buf) { + assert(buf); assert(buf->list.length); return buf->list.length - 1; } static inline char *buf_ptr(Buf *buf) { + assert(buf); assert(buf->list.length); return buf->list.items; } From 72029c2fc8083b1c0f2501cfc9e24d3fca4d66c2 Mon Sep 17 00:00:00 2001 From: Timon Kruiper Date: Thu, 13 Jun 2019 15:44:50 +0200 Subject: [PATCH 04/13] Added HashInt to function calls AutoHash\nFixes issue 2669 --- std/hash_map.zig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/std/hash_map.zig b/std/hash_map.zig index df7ba740e..13ebb0a1f 100644 --- a/std/hash_map.zig +++ b/std/hash_map.zig @@ -564,12 +564,12 @@ pub fn autoHash(key: var, comptime rng: *std.rand.Random, comptime HashInt: type }, builtin.TypeId.Float => |info| { - return autoHash(@bitCast(@IntType(false, info.bits), key), rng); + return autoHash(@bitCast(@IntType(false, info.bits), key), rng, HashInt); }, - builtin.TypeId.Bool => return autoHash(@boolToInt(key), rng), - builtin.TypeId.Enum => return autoHash(@enumToInt(key), rng), - builtin.TypeId.ErrorSet => return autoHash(@errorToInt(key), rng), - builtin.TypeId.Promise, builtin.TypeId.Fn => return autoHash(@ptrToInt(key), rng), + builtin.TypeId.Bool => return autoHash(@boolToInt(key), rng, HashInt), + builtin.TypeId.Enum => return autoHash(@enumToInt(key), rng, HashInt), + builtin.TypeId.ErrorSet => return autoHash(@errorToInt(key), rng, HashInt), + builtin.TypeId.Promise, builtin.TypeId.Fn => return autoHash(@ptrToInt(key), rng, HashInt), builtin.TypeId.BoundFn, builtin.TypeId.ComptimeFloat, From 6ce2a03985db3094634742ae2209477de998e70a Mon Sep 17 00:00:00 2001 From: daurnimator Date: Sun, 16 Jun 2019 03:02:47 +1000 Subject: [PATCH 05/13] std: add gimli permutation to crypto --- CMakeLists.txt | 1 + std/crypto.zig | 3 + std/crypto/gimli.zig | 168 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 172 insertions(+) create mode 100644 std/crypto/gimli.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index d77b6f09a..d6f8176e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -479,6 +479,7 @@ set(ZIG_STD_FILES "crypto.zig" "crypto/blake2.zig" "crypto/chacha20.zig" + "crypto/gimli.zig" "crypto/hmac.zig" "crypto/md5.zig" "crypto/poly1305.zig" diff --git a/std/crypto.zig b/std/crypto.zig index 2b57de9e6..b7703f2f7 100644 --- a/std/crypto.zig +++ b/std/crypto.zig @@ -13,6 +13,8 @@ pub const Sha3_256 = sha3.Sha3_256; pub const Sha3_384 = sha3.Sha3_384; pub const Sha3_512 = sha3.Sha3_512; +pub const gimli = @import("crypto/gimli.zig"); + const blake2 = @import("crypto/blake2.zig"); pub const Blake2s224 = blake2.Blake2s224; pub const Blake2s256 = blake2.Blake2s256; @@ -38,6 +40,7 @@ pub const randomBytes = std.os.getrandom; test "crypto" { _ = @import("crypto/blake2.zig"); _ = @import("crypto/chacha20.zig"); + _ = @import("crypto/gimli.zig"); _ = @import("crypto/hmac.zig"); _ = @import("crypto/md5.zig"); _ = @import("crypto/poly1305.zig"); diff --git a/std/crypto/gimli.zig b/std/crypto/gimli.zig new file mode 100644 index 000000000..0a0a5056c --- /dev/null +++ b/std/crypto/gimli.zig @@ -0,0 +1,168 @@ +// Gimli is a 384-bit permutation designed to achieve high security with high +// performance across a broad range of platforms, including 64-bit Intel/AMD +// server CPUs, 64-bit and 32-bit ARM smartphone CPUs, 32-bit ARM +// microcontrollers, 8-bit AVR microcontrollers, FPGAs, ASICs without +// side-channel protection, and ASICs with side-channel protection. +// +// https://gimli.cr.yp.to/ +// https://csrc.nist.gov/CSRC/media/Projects/Lightweight-Cryptography/documents/round-1/spec-doc/gimli-spec.pdf + +const std = @import("../std.zig"); +const mem = std.mem; +const math = std.math; +const debug = std.debug; +const assert = std.debug.assert; +const testing = std.testing; +const htest = @import("test.zig"); + +pub const State = struct { + pub const BLOCKBYTES = 48; + pub const RATE = 16; + + // TODO: https://github.com/ziglang/zig/issues/2673#issuecomment-501763017 + data: [BLOCKBYTES / 4]u32, + + const Self = @This(); + + pub fn toSlice(self: *Self) []u8 { + return @sliceToBytes(self.data[0..]); + } + + pub fn toSliceConst(self: *Self) []const u8 { + return @sliceToBytes(self.data[0..]); + } + + pub fn permute(self: *Self) void { + const state = &self.data; + var round = u32(24); + while (round > 0) : (round -= 1) { + var column = usize(0); + while (column < 4) : (column += 1) { + const x = math.rotl(u32, state[column], 24); + const y = math.rotl(u32, state[4 + column], 9); + const z = state[8 + column]; + state[8 + column] = ((x ^ (z << 1)) ^ ((y & z) << 2)); + state[4 + column] = ((y ^ x) ^ ((x | z) << 1)); + state[column] = ((z ^ y) ^ ((x & y) << 3)); + } + switch (round & 3) { + 0 => { + mem.swap(u32, &state[0], &state[1]); + mem.swap(u32, &state[2], &state[3]); + state[0] ^= round | 0x9e377900; + }, + 2 => { + mem.swap(u32, &state[0], &state[2]); + mem.swap(u32, &state[1], &state[3]); + }, + else => {}, + } + } + } + + pub fn squeeze(self: *Self, out: []u8) void { + var i = usize(0); + while (i + RATE <= out.len) : (i += RATE) { + self.permute(); + mem.copy(u8, out[i..], self.toSliceConst()[0..RATE]); + } + const leftover = out.len - i; + if (leftover != 0) { + self.permute(); + mem.copy(u8, out[i..], self.toSliceConst()[0..leftover]); + } + } +}; + +test "permute" { + // test vector from gimli-20170627 + var state = State{ + .data = blk: { + var input: [12]u32 = undefined; + var i = u32(0); + while (i < 12) : (i += 1) { + input[i] = i * i * i + i *% 0x9e3779b9; + } + testing.expectEqualSlices(u32, input, [_]u32{ + 0x00000000, 0x9e3779ba, 0x3c6ef37a, 0xdaa66d46, + 0x78dde724, 0x1715611a, 0xb54cdb2e, 0x53845566, + 0xf1bbcfc8, 0x8ff34a5a, 0x2e2ac522, 0xcc624026, + }); + break :blk input; + }, + }; + state.permute(); + testing.expectEqualSlices(u32, state.data, [_]u32{ + 0xba11c85a, 0x91bad119, 0x380ce880, 0xd24c2c68, + 0x3eceffea, 0x277a921c, 0x4f73a0bd, 0xda5a9cd8, + 0x84b673f0, 0x34e52ff7, 0x9e2bef49, 0xf41bb8d6, + }); +} + +pub const Hash = struct { + state: State, + buf_off: usize, + + const Self = @This(); + + pub fn init() Self { + return Self{ + .state = State{ + .data = [_]u32{0} ** (State.BLOCKBYTES / 4), + }, + .buf_off = 0, + }; + } + + /// Also known as 'absorb' + pub fn update(self: *Self, data: []const u8) void { + const buf = self.state.toSlice(); + var in = data; + while (in.len > 0) { + var left = State.RATE - self.buf_off; + if (left == 0) { + self.state.permute(); + self.buf_off = 0; + left = State.RATE; + } + const ps = math.min(in.len, left); + for (buf[self.buf_off .. self.buf_off + ps]) |*p, i| { + p.* ^= in[i]; + } + self.buf_off += ps; + in = in[ps..]; + } + } + + /// Finish the current hashing operation, writing the hash to `out` + /// + /// From 4.9 "Application to hashing" + /// By default, Gimli-Hash provides a fixed-length output of 32 bytes + /// (the concatenation of two 16-byte blocks). However, Gimli-Hash can + /// be used as an “extendable one-way function” (XOF). + pub fn final(self: *Self, out: []u8) void { + const buf = self.state.toSlice(); + + // XOR 1 into the next byte of the state + buf[self.buf_off] ^= 1; + // XOR 1 into the last byte of the state, position 47. + buf[buf.len - 1] ^= 1; + + self.state.squeeze(out); + } +}; + +pub fn hash(out: []u8, in: []const u8) void { + var st = Hash.init(); + st.update(in); + st.final(out); +} + +test "hash" { + // a test vector (30) from NIST KAT submission. + var msg: [58 / 2]u8 = undefined; + try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C"); + var md: [32]u8 = undefined; + hash(&md, msg); + htest.assertEqual("1C9A03DC6A5DDC5444CFC6F4B154CFF5CF081633B2CEA4D7D0AE7CCFED5AAA44", md); +} From 50c8a93a5e2313ffb4183894637ecbd7f4dc50fa Mon Sep 17 00:00:00 2001 From: Josh Wolfe Date: Mon, 17 Jun 2019 00:03:16 -0400 Subject: [PATCH 06/13] mem.concat --- std/mem.zig | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/std/mem.zig b/std/mem.zig index 49ea48a89..7ecd48302 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -996,6 +996,43 @@ test "mem.join" { testing.expect(eql(u8, try join(a, ",", [_][]const u8{ "a", "", "b", "", "c" }), "a,,b,,c")); } +/// Copies each T from slices into a new slice that exactly holds all the elements. +pub fn concat(allocator: *Allocator, comptime T: type, slices: []const []const T) ![]T { + if (slices.len == 0) return (([*]T)(undefined))[0..0]; + + const total_len = blk: { + var sum: usize = 0; + for (slices) |slice| { + sum += slice.len; + } + break :blk sum; + }; + + const buf = try allocator.alloc(T, total_len); + errdefer allocator.free(buf); + + var buf_index: usize = 0; + for (slices) |slice| { + copy(T, buf[buf_index..], slice); + buf_index += slice.len; + } + + // No need for shrink since buf is exactly the correct size. + return buf; +} + +test "concat" { + var buf: [1024]u8 = undefined; + const a = &std.heap.FixedBufferAllocator.init(&buf).allocator; + testing.expect(eql(u8, try concat(a, u8, [_][]const u8{ "abc", "def", "ghi" }), "abcdefghi")); + testing.expect(eql(u32, try concat(a, u32, [_][]const u32{ + [_]u32{ 0, 1 }, + [_]u32{ 2, 3, 4 }, + [_]u32{}, + [_]u32{5}, + }), [_]u32{ 0, 1, 2, 3, 4, 5 })); +} + test "testStringEquality" { testing.expect(eql(u8, "abcd", "abcd")); testing.expect(!eql(u8, "abcdef", "abZdef")); From d5d0942a0dd5366e7e9a0843bf40878e6f0e1f2c Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Mon, 17 Jun 2019 22:03:56 +1200 Subject: [PATCH 07/13] Small cleanup of fmt.zig Use inferred enum literals and split the large test case up. --- std/fmt.zig | 453 +++++++++++++++++++++++++++++----------------------- 1 file changed, 255 insertions(+), 198 deletions(-) diff --git a/std/fmt.zig b/std/fmt.zig index 75e0ce85c..7bf1fa3d4 100644 --- a/std/fmt.zig +++ b/std/fmt.zig @@ -13,7 +13,13 @@ pub const default_max_depth = 3; /// Renders fmt string with args, calling output with slices of bytes. /// If `output` returns an error, the error is returned from `format` and /// `output` is not called again. -pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void, comptime fmt: []const u8, args: ...) Errors!void { +pub fn format( + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, + comptime fmt: []const u8, + args: ..., +) Errors!void { const State = enum { Start, OpenBrace, @@ -28,7 +34,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), inline for (fmt) |c, i| { switch (state) { - State.Start => switch (c) { + .Start => switch (c) { '{' => { if (start_index < i) { try output(context, fmt[start_index..i]); @@ -45,7 +51,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), }, else => {}, }, - State.OpenBrace => switch (c) { + .OpenBrace => switch (c) { '{' => { state = State.Start; start_index = i; @@ -61,14 +67,14 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), state = State.FormatString; }, }, - State.CloseBrace => switch (c) { + .CloseBrace => switch (c) { '}' => { state = State.Start; start_index = i; }, else => @compileError("Single '}' encountered in format string"), }, - State.FormatString => switch (c) { + .FormatString => switch (c) { '}' => { const s = start_index + 1; try formatType(args[next_arg], fmt[s..i], context, Errors, output, default_max_depth); @@ -78,7 +84,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), }, else => {}, }, - State.Pointer => switch (c) { + .Pointer => switch (c) { '}' => { try output(context, @typeName(@typeOf(args[next_arg]).Child)); try output(context, "@"); @@ -114,88 +120,93 @@ pub fn formatType( ) Errors!void { const T = @typeOf(value); switch (@typeInfo(T)) { - builtin.TypeId.ComptimeInt, builtin.TypeId.Int, builtin.TypeId.Float => { + .ComptimeInt, .Int, .Float => { return formatValue(value, fmt, context, Errors, output); }, - builtin.TypeId.Void => { + .Void => { return output(context, "void"); }, - builtin.TypeId.Bool => { + .Bool => { return output(context, if (value) "true" else "false"); }, - builtin.TypeId.Optional => { + .Optional => { if (value) |payload| { return formatType(payload, fmt, context, Errors, output, max_depth); } else { return output(context, "null"); } }, - builtin.TypeId.ErrorUnion => { + .ErrorUnion => { if (value) |payload| { return formatType(payload, fmt, context, Errors, output, max_depth); } else |err| { return formatType(err, fmt, context, Errors, output, max_depth); } }, - builtin.TypeId.ErrorSet => { + .ErrorSet => { try output(context, "error."); return output(context, @errorName(value)); }, - builtin.TypeId.Promise => { + .Promise => { return format(context, Errors, output, "promise@{x}", @ptrToInt(value)); }, - builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => { - if (comptime std.meta.trait.hasFn("format")(T)) return value.format(fmt, context, Errors, output); + .Enum => { + if (comptime std.meta.trait.hasFn("format")(T)) { + return value.format(fmt, context, Errors, output); + } try output(context, @typeName(T)); - switch (comptime @typeId(T)) { - builtin.TypeId.Enum => { - try output(context, "."); - try formatType(@tagName(value), "", context, Errors, output, max_depth); - return; - }, - builtin.TypeId.Struct => { - if (max_depth == 0) { - return output(context, "{ ... }"); - } - comptime var field_i = 0; - inline while (field_i < @memberCount(T)) : (field_i += 1) { - if (field_i == 0) { - try output(context, "{ ."); - } else { - try output(context, ", ."); - } - try output(context, @memberName(T, field_i)); - try output(context, " = "); - try formatType(@field(value, @memberName(T, field_i)), "", context, Errors, output, max_depth - 1); - } - try output(context, " }"); - }, - builtin.TypeId.Union => { - if (max_depth == 0) { - return output(context, "{ ... }"); - } - const info = @typeInfo(T).Union; - if (info.tag_type) |UnionTagType| { - try output(context, "{ ."); - try output(context, @tagName(UnionTagType(value))); - try output(context, " = "); - inline for (info.fields) |u_field| { - if (@enumToInt(UnionTagType(value)) == u_field.enum_field.?.value) { - try formatType(@field(value, u_field.name), "", context, Errors, output, max_depth - 1); - } - } - try output(context, " }"); - } else { - try format(context, Errors, output, "@{x}", @ptrToInt(&value)); - } - }, - else => unreachable, - } - return; + try output(context, "."); + return formatType(@tagName(value), "", context, Errors, output, max_depth); }, - builtin.TypeId.Pointer => |ptr_info| switch (ptr_info.size) { - builtin.TypeInfo.Pointer.Size.One => switch (@typeInfo(ptr_info.child)) { + .Union => { + if (comptime std.meta.trait.hasFn("format")(T)) { + return value.format(fmt, context, Errors, output); + } + + try output(context, @typeName(T)); + if (max_depth == 0) { + return output(context, "{ ... }"); + } + const info = @typeInfo(T).Union; + if (info.tag_type) |UnionTagType| { + try output(context, "{ ."); + try output(context, @tagName(UnionTagType(value))); + try output(context, " = "); + inline for (info.fields) |u_field| { + if (@enumToInt(UnionTagType(value)) == u_field.enum_field.?.value) { + try formatType(@field(value, u_field.name), "", context, Errors, output, max_depth - 1); + } + } + try output(context, " }"); + } else { + try format(context, Errors, output, "@{x}", @ptrToInt(&value)); + } + }, + .Struct => { + if (comptime std.meta.trait.hasFn("format")(T)) { + return value.format(fmt, context, Errors, output); + } + + try output(context, @typeName(T)); + if (max_depth == 0) { + return output(context, "{ ... }"); + } + comptime var field_i = 0; + inline while (field_i < @memberCount(T)) : (field_i += 1) { + if (field_i == 0) { + try output(context, "{ ."); + } else { + try output(context, ", ."); + } + try output(context, @memberName(T, field_i)); + try output(context, " = "); + try formatType(@field(value, @memberName(T, field_i)), "", context, Errors, output, max_depth - 1); + } + try output(context, " }"); + }, + .Pointer => |ptr_info| switch (ptr_info.size) { + .One => switch (@typeInfo(ptr_info.child)) { builtin.TypeId.Array => |info| { if (info.child == u8) { return formatText(value, fmt, context, Errors, output); @@ -207,7 +218,7 @@ pub fn formatType( }, else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)), }, - builtin.TypeInfo.Pointer.Size.Many => { + .Many => { if (ptr_info.child == u8) { if (fmt.len > 0 and fmt[0] == 's') { const len = mem.len(u8, value); @@ -216,7 +227,7 @@ pub fn formatType( } return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); }, - builtin.TypeInfo.Pointer.Size.Slice => { + .Slice => { if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) { return formatText(value, fmt, context, Errors, output); } @@ -225,17 +236,17 @@ pub fn formatType( } return format(context, Errors, output, "{}@{x}", @typeName(ptr_info.child), @ptrToInt(value.ptr)); }, - builtin.TypeInfo.Pointer.Size.C => { + .C => { return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); }, }, - builtin.TypeId.Array => |info| { + .Array => |info| { if (info.child == u8) { return formatText(value, fmt, context, Errors, output); } return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(&value)); }, - builtin.TypeId.Fn => { + .Fn => { return format(context, Errors, output, "{}@{x}", @typeName(T), @ptrToInt(value)); }, else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"), @@ -249,24 +260,24 @@ fn formatValue( comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void, ) Errors!void { - if (fmt.len > 0) { - if (fmt[0] == 'B') { - comptime var width: ?usize = null; - if (fmt.len > 1) { - if (fmt[1] == 'i') { - if (fmt.len > 2) width = comptime (parseUnsigned(usize, fmt[2..], 10) catch unreachable); - return formatBytes(value, width, 1024, context, Errors, output); + if (fmt.len > 0 and fmt[0] == 'B') { + comptime var width: ?usize = null; + if (fmt.len > 1) { + if (fmt[1] == 'i') { + if (fmt.len > 2) { + width = comptime (parseUnsigned(usize, fmt[2..], 10) catch unreachable); } - width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); + return formatBytes(value, width, 1024, context, Errors, output); } - return formatBytes(value, width, 1000, context, Errors, output); + width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); } + return formatBytes(value, width, 1000, context, Errors, output); } const T = @typeOf(value); switch (@typeId(T)) { - builtin.TypeId.Float => return formatFloatValue(value, fmt, context, Errors, output), - builtin.TypeId.Int, builtin.TypeId.ComptimeInt => return formatIntValue(value, fmt, context, Errors, output), + .Float => return formatFloatValue(value, fmt, context, Errors, output), + .Int, .ComptimeInt => return formatIntValue(value, fmt, context, Errors, output), else => comptime unreachable, } } @@ -797,7 +808,7 @@ pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) !T { } } -test "fmt.parseInt" { +test "parseInt" { testing.expect((parseInt(i32, "-10", 10) catch unreachable) == -10); testing.expect((parseInt(i32, "+10", 10) catch unreachable) == 10); testing.expect(if (parseInt(i32, " 10", 10)) |_| false else |err| err == error.InvalidCharacter); @@ -828,7 +839,7 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsigned return x; } -test "fmt.parseUnsigned" { +test "parseUnsigned" { testing.expect((try parseUnsigned(u16, "050124", 10)) == 50124); testing.expect((try parseUnsigned(u16, "65535", 10)) == 65535); testing.expectError(error.Overflow, parseUnsigned(u16, "65536", 10)); @@ -913,7 +924,7 @@ fn countSize(size: *usize, bytes: []const u8) (error{}!void) { size.* += bytes.len; } -test "buf print int" { +test "bufPrintInt" { var buffer: [100]u8 = undefined; const buf = buffer[0..]; testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, 0), "-101111000110000101001110")); @@ -949,7 +960,7 @@ test "parse unsigned comptime" { } } -test "fmt.format" { +test "fmt.optional" { { const value: ?i32 = 1234; try testFmt("optional: 1234\n", "optional: {}\n", value); @@ -958,6 +969,9 @@ test "fmt.format" { const value: ?i32 = null; try testFmt("optional: null\n", "optional: {}\n", value); } +} + +test "fmt.error" { { const value: anyerror!i32 = 1234; try testFmt("error union: 1234\n", "error union: {}\n", value); @@ -966,10 +980,16 @@ test "fmt.format" { const value: anyerror!i32 = error.InvalidChar; try testFmt("error union: error.InvalidChar\n", "error union: {}\n", value); } +} + +test "fmt.int.small" { { const value: u3 = 0b101; try testFmt("u3: 5\n", "u3: {}\n", value); } +} + +test "fmt.int.specifier" { { const value: u8 = 'a'; try testFmt("u8: a\n", "u8: {c}\n", value); @@ -978,6 +998,9 @@ test "fmt.format" { const value: u8 = 0b1100; try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", value); } +} + +test "fmt.buffer" { { var buf1: [32]u8 = undefined; var context = BufPrintContext{ .remaining = buf1[0..] }; @@ -995,6 +1018,9 @@ test "fmt.format" { res = buf1[0 .. buf1.len - context.remaining.len]; testing.expect(mem.eql(u8, res, "1100")); } +} + +test "fmt.array" { { const value: [3]u8 = "abc"; try testFmt("array: abc\n", "array: {}\n", value); @@ -1007,6 +1033,9 @@ test "fmt.format" { &value, ); } +} + +test "fmt.slice" { { const value: []const u8 = "abc"; try testFmt("slice: abc\n", "slice: {}\n", value); @@ -1015,6 +1044,12 @@ test "fmt.format" { const value = @intToPtr([*]const []const u8, 0xdeadbeef)[0..0]; try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", value); } + + try testFmt("buf: Test \n", "buf: {s5}\n", "Test"); + try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test"); +} + +test "fmt.pointer" { { const value = @intToPtr(*i32, 0xdeadbeef); try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", value); @@ -1028,12 +1063,19 @@ test "fmt.format" { const value = @intToPtr(fn () void, 0xdeadbeef); try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", value); } - try testFmt("buf: Test \n", "buf: {s5}\n", "Test"); - try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test"); +} + +test "fmt.cstr" { try testFmt("cstr: Test C\n", "cstr: {s}\n", c"Test C"); try testFmt("cstr: Test C \n", "cstr: {s10}\n", c"Test C"); +} + +test "fmt.filesize" { try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024)); try testFmt("file size: 66.06MB\n", "file size: {B2}\n", usize(63 * 1024 * 1024)); +} + +test "fmt.struct" { { const Struct = struct { field: u8, @@ -1050,15 +1092,19 @@ test "fmt.format" { const value = Struct{ .a = 0, .b = 1 }; try testFmt("struct: Struct{ .a = 0, .b = 1 }\n", "struct: {}\n", value); } - { - const Enum = enum { - One, - Two, - }; - const value = Enum.Two; - try testFmt("enum: Enum.Two\n", "enum: {}\n", value); - try testFmt("enum: Enum.Two\n", "enum: {}\n", &value); - } +} + +test "fmt.enum" { + const Enum = enum { + One, + Two, + }; + const value = Enum.Two; + try testFmt("enum: Enum.Two\n", "enum: {}\n", value); + try testFmt("enum: Enum.Two\n", "enum: {}\n", &value); +} + +test "fmt.float.scientific" { { var buf1: [32]u8 = undefined; const value: f32 = 1.34; @@ -1088,6 +1134,9 @@ test "fmt.format" { testing.expect(mem.eql(u8, result, "f64: 9.99996e-40\n")); } } +} + +test "fmt.float.scientific.precision" { { var buf1: [32]u8 = undefined; const value: f64 = 1.409706e-42; @@ -1114,6 +1163,9 @@ test "fmt.format" { const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); testing.expect(mem.eql(u8, result, "f64: 1.00001e+05\n")); } +} + +test "fmt.float.special" { { var buf1: [32]u8 = undefined; const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64); @@ -1136,6 +1188,9 @@ test "fmt.format" { const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64); testing.expect(mem.eql(u8, result, "f64: -inf\n")); } +} + +test "fmt.float.decimal" { { var buf1: [64]u8 = undefined; const value: f64 = 1.52314e+29; @@ -1216,7 +1271,9 @@ test "fmt.format" { const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); testing.expect(mem.eql(u8, result, "f64: 0.00000\n")); } - // libc checks +} + +test "fmt.float.libc.sanity" { { var buf1: [32]u8 = undefined; const value: f64 = f64(@bitCast(f32, u32(916964781))); @@ -1267,127 +1324,127 @@ test "fmt.format" { const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); testing.expect(mem.eql(u8, result, "f64: 18014400656965630.00000\n")); } - //custom type format - { - const Vec2 = struct { - const SelfType = @This(); - x: f32, - y: f32, +} - pub fn format( - self: SelfType, - comptime fmt: []const u8, - context: var, - comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, - ) Errors!void { - switch (fmt.len) { - 0 => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y), - 1 => switch (fmt[0]) { - //point format - 'p' => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y), - //dimension format - 'd' => return std.fmt.format(context, Errors, output, "{.3}x{.3}", self.x, self.y), - else => unreachable, - }, +test "fmt.custom" { + const Vec2 = struct { + const SelfType = @This(); + x: f32, + y: f32, + + pub fn format( + self: SelfType, + comptime fmt: []const u8, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, + ) Errors!void { + switch (fmt.len) { + 0 => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y), + 1 => switch (fmt[0]) { + //point format + 'p' => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y), + //dimension format + 'd' => return std.fmt.format(context, Errors, output, "{.3}x{.3}", self.x, self.y), else => unreachable, - } + }, + else => unreachable, } - }; + } + }; - var buf1: [32]u8 = undefined; - var value = Vec2{ - .x = 10.2, - .y = 2.22, - }; - try testFmt("point: (10.200,2.220)\n", "point: {}\n", &value); - try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", &value); + var buf1: [32]u8 = undefined; + var value = Vec2{ + .x = 10.2, + .y = 2.22, + }; + try testFmt("point: (10.200,2.220)\n", "point: {}\n", &value); + try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", &value); - // same thing but not passing a pointer - try testFmt("point: (10.200,2.220)\n", "point: {}\n", value); - try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", value); - } - //struct format - { - const S = struct { - a: u32, - b: anyerror, - }; + // same thing but not passing a pointer + try testFmt("point: (10.200,2.220)\n", "point: {}\n", value); + try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", value); +} - const inst = S{ - .a = 456, - .b = error.Unused, - }; +test "fmt.struct" { + const S = struct { + a: u32, + b: anyerror, + }; - try testFmt("S{ .a = 456, .b = error.Unused }", "{}", inst); - } - //union format - { - const TU = union(enum) { - float: f32, - int: u32, - }; + const inst = S{ + .a = 456, + .b = error.Unused, + }; - const UU = union { - float: f32, - int: u32, - }; + try testFmt("S{ .a = 456, .b = error.Unused }", "{}", inst); +} - const EU = extern union { - float: f32, - int: u32, - }; +test "fmt.union" { + const TU = union(enum) { + float: f32, + int: u32, + }; - const tu_inst = TU{ .int = 123 }; - const uu_inst = UU{ .int = 456 }; - const eu_inst = EU{ .float = 321.123 }; + const UU = union { + float: f32, + int: u32, + }; - try testFmt("TU{ .int = 123 }", "{}", tu_inst); + const EU = extern union { + float: f32, + int: u32, + }; - var buf: [100]u8 = undefined; - const uu_result = try bufPrint(buf[0..], "{}", uu_inst); - testing.expect(mem.eql(u8, uu_result[0..3], "UU@")); + const tu_inst = TU{ .int = 123 }; + const uu_inst = UU{ .int = 456 }; + const eu_inst = EU{ .float = 321.123 }; - const eu_result = try bufPrint(buf[0..], "{}", eu_inst); - testing.expect(mem.eql(u8, uu_result[0..3], "EU@")); - } - //enum format - { - const E = enum { - One, - Two, - Three, - }; + try testFmt("TU{ .int = 123 }", "{}", tu_inst); - const inst = E.Two; + var buf: [100]u8 = undefined; + const uu_result = try bufPrint(buf[0..], "{}", uu_inst); + testing.expect(mem.eql(u8, uu_result[0..3], "UU@")); - try testFmt("E.Two", "{}", inst); - } - //self-referential struct format - { - const S = struct { - const SelfType = @This(); - a: ?*SelfType, - }; + const eu_result = try bufPrint(buf[0..], "{}", eu_inst); + testing.expect(mem.eql(u8, uu_result[0..3], "EU@")); +} - var inst = S{ - .a = null, - }; - inst.a = &inst; +test "fmt.enum" { + const E = enum { + One, + Two, + Three, + }; - try testFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", inst); - } - //print bytes as hex - { - const some_bytes = "\xCA\xFE\xBA\xBE"; - try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", some_bytes); - try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", some_bytes); - //Test Slices - try testFmt("uppercase: CAFE\n", "uppercase: {X}\n", some_bytes[0..2]); - try testFmt("lowercase: babe\n", "lowercase: {x}\n", some_bytes[2..]); - const bytes_with_zeros = "\x00\x0E\xBA\xBE"; - try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", bytes_with_zeros); - } + const inst = E.Two; + + try testFmt("E.Two", "{}", inst); +} + +test "fmt.struct.self-referential" { + const S = struct { + const SelfType = @This(); + a: ?*SelfType, + }; + + var inst = S{ + .a = null, + }; + inst.a = &inst; + + try testFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", inst); +} + +test "fmt.bytes.hex" { + const some_bytes = "\xCA\xFE\xBA\xBE"; + try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", some_bytes); + try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", some_bytes); + //Test Slices + try testFmt("uppercase: CAFE\n", "uppercase: {X}\n", some_bytes[0..2]); + try testFmt("lowercase: babe\n", "lowercase: {x}\n", some_bytes[2..]); + const bytes_with_zeros = "\x00\x0E\xBA\xBE"; + try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", bytes_with_zeros); } fn testFmt(expected: []const u8, comptime template: []const u8, args: ...) !void { From 21dff1c4e280663c7a637f3b014c0d7326efcd3d Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Sun, 16 Jun 2019 21:58:05 -0600 Subject: [PATCH 08/13] Remove duplicate exe name with zig run --- src/codegen.cpp | 3 ++- src/libc_installation.cpp | 6 +++-- src/link.cpp | 3 ++- src/main.cpp | 28 +++++++++----------- src/os.cpp | 56 +++++++++++++++++++-------------------- src/os.hpp | 4 +-- 6 files changed, 49 insertions(+), 51 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index cee8c0ec0..3dd6995c6 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8801,6 +8801,7 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) { Termination term; ZigList args = {}; + args.append(buf_ptr(self_exe_path)); args.append("cc"); Buf *out_dep_path = buf_sprintf("%s.d", buf_ptr(out_obj_path)); @@ -8819,7 +8820,7 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) { if (g->verbose_cc) { print_zig_cc_cmd("zig", &args); } - os_spawn_process(buf_ptr(self_exe_path), args, &term); + os_spawn_process(args, &term); if (term.how != TerminationIdClean || term.code != 0) { fprintf(stderr, "\nThe following command failed:\n"); print_zig_cc_cmd(buf_ptr(self_exe_path), &args); diff --git a/src/libc_installation.cpp b/src/libc_installation.cpp index d1773b89e..135941dc4 100644 --- a/src/libc_installation.cpp +++ b/src/libc_installation.cpp @@ -153,6 +153,7 @@ static Error zig_libc_find_native_include_dir_posix(ZigLibCInstallation *self, b const char *cc_exe = getenv("CC"); cc_exe = (cc_exe == nullptr) ? CC_EXE : cc_exe; ZigList args = {}; + args.append(cc_exe); args.append("-E"); args.append("-Wp,-v"); args.append("-xc"); @@ -166,7 +167,7 @@ static Error zig_libc_find_native_include_dir_posix(ZigLibCInstallation *self, b Buf *out_stderr = buf_alloc(); Buf *out_stdout = buf_alloc(); Error err; - if ((err = os_exec_process(cc_exe, args, &term, out_stderr, out_stdout))) { + if ((err = os_exec_process(args, &term, out_stderr, out_stdout))) { if (verbose) { fprintf(stderr, "unable to determine libc include path: executing '%s': %s\n", cc_exe, err_str(err)); } @@ -277,12 +278,13 @@ Error zig_libc_cc_print_file_name(const char *o_file, Buf *out, bool want_dirnam const char *cc_exe = getenv("CC"); cc_exe = (cc_exe == nullptr) ? CC_EXE : cc_exe; ZigList args = {}; + args.append(cc_exe); args.append(buf_ptr(buf_sprintf("-print-file-name=%s", o_file))); Termination term; Buf *out_stderr = buf_alloc(); Buf *out_stdout = buf_alloc(); Error err; - if ((err = os_exec_process(cc_exe, args, &term, out_stderr, out_stdout))) { + if ((err = os_exec_process(args, &term, out_stderr, out_stdout))) { if (err == ErrorFileNotFound) return ErrorNoCCompilerInstalled; if (verbose) { diff --git a/src/link.cpp b/src/link.cpp index 3a437e4ed..277dcbc5c 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -1721,10 +1721,11 @@ void codegen_link(CodeGen *g) { if (g->system_linker_hack && g->zig_target->os == OsMacOSX) { Termination term; ZigList args = {}; + args.append("ld"); for (size_t i = 1; i < lj.args.length; i += 1) { args.append(lj.args.at(i)); } - os_spawn_process("ld", args, &term); + os_spawn_process(args, &term); if (term.how != TerminationIdClean || term.code != 0) { exit(1); } diff --git a/src/main.cpp b/src/main.cpp index 9c14ba325..9b1892061 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -467,6 +467,7 @@ int main(int argc, char **argv) { init_all_targets(); ZigList args = {0}; + args.append(NULL); // placeholder args.append(zig_exe_path); args.append(NULL); // placeholder args.append(NULL); // placeholder @@ -525,8 +526,8 @@ int main(int argc, char **argv) { g->enable_time_report = timing_info; codegen_set_out_name(g, buf_create_from_str("build")); - args.items[1] = buf_ptr(&build_file_dirname); - args.items[2] = buf_ptr(&full_cache_dir); + args.items[2] = buf_ptr(&build_file_dirname); + args.items[3] = buf_ptr(&full_cache_dir); bool build_file_exists; if ((err = os_file_exists(&build_file_abs, &build_file_exists))) { @@ -580,12 +581,14 @@ int main(int argc, char **argv) { codegen_build_and_link(g); Termination term; - os_spawn_process(buf_ptr(&g->output_file_path), args, &term); + args.items[0] = buf_ptr(&g->output_file_path); + os_spawn_process(args, &term); if (term.how != TerminationIdClean || term.code != 0) { fprintf(stderr, "\nBuild failed. The following command failed:\n"); - fprintf(stderr, "%s", buf_ptr(&g->output_file_path)); + const char *prefix = ""; for (size_t i = 0; i < args.length; i += 1) { - fprintf(stderr, " %s", args.at(i)); + fprintf(stderr, "%s%s", prefix, args.at(i)); + prefix = " "; } fprintf(stderr, "\n"); } @@ -1161,7 +1164,7 @@ int main(int argc, char **argv) { args.pop(); Termination term; - os_spawn_process(exec_path, args, &term); + os_spawn_process(args, &term); return term.code; } else if (cmd == CmdBuild) { if (g->enable_cache) { @@ -1213,17 +1216,10 @@ int main(int argc, char **argv) { } Termination term; - if (test_exec_args.length > 0) { - ZigList rest_args = {0}; - for (size_t i = 1; i < test_exec_args.length; i += 1) { - rest_args.append(test_exec_args.at(i)); - } - os_spawn_process(test_exec_args.items[0], rest_args, &term); - } else { - ZigList no_args = {0}; - os_spawn_process(buf_ptr(test_exe_path), no_args, &term); + if (test_exec_args.length == 0) { + test_exec_args.append(buf_ptr(test_exe_path)); } - + os_spawn_process(test_exec_args, &term); if (term.how != TerminationIdClean || term.code != 0) { fprintf(stderr, "\nTests failed. Use the following command to reproduce the failure:\n"); fprintf(stderr, "%s\n", buf_ptr(test_exe_path)); diff --git a/src/os.cpp b/src/os.cpp index e3d223325..bc28b1e7a 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -105,16 +105,15 @@ static void populate_termination(Termination *term, int status) { } } -static void os_spawn_process_posix(const char *exe, ZigList &args, Termination *term) { - const char **argv = allocate(args.length + 2); - argv[0] = exe; - argv[args.length + 1] = nullptr; +static void os_spawn_process_posix(ZigList &args, Termination *term) { + const char **argv = allocate(args.length + 1); for (size_t i = 0; i < args.length; i += 1) { - argv[i + 1] = args.at(i); + argv[i] = args.at(i); } + argv[args.length] = nullptr; pid_t pid; - int rc = posix_spawnp(&pid, exe, nullptr, nullptr, const_cast(argv), environ); + int rc = posix_spawnp(&pid, args.at(0), nullptr, nullptr, const_cast(argv), environ); if (rc != 0) { zig_panic("posix_spawn failed: %s", strerror(rc)); } @@ -126,16 +125,14 @@ static void os_spawn_process_posix(const char *exe, ZigList &args, #endif #if defined(ZIG_OS_WINDOWS) -static void os_windows_create_command_line(Buf *command_line, const char *exe, ZigList &args) { + +static void os_windows_create_command_line(Buf *command_line, ZigList &args) { buf_resize(command_line, 0); - - buf_append_char(command_line, '\"'); - buf_append_str(command_line, exe); - buf_append_char(command_line, '\"'); - + char *prefix = "\""; for (size_t arg_i = 0; arg_i < args.length; arg_i += 1) { - buf_append_str(command_line, " \""); const char *arg = args.at(arg_i); + buf_append_str(command_line, prefix); + prefix = " \""; size_t arg_len = strlen(arg); for (size_t c_i = 0; c_i < arg_len; c_i += 1) { if (arg[c_i] == '\"') { @@ -147,14 +144,15 @@ static void os_windows_create_command_line(Buf *command_line, const char *exe, Z } } -static void os_spawn_process_windows(const char *exe, ZigList &args, Termination *term) { +static void os_spawn_process_windows(ZigList &args, Termination *term) { Buf command_line = BUF_INIT; - os_windows_create_command_line(&command_line, exe, args); + os_windows_create_command_line(&command_line, args); PROCESS_INFORMATION piProcInfo = {0}; STARTUPINFO siStartInfo = {0}; siStartInfo.cb = sizeof(STARTUPINFO); + const char *exe = args.at(0); BOOL success = CreateProcessA(exe, buf_ptr(&command_line), nullptr, nullptr, TRUE, 0, nullptr, nullptr, &siStartInfo, &piProcInfo); @@ -173,11 +171,11 @@ static void os_spawn_process_windows(const char *exe, ZigList &arg } #endif -void os_spawn_process(const char *exe, ZigList &args, Termination *term) { +void os_spawn_process(ZigList &args, Termination *term) { #if defined(ZIG_OS_WINDOWS) - os_spawn_process_windows(exe, args, term); + os_spawn_process_windows(args, term); #elif defined(ZIG_OS_POSIX) - os_spawn_process_posix(exe, args, term); + os_spawn_process_posix(args, term); #else #error "missing os_spawn_process implementation" #endif @@ -785,7 +783,7 @@ Error os_file_exists(Buf *full_path, bool *result) { } #if defined(ZIG_OS_POSIX) -static Error os_exec_process_posix(const char *exe, ZigList &args, +static Error os_exec_process_posix(ZigList &args, Termination *term, Buf *out_stderr, Buf *out_stdout) { int stdin_pipe[2]; @@ -817,13 +815,12 @@ static Error os_exec_process_posix(const char *exe, ZigList &args, if (dup2(stderr_pipe[1], STDERR_FILENO) == -1) zig_panic("dup2 failed"); - const char **argv = allocate(args.length + 2); - argv[0] = exe; - argv[args.length + 1] = nullptr; + const char **argv = allocate(args.length + 1); + argv[args.length] = nullptr; for (size_t i = 0; i < args.length; i += 1) { - argv[i + 1] = args.at(i); + argv[i] = args.at(i); } - execvp(exe, const_cast(argv)); + execvp(argv[0], const_cast(argv)); Error report_err = ErrorUnexpected; if (errno == ENOENT) { report_err = ErrorFileNotFound; @@ -874,11 +871,11 @@ static Error os_exec_process_posix(const char *exe, ZigList &args, // LocalFree(messageBuffer); //} -static Error os_exec_process_windows(const char *exe, ZigList &args, +static Error os_exec_process_windows(ZigList &args, Termination *term, Buf *out_stderr, Buf *out_stdout) { Buf command_line = BUF_INIT; - os_windows_create_command_line(&command_line, exe, args); + os_windows_create_command_line(&command_line, args); HANDLE g_hChildStd_IN_Rd = NULL; HANDLE g_hChildStd_IN_Wr = NULL; @@ -925,6 +922,7 @@ static Error os_exec_process_windows(const char *exe, ZigList &arg siStartInfo.hStdInput = g_hChildStd_IN_Rd; siStartInfo.dwFlags |= STARTF_USESTDHANDLES; + const char *exe = args.at(0); BOOL success = CreateProcess(exe, buf_ptr(&command_line), nullptr, nullptr, TRUE, 0, nullptr, nullptr, &siStartInfo, &piProcInfo); @@ -1005,13 +1003,13 @@ Error os_execv(const char *exe, const char **argv) { #endif } -Error os_exec_process(const char *exe, ZigList &args, +Error os_exec_process(ZigList &args, Termination *term, Buf *out_stderr, Buf *out_stdout) { #if defined(ZIG_OS_WINDOWS) - return os_exec_process_windows(exe, args, term, out_stderr, out_stdout); + return os_exec_process_windows(args, term, out_stderr, out_stdout); #elif defined(ZIG_OS_POSIX) - return os_exec_process_posix(exe, args, term, out_stderr, out_stdout); + return os_exec_process_posix(args, term, out_stderr, out_stdout); #else #error "missing os_exec_process implementation" #endif diff --git a/src/os.hpp b/src/os.hpp index 058bb2020..c8135e984 100644 --- a/src/os.hpp +++ b/src/os.hpp @@ -100,8 +100,8 @@ struct OsFileAttr { int os_init(void); -void os_spawn_process(const char *exe, ZigList &args, Termination *term); -Error os_exec_process(const char *exe, ZigList &args, +void os_spawn_process(ZigList &args, Termination *term); +Error os_exec_process(ZigList &args, Termination *term, Buf *out_stderr, Buf *out_stdout); Error os_execv(const char *exe, const char **argv); From 99112b5d4ac885f63a79c6f7926f92ff4f3ce819 Mon Sep 17 00:00:00 2001 From: Boris Date: Tue, 18 Jun 2019 20:26:07 +0700 Subject: [PATCH 09/13] fix tiny typo in langref.html.in --- doc/langref.html.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index c18528a2d..5ddd572e5 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4927,7 +4927,7 @@ test "peer type resolution: *const T and ?*T" {
  • The {#link|Integers#} {#syntax#}u0{#endsyntax#} and {#syntax#}i0{#endsyntax#}.
  • {#link|Arrays#} and {#link|Vectors#} with len 0, or with an element type that is a zero bit type.
  • An {#link|enum#} with only 1 tag.
  • -
  • An {#link|struct#} with all fields being zero bit types.
  • +
  • A {#link|struct#} with all fields being zero bit types.
  • A {#link|union#} with only 1 field which is a zero bit type.
  • {#link|Pointers to Zero Bit Types#} are themselves zero bit types.
  • From 8ed88280a62f2a400d00b5ac20618539693dad36 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 18 Jun 2019 11:18:53 -0400 Subject: [PATCH 10/13] Revert "fixes resolving aligment of child type in slice" This reverts commit aa60d2a688c965dcccf8e2c42afe5c180daba8fc. The copyright ownership of this 10 line patch is under dispute. See #2701 for details. So I'll revert it and then fix it myself without looking at this patch. --- src/ir.cpp | 2 +- test/stage1/behavior.zig | 1 - test/stage1/behavior/bugs/2689.zig | 7 ------- 3 files changed, 1 insertion(+), 9 deletions(-) delete mode 100644 test/stage1/behavior/bugs/2689.zig diff --git a/src/ir.cpp b/src/ir.cpp index 5c09e48b2..21751b9a9 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16794,7 +16794,7 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira, case ZigTypeIdPromise: case ZigTypeIdVector: { - if ((err = type_resolve(ira->codegen, child_type, ResolveStatusAlignmentKnown))) + if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, child_type, is_const, is_volatile, PtrLenUnknown, align_bytes, 0, 0, is_allow_zero); diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index 707d46fd8..f477bb64e 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -27,7 +27,6 @@ comptime { _ = @import("behavior/bugs/2114.zig"); _ = @import("behavior/bugs/2346.zig"); _ = @import("behavior/bugs/2578.zig"); - _ = @import("behavior/bugs/2689.zig"); _ = @import("behavior/bugs/394.zig"); _ = @import("behavior/bugs/421.zig"); _ = @import("behavior/bugs/529.zig"); diff --git a/test/stage1/behavior/bugs/2689.zig b/test/stage1/behavior/bugs/2689.zig deleted file mode 100644 index d1c1062ad..000000000 --- a/test/stage1/behavior/bugs/2689.zig +++ /dev/null @@ -1,7 +0,0 @@ -test "slice with alignment" { - const S = packed struct { - a: u8, - }; - - var a: []align(8) S = undefined; -} From 9050a07540b7387dade8184d3a3daf957e805e4f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 18 Jun 2019 11:31:05 -0400 Subject: [PATCH 11/13] when resolving slice types, might need to... ...resolve alignment if custom alignment is provided fixes #2689 --- src/ir.cpp | 4 +++- test/stage1/behavior/slice.zig | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index 21751b9a9..b74a99b37 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16794,7 +16794,9 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira, case ZigTypeIdPromise: case ZigTypeIdVector: { - if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown))) + ResolveStatus needed_status = (align_bytes == 0) ? + ResolveStatusZeroBitsKnown : ResolveStatusAlignmentKnown; + if ((err = type_resolve(ira->codegen, child_type, needed_status))) return ira->codegen->invalid_instruction; ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, child_type, is_const, is_volatile, PtrLenUnknown, align_bytes, 0, 0, is_allow_zero); diff --git a/test/stage1/behavior/slice.zig b/test/stage1/behavior/slice.zig index 5fa6c2402..d4a8353ca 100644 --- a/test/stage1/behavior/slice.zig +++ b/test/stage1/behavior/slice.zig @@ -54,3 +54,14 @@ test "comptime slices are disambiguated" { expect(sliceSum([_]u8{ 1, 2 }) == 3); expect(sliceSum([_]u8{ 3, 4 }) == 7); } + +test "slice type with custom alignment" { + const LazilyResolvedType = struct { + anything: i32, + }; + var slice: []align(32) LazilyResolvedType = undefined; + var array: [10]LazilyResolvedType align(32) = undefined; + slice = &array; + slice[1].anything = 42; + expect(array[1].anything == 42); +} From c7bcf1a447de08a18f5113c97744233a54bb9af7 Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Tue, 18 Jun 2019 01:40:37 -0600 Subject: [PATCH 12/13] Fix windows create process retry/path search --- std/child_process.zig | 33 ++++++++++++++++++++------------- std/os/windows.zig | 2 ++ 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/std/child_process.zig b/std/child_process.zig index 8e4c086d1..6e6cca335 100644 --- a/std/child_process.zig +++ b/std/child_process.zig @@ -543,25 +543,32 @@ pub const ChildProcess = struct { const PATH = try process.getEnvVarOwned(self.allocator, "PATH"); defer self.allocator.free(PATH); + const PATHEXT = try process.getEnvVarOwned(self.allocator, "PATHEXT"); + defer self.allocator.free(PATHEXT); var it = mem.tokenize(PATH, ";"); - while (it.next()) |search_path| { - const joined_path = try fs.path.join(self.allocator, [_][]const u8{ search_path, app_name }); - defer self.allocator.free(joined_path); + retry: while (it.next()) |search_path| { + var ext_it = mem.tokenize(PATHEXT, ";"); + while (ext_it.next()) |app_ext| { + const app_basename = try mem.concat(self.allocator, u8, [_][]const u8{app_name[0..app_name.len - 1], app_ext}); + defer self.allocator.free(app_basename); - const joined_path_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, joined_path); - defer self.allocator.free(joined_path_w); + const joined_path = try fs.path.join(self.allocator, [_][]const u8{ search_path, app_basename }); + defer self.allocator.free(joined_path); - if (windowsCreateProcess(joined_path_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| { - break; - } else |err| if (err == error.FileNotFound) { - continue; - } else { - return err; + const joined_path_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, joined_path); + defer self.allocator.free(joined_path_w); + + if (windowsCreateProcess(joined_path_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| { + break :retry; + } else |err| switch (err) { + error.FileNotFound => { continue; }, + error.AccessDenied => { continue; }, + else => { return err; }, + } } } else { - // Every other error would have been returned earlier. - return error.FileNotFound; + return no_path_err; // return the original error } }; diff --git a/std/os/windows.zig b/std/os/windows.zig index 7b9feb7b1..d10ab695d 100644 --- a/std/os/windows.zig +++ b/std/os/windows.zig @@ -632,6 +632,7 @@ pub fn GetEnvironmentVariableW(lpName: LPWSTR, lpBuffer: LPWSTR, nSize: DWORD) G pub const CreateProcessError = error{ FileNotFound, + AccessDenied, InvalidName, Unexpected, }; @@ -663,6 +664,7 @@ pub fn CreateProcessW( switch (kernel32.GetLastError()) { ERROR.FILE_NOT_FOUND => return error.FileNotFound, ERROR.PATH_NOT_FOUND => return error.FileNotFound, + ERROR.ACCESS_DENIED => return error.AccessDenied, ERROR.INVALID_PARAMETER => unreachable, ERROR.INVALID_NAME => return error.InvalidName, else => |err| return unexpectedError(err), From 381c6a38b145665a22440f7aa816f0ddd9b70ee5 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Sun, 16 Jun 2019 22:11:09 +0200 Subject: [PATCH 13/13] Correct the isEmpty function Integrate isEmpty into the tests for std.atomic.Queue Fix wrong test Oops Simpler checking --- std/atomic/queue.zig | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig index e8d03c4f1..73db2996b 100644 --- a/std/atomic/queue.zig +++ b/std/atomic/queue.zig @@ -100,7 +100,7 @@ pub fn Queue(comptime T: type) type { pub fn isEmpty(self: *Self) bool { const held = self.mutex.acquire(); defer held.release(); - return self.head != null; + return self.head == null; } pub fn dump(self: *Self) void { @@ -172,12 +172,14 @@ test "std.atomic.Queue" { }; if (builtin.single_threaded) { + expect(context.queue.isEmpty()); { var i: usize = 0; while (i < put_thread_count) : (i += 1) { expect(startPuts(&context) == 0); } } + expect(!context.queue.isEmpty()); context.puts_done = 1; { var i: usize = 0; @@ -185,7 +187,10 @@ test "std.atomic.Queue" { expect(startGets(&context) == 0); } } + expect(context.queue.isEmpty()); } else { + expect(context.queue.isEmpty()); + var putters: [put_thread_count]*std.Thread = undefined; for (putters) |*t| { t.* = try std.Thread.spawn(&context, startPuts); @@ -200,6 +205,8 @@ test "std.atomic.Queue" { _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); for (getters) |t| t.wait(); + + expect(context.queue.isEmpty()); } if (context.put_sum != context.get_sum) { @@ -250,6 +257,7 @@ fn startGets(ctx: *Context) u8 { test "std.atomic.Queue single-threaded" { var queue = Queue(i32).init(); + expect(queue.isEmpty()); var node_0 = Queue(i32).Node{ .data = 0, @@ -257,6 +265,7 @@ test "std.atomic.Queue single-threaded" { .prev = undefined, }; queue.put(&node_0); + expect(!queue.isEmpty()); var node_1 = Queue(i32).Node{ .data = 1, @@ -264,8 +273,10 @@ test "std.atomic.Queue single-threaded" { .prev = undefined, }; queue.put(&node_1); + expect(!queue.isEmpty()); expect(queue.get().?.data == 0); + expect(!queue.isEmpty()); var node_2 = Queue(i32).Node{ .data = 2, @@ -273,6 +284,7 @@ test "std.atomic.Queue single-threaded" { .prev = undefined, }; queue.put(&node_2); + expect(!queue.isEmpty()); var node_3 = Queue(i32).Node{ .data = 3, @@ -280,10 +292,13 @@ test "std.atomic.Queue single-threaded" { .prev = undefined, }; queue.put(&node_3); + expect(!queue.isEmpty()); expect(queue.get().?.data == 1); + expect(!queue.isEmpty()); expect(queue.get().?.data == 2); + expect(!queue.isEmpty()); var node_4 = Queue(i32).Node{ .data = 4, @@ -291,13 +306,17 @@ test "std.atomic.Queue single-threaded" { .prev = undefined, }; queue.put(&node_4); + expect(!queue.isEmpty()); expect(queue.get().?.data == 3); node_3.next = null; + expect(!queue.isEmpty()); expect(queue.get().?.data == 4); + expect(queue.isEmpty()); expect(queue.get() == null); + expect(queue.isEmpty()); } test "std.atomic.Queue dump" {