From 0f7de58b642539ad6a71368940f43f59a41e71b2 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Mon, 26 Nov 2018 01:09:12 +0900 Subject: [PATCH 01/37] std.mem: add new separate method and rework SplitIterator; --- std/mem.zig | 104 +++++++++++++++++++++++++++++++++++++++++++----- std/os/path.zig | 14 ++++--- 2 files changed, 103 insertions(+), 15 deletions(-) diff --git a/std/mem.zig b/std/mem.zig index 005d88791f2636350e8d4c824928ee685e46b989..adb8b0595667e540a14f71670184a32e6d90caf5 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -607,11 +607,15 @@ pub fn eql_slice_u8(a: []const u8, b: []const u8) bool { /// any of the bytes in `split_bytes`. /// split(" abc def ghi ", " ") /// Will return slices for "abc", "def", "ghi", null, in that order. +/// If `split_bytes` does not exist in buffer, +/// the iterator will return `buffer`, null, in that order. pub fn split(buffer: []const u8, split_bytes: []const u8) SplitIterator { return SplitIterator{ .index = 0, .buffer = buffer, .split_bytes = split_bytes, + .glob = true, + .spun = false, }; } @@ -621,6 +625,76 @@ test "mem.split" { assert(eql(u8, it.next().?, "def")); assert(eql(u8, it.next().?, "ghi")); assert(it.next() == null); + + it = split("..\\bob", "\\"); + assert(eql(u8, it.next().?, "..")); + assert(eql(u8, "..", "..\\bob"[0..it.index])); + assert(eql(u8, it.next().?, "bob")); + assert(it.next() == null); + + it = split("//a/b", "/"); + assert(eql(u8, it.next().?, "a")); + assert(eql(u8, it.next().?, "b")); + assert(eql(u8, "//a/b", "//a/b"[0..it.index])); + assert(it.next() == null); + + it = split("|", "|"); + assert(it.next() == null); + + it = split("", "|"); + assert(eql(u8, it.next().?, "")); + assert(it.next() == null); + + it = split("hello", ""); + assert(eql(u8, it.next().?, "hello")); + assert(it.next() == null); + + it = split("hello", " "); + assert(eql(u8, it.next().?, "hello")); + assert(it.next() == null); +} + +/// Returns an iterator that iterates over the slices of `buffer` that +/// seperates by bytes in `delimiter`. +/// separate("abc|def||ghi", "|") +/// Will return slices for "abc", "def", "", "ghi", null, in that order. +/// If `delimiter` does not exist in buffer, +/// the iterator will return `buffer`, null, in that order. +pub fn separate(buffer: []const u8, delimiter: []const u8) SplitIterator { + return SplitIterator{ + .index = 0, + .buffer = buffer, + .split_bytes = delimiter, + .glob = false, + .spun = false, + }; +} + +test "mem.separate" { + var it = separate("abc|def||ghi", "|"); + assert(eql(u8, it.next().?, "abc")); + assert(eql(u8, it.next().?, "def")); + assert(eql(u8, it.next().?, "")); + assert(eql(u8, it.next().?, "ghi")); + assert(it.next() == null); + + it = separate("", "|"); + assert(eql(u8, it.next().?, "")); + assert(it.next() == null); + + it = separate("|", "|"); + assert(eql(u8, it.next().?, "")); + assert(eql(u8, it.next().?, "")); + assert(it.next() == null); + + it = separate("hello", ""); + assert(eql(u8, it.next().?, "hello")); + assert(it.next() == null); + + it = separate("hello", " "); + assert(eql(u8, it.next().?, "hello")); + assert(it.next() == null); + } pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool { @@ -645,20 +719,32 @@ pub const SplitIterator = struct { buffer: []const u8, split_bytes: []const u8, index: usize, + glob: bool, + spun: bool, + /// Iterates and returns null or optionally a slice the next split segment pub fn next(self: *SplitIterator) ?[]const u8 { - // move to beginning of token - while (self.index < self.buffer.len and self.isSplitByte(self.buffer[self.index])) : (self.index += 1) {} - const start = self.index; - if (start == self.buffer.len) { - return null; + if (self.spun) { + if (self.index + 1 > self.buffer.len) return null; + self.index += 1; } - // move to end of token - while (self.index < self.buffer.len and !self.isSplitByte(self.buffer[self.index])) : (self.index += 1) {} - const end = self.index; + self.spun = true; - return self.buffer[start..end]; + if (self.glob) { + while (self.index < self.buffer.len and self.isSplitByte(self.buffer[self.index])) : (self.index += 1) {} + } + + var cursor = self.index; + while (cursor < self.buffer.len and !self.isSplitByte(self.buffer[cursor])) : (cursor += 1) {} + + defer self.index = cursor; + + if (cursor == self.buffer.len) { + return if (self.glob and self.index == cursor and self.index > 0) null else self.buffer[self.index..]; + } + + return self.buffer[self.index..cursor]; } /// Returns a slice of the remaining bytes. Does not affect iterator state. diff --git a/std/os/path.zig b/std/os/path.zig index 0d636353a8df235ac032695c63fbc28cd281b809..2938c9ab9d80e5f64436d0632e77c55dc985db9c 100644 --- a/std/os/path.zig +++ b/std/os/path.zig @@ -967,12 +967,14 @@ pub fn relativeWindows(allocator: *Allocator, from: []const u8, to: []const u8) // shave off the trailing slash result_index -= 1; - var rest_it = mem.split(to_rest, "/\\"); - while (rest_it.next()) |to_component| { - result[result_index] = '\\'; - result_index += 1; - mem.copy(u8, result[result_index..], to_component); - result_index += to_component.len; + if (to_rest.len > 0) { + var rest_it = mem.split(to_rest, "/\\"); + while (rest_it.next()) |to_component| { + result[result_index] = '\\'; + result_index += 1; + mem.copy(u8, result[result_index..], to_component); + result_index += to_component.len; + } } return result[0..result_index]; -- 2.54.0 From ff1b2889f3819610aee17ddbecdf716eb573ca2f Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 30 Nov 2018 02:17:15 +0900 Subject: [PATCH 02/37] std.mem: split: test for multiple seperator bytes; --- std/mem.zig | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/std/mem.zig b/std/mem.zig index adb8b0595667e540a14f71670184a32e6d90caf5..e27ad1a0158bb343d9b876e9695a131719814577 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -654,6 +654,16 @@ test "mem.split" { assert(it.next() == null); } +test "mem.split (multibyte)" { + var it = split("a|b,c/d e", " /,|"); + assert(eql(u8, it.next().?, "a")); + assert(eql(u8, it.next().?, "b")); + assert(eql(u8, it.next().?, "c")); + assert(eql(u8, it.next().?, "d")); + assert(eql(u8, it.next().?, "e")); + assert(it.next() == null); +} + /// Returns an iterator that iterates over the slices of `buffer` that /// seperates by bytes in `delimiter`. /// separate("abc|def||ghi", "|") @@ -694,7 +704,16 @@ test "mem.separate" { it = separate("hello", " "); assert(eql(u8, it.next().?, "hello")); assert(it.next() == null); +} +test "mem.separate (multibyte)" { + var it = separate("a|b,c/d e", " /,|"); + assert(eql(u8, it.next().?, "a")); + assert(eql(u8, it.next().?, "b")); + assert(eql(u8, it.next().?, "c")); + assert(eql(u8, it.next().?, "d")); + assert(eql(u8, it.next().?, "e")); + assert(it.next() == null); } pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool { -- 2.54.0 From 1ab66f3b55bbeac3c6f4f02c86a688cdc1bb2476 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Fri, 23 Nov 2018 10:02:14 -0600 Subject: [PATCH 03/37] Added serialization, bitstreams, traits for integer sign, TagPayloadType --- std/io.zig | 639 ++++++++++++++++++++++++++++++++++++++++++++- std/io_test.zig | 329 +++++++++++++++++++++++ std/meta/index.zig | 38 ++- std/meta/trait.zig | 31 +++ 4 files changed, 1029 insertions(+), 8 deletions(-) diff --git a/std/io.zig b/std/io.zig index f4122a2f8b7ea9c822011e0dccbd67e5854a5834..219db124a0ee1cfdaa4db7d2c5a9f2ee53cb9139 100644 --- a/std/io.zig +++ b/std/io.zig @@ -8,6 +8,8 @@ const debug = std.debug; const assert = debug.assert; const os = std.os; const mem = std.mem; +const meta = std.meta; +const trait = meta.trait; const Buffer = std.Buffer; const fmt = std.fmt; const File = std.os.File; @@ -444,6 +446,151 @@ pub const SliceInStream = struct { } }; +/// Creates a stream which allows for reading bit fields from another stream +pub fn BitInStream(endian: builtin.Endian, comptime Error: type) type { + return struct { + const Self = @This(); + + in_stream: *Stream, + bit_buffer: u7, + bit_count: u3, + stream: Stream, + + pub const Stream = InStream(Error); + const u8_bit_count = comptime meta.bitCount(u8); + const u7_bit_count = comptime meta.bitCount(u7); + const u4_bit_count = comptime meta.bitCount(u4); + + pub fn init(in_stream: *Stream) Self { + return Self{ + .in_stream = in_stream, + .bit_buffer = 0, + .bit_count = 0, + .stream = Stream{ .readFn = read }, + }; + } + + /// Reads `bits` bits from the stream and returns a specified unsigned int type + /// containing them in the least significant end, returning an error if the + /// specified number of bits could not be read. + pub fn readBitsNoEof(self: *Self, comptime U: type, bits: usize) !U { + var n: usize = undefined; + const result = try self.readBits(U, bits, &n); + if (n < bits) return error.EndOfStream; + return result; + } + + /// Reads `bits` bits from the stream and returns a specified unsigned int type + /// containing them in the least significant end. The number of bits successfully + /// read is placed in `out_bits`, as reaching the end of the stream is not an error. + pub fn readBits(self: *Self, comptime U: type, bits: usize, out_bits: *usize) Error!U { + debug.assert(trait.isUnsignedInt(U)); + + //by extending the buffer to a minimum of u8 we can cover a number of edge cases + // related to shifting and casting. + const u_bit_count = comptime meta.bitCount(U); + const buf_bit_count = bc: { + debug.assert(u_bit_count >= bits); + break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count; + }; + const Buf = @IntType(false, buf_bit_count); + const BufShift = math.Log2Int(Buf); + + out_bits.* = usize(0); + if (U == u0 or bits == 0) return 0; + var out_buffer = Buf(0); + + if (self.bit_count > 0) { + const n = if (self.bit_count >= bits) @intCast(u3, bits) else self.bit_count; + const shift = u7_bit_count - n; + switch (endian) { + builtin.Endian.Big => { + out_buffer = Buf(self.bit_buffer >> shift); + self.bit_buffer <<= n; + }, + builtin.Endian.Little => { + const value = (self.bit_buffer << shift) >> shift; + out_buffer = Buf(value); + self.bit_buffer >>= n; + }, + } + self.bit_count -= n; + out_bits.* = n; + } + //at this point we know bit_buffer is empty + + //copy bytes until we have enough bits, then leave the rest in bit_buffer + while (out_bits.* < bits) { + const n = bits - out_bits.*; + const next_byte = self.in_stream.readByte() catch |err| { + if (err == error.EndOfStream) { + return @intCast(U, out_buffer); + } + return err; + }; + + switch (endian) { + builtin.Endian.Big => { + if (n >= u8_bit_count) { + out_buffer <<= @intCast(u3, u8_bit_count - 1); + out_buffer <<= 1; + out_buffer |= Buf(next_byte); + out_bits.* += u8_bit_count; + continue; + } + + const shift = @intCast(u3, u8_bit_count - n); + out_buffer <<= @intCast(BufShift, n); + out_buffer |= Buf(next_byte >> shift); + out_bits.* += n; + self.bit_buffer = @truncate(u7, next_byte << @intCast(u3, n - 1)); + self.bit_count = shift; + }, + builtin.Endian.Little => { + if (n >= u8_bit_count) { + out_buffer |= Buf(next_byte) << @intCast(BufShift, out_bits.*); + out_bits.* += u8_bit_count; + continue; + } + + const shift = @intCast(u3, u8_bit_count - n); + const value = (next_byte << shift) >> shift; + out_buffer |= Buf(value) << @intCast(BufShift, out_bits.*); + out_bits.* += n; + self.bit_buffer = @truncate(u7, next_byte >> @intCast(u3, n)); + self.bit_count = shift; + }, + } + } + + return @intCast(U, out_buffer); + } + + pub fn alignToByte(self: *Self) void { + self.bit_buffer = 0; + self.bit_count = 0; + } + + pub fn read(self_stream: *Stream, buffer: []u8) Error!usize { + var self = @fieldParentPtr(Self, "stream", self_stream); + + var out_bits: usize = undefined; + var out_bits_total = usize(0); + //@NOTE: I'm not sure this is a good idea, maybe alignToByte should be forced + if (self.bit_count > 0) { + for (buffer) |*b, i| { + b.* = try self.readBits(u8, u8_bit_count, &out_bits); + out_bits_total += out_bits; + } + const incomplete_byte = @boolToInt(out_bits_total % u8_bit_count > 0); + return (out_bits_total / u8_bit_count) + incomplete_byte; + } + + return self.in_stream.read(buffer); + } + }; +} + /// This is a simple OutStream that writes to a slice, and returns an error /// when it runs out of space. pub const SliceOutStream = struct { @@ -637,6 +784,137 @@ pub const BufferOutStream = struct { } }; +/// Creates a stream which allows for writing bit fields to another stream +pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type { + return struct { + const Self = @This(); + + out_stream: *Stream, + bit_buffer: u8, + bit_count: u4, + stream: Stream, + + pub const Stream = OutStream(Error); + const u8_bit_count = comptime meta.bitCount(u8); + const u4_bit_count = comptime meta.bitCount(u4); + + pub fn init(out_stream: *Stream) Self { + return Self{ + .out_stream = out_stream, + .bit_buffer = 0, + .bit_count = 0, + .stream = Stream{ .writeFn = write }, + }; + } + + /// Write the specified number of bits to the stream from the least significant bits of + /// the specified unsigned int value. Bits will only be written to the stream when there + /// are enough to fill a byte. + pub fn writeBits(self: *Self, value: var, bits: usize) Error!void { + if (bits == 0) return; + + const U = @typeOf(value); + debug.assert(trait.isUnsignedInt(U)); + + //by extending the buffer to a minimum of u8 we can cover a number of edge cases + // related to shifting and casting. + const u_bit_count = comptime meta.bitCount(U); + const buf_bit_count = bc: { + debug.assert(u_bit_count >= bits); + break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count; + }; + const Buf = @IntType(false, buf_bit_count); + const BufShift = math.Log2Int(Buf); + + const buf_value = @intCast(Buf, value); + + const high_byte_shift = @intCast(BufShift, buf_bit_count - u8_bit_count); + var in_buffer = switch (endian) { + builtin.Endian.Big => buf_value << @intCast(BufShift, buf_bit_count - bits), + builtin.Endian.Little => buf_value, + }; + var in_bits = bits; + + if (self.bit_count > 0) { + const bits_remaining = u8_bit_count - self.bit_count; + const n = @intCast(u3, if (bits_remaining > bits) bits else bits_remaining); + switch (endian) { + builtin.Endian.Big => { + const shift = @intCast(BufShift, high_byte_shift + self.bit_count); + const v = @intCast(u8, in_buffer >> shift); + self.bit_buffer |= v; + in_buffer <<= n; + }, + builtin.Endian.Little => { + const v = @truncate(u8, in_buffer) << @intCast(u3, self.bit_count); + self.bit_buffer |= v; + in_buffer >>= n; + }, + } + self.bit_count += n; + in_bits -= n; + + //if we didn't fill the buffer, it's because bits < bits_remaining; + if (self.bit_count != u8_bit_count) return; + try self.out_stream.writeByte(self.bit_buffer); + self.bit_buffer = 0; + self.bit_count = 0; + } + //at this point we know bit_buffer is empty + + //copy bytes until we can't fill one anymore, then leave the rest in bit_buffer + while (in_bits >= u8_bit_count) { + switch (endian) { + builtin.Endian.Big => { + const v = @intCast(u8, in_buffer >> high_byte_shift); + try self.out_stream.writeByte(v); + in_buffer <<= @intCast(u3, u8_bit_count - 1); + in_buffer <<= 1; + }, + builtin.Endian.Little => { + const v = @truncate(u8, in_buffer); + try self.out_stream.writeByte(v); + in_buffer >>= @intCast(u3, u8_bit_count - 1); + in_buffer >>= 1; + }, + } + in_bits -= u8_bit_count; + } + + if (in_bits > 0) { + self.bit_count = @intCast(u4, in_bits); + self.bit_buffer = switch (endian) { + builtin.Endian.Big => @truncate(u8, in_buffer >> high_byte_shift), + builtin.Endian.Little => @truncate(u8, in_buffer), + }; + } + } + + /// Flush any remaining bits to the stream. + pub fn flushBits(self: *Self) !void { + if (self.bit_count == 0) return; + try self.out_stream.writeByte(self.bit_buffer); + self.bit_buffer = 0; + self.bit_count = 0; + } + + pub fn write(self_stream: *Stream, buffer: []const u8) Error!void { + var self = @fieldParentPtr(Self, "stream", self_stream); + + //@NOTE: I'm not sure this is a good idea, maybe flushBits should be forced + if (self.bit_count > 0) { + for (buffer) |b, i| + try self.writeBits(b, u8_bit_count); + return; + } + + return self.out_stream.write(buffer); + } + }; +} + + + pub const BufferedAtomicFile = struct { atomic_file: os.AtomicFile, file_stream: os.File.OutStream, @@ -677,11 +955,6 @@ pub const BufferedAtomicFile = struct { } }; -test "import io tests" { - comptime { - _ = @import("io_test.zig"); - } -} pub fn readLine(buf: *std.Buffer) ![]u8 { var stdin = try getStdIn(); @@ -753,3 +1026,359 @@ test "io.readLineSliceFrom" { debug.assert(mem.eql(u8, "Line 1", try readLineSliceFrom(stream, buf[0..]))); debug.assertError(readLineSliceFrom(stream, buf[0..]), error.OutOfMemory); } + +/// Creates a deserializer that deserializes types from any stream. +/// If `is_packed` is true, the data stream is treated as bit-packed, +/// otherwise data is expected to be packed to the smallest byte. +/// Types may implement a custom deserialization routine with a +/// function named `deserialize` in the form of: +/// pub fn deserialize(self: *Self, deserializer: var) !void +/// which will be called when the deserializer is used to deserialize +/// that type. It will pass a pointer to the type instance to deserialize +/// into and a pointer to the deserializer struct. +pub fn Deserializer(endian: builtin.Endian, is_packed: bool, comptime Error: type) type { + return struct { + const Self = @This(); + + in_stream: if (is_packed) BitInStream(endian, Stream.Error) else *Stream, + + pub const Stream = InStream(Error); + + pub fn init(in_stream: *Stream) Self { + return Self{ .in_stream = switch (is_packed) { + true => BitInStream(endian, Stream.Error).init(in_stream), + else => in_stream, + } }; + } + + //@BUG: inferred error issue + fn deserializeInt(self: *Self, comptime T: type) (Stream.Error || error{EndOfStream})!T { + debug.assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T)); + + const u8_bit_count = comptime meta.bitCount(u8); + const t_bit_count = comptime meta.bitCount(T); + + const U = @IntType(false, t_bit_count); + const Log2U = math.Log2Int(U); + const int_size = @sizeOf(U); + + if (is_packed) { + const result = try self.in_stream.readBitsNoEof(U, t_bit_count); + return @bitCast(T, result); + } + + var buffer: [int_size]u8 = undefined; + const read_size = try self.in_stream.read(buffer[0..]); + if (read_size < int_size) return error.EndOfStream; + + if (int_size == 1) return @bitCast(T, buffer[0]); + + var result = U(0); + for (buffer) |byte, i| { + switch (endian) { + builtin.Endian.Big => { + result = (result << @intCast(u4, u8_bit_count)) | byte; + }, + builtin.Endian.Little => { + result |= U(byte) << @intCast(Log2U, u8_bit_count * i); + }, + } + } + + return @bitCast(T, result); + } + + //@TODO: Replace this with @unionInit or whatever when it is added + // see: #1315 + fn setTag(ptr: var, tag: var) void { + const T = @typeOf(ptr); + comptime debug.assert(trait.isPtrTo(builtin.TypeId.Union)(T)); + const U = meta.Child(T); + + const info = @typeInfo(U).Union; + if (info.tag_type) |TagType| { + debug.assert(TagType == @typeOf(tag)); + + var ptr_tag = ptr: { + if (@alignOf(TagType) >= @alignOf(U)) break :ptr @ptrCast(*TagType, ptr); + const offset = comptime max: { + var max_field_size: comptime_int = 0; + for (info.fields) |field_info| { + const field_size = @sizeOf(field_info.field_type); + max_field_size = math.max(max_field_size, field_size); + } + break :max math.max(max_field_size, @alignOf(U)); + }; + break :ptr @intToPtr(*TagType, @ptrToInt(ptr) + offset); + }; + ptr_tag.* = tag; + } + } + + /// Deserializes and returns data of the specified type from the stream + pub fn deserialize(self: *Self, comptime T: type) !T { + var value: T = undefined; + try self.deserializeInto(&value); + return value; + } + + /// Deserializes data into the type pointed to by `ptr` + pub fn deserializeInto(self: *Self, ptr: var) !void { + const T = @typeOf(ptr); + debug.assert(trait.is(builtin.TypeId.Pointer)(T)); + + if (comptime trait.isSlice(T) or comptime trait.isPtrTo(builtin.TypeId.Array)(T)) { + for (ptr) |*v| + try self.deserializeInto(v); + return; + } + + comptime debug.assert(trait.isSingleItemPtr(T)); + + const C = comptime meta.Child(T); + const child_type_id = @typeId(C); + + //custom deserializer: fn(self: *Self, deserializer: var) !void + if (comptime trait.hasFn("deserialize")(C)) return ptr.deserialize(self); + + if (comptime trait.isPacked(C) and !is_packed) { + var packed_deserializer = Deserializer(endian, true, Error).init(self.in_stream); + return packed_deserializer.deserializeInto(ptr); + } + + switch (child_type_id) { + builtin.TypeId.Void => return, + builtin.TypeId.Bool => ptr.* = (try self.deserializeInt(u1)) > 0, + builtin.TypeId.Float, builtin.TypeId.Int => ptr.* = try self.deserializeInt(C), + builtin.TypeId.Struct => { + const info = @typeInfo(C).Struct; + + inline for (info.fields) |*field_info| { + const name = field_info.name; + const FieldType = field_info.field_type; + + if (FieldType == void or FieldType == u0) continue; + + //it doesn't make any sense to read pointers + if (comptime trait.is(builtin.TypeId.Pointer)(FieldType)) { + @compileError("Will not " ++ "read field " ++ name ++ " of struct " ++ + @typeName(C) ++ " because it " ++ "is of pointer-type " ++ + @typeName(FieldType) ++ "."); + } + + try self.deserializeInto(&@field(ptr, name)); + } + }, + builtin.TypeId.Union => { + const info = @typeInfo(C).Union; + if (info.tag_type) |TagType| { + //we avoid duplicate iteration over the enum tags + // by getting the int directly and casting it without + // safety. If it is bad, it will be caught anyway. + const TagInt = @TagType(TagType); + const tag = try self.deserializeInt(TagInt); + + { + @setRuntimeSafety(false); + //See: #1315 + setTag(ptr, @intToEnum(TagType, tag)); + } + + inline for (info.fields) |field_info| { + if (field_info.enum_field.?.value == tag) { + const name = field_info.name; + const FieldType = field_info.field_type; + @field(ptr, name) = FieldType(undefined); + try self.deserializeInto(&@field(ptr, name)); + return; + } + } + //This is reachable if the enum data is bad + return error.InvalidEnumTag; + } + @compileError("Cannot meaningfully deserialize " ++ @typeName(C) ++ + " because it is an untagged union Use a custom deserialize()."); + }, + builtin.TypeId.Optional => { + const OC = comptime meta.Child(C); + const exists = (try self.deserializeInt(u1)) > 0; + if (!exists) { + ptr.* = null; + return; + } + + //The way non-pointer optionals are implemented ensures a pointer to them + // will point to the value. The flag is stored at the end of that data. + var val_ptr = @ptrCast(*OC, ptr); + try self.deserializeInto(val_ptr); + //This bit ensures the null flag isn't set. Any actual copying should be + // optimized out... I hope. + ptr.* = val_ptr.*; + }, + builtin.TypeId.Enum => { + var value = try self.deserializeInt(@TagType(C)); + ptr.* = try meta.intToEnum(C, value); + }, + else => { + @compileError("Cannot deserialize " ++ @tagName(child_type_id) ++ " types (unimplemented)."); + }, + } + } + }; +} + +/// Creates a serializer that serializes types to any stream. +/// If `is_packed` is true, the data will be bit-packed into the stream. +/// Note that the you must call `serializer.flush()` when you are done +/// writing bit-packed data in order ensure any unwritten bits are committed. +/// If `is_packed` is false, data is packed to the smallest byte. In the case +/// of packed structs, the struct will written bit-packed and with the specified +/// endianess, after which data will resume being written at the next byte boundary. +/// Types may implement a custom serialization routine with a +/// function named `serialize` in the form of: +/// pub fn serialize(self: *const Self, serializer: var) !void +/// which will be called when the serializer is used to serialize that type. It will +/// pass a const pointer to the type instance to be serialized and a pointer +/// to the serializer struct. +pub fn Serializer(endian: builtin.Endian, is_packed: bool, comptime Error: type) type { + return struct { + const Self = @This(); + + out_stream: if (is_packed) BitOutStream(endian, Stream.Error) else *Stream, + + pub const Stream = OutStream(Error); + + pub fn init(out_stream: *Stream) Self { + return Self{ .out_stream = switch (is_packed) { + true => BitOutStream(endian, Stream.Error).init(out_stream), + else => out_stream, + } }; + } + + /// Flushes any unwritten bits to the stream + pub fn flush(self: *Self) Stream.Error!void { + if (is_packed) return self.out_stream.flushBits(); + } + + fn serializeInt(self: *Self, value: var) !void { + const T = @typeOf(value); + debug.assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T)); + + const t_bit_count = comptime meta.bitCount(T); + const u8_bit_count = comptime meta.bitCount(u8); + + const U = @IntType(false, t_bit_count); + const Log2U = math.Log2Int(U); + const int_size = @sizeOf(U); + + const u_value = @bitCast(U, value); + + if (is_packed) return self.out_stream.writeBits(u_value, t_bit_count); + + var buffer: [int_size]u8 = undefined; + if (int_size == 1) buffer[0] = u_value; + + for (buffer) |*byte, i| { + const idx = switch (endian) { + builtin.Endian.Big => int_size - i - 1, + builtin.Endian.Little => i, + }; + const shift = @intCast(Log2U, idx * u8_bit_count); + const v = u_value >> shift; + byte.* = if (t_bit_count < u8_bit_count) v else @truncate(u8, v); + } + + try self.out_stream.write(buffer); + } + + /// Serializes the passed value into the stream + pub fn serialize(self: *Self, value: var) !void { + const T = comptime @typeOf(value); + + if (comptime trait.isIndexable(T)) { + for (value) |v| + try self.serialize(v); + return; + } + + //custom serializer: fn(self: *const Self, serializer: var) !void + if (comptime trait.hasFn("serialize")(T)) return value.serialize(self); + + if (comptime trait.isPacked(T) and !is_packed) { + var packed_serializer = Serializer(endian, true, Error).init(self.out_stream); + try packed_serializer.serialize(value); + try packed_serializer.flush(); + return; + } + + switch (@typeId(T)) { + builtin.TypeId.Void => return, + builtin.TypeId.Bool => try self.serializeInt(u1(@boolToInt(value))), + builtin.TypeId.Float, builtin.TypeId.Int => try self.serializeInt(value), + builtin.TypeId.Struct => { + const info = @typeInfo(T); + + inline for (info.Struct.fields) |*field_info| { + const name = field_info.name; + const FieldType = field_info.field_type; + + if (FieldType == void or FieldType == u0) continue; + + //It doesn't make sense to write pointers + if (comptime trait.is(builtin.TypeId.Pointer)(FieldType)) { + @compileError("Will not " ++ "serialize field " ++ name ++ + " of struct " ++ @typeName(T) ++ " because it " ++ + "is of pointer-type " ++ @typeName(FieldType) ++ "."); + } + try self.serialize(@field(value, name)); + } + }, + builtin.TypeId.Union => { + const info = @typeInfo(T).Union; + if (info.tag_type) |TagType| { + const active_tag = meta.activeTag(value); + try self.serialize(active_tag); + //This inline loop is necessary because active_tag is a runtime + // value, but @field requires a comptime value. Our alternative + // is to check each field for a match + inline for (info.fields) |field_info| { + if (field_info.enum_field.?.value == @enumToInt(active_tag)) { + const name = field_info.name; + const FieldType = field_info.field_type; + try self.serialize(@field(value, name)); + return; + } + } + unreachable; + } + @compileError("Cannot meaningfully serialize " ++ @typeName(T) ++ + " because it is an untagged union Use a custom serialize()."); + }, + builtin.TypeId.Optional => { + if (value == null) { + try self.serializeInt(u1(@boolToInt(false))); + return; + } + try self.serializeInt(u1(@boolToInt(true))); + + const OC = comptime meta.Child(T); + + //The way non-pointer optionals are implemented ensures a pointer to them + // will point to the value. The flag is stored at the end of that data. + var val_ptr = @ptrCast(*const OC, &value); + try self.serialize(val_ptr.*); + }, + builtin.TypeId.Enum => { + try self.serializeInt(@enumToInt(value)); + }, + else => @compileError("Cannot serialize " ++ @tagName(@typeId(T)) ++ " types (unimplemented)."), + } + } + }; +} + +test "import io tests" { + comptime { + _ = @import("io_test.zig"); + } +} diff --git a/std/io_test.zig b/std/io_test.zig index 52241995275ece21ffae0add6aad535c7b30e988..fe06a3e2c90a56bcb99d5d472a1a0d17cef02352 100644 --- a/std/io_test.zig +++ b/std/io_test.zig @@ -1,5 +1,7 @@ const std = @import("index.zig"); const io = std.io; +const meta = std.meta; +const trait = std.trait; const DefaultPrng = std.rand.DefaultPrng; const assert = std.debug.assert; const assertError = std.debug.assertError; @@ -132,3 +134,330 @@ test "SliceOutStream" { assertError(ss.stream.write("Hello world!"), error.OutOfSpace); assert(mem.eql(u8, ss.getWritten(), "Hello worl")); } + +test "BitInStream" { + const mem_be = []u8{ 0b11001101, 0b00001011 }; + const mem_le = []u8{ 0b00011101, 0b10010101 }; + + var mem_in_be = io.SliceInStream.init(mem_be[0..]); + const InError = io.SliceInStream.Error; + var bit_stream_be = io.BitInStream(builtin.Endian.Big, InError).init(&mem_in_be.stream); + + var out_bits: usize = undefined; + + assert(1 == try bit_stream_be.readBits(u2, 1, &out_bits)); + assert(out_bits == 1); + assert(2 == try bit_stream_be.readBits(u5, 2, &out_bits)); + assert(out_bits == 2); + assert(3 == try bit_stream_be.readBits(u128, 3, &out_bits)); + assert(out_bits == 3); + assert(4 == try bit_stream_be.readBits(u8, 4, &out_bits)); + assert(out_bits == 4); + assert(5 == try bit_stream_be.readBits(u9, 5, &out_bits)); + assert(out_bits == 5); + assert(1 == try bit_stream_be.readBits(u1, 1, &out_bits)); + assert(out_bits == 1); + + mem_in_be.pos = 0; + bit_stream_be.bit_count = 0; + assert(0b110011010000101 == try bit_stream_be.readBits(u15, 15, &out_bits)); + assert(out_bits == 15); + + mem_in_be.pos = 0; + bit_stream_be.bit_count = 0; + assert(0b1100110100001011 == try bit_stream_be.readBits(u16, 16, &out_bits)); + assert(out_bits == 16); + + _ = try bit_stream_be.readBits(u0, 0, &out_bits); + + var mem_in_le = io.SliceInStream.init(mem_le[0..]); + var bit_stream_le = io.BitInStream(builtin.Endian.Little, InError).init(&mem_in_le.stream); + + assert(1 == try bit_stream_le.readBits(u2, 1, &out_bits)); + assert(out_bits == 1); + assert(2 == try bit_stream_le.readBits(u5, 2, &out_bits)); + assert(out_bits == 2); + assert(3 == try bit_stream_le.readBits(u128, 3, &out_bits)); + assert(out_bits == 3); + assert(4 == try bit_stream_le.readBits(u8, 4, &out_bits)); + assert(out_bits == 4); + assert(5 == try bit_stream_le.readBits(u9, 5, &out_bits)); + assert(out_bits == 5); + assert(1 == try bit_stream_le.readBits(u1, 1, &out_bits)); + assert(out_bits == 1); + + mem_in_le.pos = 0; + bit_stream_le.bit_count = 0; + assert(0b001010100011101 == try bit_stream_le.readBits(u15, 15, &out_bits)); + assert(out_bits == 15); + + mem_in_le.pos = 0; + bit_stream_le.bit_count = 0; + assert(0b1001010100011101 == try bit_stream_le.readBits(u16, 16, &out_bits)); + assert(out_bits == 16); + + _ = try bit_stream_le.readBits(u0, 0, &out_bits); +} + +test "BitOutStream" { + var mem_be = []u8{0} ** 2; + var mem_le = []u8{0} ** 2; + + var mem_out_be = io.SliceOutStream.init(mem_be[0..]); + const OutError = io.SliceOutStream.Error; + var bit_stream_be = io.BitOutStream(builtin.Endian.Big, OutError).init(&mem_out_be.stream); + + try bit_stream_be.writeBits(u2(1), 1); + try bit_stream_be.writeBits(u5(2), 2); + try bit_stream_be.writeBits(u128(3), 3); + try bit_stream_be.writeBits(u8(4), 4); + try bit_stream_be.writeBits(u9(5), 5); + try bit_stream_be.writeBits(u1(1), 1); + + assert(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001011); + + mem_out_be.pos = 0; + + try bit_stream_be.writeBits(u15(0b110011010000101), 15); + try bit_stream_be.flushBits(); + assert(mem_be[0] == 0b11001101 and mem_be[1] == 0b00001010); + + mem_out_be.pos = 0; + try bit_stream_be.writeBits(u32(0b110011010000101), 16); + assert(mem_be[0] == 0b01100110 and mem_be[1] == 0b10000101); + + try bit_stream_be.writeBits(u0(0), 0); + + var mem_out_le = io.SliceOutStream.init(mem_le[0..]); + var bit_stream_le = io.BitOutStream(builtin.Endian.Little, OutError).init(&mem_out_le.stream); + + try bit_stream_le.writeBits(u2(1), 1); + try bit_stream_le.writeBits(u5(2), 2); + try bit_stream_le.writeBits(u128(3), 3); + try bit_stream_le.writeBits(u8(4), 4); + try bit_stream_le.writeBits(u9(5), 5); + try bit_stream_le.writeBits(u1(1), 1); + + assert(mem_le[0] == 0b00011101 and mem_le[1] == 0b10010101); + + mem_out_le.pos = 0; + try bit_stream_le.writeBits(u15(0b110011010000101), 15); + try bit_stream_le.flushBits(); + assert(mem_le[0] == 0b10000101 and mem_le[1] == 0b01100110); + + mem_out_le.pos = 0; + try bit_stream_le.writeBits(u32(0b1100110100001011), 16); + assert(mem_le[0] == 0b00001011 and mem_le[1] == 0b11001101); + + try bit_stream_le.writeBits(u0(0), 0); +} + +fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packed: bool) !void { + const max_test_bitsize = 17; + + const total_bytes = comptime blk: { + var bytes = 0; + comptime var i = 0; + while (i <= max_test_bitsize) : (i += 1) bytes += (i / 8) + @boolToInt(i % 8 > 0); + break :blk bytes * 2; + }; + + var data_mem: [total_bytes]u8 = undefined; + var out = io.SliceOutStream.init(data_mem[0..]); + const OutError = io.SliceOutStream.Error; + var out_stream = &out.stream; + var serializer = io.Serializer(endian, is_packed, OutError).init(out_stream); + + var in = io.SliceInStream.init(data_mem[0..]); + const InError = io.SliceInStream.Error; + var in_stream = &in.stream; + var deserializer = io.Deserializer(endian, is_packed, InError).init(in_stream); + + comptime var i = 0; + inline while (i <= max_test_bitsize) : (i += 1) { + const U = @IntType(false, i); + const S = @IntType(true, i); + try serializer.serializeInt(U(i)); + if (i != 0) try serializer.serializeInt(S(-1)); + } + try serializer.flush(); + + i = 0; + inline while (i <= max_test_bitsize) : (i += 1) { + const U = @IntType(false, i); + const S = @IntType(true, i); + const x = try deserializer.deserializeInt(U); + const y = if (i != 0) try deserializer.deserializeInt(S); + assert(x == U(i)); + if (i != 0) assert(y == S(-1)); + } + + const u8_bit_count = comptime meta.bitCount(u8); + //0 + 1 + 2 + ... n = (n * (n + 1)) / 2 + //and we have each for unsigned and signed, so * 2 + const total_bits = (max_test_bitsize * (max_test_bitsize + 1)); + const extra_packed_byte = @boolToInt(total_bits % u8_bit_count > 0); + const total_packed_bytes = (total_bits / u8_bit_count) + extra_packed_byte; + + + + assert(in.pos == if (is_packed) total_packed_bytes else total_bytes); +} + +test "Serializer/Deserializer Int" { + try testIntSerializerDeserializer(builtin.Endian.Big, false); + try testIntSerializerDeserializer(builtin.Endian.Little, false); + try testIntSerializerDeserializer(builtin.Endian.Big, true); + try testIntSerializerDeserializer(builtin.Endian.Little, true); +} + +fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packed: bool) !void { + const ColorType = enum(u4) { + RGB8 = 1, + RA16 = 2, + R32 = 3, + }; + + const TagAlign = union(enum(u32)) { + A: u8, + B: u8, + C: u8, + }; + + const Color = union(ColorType) { + RGB8: struct { + r: u8, + g: u8, + b: u8, + a: u8, + }, + RA16: struct { + r: u16, + a: u16, + }, + R32: u32, + }; + + const PackedStruct = packed struct { + f_i3: i3, + f_u2: u2, + }; + + //to test custom serialization + const Custom = struct { + f_f16: f16, + f_unused_u32: u32, + + pub fn deserialize(self: *@This(), deserializer: var) !void { + try deserializer.deserializeInto(&self.f_f16); + self.f_unused_u32 = 47; + } + + pub fn serialize(self: *const @This(), serializer: var) !void { + try serializer.serialize(self.f_f16); + } + }; + + const MyStruct = struct { + f_i3: i3, + f_u8: u8, + f_tag_align: TagAlign, + f_u24: u24, + f_i19: i19, + f_void: void, + f_f32: f32, + f_f128: f128, + f_packed_0: PackedStruct, + f_i7arr: [10]i7, + f_of64n: ?f64, + f_of64v: ?f64, + f_color_type: ColorType, + f_packed_1: PackedStruct, + f_custom: Custom, + f_color: Color, + }; + + const my_inst = MyStruct{ + .f_i3 = -1, + .f_u8 = 8, + .f_tag_align = TagAlign{ .B = 148 }, + .f_u24 = 24, + .f_i19 = 19, + .f_void = {}, + .f_f32 = 32.32, + .f_f128 = 128.128, + .f_packed_0 = PackedStruct{ .f_i3 = -1, .f_u2 = 2 }, + .f_i7arr = [10]i7{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, + .f_of64n = null, + .f_of64v = 64.64, + .f_color_type = ColorType.R32, + .f_packed_1 = PackedStruct{ .f_i3 = 1, .f_u2 = 1 }, + .f_custom = Custom{ .f_f16 = 38.63, .f_unused_u32 = 47 }, + .f_color = Color{ .R32 = 123822 }, + }; + + var data_mem: [@sizeOf(MyStruct)]u8 = undefined; + var out = io.SliceOutStream.init(data_mem[0..]); + const OutError = io.SliceOutStream.Error; + var out_stream = &out.stream; + var serializer = io.Serializer(endian, is_packed, OutError).init(out_stream); + + var in = io.SliceInStream.init(data_mem[0..]); + const InError = io.SliceInStream.Error; + var in_stream = &in.stream; + var deserializer = io.Deserializer(endian, is_packed, InError).init(in_stream); + + try serializer.serialize(my_inst); + + const my_copy = try deserializer.deserialize(MyStruct); + + assert(meta.eql(my_copy, my_inst)); +} + +test "Serializer/Deserializer generic" { + try testSerializerDeserializer(builtin.Endian.Big, false); + try testSerializerDeserializer(builtin.Endian.Little, false); + try testSerializerDeserializer(builtin.Endian.Big, true); + try testSerializerDeserializer(builtin.Endian.Little, true); +} + +fn testBadData(comptime endian: builtin.Endian, comptime is_packed: bool) !void { + const E = enum(u14) { + One = 1, + Two = 2, + }; + + const A = struct { + e: E, + }; + + const C = union(E) { + One: u14, + Two: f16, + }; + + var data_mem: [4]u8 = undefined; + var out = io.SliceOutStream.init(data_mem[0..]); + const OutError = io.SliceOutStream.Error; + var out_stream = &out.stream; + var serializer = io.Serializer(endian, is_packed, OutError).init(out_stream); + + var in = io.SliceInStream.init(data_mem[0..]); + const InError = io.SliceInStream.Error; + var in_stream = &in.stream; + var deserializer = io.Deserializer(endian, is_packed, InError).init(in_stream); + + try serializer.serialize(u14(3)); + assertError(deserializer.deserialize(A), error.InvalidEnumTag); + out.pos = 0; + try serializer.serialize(u14(3)); + try serializer.serialize(u14(88)); + assertError(deserializer.deserialize(C), error.InvalidEnumTag); +} + +test "Deserializer bad data" { + try testBadData(builtin.Endian.Big, false); + try testBadData(builtin.Endian.Little, false); + try testBadData(builtin.Endian.Big, true); + try testBadData(builtin.Endian.Little, true); +} \ No newline at end of file diff --git a/std/meta/index.zig b/std/meta/index.zig index 69a30972888712272e435cd6d2da61e4c1aa51fc..4d195a6a12ac53f8a8e31151dbe5b4b352b0d62c 100644 --- a/std/meta/index.zig +++ b/std/meta/index.zig @@ -95,7 +95,7 @@ test "std.meta.stringToEnum" { debug.assert(null == stringToEnum(E1, "C")); } -pub fn bitCount(comptime T: type) u32 { +pub fn bitCount(comptime T: type) comptime_int { return switch (@typeInfo(T)) { TypeId.Int => |info| info.bits, TypeId.Float => |info| info.bits, @@ -108,7 +108,7 @@ test "std.meta.bitCount" { debug.assert(bitCount(f32) == 32); } -pub fn alignment(comptime T: type) u29 { +pub fn alignment(comptime T: type) comptime_int { //@alignOf works on non-pointer types const P = if (comptime trait.is(TypeId.Pointer)(T)) T else *T; return @typeInfo(P).Pointer.alignment; @@ -386,6 +386,33 @@ test "std.meta.activeTag" { debug.assert(activeTag(u) == UE.Float); } +///Given a tagged union type, and an enum, return the type of the union +/// field corresponding to the enum tag. +pub fn TagPayloadType(comptime U: type, tag: var) type { + const Tag = @typeOf(tag); + debug.assert(trait.is(builtin.TypeId.Union)(U)); + debug.assert(trait.is(builtin.TypeId.Enum)(Tag)); + + const info = @typeInfo(U).Union; + + inline for (info.fields) |field_info| { + if (field_info.enum_field.?.value == @enumToInt(tag)) return field_info.field_type; + } + unreachable; +} + +test "std.meta.TagPayloadType" { + const Event = union(enum) { + Moved: struct { + from: i32, + to: i32, + }, + }; + const MovedEvent = TagPayloadType(Event, Event.Moved); + var e: Event = undefined; + debug.assert(MovedEvent == @typeOf(e.Moved)); +} + ///Compares two of any type for equality. Containers are compared on a field-by-field basis, /// where possible. Pointers are not followed. pub fn eql(a: var, b: @typeOf(a)) bool { @@ -439,6 +466,11 @@ pub fn eql(a: var, b: @typeOf(a)) bool { builtin.TypeInfo.Pointer.Size.Slice => return a.ptr == b.ptr and a.len == b.len, } }, + builtin.TypeId.Optional => { + if(a == null and b == null) return true; + if(a == null or b == null) return false; + return eql(a.?, b.?); + }, else => return a == b, } } @@ -452,7 +484,7 @@ test "std.meta.eql" { const U = union(enum) { s: S, - f: f32, + f: ?f32, }; const s_1 = S{ diff --git a/std/meta/trait.zig b/std/meta/trait.zig index caf7f1be04447740e86fac9f8f504a75340477c1..c9d9e971c9f78562820d30fd4d29fb7c30e68ef3 100644 --- a/std/meta/trait.zig +++ b/std/meta/trait.zig @@ -231,6 +231,37 @@ test "std.meta.trait.isPacked" { debug.assert(!isPacked(u8)); } +/// +pub fn isUnsignedInt(comptime T: type) bool { + return switch (@typeId(T)) { + builtin.TypeId.Int => !@typeInfo(T).Int.is_signed, + else => false, + }; +} + +test "isUnsignedInt" { + debug.assert(isUnsignedInt(u32) == true); + debug.assert(isUnsignedInt(comptime_int) == false); + debug.assert(isUnsignedInt(i64) == false); + debug.assert(isUnsignedInt(f64) == false); +} + +/// +pub fn isSignedInt(comptime T: type) bool { + return switch (@typeId(T)) { + builtin.TypeId.ComptimeInt => true, + builtin.TypeId.Int => @typeInfo(T).Int.is_signed, + else => false, + }; +} + +test "isSignedInt" { + debug.assert(isSignedInt(u32) == false); + debug.assert(isSignedInt(comptime_int) == true); + debug.assert(isSignedInt(i64) == true); + debug.assert(isSignedInt(f64) == false); +} + /// pub fn isSingleItemPtr(comptime T: type) bool { if (comptime is(builtin.TypeId.Pointer)(T)) { -- 2.54.0 From b6489ff90afffcf0c69490efcb5941f3bb42fc3c Mon Sep 17 00:00:00 2001 From: tgschultz Date: Fri, 23 Nov 2018 22:29:40 -0600 Subject: [PATCH 04/37] Increased range of bitwidths tested by "serialize/deserialize Int" tests. Added tests for float inf and NaN. --- std/io.zig | 7 +++++- std/io_test.zig | 62 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/std/io.zig b/std/io.zig index 219db124a0ee1cfdaa4db7d2c5a9f2ee53cb9139..499422fdccb586c4588eb161ce6a1525ba585465 100644 --- a/std/io.zig +++ b/std/io.zig @@ -1050,8 +1050,13 @@ pub fn Deserializer(endian: builtin.Endian, is_packed: bool, comptime Error: typ else => in_stream, } }; } + + pub fn alignToByte(self: *Self) void { + if(!is_packed) return; + self.in_stream.alignToByte(); + } - //@BUG: inferred error issue + //@BUG: inferred error issue. See: #1386 fn deserializeInt(self: *Self, comptime T: type) (Stream.Error || error{EndOfStream})!T { debug.assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T)); diff --git a/std/io_test.zig b/std/io_test.zig index fe06a3e2c90a56bcb99d5d472a1a0d17cef02352..430c8f135ae253e1361cbcb9bc3052e3843dc63b 100644 --- a/std/io_test.zig +++ b/std/io_test.zig @@ -253,7 +253,8 @@ test "BitOutStream" { } fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packed: bool) !void { - const max_test_bitsize = 17; + //@NOTE: if this test is taking too long, reduce the maximum tested bitsize + const max_test_bitsize = 128; const total_bytes = comptime blk: { var bytes = 0; @@ -278,7 +279,7 @@ fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_pa const U = @IntType(false, i); const S = @IntType(true, i); try serializer.serializeInt(U(i)); - if (i != 0) try serializer.serializeInt(S(-1)); + if (i != 0) try serializer.serializeInt(S(-1)) else try serializer.serialize(S(0)); } try serializer.flush(); @@ -287,9 +288,9 @@ fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_pa const U = @IntType(false, i); const S = @IntType(true, i); const x = try deserializer.deserializeInt(U); - const y = if (i != 0) try deserializer.deserializeInt(S); + const y = try deserializer.deserializeInt(S); assert(x == U(i)); - if (i != 0) assert(y == S(-1)); + if (i != 0) assert(y == S(-1)) else assert(y == 0); } const u8_bit_count = comptime meta.bitCount(u8); @@ -299,8 +300,6 @@ fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_pa const extra_packed_byte = @boolToInt(total_bits % u8_bit_count > 0); const total_packed_bytes = (total_bits / u8_bit_count) + extra_packed_byte; - - assert(in.pos == if (is_packed) total_packed_bytes else total_bytes); } @@ -311,6 +310,56 @@ test "Serializer/Deserializer Int" { try testIntSerializerDeserializer(builtin.Endian.Little, true); } +fn testIntSerializerDeserializerInfNaN(comptime endian: builtin.Endian, + comptime is_packed: bool) !void +{ + const mem_size = (16*2 + 32*2 + 64*2 + 128*2) / comptime meta.bitCount(u8); + var data_mem: [mem_size]u8 = undefined; + + var out = io.SliceOutStream.init(data_mem[0..]); + const OutError = io.SliceOutStream.Error; + var out_stream = &out.stream; + var serializer = io.Serializer(endian, is_packed, OutError).init(out_stream); + + var in = io.SliceInStream.init(data_mem[0..]); + const InError = io.SliceInStream.Error; + var in_stream = &in.stream; + var deserializer = io.Deserializer(endian, is_packed, InError).init(in_stream); + + //@TODO: isInf/isNan not currently implemented for f128. + try serializer.serialize(std.math.nan(f16)); + try serializer.serialize(std.math.inf(f16)); + try serializer.serialize(std.math.nan(f32)); + try serializer.serialize(std.math.inf(f32)); + try serializer.serialize(std.math.nan(f64)); + try serializer.serialize(std.math.inf(f64)); + //try serializer.serialize(std.math.nan(f128)); + //try serializer.serialize(std.math.inf(f128)); + const nan_check_f16 = try deserializer.deserialize(f16); + const inf_check_f16 = try deserializer.deserialize(f16); + const nan_check_f32 = try deserializer.deserialize(f32); + const inf_check_f32 = try deserializer.deserialize(f32); + const nan_check_f64 = try deserializer.deserialize(f64); + const inf_check_f64 = try deserializer.deserialize(f64); + //const nan_check_f128 = try deserializer.deserialize(f128); + //const inf_check_f128 = try deserializer.deserialize(f128); + assert(std.math.isNan(nan_check_f16)); + assert(std.math.isInf(inf_check_f16)); + assert(std.math.isNan(nan_check_f32)); + assert(std.math.isInf(inf_check_f32)); + assert(std.math.isNan(nan_check_f64)); + assert(std.math.isInf(inf_check_f64)); + //assert(std.math.isNan(nan_check_f128)); + //assert(std.math.isInf(inf_check_f128)); +} + +test "Serializer/Deserializer Int: Inf/NaN" { + try testIntSerializerDeserializerInfNaN(builtin.Endian.Big, false); + try testIntSerializerDeserializerInfNaN(builtin.Endian.Little, false); + try testIntSerializerDeserializerInfNaN(builtin.Endian.Big, true); + try testIntSerializerDeserializerInfNaN(builtin.Endian.Little, true); +} + fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packed: bool) !void { const ColorType = enum(u4) { RGB8 = 1, @@ -410,7 +459,6 @@ fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packe try serializer.serialize(my_inst); const my_copy = try deserializer.deserialize(MyStruct); - assert(meta.eql(my_copy, my_inst)); } -- 2.54.0 From 5936bdf8a4163d0c75444e5c8554316de63e3864 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Fri, 30 Nov 2018 14:31:08 -0600 Subject: [PATCH 05/37] Fixed readBits to cast errors to the correct errorset. See #1810 for why this wasn't caught earlier. --- std/io.zig | 4 +++- std/io_test.zig | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/std/io.zig b/std/io.zig index 499422fdccb586c4588eb161ce6a1525ba585465..5f710a5033a7afb9fbf304324beb971bde1b775e 100644 --- a/std/io.zig +++ b/std/io.zig @@ -526,7 +526,9 @@ pub fn BitInStream(endian: builtin.Endian, comptime Error: type) type { if (err == error.EndOfStream) { return @intCast(U, out_buffer); } - return err; + //@BUG: See #1810. Not sure if the bug is that I have to do this for some + // streams, or that I don't for streams with emtpy errorsets. + return @errSetCast(Error, err); }; switch (endian) { diff --git a/std/io_test.zig b/std/io_test.zig index 430c8f135ae253e1361cbcb9bc3052e3843dc63b..ed8e0fcadb4d092ba6cbe8a01801d02801231bf9 100644 --- a/std/io_test.zig +++ b/std/io_test.zig @@ -169,6 +169,10 @@ test "BitInStream" { assert(out_bits == 16); _ = try bit_stream_be.readBits(u0, 0, &out_bits); + + assert(0 == try bit_stream_be.readBits(u1, 1, &out_bits)); + assert(out_bits == 0); + assertError(bit_stream_be.readBitsNoEof(u1, 1), error.EndOfStream); var mem_in_le = io.SliceInStream.init(mem_le[0..]); var bit_stream_le = io.BitInStream(builtin.Endian.Little, InError).init(&mem_in_le.stream); @@ -197,6 +201,10 @@ test "BitInStream" { assert(out_bits == 16); _ = try bit_stream_le.readBits(u0, 0, &out_bits); + + assert(0 == try bit_stream_le.readBits(u1, 1, &out_bits)); + assert(out_bits == 0); + assertError(bit_stream_le.readBitsNoEof(u1, 1), error.EndOfStream); } test "BitOutStream" { -- 2.54.0 From 8423bd423b7a8cf32246f9d27a400a2cf14ce770 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Fri, 30 Nov 2018 15:02:10 -0600 Subject: [PATCH 06/37] Added explicit test for #1810 issue to io_test.zig. --- std/io_test.zig | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/std/io_test.zig b/std/io_test.zig index ed8e0fcadb4d092ba6cbe8a01801d02801231bf9..71d46b087478088525ef2d0382fcd3afd2f233e2 100644 --- a/std/io_test.zig +++ b/std/io_test.zig @@ -260,6 +260,54 @@ test "BitOutStream" { try bit_stream_le.writeBits(u0(0), 0); } +test "BitStreams with File Stream" { + const tmp_file_name = "temp_test_file.txt"; + { + var file = try os.File.openWrite(tmp_file_name); + defer file.close(); + + var file_out = file.outStream(); + var file_out_stream = &file_out.stream; + const OutError = os.File.WriteError; + var bit_stream = io.BitOutStream(builtin.endian, OutError).init(file_out_stream); + + try bit_stream.writeBits(u2(1), 1); + try bit_stream.writeBits(u5(2), 2); + try bit_stream.writeBits(u128(3), 3); + try bit_stream.writeBits(u8(4), 4); + try bit_stream.writeBits(u9(5), 5); + try bit_stream.writeBits(u1(1), 1); + try bit_stream.flushBits(); + } + { + var file = try os.File.openRead(tmp_file_name); + defer file.close(); + + var file_in = file.inStream(); + var file_in_stream = &file_in.stream; + const InError = os.File.ReadError; + var bit_stream = io.BitInStream(builtin.endian, InError).init(file_in_stream); + + var out_bits: usize = undefined; + + assert(1 == try bit_stream.readBits(u2, 1, &out_bits)); + assert(out_bits == 1); + assert(2 == try bit_stream.readBits(u5, 2, &out_bits)); + assert(out_bits == 2); + assert(3 == try bit_stream.readBits(u128, 3, &out_bits)); + assert(out_bits == 3); + assert(4 == try bit_stream.readBits(u8, 4, &out_bits)); + assert(out_bits == 4); + assert(5 == try bit_stream.readBits(u9, 5, &out_bits)); + assert(out_bits == 5); + assert(1 == try bit_stream.readBits(u1, 1, &out_bits)); + assert(out_bits == 1); + + assertError(bit_stream.readBitsNoEof(u1, 1), error.EndOfStream); + } + try os.deleteFile(tmp_file_name); +} + fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packed: bool) !void { //@NOTE: if this test is taking too long, reduce the maximum tested bitsize const max_test_bitsize = 128; -- 2.54.0 From 1188da926ff4cddd5818d85d45b087f6207dfef9 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Sun, 9 Dec 2018 20:52:16 -0600 Subject: [PATCH 07/37] Minor change to custom (de)serializer to allow them to be defined outside of the struct and aliased inside it. This will enable alternate generic serializers (i.e. one that follows pointers) on a struct-by-struct basis. --- std/io.zig | 6 +++--- std/io_test.zig | 10 +++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/std/io.zig b/std/io.zig index 5f710a5033a7afb9fbf304324beb971bde1b775e..c397ff6edeb3ece6fa05ff36d9f80658e2a18325 100644 --- a/std/io.zig +++ b/std/io.zig @@ -1146,7 +1146,7 @@ pub fn Deserializer(endian: builtin.Endian, is_packed: bool, comptime Error: typ const child_type_id = @typeId(C); //custom deserializer: fn(self: *Self, deserializer: var) !void - if (comptime trait.hasFn("deserialize")(C)) return ptr.deserialize(self); + if (comptime trait.hasFn("deserialize")(C)) return C.deserialize(ptr, self); if (comptime trait.isPacked(C) and !is_packed) { var packed_deserializer = Deserializer(endian, true, Error).init(self.in_stream); @@ -1308,8 +1308,8 @@ pub fn Serializer(endian: builtin.Endian, is_packed: bool, comptime Error: type) return; } - //custom serializer: fn(self: *const Self, serializer: var) !void - if (comptime trait.hasFn("serialize")(T)) return value.serialize(self); + //custom serializer: fn(self: Self, serializer: var) !void + if (comptime trait.hasFn("serialize")(T)) return T.serialize(value, self); if (comptime trait.isPacked(T) and !is_packed) { var packed_serializer = Serializer(endian, true, Error).init(self.out_stream); diff --git a/std/io_test.zig b/std/io_test.zig index 71d46b087478088525ef2d0382fcd3afd2f233e2..0bee0ddaf0c2f8fe146d08e011c7c2b6550b8859 100644 --- a/std/io_test.zig +++ b/std/io_test.zig @@ -416,6 +416,10 @@ test "Serializer/Deserializer Int: Inf/NaN" { try testIntSerializerDeserializerInfNaN(builtin.Endian.Little, true); } +fn testAlternateSerializer(self: var, serializer: var) !void { + try serializer.serialize(self.f_f16); +} + fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packed: bool) !void { const ColorType = enum(u4) { RGB8 = 1, @@ -448,6 +452,8 @@ fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packe f_u2: u2, }; + + //to test custom serialization const Custom = struct { f_f16: f16, @@ -458,9 +464,7 @@ fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packe self.f_unused_u32 = 47; } - pub fn serialize(self: *const @This(), serializer: var) !void { - try serializer.serialize(self.f_f16); - } + pub const serialize = testAlternateSerializer; }; const MyStruct = struct { -- 2.54.0 From 1a8570403f070933842db7739e7139779b7e04a5 Mon Sep 17 00:00:00 2001 From: tgschultz Date: Sun, 9 Dec 2018 20:59:51 -0600 Subject: [PATCH 08/37] Minor doc-comment fix. --- std/io.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/io.zig b/std/io.zig index c397ff6edeb3ece6fa05ff36d9f80658e2a18325..7184787b29b0f746f73ed8091e550629d3086bb3 100644 --- a/std/io.zig +++ b/std/io.zig @@ -1243,7 +1243,7 @@ pub fn Deserializer(endian: builtin.Endian, is_packed: bool, comptime Error: typ /// endianess, after which data will resume being written at the next byte boundary. /// Types may implement a custom serialization routine with a /// function named `serialize` in the form of: -/// pub fn serialize(self: *const Self, serializer: var) !void +/// pub fn serialize(self: Self, serializer: var) !void /// which will be called when the serializer is used to serialize that type. It will /// pass a const pointer to the type instance to be serialized and a pointer /// to the serializer struct. -- 2.54.0 From b61bce254cc5c7075e2fb722e7deb0085eebb65e Mon Sep 17 00:00:00 2001 From: emekoi Date: Sun, 18 Nov 2018 00:07:37 -0600 Subject: [PATCH 09/37] added mutex for windows --- std/mutex.zig | 147 +++++++++++++++++++++++++----------- std/os/windows/kernel32.zig | 31 ++++++++ 2 files changed, 133 insertions(+), 45 deletions(-) diff --git a/std/mutex.zig b/std/mutex.zig index e35bd81bc43250c9691463270d8f15a7d5072be3..25e6a5bd254b45d39c773377d0284c16b7ea2066 100644 --- a/std/mutex.zig +++ b/std/mutex.zig @@ -5,70 +5,125 @@ const AtomicRmwOp = builtin.AtomicRmwOp; const assert = std.debug.assert; const SpinLock = std.SpinLock; const linux = std.os.linux; +const windows = std.os.windows; /// Lock may be held only once. If the same thread /// tries to acquire the same mutex twice, it deadlocks. /// The Linux implementation is based on mutex3 from /// https://www.akkadia.org/drepper/futex.pdf -pub const Mutex = struct { +pub const Mutex = switch(builtin.os) { + builtin.Os.linux => MutexLinux, + builtin.Os.windows => MutexWindows, + else => MutexSpinLock, +}; + +const MutexLinux = struct { /// 0: unlocked /// 1: locked, no waiters /// 2: locked, one or more waiters - linux_lock: @typeOf(linux_lock_init), - - /// TODO better implementation than spin lock - spin_lock: @typeOf(spin_lock_init), - - const linux_lock_init = if (builtin.os == builtin.Os.linux) i32(0) else {}; - const spin_lock_init = if (builtin.os != builtin.Os.linux) SpinLock.init() else {}; + lock: i32, pub const Held = struct { mutex: *Mutex, pub fn release(self: Held) void { - if (builtin.os == builtin.Os.linux) { - const c = @atomicRmw(i32, &self.mutex.linux_lock, AtomicRmwOp.Sub, 1, AtomicOrder.Release); - if (c != 1) { - _ = @atomicRmw(i32, &self.mutex.linux_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.Release); - const rc = linux.futex_wake(&self.mutex.linux_lock, linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 1); - switch (linux.getErrno(rc)) { - 0 => {}, - linux.EINVAL => unreachable, - else => unreachable, - } - } - } else { - SpinLock.Held.release(SpinLock.Held{ .spinlock = &self.mutex.spin_lock }); - } - } - }; - - pub fn init() Mutex { - return Mutex{ - .linux_lock = linux_lock_init, - .spin_lock = spin_lock_init, - }; - } - - pub fn acquire(self: *Mutex) Held { - if (builtin.os == builtin.Os.linux) { - var c = @cmpxchgWeak(i32, &self.linux_lock, 0, 1, AtomicOrder.Acquire, AtomicOrder.Monotonic) orelse - return Held{ .mutex = self }; - if (c != 2) - c = @atomicRmw(i32, &self.linux_lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire); - while (c != 0) { - const rc = linux.futex_wait(&self.linux_lock, linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, 2, null); + const c = @atomicRmw(i32, &self.mutex.lock, AtomicRmwOp.Sub, 1, AtomicOrder.Release); + if (c != 1) { + _ = @atomicRmw(i32, &self.mutex.lock, AtomicRmwOp.Xchg, 0, AtomicOrder.Release); + const rc = linux.futex_wake(&self.mutex.lock, linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 1); switch (linux.getErrno(rc)) { - 0, linux.EINTR, linux.EAGAIN => {}, + 0 => {}, linux.EINVAL => unreachable, else => unreachable, } - c = @atomicRmw(i32, &self.linux_lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire); } - } else { - _ = self.spin_lock.acquire(); } - return Held{ .mutex = self }; + }; + + pub fn init() Mutex { + return Mutex { + .lock = 0, + }; + } + + pub fn deinit(self: *Mutex) void {} + + pub fn acquire(self: *Mutex) Held { + var c = @cmpxchgWeak(i32, &self.lock, 0, 1, AtomicOrder.Acquire, AtomicOrder.Monotonic) orelse + return Held{ .mutex = self }; + if (c != 2) + c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire); + while (c != 0) { + const rc = linux.futex_wait(&self.lock, linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, 2, null); + switch (linux.getErrno(rc)) { + 0, linux.EINTR, linux.EAGAIN => {}, + linux.EINVAL => unreachable, + else => unreachable, + } + c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire); + } + return Held { .mutex = self }; + } +}; + +const MutexWindows = struct { + lock: ?windows.RTL_CRITICAL_SECTION, + + pub const Held = struct { + mutex: *Mutex, + + pub fn release(self: Held) void { + windows.LeaveCriticalSection(&self.mutex.lock); + } + }; + + fn initOsData(self: *MutexWindows) void { + if (self.lock == null) { + windows.InitializeCriticalSection(&self.lock); + } + } + + pub fn init() Mutex { + return Mutex { + .lock = null, + }; + } + + pub fn deinit(self: *Mutex) void { + windows.DeleteCriticalSection(&self.lock); + self.lock = null; + } + + pub fn acquire(self: *Mutex) Held { + self.initOsData(); + windows.EnterCriticalSection(&self.lock); + return Held { .mutex = self }; + } +}; + +const MutexSpinLock = struct { + /// TODO better implementation than spin lock + lock: SpinLock, + + pub const Held = struct { + mutex: *Mutex, + + pub fn release(self: Held) void { + SpinLock.Held.release(SpinLock.Held { .spinlock = &self.mutex.lock }); + } + }; + + pub fn init() Mutex { + return Mutex { + .lock = SpinLock.init(), + }; + } + + pub fn deinit(self: *Mutex) void {} + + pub fn acquire(self: *Mutex) Held { + _ = self.spin_lock.acquire(); + return Held { .mutex = self }; } }; @@ -90,6 +145,8 @@ test "std.Mutex" { var a = &fixed_buffer_allocator.allocator; var mutex = Mutex.init(); + defer mutex.deinit(); + var context = Context{ .mutex = &mutex, .data = 0, diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index 202b8bffeb7272dca8347830f69c6abd4814b926..1691b6523f61409aae5d056214530a3c1b97c7f9 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -220,3 +220,34 @@ pub const FOREGROUND_BLUE = 1; pub const FOREGROUND_GREEN = 2; pub const FOREGROUND_RED = 4; pub const FOREGROUND_INTENSITY = 8; + +pub extern "kernel32" stdcallcc fn InitializeCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn EnterCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn LeaveCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn DeleteCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; + +pub const LIST_ENTRY = extern struct { + Flink: *LIST_ENTRY, + Blink: *LIST_ENTRY, +}; + +pub const RTL_CRITICAL_SECTION_DEBUG = extern struct { + Type: WORD, + CreatorBackTraceIndex: WORD, + CriticalSection: *RTL_CRITICAL_SECTION, + ProcessLocksList: LIST_ENTRY, + EntryCount: DWORD, + ContentionCount: DWORD, + Flags: DWORD, + CreatorBackTraceIndexHigh: WORD, + SpareWORD: WORD, +}; + +pub const RTL_CRITICAL_SECTION = extern struct { + DebugInfo: *RTL_CRITICAL_SECTION_DEBUG, + LockCount: i32, + RecursionCount: i32, + OwningThread: HANDLE, + LockSemaphore: HANDLE, + SpinCount: ULONG_PTR, +}; -- 2.54.0 From 99f44219bcc0c8c496a4a5ebd5d8be465cb71c07 Mon Sep 17 00:00:00 2001 From: emekoi Date: Sun, 18 Nov 2018 00:27:47 -0600 Subject: [PATCH 10/37] updated structs --- std/mutex.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/mutex.zig b/std/mutex.zig index 25e6a5bd254b45d39c773377d0284c16b7ea2066..125a76347995814a76d5eb92f1b4bff3c4aa49a3 100644 --- a/std/mutex.zig +++ b/std/mutex.zig @@ -122,7 +122,7 @@ const MutexSpinLock = struct { pub fn deinit(self: *Mutex) void {} pub fn acquire(self: *Mutex) Held { - _ = self.spin_lock.acquire(); + _ = self.lock.acquire(); return Held { .mutex = self }; } }; -- 2.54.0 From aaae2f5705a2dee3a12ea49c1094c217a73bb897 Mon Sep 17 00:00:00 2001 From: emekoi Date: Sun, 18 Nov 2018 01:15:24 -0600 Subject: [PATCH 11/37] switching from EnterCriticalSection to TryEnterCriticalSection --- std/mutex.zig | 2 +- std/os/windows/kernel32.zig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/std/mutex.zig b/std/mutex.zig index 125a76347995814a76d5eb92f1b4bff3c4aa49a3..c0b5d3b002a54ee03747c299f594e1935461b072 100644 --- a/std/mutex.zig +++ b/std/mutex.zig @@ -96,7 +96,7 @@ const MutexWindows = struct { pub fn acquire(self: *Mutex) Held { self.initOsData(); - windows.EnterCriticalSection(&self.lock); + while (windows.TryEnterCriticalSection(&self.lock) == 0) {} return Held { .mutex = self }; } }; diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index 1691b6523f61409aae5d056214530a3c1b97c7f9..141039a1552f2c576c6fdf6b0b2c79f90b260440 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -222,7 +222,7 @@ pub const FOREGROUND_RED = 4; pub const FOREGROUND_INTENSITY = 8; pub extern "kernel32" stdcallcc fn InitializeCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; -pub extern "kernel32" stdcallcc fn EnterCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn TryEnterCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) BOOL; pub extern "kernel32" stdcallcc fn LeaveCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; pub extern "kernel32" stdcallcc fn DeleteCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; -- 2.54.0 From 25d7f5b65475d087ee1f046a364f8a9e6cd60135 Mon Sep 17 00:00:00 2001 From: emekoi Date: Sun, 18 Nov 2018 18:32:50 -0600 Subject: [PATCH 12/37] switching back to EnterCriticalSection --- std/mutex.zig | 13 +++++-------- std/os/windows/kernel32.zig | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/std/mutex.zig b/std/mutex.zig index c0b5d3b002a54ee03747c299f594e1935461b072..01ff891b80c8fd10424fa45211aaee669979806c 100644 --- a/std/mutex.zig +++ b/std/mutex.zig @@ -77,12 +77,6 @@ const MutexWindows = struct { } }; - fn initOsData(self: *MutexWindows) void { - if (self.lock == null) { - windows.InitializeCriticalSection(&self.lock); - } - } - pub fn init() Mutex { return Mutex { .lock = null, @@ -95,8 +89,11 @@ const MutexWindows = struct { } pub fn acquire(self: *Mutex) Held { - self.initOsData(); - while (windows.TryEnterCriticalSection(&self.lock) == 0) {} + if (self.lock == null) { + windows.InitializeCriticalSection(&self.lock); + } + + windows.EnterCriticalSection(&self.lock); return Held { .mutex = self }; } }; diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index 141039a1552f2c576c6fdf6b0b2c79f90b260440..1691b6523f61409aae5d056214530a3c1b97c7f9 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -222,7 +222,7 @@ pub const FOREGROUND_RED = 4; pub const FOREGROUND_INTENSITY = 8; pub extern "kernel32" stdcallcc fn InitializeCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; -pub extern "kernel32" stdcallcc fn TryEnterCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) BOOL; +pub extern "kernel32" stdcallcc fn EnterCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; pub extern "kernel32" stdcallcc fn LeaveCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; pub extern "kernel32" stdcallcc fn DeleteCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; -- 2.54.0 From 207fa3849ca61a6b22084d2d173b36f23ec841bd Mon Sep 17 00:00:00 2001 From: emekoi Date: Mon, 19 Nov 2018 13:52:10 -0600 Subject: [PATCH 13/37] moved to InitializeCriticalSection to init --- std/mutex.zig | 164 +++++++++++++++++------------------- std/os/windows/kernel32.zig | 8 +- 2 files changed, 82 insertions(+), 90 deletions(-) diff --git a/std/mutex.zig b/std/mutex.zig index 01ff891b80c8fd10424fa45211aaee669979806c..930763725321f52ba69f13070414d407132380be 100644 --- a/std/mutex.zig +++ b/std/mutex.zig @@ -12,116 +12,108 @@ const windows = std.os.windows; /// The Linux implementation is based on mutex3 from /// https://www.akkadia.org/drepper/futex.pdf pub const Mutex = switch(builtin.os) { - builtin.Os.linux => MutexLinux, - builtin.Os.windows => MutexWindows, - else => MutexSpinLock, -}; + builtin.Os.linux => struct { + /// 0: unlocked + /// 1: locked, no waiters + /// 2: locked, one or more waiters + lock: i32, -const MutexLinux = struct { - /// 0: unlocked - /// 1: locked, no waiters - /// 2: locked, one or more waiters - lock: i32, + pub const Held = struct { + mutex: *Mutex, - pub const Held = struct { - mutex: *Mutex, + pub fn release(self: Held) void { + const c = @atomicRmw(i32, &self.mutex.lock, AtomicRmwOp.Sub, 1, AtomicOrder.Release); + if (c != 1) { + _ = @atomicRmw(i32, &self.mutex.lock, AtomicRmwOp.Xchg, 0, AtomicOrder.Release); + const rc = linux.futex_wake(&self.mutex.lock, linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 1); + switch (linux.getErrno(rc)) { + 0 => {}, + linux.EINVAL => unreachable, + else => unreachable, + } + } + } + }; - pub fn release(self: Held) void { - const c = @atomicRmw(i32, &self.mutex.lock, AtomicRmwOp.Sub, 1, AtomicOrder.Release); - if (c != 1) { - _ = @atomicRmw(i32, &self.mutex.lock, AtomicRmwOp.Xchg, 0, AtomicOrder.Release); - const rc = linux.futex_wake(&self.mutex.lock, linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 1); + pub fn init() Mutex { + return Mutex { + .lock = 0, + }; + } + + pub fn deinit(self: *Mutex) void {} + + pub fn acquire(self: *Mutex) Held { + var c = @cmpxchgWeak(i32, &self.lock, 0, 1, AtomicOrder.Acquire, AtomicOrder.Monotonic) orelse + return Held{ .mutex = self }; + if (c != 2) + c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire); + while (c != 0) { + const rc = linux.futex_wait(&self.lock, linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, 2, null); switch (linux.getErrno(rc)) { - 0 => {}, + 0, linux.EINTR, linux.EAGAIN => {}, linux.EINVAL => unreachable, else => unreachable, } + c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire); } + return Held { .mutex = self }; } - }; + }, + builtin.Os.windows => struct { + lock: ?*windows.RTL_CRITICAL_SECTION, - pub fn init() Mutex { - return Mutex { - .lock = 0, + pub const Held = struct { + mutex: *Mutex, + + pub fn release(self: Held) void { + windows.LeaveCriticalSection(self.mutex.lock); + } }; - } - pub fn deinit(self: *Mutex) void {} + pub fn init() Mutex { + var lock: ?*windows.RTL_CRITICAL_SECTION = null; + windows.InitializeCriticalSection(lock); + return Mutex { .lock = lock }; + } - pub fn acquire(self: *Mutex) Held { - var c = @cmpxchgWeak(i32, &self.lock, 0, 1, AtomicOrder.Acquire, AtomicOrder.Monotonic) orelse - return Held{ .mutex = self }; - if (c != 2) - c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire); - while (c != 0) { - const rc = linux.futex_wait(&self.lock, linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, 2, null); - switch (linux.getErrno(rc)) { - 0, linux.EINTR, linux.EAGAIN => {}, - linux.EINVAL => unreachable, - else => unreachable, + pub fn deinit(self: *Mutex) void { + if (self.lock != null) { + windows.DeleteCriticalSection(self.lock); + self.lock = null; } - c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire); } - return Held { .mutex = self }; - } -}; - -const MutexWindows = struct { - lock: ?windows.RTL_CRITICAL_SECTION, - pub const Held = struct { - mutex: *Mutex, - - pub fn release(self: Held) void { - windows.LeaveCriticalSection(&self.mutex.lock); + pub fn acquire(self: *Mutex) Held { + windows.EnterCriticalSection(self.lock); + return Held { .mutex = self }; } - }; + }, + else => struct { + /// TODO better implementation than spin lock + lock: SpinLock, - pub fn init() Mutex { - return Mutex { - .lock = null, - }; - } + pub const Held = struct { + mutex: *Mutex, - pub fn deinit(self: *Mutex) void { - windows.DeleteCriticalSection(&self.lock); - self.lock = null; - } + pub fn release(self: Held) void { + SpinLock.Held.release(SpinLock.Held { .spinlock = &self.mutex.lock }); + } + }; - pub fn acquire(self: *Mutex) Held { - if (self.lock == null) { - windows.InitializeCriticalSection(&self.lock); + pub fn init() Mutex { + return Mutex { + .lock = SpinLock.init(), + }; } - windows.EnterCriticalSection(&self.lock); - return Held { .mutex = self }; - } -}; - -const MutexSpinLock = struct { - /// TODO better implementation than spin lock - lock: SpinLock, + pub fn deinit(self: *Mutex) void {} - pub const Held = struct { - mutex: *Mutex, - - pub fn release(self: Held) void { - SpinLock.Held.release(SpinLock.Held { .spinlock = &self.mutex.lock }); + pub fn acquire(self: *Mutex) Held { + _ = self.lock.acquire(); + return Held { .mutex = self }; } - }; - - pub fn init() Mutex { - return Mutex { - .lock = SpinLock.init(), - }; - } - - pub fn deinit(self: *Mutex) void {} - - pub fn acquire(self: *Mutex) Held { - _ = self.lock.acquire(); - return Held { .mutex = self }; - } + }, }; const Context = struct { diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index 1691b6523f61409aae5d056214530a3c1b97c7f9..e34092ad5134c5ff26fc1cf8f4fd9b4c1928d8b8 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -221,10 +221,10 @@ pub const FOREGROUND_GREEN = 2; pub const FOREGROUND_RED = 4; pub const FOREGROUND_INTENSITY = 8; -pub extern "kernel32" stdcallcc fn InitializeCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; -pub extern "kernel32" stdcallcc fn EnterCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; -pub extern "kernel32" stdcallcc fn LeaveCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; -pub extern "kernel32" stdcallcc fn DeleteCriticalSection(lpCriticalSection: *?RTL_CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn InitializeCriticalSection(lpCriticalSection: ?*RTL_CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn EnterCriticalSection(lpCriticalSection: ?*RTL_CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn LeaveCriticalSection(lpCriticalSection: ?*RTL_CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn DeleteCriticalSection(lpCriticalSection: ?*RTL_CRITICAL_SECTION) void; pub const LIST_ENTRY = extern struct { Flink: *LIST_ENTRY, -- 2.54.0 From bb31695fbfbf7c808dc3fd8c3cfc0b654ed15e5e Mon Sep 17 00:00:00 2001 From: emekoi Date: Mon, 26 Nov 2018 21:07:01 -0600 Subject: [PATCH 14/37] fixed mutex on windows --- std/mutex.zig | 54 +++++++++++++++++++++++++++---------- std/os/windows/index.zig | 1 + std/os/windows/kernel32.zig | 24 ++++++++++++----- 3 files changed, 59 insertions(+), 20 deletions(-) diff --git a/std/mutex.zig b/std/mutex.zig index 930763725321f52ba69f13070414d407132380be..45336f353e8ea2c35bfd08e96d53cd7f79ec2595 100644 --- a/std/mutex.zig +++ b/std/mutex.zig @@ -61,31 +61,57 @@ pub const Mutex = switch(builtin.os) { } }, builtin.Os.windows => struct { - lock: ?*windows.RTL_CRITICAL_SECTION, + + lock: ?windows.CRITICAL_SECTION, + init_once: windows.PINIT_ONCE, pub const Held = struct { mutex: *Mutex, pub fn release(self: Held) void { - windows.LeaveCriticalSection(self.mutex.lock); + if (self.mutex.lock) |*lock| { + windows.LeaveCriticalSection(lock); + } } }; pub fn init() Mutex { - var lock: ?*windows.RTL_CRITICAL_SECTION = null; - windows.InitializeCriticalSection(lock); - return Mutex { .lock = lock }; + return Mutex { + .lock = null, + .init_once = undefined, + }; + } + + extern fn initCriticalSection(InitOnce: *windows.PINIT_ONCE, Parameter: ?windows.PVOID, Context: ?*windows.PVOID) windows.BOOL { + if (Context) |ctx| { + var mutex = @ptrCast(*?windows.CRITICAL_SECTION, ctx); + if (mutex.* == null) { + var lock: windows.CRITICAL_SECTION = undefined; + windows.InitializeCriticalSection(&lock); + mutex.* = lock; + } + return windows.TRUE; + } + return windows.FALSE; } pub fn deinit(self: *Mutex) void { - if (self.lock != null) { - windows.DeleteCriticalSection(self.lock); - self.lock = null; + if (self.lock) |*lock| { + windows.DeleteCriticalSection(lock); } } pub fn acquire(self: *Mutex) Held { - windows.EnterCriticalSection(self.lock); + if (self.lock) |*lock| { + windows.EnterCriticalSection(lock); + } else { + if (windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, null, @ptrCast(?*windows.PVOID, self)) == windows.TRUE) { + windows.EnterCriticalSection(&self.lock.?); + } else { + @panic("unable to initialize Mutex"); + } + } + return Held { .mutex = self }; } }, @@ -116,7 +142,7 @@ pub const Mutex = switch(builtin.os) { }, }; -const Context = struct { +const TestContext = struct { mutex: *Mutex, data: i128, @@ -136,7 +162,7 @@ test "std.Mutex" { var mutex = Mutex.init(); defer mutex.deinit(); - var context = Context{ + var context = TestContext{ .mutex = &mutex, .data = 0, }; @@ -149,12 +175,12 @@ test "std.Mutex" { for (threads) |t| t.wait(); - std.debug.assertOrPanic(context.data == thread_count * Context.incr_count); + std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count); } -fn worker(ctx: *Context) void { +fn worker(ctx: *TestContext) void { var i: usize = 0; - while (i != Context.incr_count) : (i += 1) { + while (i != TestContext.incr_count) : (i += 1) { const held = ctx.mutex.acquire(); defer held.release(); diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index 3d6ee6711356d93d0a04a64e4d92f5ab40ebf836..3f19905835d8a16162aee213818b716345c3126c 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -49,6 +49,7 @@ pub const UNICODE = false; pub const WCHAR = u16; pub const WORD = u16; pub const LARGE_INTEGER = i64; +pub const LONG = c_long; pub const TRUE = 1; pub const FALSE = 0; diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index e34092ad5134c5ff26fc1cf8f4fd9b4c1928d8b8..07d6cd80a28dd12be99d8cfc8d8cc08c5c961a25 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -221,10 +221,10 @@ pub const FOREGROUND_GREEN = 2; pub const FOREGROUND_RED = 4; pub const FOREGROUND_INTENSITY = 8; -pub extern "kernel32" stdcallcc fn InitializeCriticalSection(lpCriticalSection: ?*RTL_CRITICAL_SECTION) void; -pub extern "kernel32" stdcallcc fn EnterCriticalSection(lpCriticalSection: ?*RTL_CRITICAL_SECTION) void; -pub extern "kernel32" stdcallcc fn LeaveCriticalSection(lpCriticalSection: ?*RTL_CRITICAL_SECTION) void; -pub extern "kernel32" stdcallcc fn DeleteCriticalSection(lpCriticalSection: ?*RTL_CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn InitializeCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn EnterCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn LeaveCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn DeleteCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; pub const LIST_ENTRY = extern struct { Flink: *LIST_ENTRY, @@ -245,9 +245,21 @@ pub const RTL_CRITICAL_SECTION_DEBUG = extern struct { pub const RTL_CRITICAL_SECTION = extern struct { DebugInfo: *RTL_CRITICAL_SECTION_DEBUG, - LockCount: i32, - RecursionCount: i32, + LockCount: LONG, + RecursionCount: LONG, OwningThread: HANDLE, LockSemaphore: HANDLE, SpinCount: ULONG_PTR, }; + +pub const CRITICAL_SECTION = RTL_CRITICAL_SECTION; + +pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *PINIT_ONCE, InitFn: PINIT_ONCE_FN, Context: ?PVOID, Parameter: ?*LPVOID) BOOL; + +pub const PINIT_ONCE_FN = ?extern fn(InitOnce: *PINIT_ONCE, Parameter: ?PVOID, Context: ?*PVOID) BOOL; + +pub const RTL_RUN_ONCE = extern struct { + Ptr: PVOID, +}; + +pub const PINIT_ONCE = RTL_RUN_ONCE; -- 2.54.0 From 93851270526e71407b4225a23daa66214a4010f9 Mon Sep 17 00:00:00 2001 From: emekoi Date: Tue, 27 Nov 2018 01:39:37 -0600 Subject: [PATCH 15/37] changed pointer types --- std/mutex.zig | 7 ++++--- std/os/windows/kernel32.zig | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/std/mutex.zig b/std/mutex.zig index 45336f353e8ea2c35bfd08e96d53cd7f79ec2595..3469b9a118d69a196d5a0be923c7c832c8204afe 100644 --- a/std/mutex.zig +++ b/std/mutex.zig @@ -82,9 +82,9 @@ pub const Mutex = switch(builtin.os) { }; } - extern fn initCriticalSection(InitOnce: *windows.PINIT_ONCE, Parameter: ?windows.PVOID, Context: ?*windows.PVOID) windows.BOOL { + extern fn initCriticalSection(InitOnce: *windows.PINIT_ONCE, Parameter: ?windows.PVOID, Context: ?windows.PVOID) windows.BOOL { if (Context) |ctx| { - var mutex = @ptrCast(*?windows.CRITICAL_SECTION, ctx); + var mutex = @ptrCast(*?windows.CRITICAL_SECTION, @alignCast(8, ctx)); if (mutex.* == null) { var lock: windows.CRITICAL_SECTION = undefined; windows.InitializeCriticalSection(&lock); @@ -105,7 +105,8 @@ pub const Mutex = switch(builtin.os) { if (self.lock) |*lock| { windows.EnterCriticalSection(lock); } else { - if (windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, null, @ptrCast(?*windows.PVOID, self)) == windows.TRUE) { + var mutex = @ptrCast(?windows.PVOID, self); + if (windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, null, mutex) == windows.TRUE) { windows.EnterCriticalSection(&self.lock.?); } else { @panic("unable to initialize Mutex"); diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index 07d6cd80a28dd12be99d8cfc8d8cc08c5c961a25..56e2163fb67ecd78e08f277c56618771635fdba3 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -254,9 +254,9 @@ pub const RTL_CRITICAL_SECTION = extern struct { pub const CRITICAL_SECTION = RTL_CRITICAL_SECTION; -pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *PINIT_ONCE, InitFn: PINIT_ONCE_FN, Context: ?PVOID, Parameter: ?*LPVOID) BOOL; +pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *PINIT_ONCE, InitFn: PINIT_ONCE_FN, Context: ?PVOID, Parameter: ?LPVOID) BOOL; -pub const PINIT_ONCE_FN = ?extern fn(InitOnce: *PINIT_ONCE, Parameter: ?PVOID, Context: ?*PVOID) BOOL; +pub const PINIT_ONCE_FN = ?extern fn(InitOnce: *PINIT_ONCE, Parameter: ?PVOID, Context: ?PVOID) BOOL; pub const RTL_RUN_ONCE = extern struct { Ptr: PVOID, -- 2.54.0 From 35d93d22db6f1cdf20011b0db84586107513b462 Mon Sep 17 00:00:00 2001 From: emekoi Date: Tue, 27 Nov 2018 16:20:09 +0000 Subject: [PATCH 16/37] removed nullables --- std/mutex.zig | 55 ++++++++++++++++--------------------- std/os/windows/kernel32.zig | 12 ++++---- 2 files changed, 29 insertions(+), 38 deletions(-) diff --git a/std/mutex.zig b/std/mutex.zig index 3469b9a118d69a196d5a0be923c7c832c8204afe..30b68511ea2089ebba7feb9e2e4cdc880b7a7425 100644 --- a/std/mutex.zig +++ b/std/mutex.zig @@ -62,57 +62,50 @@ pub const Mutex = switch(builtin.os) { }, builtin.Os.windows => struct { - lock: ?windows.CRITICAL_SECTION, - init_once: windows.PINIT_ONCE, + lock: windows.CRITICAL_SECTION, + init_once: windows.RTL_RUN_ONCE, pub const Held = struct { mutex: *Mutex, pub fn release(self: Held) void { - if (self.mutex.lock) |*lock| { - windows.LeaveCriticalSection(lock); - } + windows.LeaveCriticalSection(&self.mutex.lock); } }; pub fn init() Mutex { - return Mutex { - .lock = null, + return Mutex { + .lock = undefined, .init_once = undefined, }; } - extern fn initCriticalSection(InitOnce: *windows.PINIT_ONCE, Parameter: ?windows.PVOID, Context: ?windows.PVOID) windows.BOOL { - if (Context) |ctx| { - var mutex = @ptrCast(*?windows.CRITICAL_SECTION, @alignCast(8, ctx)); - if (mutex.* == null) { - var lock: windows.CRITICAL_SECTION = undefined; - windows.InitializeCriticalSection(&lock); - mutex.* = lock; - } - return windows.TRUE; - } - return windows.FALSE; + extern fn initCriticalSection( + InitOnce: *windows.RTL_RUN_ONCE, + Parameter: ?windows.PVOID, + Context: ?windows.PVOID + ) windows.BOOL { + var lock = @ptrCast( + *windows.CRITICAL_SECTION, + @alignCast(@alignOf(*windows.CRITICAL_SECTION), ctx.?) + ); + windows.InitializeCriticalSection(lock); + return windows.TRUE; } pub fn deinit(self: *Mutex) void { - if (self.lock) |*lock| { - windows.DeleteCriticalSection(lock); - } + windows.DeleteCriticalSection(&self.lock); } pub fn acquire(self: *Mutex) Held { - if (self.lock) |*lock| { - windows.EnterCriticalSection(lock); - } else { - var mutex = @ptrCast(?windows.PVOID, self); - if (windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, null, mutex) == windows.TRUE) { - windows.EnterCriticalSection(&self.lock.?); - } else { - @panic("unable to initialize Mutex"); - } + if (windows.InitOnceExecuteOnce( + &self.init_once, + initCriticalSection, + null, @ptrCast(?windows.PVOID, self) + ) == windows.FALSE) { + unreachable; } - + windows.EnterCriticalSection(&self.lock); return Held { .mutex = self }; } }, diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index 56e2163fb67ecd78e08f277c56618771635fdba3..a0b23acf5daed5326f2f78c41e4670d96cbd7d80 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -222,9 +222,9 @@ pub const FOREGROUND_RED = 4; pub const FOREGROUND_INTENSITY = 8; pub extern "kernel32" stdcallcc fn InitializeCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; -pub extern "kernel32" stdcallcc fn EnterCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; -pub extern "kernel32" stdcallcc fn LeaveCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; -pub extern "kernel32" stdcallcc fn DeleteCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn EnterCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn LeaveCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; +pub extern "kernel32" stdcallcc fn DeleteCriticalSection(lpCriticalSection: *CRITICAL_SECTION) void; pub const LIST_ENTRY = extern struct { Flink: *LIST_ENTRY, @@ -254,12 +254,10 @@ pub const RTL_CRITICAL_SECTION = extern struct { pub const CRITICAL_SECTION = RTL_CRITICAL_SECTION; -pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *PINIT_ONCE, InitFn: PINIT_ONCE_FN, Context: ?PVOID, Parameter: ?LPVOID) BOOL; +pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *RTL_RUN_ONCE, InitFn: PINIT_ONCE_FN, Context: ?PVOID, Parameter: ?LPVOID) BOOL; -pub const PINIT_ONCE_FN = ?extern fn(InitOnce: *PINIT_ONCE, Parameter: ?PVOID, Context: ?PVOID) BOOL; +pub const PINIT_ONCE_FN = ?extern fn(InitOnce: *RTL_RUN_ONCE, Parameter: ?PVOID, Context: ?PVOID) BOOL; pub const RTL_RUN_ONCE = extern struct { Ptr: PVOID, }; - -pub const PINIT_ONCE = RTL_RUN_ONCE; -- 2.54.0 From 51fff9fa8212f514f76ef69c214e570d4ef98655 Mon Sep 17 00:00:00 2001 From: emekoi Date: Tue, 27 Nov 2018 17:43:59 +0000 Subject: [PATCH 17/37] fixed initializer and typos --- std/mutex.zig | 4 ++-- std/os/windows/kernel32.zig | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/std/mutex.zig b/std/mutex.zig index 30b68511ea2089ebba7feb9e2e4cdc880b7a7425..2885a99ed7a3d37d3c4d2c3373769fe2c10c2978 100644 --- a/std/mutex.zig +++ b/std/mutex.zig @@ -76,7 +76,7 @@ pub const Mutex = switch(builtin.os) { pub fn init() Mutex { return Mutex { .lock = undefined, - .init_once = undefined, + .init_once = windows.INIT_ONCE_STATIC_INIT, }; } @@ -87,7 +87,7 @@ pub const Mutex = switch(builtin.os) { ) windows.BOOL { var lock = @ptrCast( *windows.CRITICAL_SECTION, - @alignCast(@alignOf(*windows.CRITICAL_SECTION), ctx.?) + @alignCast(@alignOf(*windows.CRITICAL_SECTION), Context.?) ); windows.InitializeCriticalSection(lock); return windows.TRUE; diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index a0b23acf5daed5326f2f78c41e4670d96cbd7d80..8fd351c125bdf5727ccb2cf3dfb2754515c96758 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -261,3 +261,7 @@ pub const PINIT_ONCE_FN = ?extern fn(InitOnce: *RTL_RUN_ONCE, Parameter: ?PVOID, pub const RTL_RUN_ONCE = extern struct { Ptr: PVOID, }; + +pub const INIT_ONCE_STATIC_INIT = RTL_RUN_ONCE { + .Ptr = null, +}; \ No newline at end of file -- 2.54.0 From 3ff9ab332cf909da12361bf6ea495b2025ca8833 Mon Sep 17 00:00:00 2001 From: emekoi Date: Tue, 27 Nov 2018 17:54:09 +0000 Subject: [PATCH 18/37] fixed type signature --- std/os/windows/kernel32.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index 8fd351c125bdf5727ccb2cf3dfb2754515c96758..7a6126133a0a591d68c8f206cf7c5b72d31456c1 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -259,7 +259,7 @@ pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *RTL_RUN_ONCE, pub const PINIT_ONCE_FN = ?extern fn(InitOnce: *RTL_RUN_ONCE, Parameter: ?PVOID, Context: ?PVOID) BOOL; pub const RTL_RUN_ONCE = extern struct { - Ptr: PVOID, + Ptr: ?PVOID, }; pub const INIT_ONCE_STATIC_INIT = RTL_RUN_ONCE { -- 2.54.0 From 5bf9ffdc5be02e67b57fe9398ad9d13147bfb0c8 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 26 Jan 2019 05:10:40 +0900 Subject: [PATCH 19/37] Hint at use of and/or when &&/|| is improperly used (#1886) --- doc/langref.html.in | 2 +- src/ir.cpp | 227 +++++++++++++++++----------------------- src/parser.cpp | 35 +++++-- src/tokenizer.cpp | 12 +++ src/tokenizer.hpp | 1 + test/compile_errors.zig | 145 +++++++++++++++---------- 6 files changed, 226 insertions(+), 196 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 6e03d3ec6d676d64a2ac30e2dcb60e3dffe84247..1fb751cef431f1a2d854653c09bf5963c1c983db 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -6024,7 +6024,7 @@ fn add(a: i32, b: i32) i32 { This is typically used for type safety when interacting with C code that does not expose struct details. Example:

- {#code_begin|test_err|expected type '*Derp', found '*Wat'#} + {#code_begin|test_err|expected '*Derp' type, found '*Wat'#} const Derp = @OpaqueType(); const Wat = @OpaqueType(); diff --git a/src/ir.cpp b/src/ir.cpp index c651f30dd54f41ba963a0bb6ca9945f7d5b75db4..df2c8cc9beb791da55465e197df56f5122563c5a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9771,13 +9771,19 @@ IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node return ir_exec_const_result(codegen, analyzed_executable); } -static ZigType *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value) { +static ZigType *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value, ZigTypeId wanted_type) { if (type_is_invalid(type_value->value.type)) return ira->codegen->builtin_types.entry_invalid; + const char *expected_type_str = type_id_name(ZigTypeIdMetaType); + + if (wanted_type != ZigTypeIdInvalid) { + expected_type_str = type_id_name(wanted_type); + } + if (type_value->value.type->id != ZigTypeIdMetaType) { - ir_add_error(ira, type_value, - buf_sprintf("expected type 'type', found '%s'", buf_ptr(&type_value->value.type->name))); + ir_add_error( ira, type_value, + buf_sprintf("expected %s type, found '%s'", expected_type_str, buf_ptr(&type_value->value.type->name))); return ira->codegen->builtin_types.entry_invalid; } @@ -9786,7 +9792,16 @@ static ZigType *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value) { return ira->codegen->builtin_types.entry_invalid; assert(const_val->data.x_type != nullptr); - return const_val->data.x_type; + + ZigType *out_type = const_val->data.x_type; + + if (wanted_type != ZigTypeIdInvalid && out_type->id != wanted_type) { + ir_add_error(ira, type_value, + buf_sprintf( "expected %s type, found '%s'", expected_type_str, buf_ptr(&out_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + return out_type; } static ZigFn *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) { @@ -10986,7 +11001,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } ErrorMsg *parent_msg = ir_add_error_node(ira, source_instr->source_node, - buf_sprintf("expected type '%s', found '%s'", + buf_sprintf("expected '%s' type, found '%s'", buf_ptr(&wanted_type->name), buf_ptr(&actual_type->name))); report_recursive_error(ira, source_instr->source_node, &const_cast_result, parent_msg); @@ -12214,7 +12229,7 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i size_t op2_array_end; if (op2_type->id == ZigTypeIdArray) { if (op2_type->data.array.child_type != child_type) { - ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'", + ir_add_error(ira, op2, buf_sprintf("expected array of '%s' type, found '%s'", buf_ptr(&child_type->name), buf_ptr(&op2->value.type->name))); return ira->codegen->invalid_instruction; @@ -12228,7 +12243,7 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i op2_val->data.x_ptr.data.base_array.is_cstr) { if (child_type != ira->codegen->builtin_types.entry_u8) { - ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'", + ir_add_error(ira, op2, buf_sprintf("expected array of '%s' type, found '%s'", buf_ptr(&child_type->name), buf_ptr(&op2->value.type->name))); return ira->codegen->invalid_instruction; @@ -12239,7 +12254,7 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i } else if (is_slice(op2_type)) { ZigType *ptr_type = op2_type->data.structure.fields[slice_ptr_index].type_entry; if (ptr_type->data.pointer.child_type != child_type) { - ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'", + ir_add_error(ira, op2, buf_sprintf("expected array of '%s' type, found '%s'", buf_ptr(&child_type->name), buf_ptr(&op2->value.type->name))); return ira->codegen->invalid_instruction; @@ -12253,7 +12268,7 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i op2_array_end = bigint_as_unsigned(&len_val->data.x_bigint); } else { ir_add_error(ira, op2, - buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op2->value.type->name))); + buf_sprintf("expected array or C string literal type, found '%s'", buf_ptr(&op2->value.type->name))); return ira->codegen->invalid_instruction; } @@ -12388,26 +12403,22 @@ static IrInstruction *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp * } static IrInstruction *ir_analyze_merge_error_sets(IrAnalyze *ira, IrInstructionBinOp *instruction) { - ZigType *op1_type = ir_resolve_type(ira, instruction->op1->child); - if (type_is_invalid(op1_type)) - return ira->codegen->invalid_instruction; - - if (op1_type->id != ZigTypeIdErrorSet) { - ir_add_error(ira, instruction->op1, - buf_sprintf("expected error set type, found '%s'", buf_ptr(&op1_type->name))); + ZigType *op1_type = ir_resolve_type(ira, instruction->op1->child, ZigTypeIdErrorSet); + if (type_is_invalid(op1_type)) { + if (ira->codegen->errors.length != 0) { + add_error_note( ira->codegen + , ira->codegen->errors.last() + , instruction->base.source_node + , buf_sprintf("did you mean to use `or`?") + ); + } return ira->codegen->invalid_instruction; } - ZigType *op2_type = ir_resolve_type(ira, instruction->op2->child); + ZigType *op2_type = ir_resolve_type(ira, instruction->op2->child, ZigTypeIdErrorSet); if (type_is_invalid(op2_type)) return ira->codegen->invalid_instruction; - if (op2_type->id != ZigTypeIdErrorSet) { - ir_add_error(ira, instruction->op2, - buf_sprintf("expected error set type, found '%s'", buf_ptr(&op2_type->name))); - return ira->codegen->invalid_instruction; - } - if (type_is_global_error_set(op1_type) || type_is_global_error_set(op2_type)) { @@ -12495,7 +12506,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct IrInstruction *var_type = nullptr; if (decl_var_instruction->var_type != nullptr) { var_type = decl_var_instruction->var_type->child; - ZigType *proposed_type = ir_resolve_type(ira, var_type); + ZigType *proposed_type = ir_resolve_type(ira, var_type, ZigTypeIdInvalid); explicit_type = validate_var_type(ira->codegen, var_type->source_node, proposed_type); if (type_is_invalid(explicit_type)) { var->value->type = ira->codegen->builtin_types.entry_invalid; @@ -12824,21 +12835,14 @@ static IrInstruction *ir_analyze_instruction_error_union(IrAnalyze *ira, { Error err; - ZigType *err_set_type = ir_resolve_type(ira, instruction->err_set->child); + ZigType *err_set_type = ir_resolve_type(ira, instruction->err_set->child, ZigTypeIdErrorSet); if (type_is_invalid(err_set_type)) return ira->codegen->invalid_instruction; - ZigType *payload_type = ir_resolve_type(ira, instruction->payload->child); + ZigType *payload_type = ir_resolve_type(ira, instruction->payload->child, ZigTypeIdInvalid); if (type_is_invalid(payload_type)) return ira->codegen->invalid_instruction; - if (err_set_type->id != ZigTypeIdErrorSet) { - ir_add_error(ira, instruction->err_set->child, - buf_sprintf("expected error set type, found type '%s'", - buf_ptr(&err_set_type->name))); - return ira->codegen->invalid_instruction; - } - if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_instruction; ZigType *result_type = get_error_union_type(ira->codegen, err_set_type, payload_type); @@ -12900,7 +12904,7 @@ static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *c ZigType *alloc_fn_type = ptr_to_alloc_fn_type->data.pointer.child_type; if (alloc_fn_type->id != ZigTypeIdFn) { ir_add_error(ira, &call_instruction->base, - buf_sprintf("expected allocation function, found '%s'", buf_ptr(&alloc_fn_type->name))); + buf_sprintf("expected allocation function type, found '%s'", buf_ptr(&alloc_fn_type->name))); return ira->codegen->invalid_instruction; } @@ -13692,7 +13696,7 @@ static IrInstruction *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionC if (is_comptime || instr_is_comptime(fn_ref)) { if (fn_ref->value.type->id == ZigTypeIdMetaType) { - ZigType *dest_type = ir_resolve_type(ira, fn_ref); + ZigType *dest_type = ir_resolve_type(ira, fn_ref, ZigTypeIdInvalid); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; @@ -13817,7 +13821,7 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source static IrInstruction *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) { Error err; IrInstruction *value = un_op_instruction->value->child; - ZigType *type_entry = ir_resolve_type(ira, value); + ZigType *type_entry = ir_resolve_type(ira, value, ZigTypeIdInvalid); if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; if ((err = ensure_complete_type(ira->codegen, type_entry))) @@ -15300,16 +15304,10 @@ static IrInstruction *ir_analyze_instruction_ptr_type_child(IrAnalyze *ira, IrInstructionPtrTypeChild *ptr_type_child_instruction) { IrInstruction *type_value = ptr_type_child_instruction->value->child; - ZigType *type_entry = ir_resolve_type(ira, type_value); + ZigType *type_entry = ir_resolve_type(ira, type_value, ZigTypeIdPointer); if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; - if (type_entry->id != ZigTypeIdPointer) { - ir_add_error_node(ira, ptr_type_child_instruction->base.source_node, - buf_sprintf("expected pointer type, found '%s'", buf_ptr(&type_entry->name))); - return ira->codegen->invalid_instruction; - } - return ir_const_type(ira, &ptr_type_child_instruction->base, type_entry->data.pointer.child_type); } @@ -15462,7 +15460,7 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira, return ira->codegen->invalid_instruction; } - ZigType *child_type = ir_resolve_type(ira, slice_type_instruction->child_type->child); + ZigType *child_type = ir_resolve_type(ira, slice_type_instruction->child_type->child, ZigTypeIdInvalid); if (type_is_invalid(child_type)) return ira->codegen->invalid_instruction; @@ -15545,7 +15543,7 @@ static IrInstruction *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAs AsmOutput *asm_output = asm_expr->output_list.at(i); if (asm_output->return_type) { output_types[i] = asm_instruction->output_types[i]->child; - return_type = ir_resolve_type(ira, output_types[i]); + return_type = ir_resolve_type(ira, output_types[i], ZigTypeIdInvalid); if (type_is_invalid(return_type)) return ira->codegen->invalid_instruction; } @@ -15560,7 +15558,7 @@ static IrInstruction *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAs (input_value->value.type->id == ZigTypeIdComptimeInt || input_value->value.type->id == ZigTypeIdComptimeFloat)) { ir_add_error_node(ira, input_value->source_node, - buf_sprintf("expected sized integer or sized float, found %s", buf_ptr(&input_value->value.type->name))); + buf_sprintf("expected sized integer or sized float type, found %s", buf_ptr(&input_value->value.type->name))); return ira->codegen->invalid_instruction; } @@ -15586,7 +15584,7 @@ static IrInstruction *ir_analyze_instruction_array_type(IrAnalyze *ira, return ira->codegen->invalid_instruction; IrInstruction *child_type_value = array_type_instruction->child_type->child; - ZigType *child_type = ir_resolve_type(ira, child_type_value); + ZigType *child_type = ir_resolve_type(ira, child_type_value, ZigTypeIdInvalid); if (type_is_invalid(child_type)) return ira->codegen->invalid_instruction; switch (child_type->id) { @@ -15635,7 +15633,7 @@ static IrInstruction *ir_analyze_instruction_promise_type(IrAnalyze *ira, IrInst if (instruction->payload_type == nullptr) { promise_type = ira->codegen->builtin_types.entry_promise; } else { - ZigType *payload_type = ir_resolve_type(ira, instruction->payload_type->child); + ZigType *payload_type = ir_resolve_type(ira, instruction->payload_type->child, ZigTypeIdInvalid); if (type_is_invalid(payload_type)) return ira->codegen->invalid_instruction; @@ -15650,7 +15648,7 @@ static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira, { Error err; IrInstruction *type_value = size_of_instruction->type_value->child; - ZigType *type_entry = ir_resolve_type(ira, type_value); + ZigType *type_entry = ir_resolve_type(ira, type_value, ZigTypeIdInvalid); if ((err = ensure_complete_type(ira->codegen, type_entry))) return ira->codegen->invalid_instruction; @@ -16485,7 +16483,7 @@ static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, size_t elem_count = instruction->item_count; if (container_type_value->value.type->id == ZigTypeIdMetaType) { - ZigType *container_type = ir_resolve_type(ira, container_type_value); + ZigType *container_type = ir_resolve_type(ira, container_type_value, ZigTypeIdInvalid); if (type_is_invalid(container_type)) return ira->codegen->invalid_instruction; @@ -16601,7 +16599,7 @@ static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, static IrInstruction *ir_analyze_instruction_container_init_fields(IrAnalyze *ira, IrInstructionContainerInitFields *instruction) { IrInstruction *container_type_value = instruction->container_type->child; - ZigType *container_type = ir_resolve_type(ira, container_type_value); + ZigType *container_type = ir_resolve_type(ira, container_type_value, ZigTypeIdInvalid); if (type_is_invalid(container_type)) return ira->codegen->invalid_instruction; @@ -16719,7 +16717,7 @@ static IrInstruction *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, { Error err; IrInstruction *type_value = instruction->type_value->child; - ZigType *container_type = ir_resolve_type(ira, type_value); + ZigType *container_type = ir_resolve_type(ira, type_value, ZigTypeIdStruct); if (type_is_invalid(container_type)) return ira->codegen->invalid_instruction; @@ -16732,12 +16730,6 @@ static IrInstruction *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, if (type_is_invalid(field_ptr->value.type)) return ira->codegen->invalid_instruction; - if (container_type->id != ZigTypeIdStruct) { - ir_add_error(ira, type_value, - buf_sprintf("expected struct type, found '%s'", buf_ptr(&container_type->name))); - return ira->codegen->invalid_instruction; - } - if ((err = ensure_complete_type(ira->codegen, container_type))) return ira->codegen->invalid_instruction; @@ -16751,7 +16743,7 @@ static IrInstruction *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, if (field_ptr->value.type->id != ZigTypeIdPointer) { ir_add_error(ira, field_ptr, - buf_sprintf("expected pointer, found '%s'", buf_ptr(&field_ptr->value.type->name))); + buf_sprintf("expected Pointer type, found '%s'", buf_ptr(&field_ptr->value.type->name))); return ira->codegen->invalid_instruction; } @@ -16810,9 +16802,9 @@ static IrInstruction *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, static TypeStructField *validate_byte_offset(IrAnalyze *ira, IrInstruction *type_value, IrInstruction *field_name_value, - size_t *byte_offset) + size_t *byte_offset) { - ZigType *container_type = ir_resolve_type(ira, type_value); + ZigType *container_type = ir_resolve_type(ira, type_value, ZigTypeIdStruct); if (type_is_invalid(container_type)) return nullptr; @@ -16824,12 +16816,6 @@ static TypeStructField *validate_byte_offset(IrAnalyze *ira, if (!field_name) return nullptr; - if (container_type->id != ZigTypeIdStruct) { - ir_add_error(ira, type_value, - buf_sprintf("expected struct type, found '%s'", buf_ptr(&container_type->name))); - return nullptr; - } - TypeStructField *field = find_struct_type_field(container_type, field_name); if (field == nullptr) { ir_add_error(ira, field_name_value, @@ -17807,7 +17793,7 @@ static IrInstruction *ir_analyze_instruction_type_info(IrAnalyze *ira, { Error err; IrInstruction *type_value = instruction->type_value->child; - ZigType *type_entry = ir_resolve_type(ira, type_value); + ZigType *type_entry = ir_resolve_type(ira, type_value, ZigTypeIdInvalid); if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; @@ -17835,7 +17821,7 @@ static IrInstruction *ir_analyze_instruction_type_id(IrAnalyze *ira, IrInstructionTypeId *instruction) { IrInstruction *type_value = instruction->type_value->child; - ZigType *type_entry = ir_resolve_type(ira, type_value); + ZigType *type_entry = ir_resolve_type(ira, type_value, ZigTypeIdInvalid); if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; @@ -17870,7 +17856,7 @@ static IrInstruction *ir_analyze_instruction_set_eval_branch_quota(IrAnalyze *ir static IrInstruction *ir_analyze_instruction_type_name(IrAnalyze *ira, IrInstructionTypeName *instruction) { IrInstruction *type_value = instruction->type_value->child; - ZigType *type_entry = ir_resolve_type(ira, type_value); + ZigType *type_entry = ir_resolve_type(ira, type_value, ZigTypeIdInvalid); if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; @@ -18147,7 +18133,7 @@ static IrInstruction *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstruction static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstructionTruncate *instruction) { IrInstruction *dest_type_value = instruction->dest_type->child; - ZigType *dest_type = ir_resolve_type(ira, dest_type_value); + ZigType *dest_type = ir_resolve_type(ira, dest_type_value, ZigTypeIdInvalid); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; @@ -18200,7 +18186,7 @@ static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruct } static IrInstruction *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstructionIntCast *instruction) { - ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); + ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child, ZigTypeIdInvalid); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; @@ -18233,7 +18219,7 @@ static IrInstruction *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstruct } static IrInstruction *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstructionFloatCast *instruction) { - ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); + ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child, ZigTypeIdInvalid); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; @@ -18273,16 +18259,10 @@ static IrInstruction *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstru } static IrInstruction *ir_analyze_instruction_err_set_cast(IrAnalyze *ira, IrInstructionErrSetCast *instruction) { - ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); + ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child, ZigTypeIdErrorSet); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; - if (dest_type->id != ZigTypeIdErrorSet) { - ir_add_error(ira, instruction->dest_type, - buf_sprintf("expected error set type, found '%s'", buf_ptr(&dest_type->name))); - return ira->codegen->invalid_instruction; - } - IrInstruction *target = instruction->target->child; if (type_is_invalid(target->value.type)) return ira->codegen->invalid_instruction; @@ -18311,7 +18291,7 @@ static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_ali static IrInstruction *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstructionFromBytes *instruction) { Error err; - ZigType *dest_child_type = ir_resolve_type(ira, instruction->dest_child_type->child); + ZigType *dest_child_type = ir_resolve_type(ira, instruction->dest_child_type->child, ZigTypeIdInvalid); if (type_is_invalid(dest_child_type)) return ira->codegen->invalid_instruction; @@ -18408,7 +18388,7 @@ static IrInstruction *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstruct if (!is_slice(target->value.type)) { ir_add_error(ira, instruction->target, - buf_sprintf("expected slice, found '%s'", buf_ptr(&target->value.type->name))); + buf_sprintf("expected slice type, found '%s'", buf_ptr(&target->value.type->name))); return ira->codegen->invalid_instruction; } @@ -18427,7 +18407,7 @@ static IrInstruction *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstruct } static IrInstruction *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrInstructionIntToFloat *instruction) { - ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); + ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child, ZigTypeIdInvalid); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; @@ -18445,7 +18425,7 @@ static IrInstruction *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrInst } static IrInstruction *ir_analyze_instruction_float_to_int(IrAnalyze *ira, IrInstructionFloatToInt *instruction) { - ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); + ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child, ZigTypeIdInvalid); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; @@ -18501,7 +18481,7 @@ static IrInstruction *ir_analyze_instruction_bool_to_int(IrAnalyze *ira, IrInstr return ira->codegen->invalid_instruction; if (target->value.type->id != ZigTypeIdBool) { - ir_add_error(ira, instruction->target, buf_sprintf("expected bool, found '%s'", + ir_add_error(ira, instruction->target, buf_sprintf("expected bool type, found '%s'", buf_ptr(&target->value.type->name))); return ira->codegen->invalid_instruction; } @@ -19082,7 +19062,7 @@ static IrInstruction *ir_analyze_instruction_member_count(IrAnalyze *ira, IrInst IrInstruction *container = instruction->container->child; if (type_is_invalid(container->value.type)) return ira->codegen->invalid_instruction; - ZigType *container_type = ir_resolve_type(ira, container); + ZigType *container_type = ir_resolve_type(ira, container, ZigTypeIdInvalid); if ((err = ensure_complete_type(ira->codegen, container_type))) return ira->codegen->invalid_instruction; @@ -19116,7 +19096,7 @@ static IrInstruction *ir_analyze_instruction_member_count(IrAnalyze *ira, IrInst static IrInstruction *ir_analyze_instruction_member_type(IrAnalyze *ira, IrInstructionMemberType *instruction) { Error err; IrInstruction *container_type_value = instruction->container_type->child; - ZigType *container_type = ir_resolve_type(ira, container_type_value); + ZigType *container_type = ir_resolve_type(ira, container_type_value, ZigTypeIdInvalid); if (type_is_invalid(container_type)) return ira->codegen->invalid_instruction; @@ -19159,7 +19139,7 @@ static IrInstruction *ir_analyze_instruction_member_type(IrAnalyze *ira, IrInstr static IrInstruction *ir_analyze_instruction_member_name(IrAnalyze *ira, IrInstructionMemberName *instruction) { Error err; IrInstruction *container_type_value = instruction->container_type->child; - ZigType *container_type = ir_resolve_type(ira, container_type_value); + ZigType *container_type = ir_resolve_type(ira, container_type_value, ZigTypeIdInvalid); if (type_is_invalid(container_type)) return ira->codegen->invalid_instruction; @@ -19252,7 +19232,7 @@ static IrInstruction *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruct IrInstruction *type_value = instruction->type_value->child; if (type_is_invalid(type_value->value.type)) return ira->codegen->invalid_instruction; - ZigType *type_entry = ir_resolve_type(ira, type_value); + ZigType *type_entry = ir_resolve_type(ira, type_value, ZigTypeIdInvalid); if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusAlignmentKnown))) return ira->codegen->invalid_instruction; @@ -19302,16 +19282,10 @@ static IrInstruction *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInstr if (type_is_invalid(type_value->value.type)) return ira->codegen->invalid_instruction; - ZigType *dest_type = ir_resolve_type(ira, type_value); + ZigType *dest_type = ir_resolve_type(ira, type_value, ZigTypeIdInt); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; - if (dest_type->id != ZigTypeIdInt) { - ir_add_error(ira, type_value, - buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name))); - return ira->codegen->invalid_instruction; - } - IrInstruction *op1 = instruction->op1->child; if (type_is_invalid(op1->value.type)) return ira->codegen->invalid_instruction; @@ -19581,7 +19555,7 @@ static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruct IrInstruction *param_type_value = instruction->param_types[fn_type_id.next_param_index]->child; if (type_is_invalid(param_type_value->value.type)) return ira->codegen->invalid_instruction; - ZigType *param_type = ir_resolve_type(ira, param_type_value); + ZigType *param_type = ir_resolve_type(ira, param_type_value, ZigTypeIdInvalid); switch (type_requires_comptime(ira->codegen, param_type)) { case ReqCompTimeYes: if (!calling_convention_allows_zig_types(fn_type_id.cc)) { @@ -19615,7 +19589,7 @@ static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruct } IrInstruction *return_type_value = instruction->return_type->child; - fn_type_id.return_type = ir_resolve_type(ira, return_type_value); + fn_type_id.return_type = ir_resolve_type(ira, return_type_value, ZigTypeIdInvalid); if (type_is_invalid(fn_type_id.return_type)) return ira->codegen->invalid_instruction; if (fn_type_id.return_type->id == ZigTypeIdOpaque) { @@ -19631,7 +19605,7 @@ static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruct return ira->codegen->invalid_instruction; } IrInstruction *async_allocator_type_value = instruction->async_allocator_type_value->child; - fn_type_id.async_allocator_type = ir_resolve_type(ira, async_allocator_type_value); + fn_type_id.async_allocator_type = ir_resolve_type(ira, async_allocator_type_value, ZigTypeIdInvalid); if (type_is_invalid(fn_type_id.async_allocator_type)) return ira->codegen->invalid_instruction; } @@ -19939,7 +19913,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 result_type = get_slice_type(ira->codegen, result_ptr_type); } else { ir_add_error(ira, target, - buf_sprintf("expected pointer or slice, found '%s'", buf_ptr(&target_type->name))); + buf_sprintf("expected pointer or slice type, found '%s'", buf_ptr(&target_type->name))); return ira->codegen->invalid_instruction; } @@ -19985,13 +19959,13 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ // validate src_type and dest_type. if (get_src_ptr_type(src_type) == nullptr) { - ir_add_error(ira, ptr, buf_sprintf("expected pointer, found '%s'", buf_ptr(&src_type->name))); + ir_add_error(ira, ptr, buf_sprintf("expected Pointer type, found '%s'", buf_ptr(&src_type->name))); return ira->codegen->invalid_instruction; } if (get_src_ptr_type(dest_type) == nullptr) { ir_add_error(ira, dest_type_src, - buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name))); + buf_sprintf("expected Pointer type, found '%s'", buf_ptr(&dest_type->name))); return ira->codegen->invalid_instruction; } @@ -20059,7 +20033,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtrCast *instruction) { IrInstruction *dest_type_value = instruction->dest_type->child; - ZigType *dest_type = ir_resolve_type(ira, dest_type_value); + ZigType *dest_type = ir_resolve_type(ira, dest_type_value, ZigTypeIdInvalid); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; @@ -20255,7 +20229,7 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstructionBitCast *instruction) { Error err; IrInstruction *dest_type_value = instruction->dest_type->child; - ZigType *dest_type = ir_resolve_type(ira, dest_type_value); + ZigType *dest_type = ir_resolve_type(ira, dest_type_value, ZigTypeIdInvalid); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; @@ -20352,13 +20326,13 @@ static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruct static IrInstruction *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstructionIntToPtr *instruction) { Error err; IrInstruction *dest_type_value = instruction->dest_type->child; - ZigType *dest_type = ir_resolve_type(ira, dest_type_value); + ZigType *dest_type = ir_resolve_type(ira, dest_type_value, ZigTypeIdInvalid); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; // We explicitly check for the size, so we can use get_src_ptr_type if (get_src_ptr_type(dest_type) == nullptr) { - ir_add_error(ira, dest_type_value, buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name))); + ir_add_error(ira, dest_type_value, buf_sprintf("expected Pointer type, found '%s'", buf_ptr(&dest_type->name))); return ira->codegen->invalid_instruction; } @@ -20461,7 +20435,7 @@ static IrInstruction *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstru // We check size explicitly so we can use get_src_ptr_type here. if (get_src_ptr_type(target->value.type) == nullptr) { ir_add_error(ira, target, - buf_sprintf("expected pointer, found '%s'", buf_ptr(&target->value.type->name))); + buf_sprintf("expected Pointer type, found '%s'", buf_ptr(&target->value.type->name))); return ira->codegen->invalid_instruction; } @@ -20492,7 +20466,7 @@ static IrInstruction *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstru static IrInstruction *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstructionPtrType *instruction) { Error err; - ZigType *child_type = ir_resolve_type(ira, instruction->child_type->child); + ZigType *child_type = ir_resolve_type(ira, instruction->child_type->child, ZigTypeIdInvalid); if (type_is_invalid(child_type)) return ira->codegen->invalid_instruction; @@ -20591,7 +20565,7 @@ static IrInstruction *ir_analyze_instruction_set_align_stack(IrAnalyze *ira, IrI static IrInstruction *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstructionArgType *instruction) { IrInstruction *fn_type_inst = instruction->fn_type->child; - ZigType *fn_type = ir_resolve_type(ira, fn_type_inst); + ZigType *fn_type = ir_resolve_type(ira, fn_type_inst, ZigTypeIdFn); if (type_is_invalid(fn_type)) return ira->codegen->invalid_instruction; @@ -20600,11 +20574,6 @@ static IrInstruction *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruct if (!ir_resolve_usize(ira, arg_index_inst, &arg_index)) return ira->codegen->invalid_instruction; - if (fn_type->id != ZigTypeIdFn) { - ir_add_error(ira, fn_type_inst, buf_sprintf("expected function, found '%s'", buf_ptr(&fn_type->name))); - return ira->codegen->invalid_instruction; - } - FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; if (arg_index >= fn_type_id->param_count) { ir_add_error(ira, arg_index_inst, @@ -20629,7 +20598,7 @@ static IrInstruction *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruct static IrInstruction *ir_analyze_instruction_tag_type(IrAnalyze *ira, IrInstructionTagType *instruction) { Error err; IrInstruction *target_inst = instruction->target->child; - ZigType *enum_type = ir_resolve_type(ira, target_inst); + ZigType *enum_type = ir_resolve_type(ira, target_inst, ZigTypeIdInvalid); if (type_is_invalid(enum_type)) return ira->codegen->invalid_instruction; @@ -20654,7 +20623,7 @@ static IrInstruction *ir_analyze_instruction_tag_type(IrAnalyze *ira, IrInstruct return ira->codegen->invalid_instruction; } } else { - ir_add_error(ira, target_inst, buf_sprintf("expected enum or union, found '%s'", + ir_add_error(ira, target_inst, buf_sprintf("expected enum or union type, found '%s'", buf_ptr(&enum_type->name))); return ira->codegen->invalid_instruction; } @@ -20808,7 +20777,7 @@ static IrInstruction *ir_analyze_instruction_coro_promise(IrAnalyze *ira, IrInst if (coro_handle->value.type->id != ZigTypeIdPromise || coro_handle->value.type->data.promise.result_type == nullptr) { - ir_add_error(ira, &instruction->base, buf_sprintf("expected promise->T, found '%s'", + ir_add_error(ira, &instruction->base, buf_sprintf("expected promise->T type, found '%s'", buf_ptr(&coro_handle->value.type->name))); return ira->codegen->invalid_instruction; } @@ -20839,7 +20808,7 @@ static IrInstruction *ir_analyze_instruction_coro_alloc_helper(IrAnalyze *ira, I } static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op) { - ZigType *operand_type = ir_resolve_type(ira, op); + ZigType *operand_type = ir_resolve_type(ira, op, ZigTypeIdInvalid); if (type_is_invalid(operand_type)) return ira->codegen->builtin_types.entry_invalid; @@ -20969,12 +20938,12 @@ static IrInstruction *ir_analyze_instruction_atomic_load(IrAnalyze *ira, IrInstr } static IrInstruction *ir_analyze_instruction_promise_result_type(IrAnalyze *ira, IrInstructionPromiseResultType *instruction) { - ZigType *promise_type = ir_resolve_type(ira, instruction->promise_type->child); + ZigType *promise_type = ir_resolve_type(ira, instruction->promise_type->child, ZigTypeIdInvalid); if (type_is_invalid(promise_type)) return ira->codegen->invalid_instruction; if (promise_type->id != ZigTypeIdPromise || promise_type->data.promise.result_type == nullptr) { - ir_add_error(ira, &instruction->base, buf_sprintf("expected promise->T, found '%s'", + ir_add_error(ira, &instruction->base, buf_sprintf("expected promise->T type, found '%s'", buf_ptr(&promise_type->name))); return ira->codegen->invalid_instruction; } @@ -20983,7 +20952,7 @@ static IrInstruction *ir_analyze_instruction_promise_result_type(IrAnalyze *ira, } static IrInstruction *ir_analyze_instruction_await_bookkeeping(IrAnalyze *ira, IrInstructionAwaitBookkeeping *instruction) { - ZigType *promise_result_type = ir_resolve_type(ira, instruction->promise_result_type->child); + ZigType *promise_result_type = ir_resolve_type(ira, instruction->promise_result_type->child, ZigTypeIdInvalid); if (type_is_invalid(promise_result_type)) return ira->codegen->invalid_instruction; @@ -21046,7 +21015,7 @@ static IrInstruction *ir_analyze_instruction_mark_err_ret_trace_ptr(IrAnalyze *i } static IrInstruction *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstructionSqrt *instruction) { - ZigType *float_type = ir_resolve_type(ira, instruction->type->child); + ZigType *float_type = ir_resolve_type(ira, instruction->type->child, ZigTypeIdInvalid); if (type_is_invalid(float_type)) return ira->codegen->invalid_instruction; @@ -21113,7 +21082,7 @@ static IrInstruction *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstructionS } static IrInstruction *ir_analyze_instruction_bswap(IrAnalyze *ira, IrInstructionBswap *instruction) { - ZigType *int_type = ir_resolve_type(ira, instruction->type->child); + ZigType *int_type = ir_resolve_type(ira, instruction->type->child, ZigTypeIdInvalid); if (type_is_invalid(int_type)) return ira->codegen->invalid_instruction; @@ -21169,7 +21138,7 @@ static IrInstruction *ir_analyze_instruction_bswap(IrAnalyze *ira, IrInstruction } static IrInstruction *ir_analyze_instruction_bit_reverse(IrAnalyze *ira, IrInstructionBitReverse *instruction) { - ZigType *int_type = ir_resolve_type(ira, instruction->type->child); + ZigType *int_type = ir_resolve_type(ira, instruction->type->child, ZigTypeIdInvalid); if (type_is_invalid(int_type)) return ira->codegen->invalid_instruction; @@ -21240,7 +21209,7 @@ static IrInstruction *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInstr if (target->value.type->id != ZigTypeIdEnum) { ir_add_error(ira, instruction->target, - buf_sprintf("expected enum, found type '%s'", buf_ptr(&target->value.type->name))); + buf_sprintf("expected enum type, found '%s'", buf_ptr(&target->value.type->name))); return ira->codegen->invalid_instruction; } @@ -21255,16 +21224,10 @@ static IrInstruction *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInstr static IrInstruction *ir_analyze_instruction_int_to_enum(IrAnalyze *ira, IrInstructionIntToEnum *instruction) { Error err; IrInstruction *dest_type_value = instruction->dest_type->child; - ZigType *dest_type = ir_resolve_type(ira, dest_type_value); + ZigType *dest_type = ir_resolve_type(ira, dest_type_value, ZigTypeIdEnum); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; - if (dest_type->id != ZigTypeIdEnum) { - ir_add_error(ira, instruction->dest_type, - buf_sprintf("expected enum, found type '%s'", buf_ptr(&dest_type->name))); - return ira->codegen->invalid_instruction; - } - if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; diff --git a/src/parser.cpp b/src/parser.cpp index 077365995e87d5d38f491d87ef121f68cb0f90c9..9425df2430c8e6a5cbbc860ea09fc3e0ac89cd18 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -122,19 +122,37 @@ static AstNode *ast_parse_container_decl_type(ParseContext *pc); static AstNode *ast_parse_byte_align(ParseContext *pc); ATTRIBUTE_PRINTF(3, 4) +static ErrorMsg *ast_error(ParseContext *pc, Token *token, const char *format, ...) { + va_list ap; + va_start(ap, format); + Buf *msg = buf_vprintf(format, ap); + va_end(ap); + + ErrorMsg *err = err_msg_create_with_line(pc->owner->path, token->start_line, token->start_column, + pc->owner->source_code, pc->owner->line_offsets, msg); + err->line_start = token->start_line; + err->column_start = token->start_column; + + return err; +} + +ATTRIBUTE_PRINTF(4, 5) ATTRIBUTE_NORETURN -static void ast_error(ParseContext *pc, Token *token, const char *format, ...) { +static void ast_error_exit(ParseContext *pc, Token *token, ErrorMsg *note, const char *format, ...) { va_list ap; va_start(ap, format); Buf *msg = buf_vprintf(format, ap); va_end(ap); - ErrorMsg *err = err_msg_create_with_line(pc->owner->path, token->start_line, token->start_column, pc->owner->source_code, pc->owner->line_offsets, msg); err->line_start = token->start_line; err->column_start = token->start_column; + if (note) { + err->notes.append(note); + } + print_err_msg(err, pc->err_color); exit(EXIT_FAILURE); } @@ -164,7 +182,7 @@ static Buf ast_token_str(Buf *input, Token *token) { ATTRIBUTE_NORETURN static void ast_invalid_token_error(ParseContext *pc, Token *token) { Buf token_value = ast_token_str(pc->buf, token); - ast_error(pc, token, "invalid token: '%s'", buf_ptr(&token_value)); + ast_error_exit(pc, token, NULL, "invalid token: '%s'", buf_ptr(&token_value)); } static AstNode *ast_create_node_no_line_info(ParseContext *pc, NodeType type) { @@ -214,8 +232,13 @@ static Token *eat_token_if(ParseContext *pc, TokenId id) { static Token *expect_token(ParseContext *pc, TokenId id) { Token *res = eat_token(pc); - if (res->id != id) - ast_error(pc, res, "expected token '%s', found '%s'", token_name(id), token_name(res->id)); + if (res->id != id) { + ErrorMsg *note = NULL; + if (res->id == TokenIdAmpersandAmpersand) { + note = ast_error(pc, res, "did you mean to use `and`?"); + } + ast_error_exit(pc, res, note, "expected token '%s', found '%s'", token_name(id), token_name(res->id)); + } return res; } @@ -837,7 +860,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) { if (param_decl->data.param_decl.is_var_args) res->data.fn_proto.is_var_args = true; if (i != params.length - 1 && res->data.fn_proto.is_var_args) - ast_error(pc, first, "Function prototype have varargs as a none last paramter."); + ast_error_exit(pc, first, NULL, "Function prototype have varargs as a none last paramter."); } return res; } diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 921ee4de09b5d022f40b3014303307aa78312f07..464412d4435b7ca0263f602db76eee88924ec9fb 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -199,6 +199,7 @@ enum TokenizeState { TokenizeStateSawDash, TokenizeStateSawMinusPercent, TokenizeStateSawAmpersand, + TokenizeStateSawAmpersandAmpersand, TokenizeStateSawCaret, TokenizeStateSawBar, TokenizeStateSawBarBar, @@ -891,6 +892,10 @@ void tokenize(Buf *buf, Tokenization *out) { end_token(&t); t.state = TokenizeStateStart; break; + case '&': + set_token_id(&t, t.cur_tok, TokenIdAmpersandAmpersand); + t.state = TokenizeStateSawAmpersandAmpersand; + break; default: t.pos -= 1; end_token(&t); @@ -898,6 +903,11 @@ void tokenize(Buf *buf, Tokenization *out) { continue; } break; + case TokenizeStateSawAmpersandAmpersand: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; case TokenizeStateSawCaret: switch (c) { case '=': @@ -1468,6 +1478,7 @@ void tokenize(Buf *buf, Tokenization *out) { case TokenizeStateSawPlus: case TokenizeStateSawDash: case TokenizeStateSawAmpersand: + case TokenizeStateSawAmpersandAmpersand: case TokenizeStateSawCaret: case TokenizeStateSawBar: case TokenizeStateSawEq: @@ -1515,6 +1526,7 @@ void tokenize(Buf *buf, Tokenization *out) { const char * token_name(TokenId id) { switch (id) { case TokenIdAmpersand: return "&"; + case TokenIdAmpersandAmpersand: return "&&"; case TokenIdArrow: return "->"; case TokenIdAtSign: return "@"; case TokenIdBang: return "!"; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index 1574e95571d2f652fd7429831b2f60942a2b0ae3..2e872ce4de54d003275f69c9ad073ce0ab79595c 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -14,6 +14,7 @@ enum TokenId { TokenIdAmpersand, + TokenIdAmpersandAmpersand, TokenIdArrow, TokenIdAtSign, TokenIdBang, diff --git a/test/compile_errors.zig b/test/compile_errors.zig index d9603ba4ff3a761d07aa0fc5d5e265dd95bef900..d262405febee4a0fa4e3712faebecc3196d63ef2 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,36 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "Use of && in place of `and`", + \\export fn entry() void { + \\ if (true && false) return; + \\} + , + ".tmp_source.zig:2:14: error: expected token ')', found '&&'", + ".tmp_source.zig:2:14: note: did you mean to use `and`?", + ); + + cases.add( + "Use of || in place of `or` (using comptime_int)", + \\export fn entry() void { + \\ if (1 || 0) return; + \\} + , + ".tmp_source.zig:2:9: error: expected ErrorSet type, found 'comptime_int'", + ".tmp_source.zig:2:11: note: did you mean to use `or`?", + ); + + cases.add( + "Use of || in place of `or` (using booleans)", + \\export fn entry() void { + \\ if (true || false) return; + \\} + , + ".tmp_source.zig:2:9: error: expected ErrorSet type, found 'bool'", + ".tmp_source.zig:2:14: note: did you mean to use `or`?", + ); + cases.add( "compile log a pointer to an opaque value", \\export fn entry() void { @@ -68,7 +98,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ do_the_thing(bar); \\} , - ".tmp_source.zig:4:18: error: expected type 'fn(i32) void', found 'fn(bool) void", + ".tmp_source.zig:4:18: error: expected 'fn(i32) void' type, found 'fn(bool) void", ".tmp_source.zig:4:18: note: parameter 0: 'bool' cannot cast into 'i32'", ); @@ -104,7 +134,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , ".tmp_source.zig:3:31: error: integer value 300 cannot be implicitly casted to type 'u8'", ".tmp_source.zig:7:22: error: integer value 300 cannot be implicitly casted to type 'u8'", - ".tmp_source.zig:11:20: error: expected type 'u8', found 'u16'", + ".tmp_source.zig:11:20: error: expected 'u8' type, found 'u16'", ); cases.add( @@ -124,7 +154,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(y)); } , - ".tmp_source.zig:2:14: error: expected type 'f32', found 'f64'", + ".tmp_source.zig:2:14: error: expected 'f32' type, found 'f64'", ); cases.add( @@ -172,7 +202,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var ptr2: *c_void = &b; \\} , - ".tmp_source.zig:5:26: error: expected type '*c_void', found '**u32'", + ".tmp_source.zig:5:26: error: expected '*c_void' type, found '**u32'", ); cases.add( @@ -222,7 +252,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ const sliceA: []u8 = &buffer; \\} , - ".tmp_source.zig:3:27: error: expected type '[]u8', found '*const [1]u8'", + ".tmp_source.zig:3:27: error: expected '[]u8' type, found '*const [1]u8'", ); cases.add( @@ -267,8 +297,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ const Errors = error{} || u16; \\} , - ".tmp_source.zig:2:20: error: expected error set type, found 'u8'", - ".tmp_source.zig:5:31: error: expected error set type, found 'u16'", + ".tmp_source.zig:2:20: error: expected ErrorSet type, found 'u8'", + ".tmp_source.zig:2:23: note: did you mean to use `or`?", + ".tmp_source.zig:5:31: error: expected ErrorSet type, found 'u16'", ); cases.add( @@ -763,7 +794,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\fn bar(x: *b.Foo) void {} , - ".tmp_source.zig:6:10: error: expected type '*Foo', found '*Foo'", + ".tmp_source.zig:6:10: error: expected '*Foo' type, found '*Foo'", ".tmp_source.zig:6:10: note: pointer type child 'Foo' cannot cast into pointer type child 'Foo'", "a.zig:1:17: note: Foo declared here", "b.zig:1:17: note: Foo declared here", @@ -833,7 +864,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var y = p.*; \\} , - ".tmp_source.zig:4:23: error: expected type '*?*i32', found '**i32'", + ".tmp_source.zig:4:23: error: expected '*?*i32' type, found '**i32'", ); cases.add( @@ -842,7 +873,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ const x: [*]const bool = true; \\} , - ".tmp_source.zig:2:30: error: expected type '[*]const bool', found 'bool'", + ".tmp_source.zig:2:30: error: expected '[*]const bool' type, found 'bool'", ); cases.add( @@ -887,7 +918,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var rule_set = try Foo.init(); \\} , - ".tmp_source.zig:2:13: error: expected type 'i32', found 'type'", + ".tmp_source.zig:2:13: error: expected 'i32' type, found 'type'", ); cases.add( @@ -921,7 +952,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return null; \\} , - ".tmp_source.zig:5:34: error: expected type '?NextError!i32', found '?OtherError!i32'", + ".tmp_source.zig:5:34: error: expected '?NextError!i32' type, found '?OtherError!i32'", ".tmp_source.zig:5:34: note: optional type child 'OtherError!i32' cannot cast into optional type child 'NextError!i32'", ".tmp_source.zig:5:34: note: error set 'OtherError' cannot cast into error set 'NextError'", ".tmp_source.zig:2:26: note: 'error.OutOfMemory' not a member of destination error set", @@ -991,7 +1022,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ @panic(e); \\} , - ".tmp_source.zig:3:12: error: expected type '[]const u8', found 'error{Foo}'", + ".tmp_source.zig:3:12: error: expected '[]const u8' type, found 'error{Foo}'", ); cases.add( @@ -1019,7 +1050,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return error.ShouldBeCompileError; \\} , - ".tmp_source.zig:6:17: error: expected type 'void', found 'error{ShouldBeCompileError}'", + ".tmp_source.zig:6:17: error: expected 'void' type, found 'error{ShouldBeCompileError}'", ); cases.add( @@ -1062,7 +1093,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ a(c); \\} , - ".tmp_source.zig:8:7: error: expected type 'fn(*const u8) void', found 'fn(u8) void'", + ".tmp_source.zig:8:7: error: expected 'fn(*const u8) void' type, found 'fn(u8) void'", ); cases.add( @@ -1144,7 +1175,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ E.One => {}, \\ } \\} - , ".tmp_source.zig:9:10: error: expected type 'usize', found 'E'"); + , ".tmp_source.zig:9:10: error: expected 'usize' type, found 'E'"); cases.add( "range operator in switch used on error set", @@ -1190,7 +1221,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ const z = i32!i32; \\} , - ".tmp_source.zig:2:15: error: expected error set type, found type 'i32'", + ".tmp_source.zig:2:15: error: expected ErrorSet type, found 'i32'", ); cases.add( @@ -1240,7 +1271,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return error.B; \\} , - ".tmp_source.zig:3:35: error: expected type 'SmallErrorSet!i32', found 'anyerror!i32'", + ".tmp_source.zig:3:35: error: expected 'SmallErrorSet!i32' type, found 'anyerror!i32'", ".tmp_source.zig:3:35: note: error set 'anyerror' cannot cast into error set 'SmallErrorSet'", ".tmp_source.zig:3:35: note: cannot cast global error set into smaller set", ); @@ -1255,7 +1286,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return error.B; \\} , - ".tmp_source.zig:3:31: error: expected type 'SmallErrorSet', found 'anyerror'", + ".tmp_source.zig:3:31: error: expected 'SmallErrorSet' type, found 'anyerror'", ".tmp_source.zig:3:31: note: cannot cast global error set into smaller set", ); @@ -1282,7 +1313,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var x: Set2 = set1; \\} , - ".tmp_source.zig:7:19: error: expected type 'Set2', found 'Set1'", + ".tmp_source.zig:7:19: error: expected 'Set2' type, found 'Set1'", ".tmp_source.zig:1:23: note: 'error.B' not a member of destination error set", ); @@ -1822,7 +1853,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn a() noreturn {return;} \\export fn entry() void { a(); } , - ".tmp_source.zig:1:18: error: expected type 'noreturn', found 'void'", + ".tmp_source.zig:1:18: error: expected 'noreturn' type, found 'void'", ); cases.add( @@ -1830,7 +1861,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn a() i32 {} \\export fn entry() void { _ = a(); } , - ".tmp_source.zig:1:12: error: expected type 'i32', found 'void'", + ".tmp_source.zig:1:12: error: expected 'i32' type, found 'void'", ); cases.add( @@ -1936,7 +1967,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return a; \\} , - ".tmp_source.zig:3:12: error: expected type 'i32', found '[*]const u8'", + ".tmp_source.zig:3:12: error: expected 'i32' type, found '[*]const u8'", ); cases.add( @@ -1945,7 +1976,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ if (0) {} \\} , - ".tmp_source.zig:2:9: error: expected type 'bool', found 'comptime_int'", + ".tmp_source.zig:2:9: error: expected 'bool' type, found 'comptime_int'", ); cases.add( @@ -2040,8 +2071,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ array[bad] = array[bad]; \\} , - ".tmp_source.zig:4:11: error: expected type 'usize', found 'bool'", - ".tmp_source.zig:4:24: error: expected type 'usize', found 'bool'", + ".tmp_source.zig:4:11: error: expected 'usize' type, found 'bool'", + ".tmp_source.zig:4:24: error: expected 'usize' type, found 'bool'", ); cases.add( @@ -2455,7 +2486,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn foo() *const i32 { return y; } \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } , - ".tmp_source.zig:3:30: error: expected type '*const i32', found '*const comptime_int'", + ".tmp_source.zig:3:30: error: expected '*const i32' type, found '*const comptime_int'", ); cases.add( @@ -2533,7 +2564,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn c() i32 {return 2;} \\export fn entry() usize { return @sizeOf(@typeOf(fns)); } , - ".tmp_source.zig:1:27: error: expected type 'fn() void', found 'fn() i32'", + ".tmp_source.zig:1:27: error: expected 'fn() void' type, found 'fn() i32'", ); cases.add( @@ -2545,7 +2576,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(fns)); } , - ".tmp_source.zig:1:36: error: expected type 'fn(i32) i32', found 'extern fn(i32) i32'", + ".tmp_source.zig:1:36: error: expected 'fn(i32) i32' type, found 'extern fn(i32) i32'", ); cases.add( @@ -2649,7 +2680,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(a)); } , - ".tmp_source.zig:1:16: error: expected type '*u8', found '(null)'", + ".tmp_source.zig:1:16: error: expected '*u8' type, found '(null)'", ); cases.add( @@ -3289,7 +3320,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\} \\fn something() anyerror!void { } , - ".tmp_source.zig:2:5: error: expected type 'void', found 'anyerror'", + ".tmp_source.zig:2:5: error: expected 'void' type, found 'anyerror'", ".tmp_source.zig:1:15: note: return type declared here", ); @@ -3468,7 +3499,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ derp.init(); \\} , - ".tmp_source.zig:14:5: error: expected type 'i32', found 'Foo'", + ".tmp_source.zig:14:5: error: expected 'i32' type, found 'Foo'", ); cases.add( @@ -3498,7 +3529,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ x.init(); \\} , - ".tmp_source.zig:23:5: error: expected type '*Allocator', found '*List'", + ".tmp_source.zig:23:5: error: expected '*Allocator' type, found '*List'", ); cases.add( @@ -3670,7 +3701,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } , - ".tmp_source.zig:8:26: error: expected type '*const u3', found '*align(:3:1) const u3'", + ".tmp_source.zig:8:26: error: expected '*const u3' type, found '*align(:3:1) const u3'", ); cases.add( @@ -3799,7 +3830,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } , - ".tmp_source.zig:4:19: error: expected type '*[]const u8', found '*const []const u8'", + ".tmp_source.zig:4:19: error: expected '*[]const u8' type, found '*const []const u8'", ); cases.addCase(x: { @@ -3831,7 +3862,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ foo(global_array); \\} , - ".tmp_source.zig:4:9: error: expected type '[]i32', found '[10]i32'", + ".tmp_source.zig:4:9: error: expected '[]i32' type, found '[10]i32'", ); cases.add( @@ -3840,7 +3871,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return @ptrCast(usize, a); \\} , - ".tmp_source.zig:2:21: error: expected pointer, found 'usize'", + ".tmp_source.zig:2:21: error: expected Pointer type, found 'usize'", ); cases.add( @@ -3887,7 +3918,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return @fieldParentPtr(Foo, "a", a); \\} , - ".tmp_source.zig:3:28: error: expected struct type, found 'i32'", + ".tmp_source.zig:3:28: error: expected Struct type, found 'i32'", ); cases.add( @@ -3911,7 +3942,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return @fieldParentPtr(Foo, "a", a); \\} , - ".tmp_source.zig:5:38: error: expected pointer, found 'i32'", + ".tmp_source.zig:5:38: error: expected Pointer type, found 'i32'", ); cases.add( @@ -3952,7 +3983,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return @byteOffsetOf(Foo, "a",); \\} , - ".tmp_source.zig:3:26: error: expected struct type, found 'i32'", + ".tmp_source.zig:3:26: error: expected Struct type, found 'i32'", ); cases.add( @@ -4066,7 +4097,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\} \\fn bar() ?i32 { return 1; } , - ".tmp_source.zig:2:15: error: expected type 'bool', found '?i32'", + ".tmp_source.zig:2:15: error: expected 'bool' type, found '?i32'", ); cases.add( @@ -4076,7 +4107,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\} \\fn bar() anyerror!i32 { return 1; } , - ".tmp_source.zig:2:15: error: expected type 'bool', found 'anyerror!i32'", + ".tmp_source.zig:2:15: error: expected 'bool' type, found 'anyerror!i32'", ); cases.add( @@ -4337,7 +4368,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return @ptrToInt(x); \\} , - ".tmp_source.zig:2:22: error: expected pointer, found 'i32'", + ".tmp_source.zig:2:22: error: expected Pointer type, found 'i32'", ); cases.add( @@ -4373,7 +4404,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return x << y; \\} , - ".tmp_source.zig:2:17: error: expected type 'u3', found 'u8'", + ".tmp_source.zig:2:17: error: expected 'u3' type, found 'u8'", ); cases.add( @@ -4402,7 +4433,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ x.* += 1; \\} , - ".tmp_source.zig:8:13: error: expected type '*u32', found '*align(1) u32'", + ".tmp_source.zig:8:13: error: expected '*u32' type, found '*align(1) u32'", ); cases.add( @@ -4446,7 +4477,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ @alignCast(4, u32(3)); \\} , - ".tmp_source.zig:2:22: error: expected pointer or slice, found 'u32'", + ".tmp_source.zig:2:22: error: expected pointer or slice type, found 'u32'", ); cases.add( @@ -4459,7 +4490,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\} \\fn alignedSmall() align(4) i32 { return 1234; } , - ".tmp_source.zig:2:35: error: expected type 'fn() align(8) i32', found 'fn() align(4) i32'", + ".tmp_source.zig:2:35: error: expected 'fn() align(8) i32' type, found 'fn() align(4) i32'", ); cases.add( @@ -4471,7 +4502,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return x == 5678; \\} , - ".tmp_source.zig:4:32: error: expected type '*i32', found '*align(1) i32'", + ".tmp_source.zig:4:32: error: expected '*i32' type, found '*align(1) i32'", ); cases.add( @@ -4506,7 +4537,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ bar(@ptrCast(*c_void, &x)); \\} , - ".tmp_source.zig:5:9: error: expected type '*Derp', found '*c_void'", + ".tmp_source.zig:5:9: error: expected '*Derp' type, found '*c_void'", ); cases.add( @@ -4552,7 +4583,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, u32(1234), u32(1234))) {} \\} , - ".tmp_source.zig:3:50: error: expected type 'AtomicOrder', found 'u32'", + ".tmp_source.zig:3:50: error: expected 'AtomicOrder' type, found 'u32'", ); cases.add( @@ -4562,7 +4593,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ @export("entry", entry, u32(1234)); \\} , - ".tmp_source.zig:3:32: error: expected type 'GlobalLinkage', found 'u32'", + ".tmp_source.zig:3:32: error: expected 'GlobalLinkage' type, found 'u32'", ); cases.add( @@ -4739,7 +4770,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = @ArgType(i32, 3); \\} , - ".tmp_source.zig:2:18: error: expected function, found 'i32'", + ".tmp_source.zig:2:18: error: expected Fn type, found 'i32'", ); cases.add( @@ -4837,7 +4868,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\} \\pub extern fn foo(format: *const u8, ...) void; , - ".tmp_source.zig:2:9: error: expected type '*const u8', found '[5]u8'", + ".tmp_source.zig:2:9: error: expected '*const u8' type, found '[5]u8'", ); cases.add( @@ -4906,7 +4937,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var x: u2 = Small.Two; \\} , - ".tmp_source.zig:9:22: error: expected type 'u2', found 'Small'", + ".tmp_source.zig:9:22: error: expected 'u2' type, found 'Small'", ); cases.add( @@ -4923,7 +4954,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var x = @intToEnum(Small, y); \\} , - ".tmp_source.zig:10:31: error: expected type 'u2', found 'u3'", + ".tmp_source.zig:10:31: error: expected 'u2' type, found 'u3'", ); cases.add( @@ -5320,7 +5351,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ asm volatile ("" : : [bar]"r"(3) : ""); \\} , - ".tmp_source.zig:2:35: error: expected sized integer or sized float, found comptime_int", + ".tmp_source.zig:2:35: error: expected sized integer or sized float type, found comptime_int", ); cases.add( @@ -5329,6 +5360,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ asm volatile ("" : : [bar]"r"(3.17) : ""); \\} , - ".tmp_source.zig:2:35: error: expected sized integer or sized float, found comptime_float", + ".tmp_source.zig:2:35: error: expected sized integer or sized float type, found comptime_float", ); } -- 2.54.0 From 9c328b42916d463465b134457c7f13b5c65da406 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 29 Jan 2019 22:28:33 -0500 Subject: [PATCH 20/37] simpler implementation of `&&` and `||` hints This accomplishes the same goal, but with less changes, so that I can backport copy elision stuff easier. --- doc/langref.html.in | 2 +- src/ir.cpp | 253 ++++++++++++++++++++++++---------------- src/parser.cpp | 35 +----- src/tokenizer.cpp | 15 +-- src/tokenizer.hpp | 1 - test/compile_errors.zig | 152 ++++++++++++------------ 6 files changed, 237 insertions(+), 221 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 1fb751cef431f1a2d854653c09bf5963c1c983db..6e03d3ec6d676d64a2ac30e2dcb60e3dffe84247 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -6024,7 +6024,7 @@ fn add(a: i32, b: i32) i32 { This is typically used for type safety when interacting with C code that does not expose struct details. Example:

- {#code_begin|test_err|expected '*Derp' type, found '*Wat'#} + {#code_begin|test_err|expected type '*Derp', found '*Wat'#} const Derp = @OpaqueType(); const Wat = @OpaqueType(); diff --git a/src/ir.cpp b/src/ir.cpp index df2c8cc9beb791da55465e197df56f5122563c5a..b184252a2e83289557442d5ad39e56af1a714d64 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9771,37 +9771,50 @@ IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node return ir_exec_const_result(codegen, analyzed_executable); } -static ZigType *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value, ZigTypeId wanted_type) { +static ZigType *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value) { if (type_is_invalid(type_value->value.type)) return ira->codegen->builtin_types.entry_invalid; - const char *expected_type_str = type_id_name(ZigTypeIdMetaType); - - if (wanted_type != ZigTypeIdInvalid) { - expected_type_str = type_id_name(wanted_type); - } - if (type_value->value.type->id != ZigTypeIdMetaType) { - ir_add_error( ira, type_value, - buf_sprintf("expected %s type, found '%s'", expected_type_str, buf_ptr(&type_value->value.type->name))); - return ira->codegen->builtin_types.entry_invalid; - } - - ConstExprValue *const_val = ir_resolve_const(ira, type_value, UndefBad); - if (!const_val) - return ira->codegen->builtin_types.entry_invalid; - - assert(const_val->data.x_type != nullptr); - - ZigType *out_type = const_val->data.x_type; - - if (wanted_type != ZigTypeIdInvalid && out_type->id != wanted_type) { ir_add_error(ira, type_value, - buf_sprintf( "expected %s type, found '%s'", expected_type_str, buf_ptr(&out_type->name))); + buf_sprintf("expected type 'type', found '%s'", buf_ptr(&type_value->value.type->name))); return ira->codegen->builtin_types.entry_invalid; } - return out_type; + ConstExprValue *const_val = ir_resolve_const(ira, type_value, UndefBad); + if (!const_val) + return ira->codegen->builtin_types.entry_invalid; + + assert(const_val->data.x_type != nullptr); + return const_val->data.x_type; +} + +static ZigType *ir_resolve_error_set_type(IrAnalyze *ira, IrInstruction *op_source, IrInstruction *type_value) { + if (type_is_invalid(type_value->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + if (type_value->value.type->id != ZigTypeIdMetaType) { + ErrorMsg *msg = ir_add_error(ira, type_value, + buf_sprintf("expected error set type, found '%s'", buf_ptr(&type_value->value.type->name))); + add_error_note(ira->codegen, msg, op_source->source_node, + buf_sprintf("`||` merges error sets; `or` performs boolean OR")); + return ira->codegen->builtin_types.entry_invalid; + } + + ConstExprValue *const_val = ir_resolve_const(ira, type_value, UndefBad); + if (!const_val) + return ira->codegen->builtin_types.entry_invalid; + + assert(const_val->data.x_type != nullptr); + ZigType *result_type = const_val->data.x_type; + if (result_type->id != ZigTypeIdErrorSet) { + ErrorMsg *msg = ir_add_error(ira, type_value, + buf_sprintf("expected error set type, found type '%s'", buf_ptr(&result_type->name))); + add_error_note(ira->codegen, msg, op_source->source_node, + buf_sprintf("`||` merges error sets; `or` performs boolean OR")); + return ira->codegen->builtin_types.entry_invalid; + } + return result_type; } static ZigFn *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) { @@ -11001,7 +11014,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } ErrorMsg *parent_msg = ir_add_error_node(ira, source_instr->source_node, - buf_sprintf("expected '%s' type, found '%s'", + buf_sprintf("expected type '%s', found '%s'", buf_ptr(&wanted_type->name), buf_ptr(&actual_type->name))); report_recursive_error(ira, source_instr->source_node, &const_cast_result, parent_msg); @@ -12229,7 +12242,7 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i size_t op2_array_end; if (op2_type->id == ZigTypeIdArray) { if (op2_type->data.array.child_type != child_type) { - ir_add_error(ira, op2, buf_sprintf("expected array of '%s' type, found '%s'", + ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'", buf_ptr(&child_type->name), buf_ptr(&op2->value.type->name))); return ira->codegen->invalid_instruction; @@ -12243,7 +12256,7 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i op2_val->data.x_ptr.data.base_array.is_cstr) { if (child_type != ira->codegen->builtin_types.entry_u8) { - ir_add_error(ira, op2, buf_sprintf("expected array of '%s' type, found '%s'", + ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'", buf_ptr(&child_type->name), buf_ptr(&op2->value.type->name))); return ira->codegen->invalid_instruction; @@ -12254,7 +12267,7 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i } else if (is_slice(op2_type)) { ZigType *ptr_type = op2_type->data.structure.fields[slice_ptr_index].type_entry; if (ptr_type->data.pointer.child_type != child_type) { - ir_add_error(ira, op2, buf_sprintf("expected array of '%s' type, found '%s'", + ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'", buf_ptr(&child_type->name), buf_ptr(&op2->value.type->name))); return ira->codegen->invalid_instruction; @@ -12268,7 +12281,7 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i op2_array_end = bigint_as_unsigned(&len_val->data.x_bigint); } else { ir_add_error(ira, op2, - buf_sprintf("expected array or C string literal type, found '%s'", buf_ptr(&op2->value.type->name))); + buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op2->value.type->name))); return ira->codegen->invalid_instruction; } @@ -12403,19 +12416,11 @@ static IrInstruction *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp * } static IrInstruction *ir_analyze_merge_error_sets(IrAnalyze *ira, IrInstructionBinOp *instruction) { - ZigType *op1_type = ir_resolve_type(ira, instruction->op1->child, ZigTypeIdErrorSet); - if (type_is_invalid(op1_type)) { - if (ira->codegen->errors.length != 0) { - add_error_note( ira->codegen - , ira->codegen->errors.last() - , instruction->base.source_node - , buf_sprintf("did you mean to use `or`?") - ); - } + ZigType *op1_type = ir_resolve_error_set_type(ira, &instruction->base, instruction->op1->child); + if (type_is_invalid(op1_type)) return ira->codegen->invalid_instruction; - } - ZigType *op2_type = ir_resolve_type(ira, instruction->op2->child, ZigTypeIdErrorSet); + ZigType *op2_type = ir_resolve_error_set_type(ira, &instruction->base, instruction->op2->child); if (type_is_invalid(op2_type)) return ira->codegen->invalid_instruction; @@ -12506,7 +12511,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct IrInstruction *var_type = nullptr; if (decl_var_instruction->var_type != nullptr) { var_type = decl_var_instruction->var_type->child; - ZigType *proposed_type = ir_resolve_type(ira, var_type, ZigTypeIdInvalid); + ZigType *proposed_type = ir_resolve_type(ira, var_type); explicit_type = validate_var_type(ira->codegen, var_type->source_node, proposed_type); if (type_is_invalid(explicit_type)) { var->value->type = ira->codegen->builtin_types.entry_invalid; @@ -12835,14 +12840,21 @@ static IrInstruction *ir_analyze_instruction_error_union(IrAnalyze *ira, { Error err; - ZigType *err_set_type = ir_resolve_type(ira, instruction->err_set->child, ZigTypeIdErrorSet); + ZigType *err_set_type = ir_resolve_type(ira, instruction->err_set->child); if (type_is_invalid(err_set_type)) return ira->codegen->invalid_instruction; - ZigType *payload_type = ir_resolve_type(ira, instruction->payload->child, ZigTypeIdInvalid); + ZigType *payload_type = ir_resolve_type(ira, instruction->payload->child); if (type_is_invalid(payload_type)) return ira->codegen->invalid_instruction; + if (err_set_type->id != ZigTypeIdErrorSet) { + ir_add_error(ira, instruction->err_set->child, + buf_sprintf("expected error set type, found type '%s'", + buf_ptr(&err_set_type->name))); + return ira->codegen->invalid_instruction; + } + if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_instruction; ZigType *result_type = get_error_union_type(ira->codegen, err_set_type, payload_type); @@ -12904,7 +12916,7 @@ static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *c ZigType *alloc_fn_type = ptr_to_alloc_fn_type->data.pointer.child_type; if (alloc_fn_type->id != ZigTypeIdFn) { ir_add_error(ira, &call_instruction->base, - buf_sprintf("expected allocation function type, found '%s'", buf_ptr(&alloc_fn_type->name))); + buf_sprintf("expected allocation function, found '%s'", buf_ptr(&alloc_fn_type->name))); return ira->codegen->invalid_instruction; } @@ -13696,7 +13708,7 @@ static IrInstruction *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionC if (is_comptime || instr_is_comptime(fn_ref)) { if (fn_ref->value.type->id == ZigTypeIdMetaType) { - ZigType *dest_type = ir_resolve_type(ira, fn_ref, ZigTypeIdInvalid); + ZigType *dest_type = ir_resolve_type(ira, fn_ref); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; @@ -13821,7 +13833,7 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source static IrInstruction *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) { Error err; IrInstruction *value = un_op_instruction->value->child; - ZigType *type_entry = ir_resolve_type(ira, value, ZigTypeIdInvalid); + ZigType *type_entry = ir_resolve_type(ira, value); if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; if ((err = ensure_complete_type(ira->codegen, type_entry))) @@ -15304,10 +15316,16 @@ static IrInstruction *ir_analyze_instruction_ptr_type_child(IrAnalyze *ira, IrInstructionPtrTypeChild *ptr_type_child_instruction) { IrInstruction *type_value = ptr_type_child_instruction->value->child; - ZigType *type_entry = ir_resolve_type(ira, type_value, ZigTypeIdPointer); + ZigType *type_entry = ir_resolve_type(ira, type_value); if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; + if (type_entry->id != ZigTypeIdPointer) { + ir_add_error_node(ira, ptr_type_child_instruction->base.source_node, + buf_sprintf("expected pointer type, found '%s'", buf_ptr(&type_entry->name))); + return ira->codegen->invalid_instruction; + } + return ir_const_type(ira, &ptr_type_child_instruction->base, type_entry->data.pointer.child_type); } @@ -15460,7 +15478,7 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira, return ira->codegen->invalid_instruction; } - ZigType *child_type = ir_resolve_type(ira, slice_type_instruction->child_type->child, ZigTypeIdInvalid); + ZigType *child_type = ir_resolve_type(ira, slice_type_instruction->child_type->child); if (type_is_invalid(child_type)) return ira->codegen->invalid_instruction; @@ -15543,7 +15561,7 @@ static IrInstruction *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAs AsmOutput *asm_output = asm_expr->output_list.at(i); if (asm_output->return_type) { output_types[i] = asm_instruction->output_types[i]->child; - return_type = ir_resolve_type(ira, output_types[i], ZigTypeIdInvalid); + return_type = ir_resolve_type(ira, output_types[i]); if (type_is_invalid(return_type)) return ira->codegen->invalid_instruction; } @@ -15558,7 +15576,7 @@ static IrInstruction *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAs (input_value->value.type->id == ZigTypeIdComptimeInt || input_value->value.type->id == ZigTypeIdComptimeFloat)) { ir_add_error_node(ira, input_value->source_node, - buf_sprintf("expected sized integer or sized float type, found %s", buf_ptr(&input_value->value.type->name))); + buf_sprintf("expected sized integer or sized float, found %s", buf_ptr(&input_value->value.type->name))); return ira->codegen->invalid_instruction; } @@ -15584,7 +15602,7 @@ static IrInstruction *ir_analyze_instruction_array_type(IrAnalyze *ira, return ira->codegen->invalid_instruction; IrInstruction *child_type_value = array_type_instruction->child_type->child; - ZigType *child_type = ir_resolve_type(ira, child_type_value, ZigTypeIdInvalid); + ZigType *child_type = ir_resolve_type(ira, child_type_value); if (type_is_invalid(child_type)) return ira->codegen->invalid_instruction; switch (child_type->id) { @@ -15633,7 +15651,7 @@ static IrInstruction *ir_analyze_instruction_promise_type(IrAnalyze *ira, IrInst if (instruction->payload_type == nullptr) { promise_type = ira->codegen->builtin_types.entry_promise; } else { - ZigType *payload_type = ir_resolve_type(ira, instruction->payload_type->child, ZigTypeIdInvalid); + ZigType *payload_type = ir_resolve_type(ira, instruction->payload_type->child); if (type_is_invalid(payload_type)) return ira->codegen->invalid_instruction; @@ -15648,7 +15666,7 @@ static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira, { Error err; IrInstruction *type_value = size_of_instruction->type_value->child; - ZigType *type_entry = ir_resolve_type(ira, type_value, ZigTypeIdInvalid); + ZigType *type_entry = ir_resolve_type(ira, type_value); if ((err = ensure_complete_type(ira->codegen, type_entry))) return ira->codegen->invalid_instruction; @@ -16483,7 +16501,7 @@ static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, size_t elem_count = instruction->item_count; if (container_type_value->value.type->id == ZigTypeIdMetaType) { - ZigType *container_type = ir_resolve_type(ira, container_type_value, ZigTypeIdInvalid); + ZigType *container_type = ir_resolve_type(ira, container_type_value); if (type_is_invalid(container_type)) return ira->codegen->invalid_instruction; @@ -16599,7 +16617,7 @@ static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, static IrInstruction *ir_analyze_instruction_container_init_fields(IrAnalyze *ira, IrInstructionContainerInitFields *instruction) { IrInstruction *container_type_value = instruction->container_type->child; - ZigType *container_type = ir_resolve_type(ira, container_type_value, ZigTypeIdInvalid); + ZigType *container_type = ir_resolve_type(ira, container_type_value); if (type_is_invalid(container_type)) return ira->codegen->invalid_instruction; @@ -16717,7 +16735,7 @@ static IrInstruction *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, { Error err; IrInstruction *type_value = instruction->type_value->child; - ZigType *container_type = ir_resolve_type(ira, type_value, ZigTypeIdStruct); + ZigType *container_type = ir_resolve_type(ira, type_value); if (type_is_invalid(container_type)) return ira->codegen->invalid_instruction; @@ -16730,6 +16748,12 @@ static IrInstruction *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, if (type_is_invalid(field_ptr->value.type)) return ira->codegen->invalid_instruction; + if (container_type->id != ZigTypeIdStruct) { + ir_add_error(ira, type_value, + buf_sprintf("expected struct type, found '%s'", buf_ptr(&container_type->name))); + return ira->codegen->invalid_instruction; + } + if ((err = ensure_complete_type(ira->codegen, container_type))) return ira->codegen->invalid_instruction; @@ -16743,7 +16767,7 @@ static IrInstruction *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, if (field_ptr->value.type->id != ZigTypeIdPointer) { ir_add_error(ira, field_ptr, - buf_sprintf("expected Pointer type, found '%s'", buf_ptr(&field_ptr->value.type->name))); + buf_sprintf("expected pointer, found '%s'", buf_ptr(&field_ptr->value.type->name))); return ira->codegen->invalid_instruction; } @@ -16802,9 +16826,9 @@ static IrInstruction *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, static TypeStructField *validate_byte_offset(IrAnalyze *ira, IrInstruction *type_value, IrInstruction *field_name_value, - size_t *byte_offset) + size_t *byte_offset) { - ZigType *container_type = ir_resolve_type(ira, type_value, ZigTypeIdStruct); + ZigType *container_type = ir_resolve_type(ira, type_value); if (type_is_invalid(container_type)) return nullptr; @@ -16816,6 +16840,12 @@ static TypeStructField *validate_byte_offset(IrAnalyze *ira, if (!field_name) return nullptr; + if (container_type->id != ZigTypeIdStruct) { + ir_add_error(ira, type_value, + buf_sprintf("expected struct type, found '%s'", buf_ptr(&container_type->name))); + return nullptr; + } + TypeStructField *field = find_struct_type_field(container_type, field_name); if (field == nullptr) { ir_add_error(ira, field_name_value, @@ -17793,7 +17823,7 @@ static IrInstruction *ir_analyze_instruction_type_info(IrAnalyze *ira, { Error err; IrInstruction *type_value = instruction->type_value->child; - ZigType *type_entry = ir_resolve_type(ira, type_value, ZigTypeIdInvalid); + ZigType *type_entry = ir_resolve_type(ira, type_value); if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; @@ -17821,7 +17851,7 @@ static IrInstruction *ir_analyze_instruction_type_id(IrAnalyze *ira, IrInstructionTypeId *instruction) { IrInstruction *type_value = instruction->type_value->child; - ZigType *type_entry = ir_resolve_type(ira, type_value, ZigTypeIdInvalid); + ZigType *type_entry = ir_resolve_type(ira, type_value); if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; @@ -17856,7 +17886,7 @@ static IrInstruction *ir_analyze_instruction_set_eval_branch_quota(IrAnalyze *ir static IrInstruction *ir_analyze_instruction_type_name(IrAnalyze *ira, IrInstructionTypeName *instruction) { IrInstruction *type_value = instruction->type_value->child; - ZigType *type_entry = ir_resolve_type(ira, type_value, ZigTypeIdInvalid); + ZigType *type_entry = ir_resolve_type(ira, type_value); if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; @@ -18133,7 +18163,7 @@ static IrInstruction *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstruction static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstructionTruncate *instruction) { IrInstruction *dest_type_value = instruction->dest_type->child; - ZigType *dest_type = ir_resolve_type(ira, dest_type_value, ZigTypeIdInvalid); + ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; @@ -18186,7 +18216,7 @@ static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruct } static IrInstruction *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstructionIntCast *instruction) { - ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child, ZigTypeIdInvalid); + ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; @@ -18219,7 +18249,7 @@ static IrInstruction *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstruct } static IrInstruction *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstructionFloatCast *instruction) { - ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child, ZigTypeIdInvalid); + ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; @@ -18259,10 +18289,16 @@ static IrInstruction *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstru } static IrInstruction *ir_analyze_instruction_err_set_cast(IrAnalyze *ira, IrInstructionErrSetCast *instruction) { - ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child, ZigTypeIdErrorSet); + ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; + if (dest_type->id != ZigTypeIdErrorSet) { + ir_add_error(ira, instruction->dest_type, + buf_sprintf("expected error set type, found '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_instruction; + } + IrInstruction *target = instruction->target->child; if (type_is_invalid(target->value.type)) return ira->codegen->invalid_instruction; @@ -18291,7 +18327,7 @@ static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_ali static IrInstruction *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstructionFromBytes *instruction) { Error err; - ZigType *dest_child_type = ir_resolve_type(ira, instruction->dest_child_type->child, ZigTypeIdInvalid); + ZigType *dest_child_type = ir_resolve_type(ira, instruction->dest_child_type->child); if (type_is_invalid(dest_child_type)) return ira->codegen->invalid_instruction; @@ -18388,7 +18424,7 @@ static IrInstruction *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstruct if (!is_slice(target->value.type)) { ir_add_error(ira, instruction->target, - buf_sprintf("expected slice type, found '%s'", buf_ptr(&target->value.type->name))); + buf_sprintf("expected slice, found '%s'", buf_ptr(&target->value.type->name))); return ira->codegen->invalid_instruction; } @@ -18407,7 +18443,7 @@ static IrInstruction *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstruct } static IrInstruction *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrInstructionIntToFloat *instruction) { - ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child, ZigTypeIdInvalid); + ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; @@ -18425,7 +18461,7 @@ static IrInstruction *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrInst } static IrInstruction *ir_analyze_instruction_float_to_int(IrAnalyze *ira, IrInstructionFloatToInt *instruction) { - ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child, ZigTypeIdInvalid); + ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; @@ -18481,7 +18517,7 @@ static IrInstruction *ir_analyze_instruction_bool_to_int(IrAnalyze *ira, IrInstr return ira->codegen->invalid_instruction; if (target->value.type->id != ZigTypeIdBool) { - ir_add_error(ira, instruction->target, buf_sprintf("expected bool type, found '%s'", + ir_add_error(ira, instruction->target, buf_sprintf("expected bool, found '%s'", buf_ptr(&target->value.type->name))); return ira->codegen->invalid_instruction; } @@ -19062,7 +19098,7 @@ static IrInstruction *ir_analyze_instruction_member_count(IrAnalyze *ira, IrInst IrInstruction *container = instruction->container->child; if (type_is_invalid(container->value.type)) return ira->codegen->invalid_instruction; - ZigType *container_type = ir_resolve_type(ira, container, ZigTypeIdInvalid); + ZigType *container_type = ir_resolve_type(ira, container); if ((err = ensure_complete_type(ira->codegen, container_type))) return ira->codegen->invalid_instruction; @@ -19096,7 +19132,7 @@ static IrInstruction *ir_analyze_instruction_member_count(IrAnalyze *ira, IrInst static IrInstruction *ir_analyze_instruction_member_type(IrAnalyze *ira, IrInstructionMemberType *instruction) { Error err; IrInstruction *container_type_value = instruction->container_type->child; - ZigType *container_type = ir_resolve_type(ira, container_type_value, ZigTypeIdInvalid); + ZigType *container_type = ir_resolve_type(ira, container_type_value); if (type_is_invalid(container_type)) return ira->codegen->invalid_instruction; @@ -19139,7 +19175,7 @@ static IrInstruction *ir_analyze_instruction_member_type(IrAnalyze *ira, IrInstr static IrInstruction *ir_analyze_instruction_member_name(IrAnalyze *ira, IrInstructionMemberName *instruction) { Error err; IrInstruction *container_type_value = instruction->container_type->child; - ZigType *container_type = ir_resolve_type(ira, container_type_value, ZigTypeIdInvalid); + ZigType *container_type = ir_resolve_type(ira, container_type_value); if (type_is_invalid(container_type)) return ira->codegen->invalid_instruction; @@ -19232,7 +19268,7 @@ static IrInstruction *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruct IrInstruction *type_value = instruction->type_value->child; if (type_is_invalid(type_value->value.type)) return ira->codegen->invalid_instruction; - ZigType *type_entry = ir_resolve_type(ira, type_value, ZigTypeIdInvalid); + ZigType *type_entry = ir_resolve_type(ira, type_value); if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusAlignmentKnown))) return ira->codegen->invalid_instruction; @@ -19282,10 +19318,16 @@ static IrInstruction *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInstr if (type_is_invalid(type_value->value.type)) return ira->codegen->invalid_instruction; - ZigType *dest_type = ir_resolve_type(ira, type_value, ZigTypeIdInt); + ZigType *dest_type = ir_resolve_type(ira, type_value); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; + if (dest_type->id != ZigTypeIdInt) { + ir_add_error(ira, type_value, + buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_instruction; + } + IrInstruction *op1 = instruction->op1->child; if (type_is_invalid(op1->value.type)) return ira->codegen->invalid_instruction; @@ -19555,7 +19597,7 @@ static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruct IrInstruction *param_type_value = instruction->param_types[fn_type_id.next_param_index]->child; if (type_is_invalid(param_type_value->value.type)) return ira->codegen->invalid_instruction; - ZigType *param_type = ir_resolve_type(ira, param_type_value, ZigTypeIdInvalid); + ZigType *param_type = ir_resolve_type(ira, param_type_value); switch (type_requires_comptime(ira->codegen, param_type)) { case ReqCompTimeYes: if (!calling_convention_allows_zig_types(fn_type_id.cc)) { @@ -19589,7 +19631,7 @@ static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruct } IrInstruction *return_type_value = instruction->return_type->child; - fn_type_id.return_type = ir_resolve_type(ira, return_type_value, ZigTypeIdInvalid); + fn_type_id.return_type = ir_resolve_type(ira, return_type_value); if (type_is_invalid(fn_type_id.return_type)) return ira->codegen->invalid_instruction; if (fn_type_id.return_type->id == ZigTypeIdOpaque) { @@ -19605,7 +19647,7 @@ static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruct return ira->codegen->invalid_instruction; } IrInstruction *async_allocator_type_value = instruction->async_allocator_type_value->child; - fn_type_id.async_allocator_type = ir_resolve_type(ira, async_allocator_type_value, ZigTypeIdInvalid); + fn_type_id.async_allocator_type = ir_resolve_type(ira, async_allocator_type_value); if (type_is_invalid(fn_type_id.async_allocator_type)) return ira->codegen->invalid_instruction; } @@ -19913,7 +19955,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 result_type = get_slice_type(ira->codegen, result_ptr_type); } else { ir_add_error(ira, target, - buf_sprintf("expected pointer or slice type, found '%s'", buf_ptr(&target_type->name))); + buf_sprintf("expected pointer or slice, found '%s'", buf_ptr(&target_type->name))); return ira->codegen->invalid_instruction; } @@ -19959,13 +20001,13 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ // validate src_type and dest_type. if (get_src_ptr_type(src_type) == nullptr) { - ir_add_error(ira, ptr, buf_sprintf("expected Pointer type, found '%s'", buf_ptr(&src_type->name))); + ir_add_error(ira, ptr, buf_sprintf("expected pointer, found '%s'", buf_ptr(&src_type->name))); return ira->codegen->invalid_instruction; } if (get_src_ptr_type(dest_type) == nullptr) { ir_add_error(ira, dest_type_src, - buf_sprintf("expected Pointer type, found '%s'", buf_ptr(&dest_type->name))); + buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name))); return ira->codegen->invalid_instruction; } @@ -20033,7 +20075,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtrCast *instruction) { IrInstruction *dest_type_value = instruction->dest_type->child; - ZigType *dest_type = ir_resolve_type(ira, dest_type_value, ZigTypeIdInvalid); + ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; @@ -20229,7 +20271,7 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstructionBitCast *instruction) { Error err; IrInstruction *dest_type_value = instruction->dest_type->child; - ZigType *dest_type = ir_resolve_type(ira, dest_type_value, ZigTypeIdInvalid); + ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; @@ -20326,13 +20368,13 @@ static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruct static IrInstruction *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstructionIntToPtr *instruction) { Error err; IrInstruction *dest_type_value = instruction->dest_type->child; - ZigType *dest_type = ir_resolve_type(ira, dest_type_value, ZigTypeIdInvalid); + ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; // We explicitly check for the size, so we can use get_src_ptr_type if (get_src_ptr_type(dest_type) == nullptr) { - ir_add_error(ira, dest_type_value, buf_sprintf("expected Pointer type, found '%s'", buf_ptr(&dest_type->name))); + ir_add_error(ira, dest_type_value, buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name))); return ira->codegen->invalid_instruction; } @@ -20435,7 +20477,7 @@ static IrInstruction *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstru // We check size explicitly so we can use get_src_ptr_type here. if (get_src_ptr_type(target->value.type) == nullptr) { ir_add_error(ira, target, - buf_sprintf("expected Pointer type, found '%s'", buf_ptr(&target->value.type->name))); + buf_sprintf("expected pointer, found '%s'", buf_ptr(&target->value.type->name))); return ira->codegen->invalid_instruction; } @@ -20466,7 +20508,7 @@ static IrInstruction *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstru static IrInstruction *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstructionPtrType *instruction) { Error err; - ZigType *child_type = ir_resolve_type(ira, instruction->child_type->child, ZigTypeIdInvalid); + ZigType *child_type = ir_resolve_type(ira, instruction->child_type->child); if (type_is_invalid(child_type)) return ira->codegen->invalid_instruction; @@ -20565,7 +20607,7 @@ static IrInstruction *ir_analyze_instruction_set_align_stack(IrAnalyze *ira, IrI static IrInstruction *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstructionArgType *instruction) { IrInstruction *fn_type_inst = instruction->fn_type->child; - ZigType *fn_type = ir_resolve_type(ira, fn_type_inst, ZigTypeIdFn); + ZigType *fn_type = ir_resolve_type(ira, fn_type_inst); if (type_is_invalid(fn_type)) return ira->codegen->invalid_instruction; @@ -20574,6 +20616,11 @@ static IrInstruction *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruct if (!ir_resolve_usize(ira, arg_index_inst, &arg_index)) return ira->codegen->invalid_instruction; + if (fn_type->id != ZigTypeIdFn) { + ir_add_error(ira, fn_type_inst, buf_sprintf("expected function, found '%s'", buf_ptr(&fn_type->name))); + return ira->codegen->invalid_instruction; + } + FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; if (arg_index >= fn_type_id->param_count) { ir_add_error(ira, arg_index_inst, @@ -20598,7 +20645,7 @@ static IrInstruction *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruct static IrInstruction *ir_analyze_instruction_tag_type(IrAnalyze *ira, IrInstructionTagType *instruction) { Error err; IrInstruction *target_inst = instruction->target->child; - ZigType *enum_type = ir_resolve_type(ira, target_inst, ZigTypeIdInvalid); + ZigType *enum_type = ir_resolve_type(ira, target_inst); if (type_is_invalid(enum_type)) return ira->codegen->invalid_instruction; @@ -20623,7 +20670,7 @@ static IrInstruction *ir_analyze_instruction_tag_type(IrAnalyze *ira, IrInstruct return ira->codegen->invalid_instruction; } } else { - ir_add_error(ira, target_inst, buf_sprintf("expected enum or union type, found '%s'", + ir_add_error(ira, target_inst, buf_sprintf("expected enum or union, found '%s'", buf_ptr(&enum_type->name))); return ira->codegen->invalid_instruction; } @@ -20777,7 +20824,7 @@ static IrInstruction *ir_analyze_instruction_coro_promise(IrAnalyze *ira, IrInst if (coro_handle->value.type->id != ZigTypeIdPromise || coro_handle->value.type->data.promise.result_type == nullptr) { - ir_add_error(ira, &instruction->base, buf_sprintf("expected promise->T type, found '%s'", + ir_add_error(ira, &instruction->base, buf_sprintf("expected promise->T, found '%s'", buf_ptr(&coro_handle->value.type->name))); return ira->codegen->invalid_instruction; } @@ -20808,7 +20855,7 @@ static IrInstruction *ir_analyze_instruction_coro_alloc_helper(IrAnalyze *ira, I } static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op) { - ZigType *operand_type = ir_resolve_type(ira, op, ZigTypeIdInvalid); + ZigType *operand_type = ir_resolve_type(ira, op); if (type_is_invalid(operand_type)) return ira->codegen->builtin_types.entry_invalid; @@ -20938,12 +20985,12 @@ static IrInstruction *ir_analyze_instruction_atomic_load(IrAnalyze *ira, IrInstr } static IrInstruction *ir_analyze_instruction_promise_result_type(IrAnalyze *ira, IrInstructionPromiseResultType *instruction) { - ZigType *promise_type = ir_resolve_type(ira, instruction->promise_type->child, ZigTypeIdInvalid); + ZigType *promise_type = ir_resolve_type(ira, instruction->promise_type->child); if (type_is_invalid(promise_type)) return ira->codegen->invalid_instruction; if (promise_type->id != ZigTypeIdPromise || promise_type->data.promise.result_type == nullptr) { - ir_add_error(ira, &instruction->base, buf_sprintf("expected promise->T type, found '%s'", + ir_add_error(ira, &instruction->base, buf_sprintf("expected promise->T, found '%s'", buf_ptr(&promise_type->name))); return ira->codegen->invalid_instruction; } @@ -20952,7 +20999,7 @@ static IrInstruction *ir_analyze_instruction_promise_result_type(IrAnalyze *ira, } static IrInstruction *ir_analyze_instruction_await_bookkeeping(IrAnalyze *ira, IrInstructionAwaitBookkeeping *instruction) { - ZigType *promise_result_type = ir_resolve_type(ira, instruction->promise_result_type->child, ZigTypeIdInvalid); + ZigType *promise_result_type = ir_resolve_type(ira, instruction->promise_result_type->child); if (type_is_invalid(promise_result_type)) return ira->codegen->invalid_instruction; @@ -21015,7 +21062,7 @@ static IrInstruction *ir_analyze_instruction_mark_err_ret_trace_ptr(IrAnalyze *i } static IrInstruction *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstructionSqrt *instruction) { - ZigType *float_type = ir_resolve_type(ira, instruction->type->child, ZigTypeIdInvalid); + ZigType *float_type = ir_resolve_type(ira, instruction->type->child); if (type_is_invalid(float_type)) return ira->codegen->invalid_instruction; @@ -21082,7 +21129,7 @@ static IrInstruction *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstructionS } static IrInstruction *ir_analyze_instruction_bswap(IrAnalyze *ira, IrInstructionBswap *instruction) { - ZigType *int_type = ir_resolve_type(ira, instruction->type->child, ZigTypeIdInvalid); + ZigType *int_type = ir_resolve_type(ira, instruction->type->child); if (type_is_invalid(int_type)) return ira->codegen->invalid_instruction; @@ -21138,7 +21185,7 @@ static IrInstruction *ir_analyze_instruction_bswap(IrAnalyze *ira, IrInstruction } static IrInstruction *ir_analyze_instruction_bit_reverse(IrAnalyze *ira, IrInstructionBitReverse *instruction) { - ZigType *int_type = ir_resolve_type(ira, instruction->type->child, ZigTypeIdInvalid); + ZigType *int_type = ir_resolve_type(ira, instruction->type->child); if (type_is_invalid(int_type)) return ira->codegen->invalid_instruction; @@ -21209,7 +21256,7 @@ static IrInstruction *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInstr if (target->value.type->id != ZigTypeIdEnum) { ir_add_error(ira, instruction->target, - buf_sprintf("expected enum type, found '%s'", buf_ptr(&target->value.type->name))); + buf_sprintf("expected enum, found type '%s'", buf_ptr(&target->value.type->name))); return ira->codegen->invalid_instruction; } @@ -21224,10 +21271,16 @@ static IrInstruction *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInstr static IrInstruction *ir_analyze_instruction_int_to_enum(IrAnalyze *ira, IrInstructionIntToEnum *instruction) { Error err; IrInstruction *dest_type_value = instruction->dest_type->child; - ZigType *dest_type = ir_resolve_type(ira, dest_type_value, ZigTypeIdEnum); + ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) return ira->codegen->invalid_instruction; + if (dest_type->id != ZigTypeIdEnum) { + ir_add_error(ira, instruction->dest_type, + buf_sprintf("expected enum, found type '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_instruction; + } + if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; diff --git a/src/parser.cpp b/src/parser.cpp index 9425df2430c8e6a5cbbc860ea09fc3e0ac89cd18..077365995e87d5d38f491d87ef121f68cb0f90c9 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -122,37 +122,19 @@ static AstNode *ast_parse_container_decl_type(ParseContext *pc); static AstNode *ast_parse_byte_align(ParseContext *pc); ATTRIBUTE_PRINTF(3, 4) -static ErrorMsg *ast_error(ParseContext *pc, Token *token, const char *format, ...) { - va_list ap; - va_start(ap, format); - Buf *msg = buf_vprintf(format, ap); - va_end(ap); - - ErrorMsg *err = err_msg_create_with_line(pc->owner->path, token->start_line, token->start_column, - pc->owner->source_code, pc->owner->line_offsets, msg); - err->line_start = token->start_line; - err->column_start = token->start_column; - - return err; -} - -ATTRIBUTE_PRINTF(4, 5) ATTRIBUTE_NORETURN -static void ast_error_exit(ParseContext *pc, Token *token, ErrorMsg *note, const char *format, ...) { +static void ast_error(ParseContext *pc, Token *token, const char *format, ...) { va_list ap; va_start(ap, format); Buf *msg = buf_vprintf(format, ap); va_end(ap); + ErrorMsg *err = err_msg_create_with_line(pc->owner->path, token->start_line, token->start_column, pc->owner->source_code, pc->owner->line_offsets, msg); err->line_start = token->start_line; err->column_start = token->start_column; - if (note) { - err->notes.append(note); - } - print_err_msg(err, pc->err_color); exit(EXIT_FAILURE); } @@ -182,7 +164,7 @@ static Buf ast_token_str(Buf *input, Token *token) { ATTRIBUTE_NORETURN static void ast_invalid_token_error(ParseContext *pc, Token *token) { Buf token_value = ast_token_str(pc->buf, token); - ast_error_exit(pc, token, NULL, "invalid token: '%s'", buf_ptr(&token_value)); + ast_error(pc, token, "invalid token: '%s'", buf_ptr(&token_value)); } static AstNode *ast_create_node_no_line_info(ParseContext *pc, NodeType type) { @@ -232,13 +214,8 @@ static Token *eat_token_if(ParseContext *pc, TokenId id) { static Token *expect_token(ParseContext *pc, TokenId id) { Token *res = eat_token(pc); - if (res->id != id) { - ErrorMsg *note = NULL; - if (res->id == TokenIdAmpersandAmpersand) { - note = ast_error(pc, res, "did you mean to use `and`?"); - } - ast_error_exit(pc, res, note, "expected token '%s', found '%s'", token_name(id), token_name(res->id)); - } + if (res->id != id) + ast_error(pc, res, "expected token '%s', found '%s'", token_name(id), token_name(res->id)); return res; } @@ -860,7 +837,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) { if (param_decl->data.param_decl.is_var_args) res->data.fn_proto.is_var_args = true; if (i != params.length - 1 && res->data.fn_proto.is_var_args) - ast_error_exit(pc, first, NULL, "Function prototype have varargs as a none last paramter."); + ast_error(pc, first, "Function prototype have varargs as a none last paramter."); } return res; } diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 464412d4435b7ca0263f602db76eee88924ec9fb..6215541876f23241ba7ea2f738e83ad61c233c42 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -199,7 +199,6 @@ enum TokenizeState { TokenizeStateSawDash, TokenizeStateSawMinusPercent, TokenizeStateSawAmpersand, - TokenizeStateSawAmpersandAmpersand, TokenizeStateSawCaret, TokenizeStateSawBar, TokenizeStateSawBarBar, @@ -887,15 +886,14 @@ void tokenize(Buf *buf, Tokenization *out) { break; case TokenizeStateSawAmpersand: switch (c) { + case '&': + tokenize_error(&t, "`&&` is invalid. Note that `and` is boolean AND."); + break; case '=': set_token_id(&t, t.cur_tok, TokenIdBitAndEq); end_token(&t); t.state = TokenizeStateStart; break; - case '&': - set_token_id(&t, t.cur_tok, TokenIdAmpersandAmpersand); - t.state = TokenizeStateSawAmpersandAmpersand; - break; default: t.pos -= 1; end_token(&t); @@ -903,11 +901,6 @@ void tokenize(Buf *buf, Tokenization *out) { continue; } break; - case TokenizeStateSawAmpersandAmpersand: - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; case TokenizeStateSawCaret: switch (c) { case '=': @@ -1478,7 +1471,6 @@ void tokenize(Buf *buf, Tokenization *out) { case TokenizeStateSawPlus: case TokenizeStateSawDash: case TokenizeStateSawAmpersand: - case TokenizeStateSawAmpersandAmpersand: case TokenizeStateSawCaret: case TokenizeStateSawBar: case TokenizeStateSawEq: @@ -1526,7 +1518,6 @@ void tokenize(Buf *buf, Tokenization *out) { const char * token_name(TokenId id) { switch (id) { case TokenIdAmpersand: return "&"; - case TokenIdAmpersandAmpersand: return "&&"; case TokenIdArrow: return "->"; case TokenIdAtSign: return "@"; case TokenIdBang: return "!"; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index 2e872ce4de54d003275f69c9ad073ce0ab79595c..1574e95571d2f652fd7429831b2f60942a2b0ae3 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -14,7 +14,6 @@ enum TokenId { TokenIdAmpersand, - TokenIdAmpersandAmpersand, TokenIdArrow, TokenIdAtSign, TokenIdBang, diff --git a/test/compile_errors.zig b/test/compile_errors.zig index d262405febee4a0fa4e3712faebecc3196d63ef2..0754f223e82730bceb3da1ed33e6df4587fd8851 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2,33 +2,28 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { cases.add( - "Use of && in place of `and`", - \\export fn entry() void { - \\ if (true && false) return; + "attempted `&&`", + \\export fn entry(a: bool, b: bool) i32 { + \\ if (a && b) { + \\ return 1234; + \\ } + \\ return 5678; \\} , - ".tmp_source.zig:2:14: error: expected token ')', found '&&'", - ".tmp_source.zig:2:14: note: did you mean to use `and`?", + ".tmp_source.zig:2:11: error: `&&` is invalid. Note that `and` is boolean AND.", ); cases.add( - "Use of || in place of `or` (using comptime_int)", - \\export fn entry() void { - \\ if (1 || 0) return; + "attempted `||` on boolean values", + \\export fn entry(a: bool, b: bool) i32 { + \\ if (a || b) { + \\ return 1234; + \\ } + \\ return 5678; \\} , - ".tmp_source.zig:2:9: error: expected ErrorSet type, found 'comptime_int'", - ".tmp_source.zig:2:11: note: did you mean to use `or`?", - ); - - cases.add( - "Use of || in place of `or` (using booleans)", - \\export fn entry() void { - \\ if (true || false) return; - \\} - , - ".tmp_source.zig:2:9: error: expected ErrorSet type, found 'bool'", - ".tmp_source.zig:2:14: note: did you mean to use `or`?", + ".tmp_source.zig:2:9: error: expected error set type, found 'bool'", + ".tmp_source.zig:2:11: note: `||` merges error sets; `or` performs boolean OR", ); cases.add( @@ -98,7 +93,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ do_the_thing(bar); \\} , - ".tmp_source.zig:4:18: error: expected 'fn(i32) void' type, found 'fn(bool) void", + ".tmp_source.zig:4:18: error: expected type 'fn(i32) void', found 'fn(bool) void", ".tmp_source.zig:4:18: note: parameter 0: 'bool' cannot cast into 'i32'", ); @@ -134,7 +129,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , ".tmp_source.zig:3:31: error: integer value 300 cannot be implicitly casted to type 'u8'", ".tmp_source.zig:7:22: error: integer value 300 cannot be implicitly casted to type 'u8'", - ".tmp_source.zig:11:20: error: expected 'u8' type, found 'u16'", + ".tmp_source.zig:11:20: error: expected type 'u8', found 'u16'", ); cases.add( @@ -154,7 +149,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(y)); } , - ".tmp_source.zig:2:14: error: expected 'f32' type, found 'f64'", + ".tmp_source.zig:2:14: error: expected type 'f32', found 'f64'", ); cases.add( @@ -202,7 +197,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var ptr2: *c_void = &b; \\} , - ".tmp_source.zig:5:26: error: expected '*c_void' type, found '**u32'", + ".tmp_source.zig:5:26: error: expected type '*c_void', found '**u32'", ); cases.add( @@ -252,7 +247,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ const sliceA: []u8 = &buffer; \\} , - ".tmp_source.zig:3:27: error: expected '[]u8' type, found '*const [1]u8'", + ".tmp_source.zig:3:27: error: expected type '[]u8', found '*const [1]u8'", ); cases.add( @@ -297,9 +292,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ const Errors = error{} || u16; \\} , - ".tmp_source.zig:2:20: error: expected ErrorSet type, found 'u8'", - ".tmp_source.zig:2:23: note: did you mean to use `or`?", - ".tmp_source.zig:5:31: error: expected ErrorSet type, found 'u16'", + ".tmp_source.zig:2:20: error: expected error set type, found type 'u8'", + ".tmp_source.zig:2:23: note: `||` merges error sets; `or` performs boolean OR", + ".tmp_source.zig:5:31: error: expected error set type, found type 'u16'", + ".tmp_source.zig:5:28: note: `||` merges error sets; `or` performs boolean OR", ); cases.add( @@ -794,7 +790,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\fn bar(x: *b.Foo) void {} , - ".tmp_source.zig:6:10: error: expected '*Foo' type, found '*Foo'", + ".tmp_source.zig:6:10: error: expected type '*Foo', found '*Foo'", ".tmp_source.zig:6:10: note: pointer type child 'Foo' cannot cast into pointer type child 'Foo'", "a.zig:1:17: note: Foo declared here", "b.zig:1:17: note: Foo declared here", @@ -864,7 +860,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var y = p.*; \\} , - ".tmp_source.zig:4:23: error: expected '*?*i32' type, found '**i32'", + ".tmp_source.zig:4:23: error: expected type '*?*i32', found '**i32'", ); cases.add( @@ -873,7 +869,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ const x: [*]const bool = true; \\} , - ".tmp_source.zig:2:30: error: expected '[*]const bool' type, found 'bool'", + ".tmp_source.zig:2:30: error: expected type '[*]const bool', found 'bool'", ); cases.add( @@ -918,7 +914,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var rule_set = try Foo.init(); \\} , - ".tmp_source.zig:2:13: error: expected 'i32' type, found 'type'", + ".tmp_source.zig:2:13: error: expected type 'i32', found 'type'", ); cases.add( @@ -952,7 +948,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return null; \\} , - ".tmp_source.zig:5:34: error: expected '?NextError!i32' type, found '?OtherError!i32'", + ".tmp_source.zig:5:34: error: expected type '?NextError!i32', found '?OtherError!i32'", ".tmp_source.zig:5:34: note: optional type child 'OtherError!i32' cannot cast into optional type child 'NextError!i32'", ".tmp_source.zig:5:34: note: error set 'OtherError' cannot cast into error set 'NextError'", ".tmp_source.zig:2:26: note: 'error.OutOfMemory' not a member of destination error set", @@ -1022,7 +1018,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ @panic(e); \\} , - ".tmp_source.zig:3:12: error: expected '[]const u8' type, found 'error{Foo}'", + ".tmp_source.zig:3:12: error: expected type '[]const u8', found 'error{Foo}'", ); cases.add( @@ -1050,7 +1046,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return error.ShouldBeCompileError; \\} , - ".tmp_source.zig:6:17: error: expected 'void' type, found 'error{ShouldBeCompileError}'", + ".tmp_source.zig:6:17: error: expected type 'void', found 'error{ShouldBeCompileError}'", ); cases.add( @@ -1093,7 +1089,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ a(c); \\} , - ".tmp_source.zig:8:7: error: expected 'fn(*const u8) void' type, found 'fn(u8) void'", + ".tmp_source.zig:8:7: error: expected type 'fn(*const u8) void', found 'fn(u8) void'", ); cases.add( @@ -1175,7 +1171,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ E.One => {}, \\ } \\} - , ".tmp_source.zig:9:10: error: expected 'usize' type, found 'E'"); + , ".tmp_source.zig:9:10: error: expected type 'usize', found 'E'"); cases.add( "range operator in switch used on error set", @@ -1221,7 +1217,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ const z = i32!i32; \\} , - ".tmp_source.zig:2:15: error: expected ErrorSet type, found 'i32'", + ".tmp_source.zig:2:15: error: expected error set type, found type 'i32'", ); cases.add( @@ -1271,7 +1267,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return error.B; \\} , - ".tmp_source.zig:3:35: error: expected 'SmallErrorSet!i32' type, found 'anyerror!i32'", + ".tmp_source.zig:3:35: error: expected type 'SmallErrorSet!i32', found 'anyerror!i32'", ".tmp_source.zig:3:35: note: error set 'anyerror' cannot cast into error set 'SmallErrorSet'", ".tmp_source.zig:3:35: note: cannot cast global error set into smaller set", ); @@ -1286,7 +1282,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return error.B; \\} , - ".tmp_source.zig:3:31: error: expected 'SmallErrorSet' type, found 'anyerror'", + ".tmp_source.zig:3:31: error: expected type 'SmallErrorSet', found 'anyerror'", ".tmp_source.zig:3:31: note: cannot cast global error set into smaller set", ); @@ -1313,7 +1309,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var x: Set2 = set1; \\} , - ".tmp_source.zig:7:19: error: expected 'Set2' type, found 'Set1'", + ".tmp_source.zig:7:19: error: expected type 'Set2', found 'Set1'", ".tmp_source.zig:1:23: note: 'error.B' not a member of destination error set", ); @@ -1853,7 +1849,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn a() noreturn {return;} \\export fn entry() void { a(); } , - ".tmp_source.zig:1:18: error: expected 'noreturn' type, found 'void'", + ".tmp_source.zig:1:18: error: expected type 'noreturn', found 'void'", ); cases.add( @@ -1861,7 +1857,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn a() i32 {} \\export fn entry() void { _ = a(); } , - ".tmp_source.zig:1:12: error: expected 'i32' type, found 'void'", + ".tmp_source.zig:1:12: error: expected type 'i32', found 'void'", ); cases.add( @@ -1967,7 +1963,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return a; \\} , - ".tmp_source.zig:3:12: error: expected 'i32' type, found '[*]const u8'", + ".tmp_source.zig:3:12: error: expected type 'i32', found '[*]const u8'", ); cases.add( @@ -1976,7 +1972,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ if (0) {} \\} , - ".tmp_source.zig:2:9: error: expected 'bool' type, found 'comptime_int'", + ".tmp_source.zig:2:9: error: expected type 'bool', found 'comptime_int'", ); cases.add( @@ -2071,8 +2067,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ array[bad] = array[bad]; \\} , - ".tmp_source.zig:4:11: error: expected 'usize' type, found 'bool'", - ".tmp_source.zig:4:24: error: expected 'usize' type, found 'bool'", + ".tmp_source.zig:4:11: error: expected type 'usize', found 'bool'", + ".tmp_source.zig:4:24: error: expected type 'usize', found 'bool'", ); cases.add( @@ -2486,7 +2482,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn foo() *const i32 { return y; } \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } , - ".tmp_source.zig:3:30: error: expected '*const i32' type, found '*const comptime_int'", + ".tmp_source.zig:3:30: error: expected type '*const i32', found '*const comptime_int'", ); cases.add( @@ -2564,7 +2560,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\fn c() i32 {return 2;} \\export fn entry() usize { return @sizeOf(@typeOf(fns)); } , - ".tmp_source.zig:1:27: error: expected 'fn() void' type, found 'fn() i32'", + ".tmp_source.zig:1:27: error: expected type 'fn() void', found 'fn() i32'", ); cases.add( @@ -2576,7 +2572,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(fns)); } , - ".tmp_source.zig:1:36: error: expected 'fn(i32) i32' type, found 'extern fn(i32) i32'", + ".tmp_source.zig:1:36: error: expected type 'fn(i32) i32', found 'extern fn(i32) i32'", ); cases.add( @@ -2680,7 +2676,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(a)); } , - ".tmp_source.zig:1:16: error: expected '*u8' type, found '(null)'", + ".tmp_source.zig:1:16: error: expected type '*u8', found '(null)'", ); cases.add( @@ -3320,7 +3316,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\} \\fn something() anyerror!void { } , - ".tmp_source.zig:2:5: error: expected 'void' type, found 'anyerror'", + ".tmp_source.zig:2:5: error: expected type 'void', found 'anyerror'", ".tmp_source.zig:1:15: note: return type declared here", ); @@ -3499,7 +3495,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ derp.init(); \\} , - ".tmp_source.zig:14:5: error: expected 'i32' type, found 'Foo'", + ".tmp_source.zig:14:5: error: expected type 'i32', found 'Foo'", ); cases.add( @@ -3529,7 +3525,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ x.init(); \\} , - ".tmp_source.zig:23:5: error: expected '*Allocator' type, found '*List'", + ".tmp_source.zig:23:5: error: expected type '*Allocator', found '*List'", ); cases.add( @@ -3701,7 +3697,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } , - ".tmp_source.zig:8:26: error: expected '*const u3' type, found '*align(:3:1) const u3'", + ".tmp_source.zig:8:26: error: expected type '*const u3', found '*align(:3:1) const u3'", ); cases.add( @@ -3830,7 +3826,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } , - ".tmp_source.zig:4:19: error: expected '*[]const u8' type, found '*const []const u8'", + ".tmp_source.zig:4:19: error: expected type '*[]const u8', found '*const []const u8'", ); cases.addCase(x: { @@ -3862,7 +3858,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ foo(global_array); \\} , - ".tmp_source.zig:4:9: error: expected '[]i32' type, found '[10]i32'", + ".tmp_source.zig:4:9: error: expected type '[]i32', found '[10]i32'", ); cases.add( @@ -3871,7 +3867,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return @ptrCast(usize, a); \\} , - ".tmp_source.zig:2:21: error: expected Pointer type, found 'usize'", + ".tmp_source.zig:2:21: error: expected pointer, found 'usize'", ); cases.add( @@ -3918,7 +3914,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return @fieldParentPtr(Foo, "a", a); \\} , - ".tmp_source.zig:3:28: error: expected Struct type, found 'i32'", + ".tmp_source.zig:3:28: error: expected struct type, found 'i32'", ); cases.add( @@ -3942,7 +3938,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return @fieldParentPtr(Foo, "a", a); \\} , - ".tmp_source.zig:5:38: error: expected Pointer type, found 'i32'", + ".tmp_source.zig:5:38: error: expected pointer, found 'i32'", ); cases.add( @@ -3983,7 +3979,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return @byteOffsetOf(Foo, "a",); \\} , - ".tmp_source.zig:3:26: error: expected Struct type, found 'i32'", + ".tmp_source.zig:3:26: error: expected struct type, found 'i32'", ); cases.add( @@ -4097,7 +4093,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\} \\fn bar() ?i32 { return 1; } , - ".tmp_source.zig:2:15: error: expected 'bool' type, found '?i32'", + ".tmp_source.zig:2:15: error: expected type 'bool', found '?i32'", ); cases.add( @@ -4107,7 +4103,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\} \\fn bar() anyerror!i32 { return 1; } , - ".tmp_source.zig:2:15: error: expected 'bool' type, found 'anyerror!i32'", + ".tmp_source.zig:2:15: error: expected type 'bool', found 'anyerror!i32'", ); cases.add( @@ -4368,7 +4364,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return @ptrToInt(x); \\} , - ".tmp_source.zig:2:22: error: expected Pointer type, found 'i32'", + ".tmp_source.zig:2:22: error: expected pointer, found 'i32'", ); cases.add( @@ -4404,7 +4400,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return x << y; \\} , - ".tmp_source.zig:2:17: error: expected 'u3' type, found 'u8'", + ".tmp_source.zig:2:17: error: expected type 'u3', found 'u8'", ); cases.add( @@ -4433,7 +4429,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ x.* += 1; \\} , - ".tmp_source.zig:8:13: error: expected '*u32' type, found '*align(1) u32'", + ".tmp_source.zig:8:13: error: expected type '*u32', found '*align(1) u32'", ); cases.add( @@ -4477,7 +4473,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ @alignCast(4, u32(3)); \\} , - ".tmp_source.zig:2:22: error: expected pointer or slice type, found 'u32'", + ".tmp_source.zig:2:22: error: expected pointer or slice, found 'u32'", ); cases.add( @@ -4490,7 +4486,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\} \\fn alignedSmall() align(4) i32 { return 1234; } , - ".tmp_source.zig:2:35: error: expected 'fn() align(8) i32' type, found 'fn() align(4) i32'", + ".tmp_source.zig:2:35: error: expected type 'fn() align(8) i32', found 'fn() align(4) i32'", ); cases.add( @@ -4502,7 +4498,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return x == 5678; \\} , - ".tmp_source.zig:4:32: error: expected '*i32' type, found '*align(1) i32'", + ".tmp_source.zig:4:32: error: expected type '*i32', found '*align(1) i32'", ); cases.add( @@ -4537,7 +4533,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ bar(@ptrCast(*c_void, &x)); \\} , - ".tmp_source.zig:5:9: error: expected '*Derp' type, found '*c_void'", + ".tmp_source.zig:5:9: error: expected type '*Derp', found '*c_void'", ); cases.add( @@ -4583,7 +4579,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, u32(1234), u32(1234))) {} \\} , - ".tmp_source.zig:3:50: error: expected 'AtomicOrder' type, found 'u32'", + ".tmp_source.zig:3:50: error: expected type 'AtomicOrder', found 'u32'", ); cases.add( @@ -4593,7 +4589,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ @export("entry", entry, u32(1234)); \\} , - ".tmp_source.zig:3:32: error: expected 'GlobalLinkage' type, found 'u32'", + ".tmp_source.zig:3:32: error: expected type 'GlobalLinkage', found 'u32'", ); cases.add( @@ -4770,7 +4766,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ _ = @ArgType(i32, 3); \\} , - ".tmp_source.zig:2:18: error: expected Fn type, found 'i32'", + ".tmp_source.zig:2:18: error: expected function, found 'i32'", ); cases.add( @@ -4868,7 +4864,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\} \\pub extern fn foo(format: *const u8, ...) void; , - ".tmp_source.zig:2:9: error: expected '*const u8' type, found '[5]u8'", + ".tmp_source.zig:2:9: error: expected type '*const u8', found '[5]u8'", ); cases.add( @@ -4937,7 +4933,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var x: u2 = Small.Two; \\} , - ".tmp_source.zig:9:22: error: expected 'u2' type, found 'Small'", + ".tmp_source.zig:9:22: error: expected type 'u2', found 'Small'", ); cases.add( @@ -4954,7 +4950,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var x = @intToEnum(Small, y); \\} , - ".tmp_source.zig:10:31: error: expected 'u2' type, found 'u3'", + ".tmp_source.zig:10:31: error: expected type 'u2', found 'u3'", ); cases.add( @@ -5351,7 +5347,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ asm volatile ("" : : [bar]"r"(3) : ""); \\} , - ".tmp_source.zig:2:35: error: expected sized integer or sized float type, found comptime_int", + ".tmp_source.zig:2:35: error: expected sized integer or sized float, found comptime_int", ); cases.add( @@ -5360,6 +5356,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ asm volatile ("" : : [bar]"r"(3.17) : ""); \\} , - ".tmp_source.zig:2:35: error: expected sized integer or sized float type, found comptime_float", + ".tmp_source.zig:2:35: error: expected sized integer or sized float, found comptime_float", ); } -- 2.54.0 From 581edd643fb18a66c472f77e2f8cd3f4cea524a2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 29 Jan 2019 21:47:26 -0500 Subject: [PATCH 21/37] backport copy elision changes This commit contains everything from the copy-elision-2 branch that does not have to do with copy elision directly, but is generally useful for master branch. * All const values know their parents, when applicable, not just structs and unions. * Null pointers in const values are represented explicitly, rather than as a HardCodedAddr value of 0. * Rename "maybe" to "optional" in various code locations. * Separate DeclVarSrc and DeclVarGen * Separate PtrCastSrc and PtrCastGen * Separate CmpxchgSrc and CmpxchgGen * Represent optional error set as an integer, using the 0 value. In a const value, it uses nullptr. * Introduce type_has_one_possible_value and use it where applicable. * Fix debug builds not setting memory to 0xaa when storing undefined. * Separate the type of a variable from the const value of a variable. * Use copy_const_val where appropriate. * Rearrange structs to pack data more efficiently. * Move test/cases/* to test/behavior/* * Use `std.debug.assertOrPanic` in behavior tests instead of `std.debug.assert`. * Fix outdated slice syntax in docs. --- build.zig | 5 +- doc/langref.html.in | 13 +- src/all_types.hpp | 165 +- src/analyze.cpp | 297 +++- src/analyze.hpp | 9 +- src/ast_render.cpp | 8 +- src/codegen.cpp | 385 +++-- src/ir.cpp | 1479 +++++++++-------- src/ir.hpp | 2 +- src/ir_print.cpp | 83 +- src/parser.cpp | 8 +- std/event/fs.zig | 49 +- test/behavior.zig | 82 - test/cases/array.zig | 173 -- test/cases/asm.zig | 48 - test/cases/bitreverse.zig | 81 - test/cases/bswap.zig | 32 - test/cases/import.zig | 10 - test/cases/optional.zig | 30 - test/cases/popcount.zig | 24 - test/cases/reflection.zig | 95 -- test/cases/sizeof_and_typeof.zig | 69 - test/cases/type_info.zig | 264 --- test/compile_errors.zig | 4 +- test/stage1/behavior.zig | 80 + test/{cases => stage1/behavior}/align.zig | 72 +- test/{cases => stage1/behavior}/alignof.zig | 7 +- test/stage1/behavior/array.zig | 270 +++ test/stage1/behavior/asm.zig | 92 + test/{cases => stage1/behavior}/atomics.zig | 32 +- .../behavior}/bit_shifting.zig | 10 +- test/{cases => stage1/behavior}/bitcast.zig | 9 +- test/stage1/behavior/bitreverse.zig | 81 + test/{cases => stage1/behavior}/bool.zig | 20 +- test/stage1/behavior/bswap.zig | 32 + test/{cases => stage1/behavior}/bugs/1076.zig | 4 +- test/{cases => stage1/behavior}/bugs/1111.zig | 0 test/{cases => stage1/behavior}/bugs/1277.zig | 0 test/{cases => stage1/behavior}/bugs/1322.zig | 4 +- test/{cases => stage1/behavior}/bugs/1381.zig | 0 test/{cases => stage1/behavior}/bugs/1421.zig | 4 +- test/{cases => stage1/behavior}/bugs/1442.zig | 0 test/{cases => stage1/behavior}/bugs/1486.zig | 6 +- test/{cases => stage1/behavior}/bugs/394.zig | 4 +- test/{cases => stage1/behavior}/bugs/655.zig | 4 +- .../behavior}/bugs/655_other_file.zig | 0 test/{cases => stage1/behavior}/bugs/656.zig | 4 +- test/{cases => stage1/behavior}/bugs/726.zig | 6 +- test/{cases => stage1/behavior}/bugs/828.zig | 0 test/{cases => stage1/behavior}/bugs/920.zig | 2 +- .../behavior}/byval_arg_var.zig | 4 +- test/{cases => stage1/behavior}/cancel.zig | 14 +- test/{cases => stage1/behavior}/cast.zig | 155 +- .../behavior}/const_slice_child.zig | 10 +- .../behavior}/coroutine_await_struct.zig | 6 +- .../{cases => stage1/behavior}/coroutines.zig | 40 +- test/{cases => stage1/behavior}/defer.zig | 24 +- test/{cases => stage1/behavior}/enum.zig | 76 +- .../behavior}/enum_with_members.zig | 10 +- test/{cases => stage1/behavior}/error.zig | 125 +- test/{cases => stage1/behavior}/eval.zig | 236 +-- .../behavior}/field_parent_ptr.zig | 14 +- test/{cases => stage1/behavior}/fn.zig | 39 +- .../behavior}/fn_in_struct_in_comptime.zig | 4 +- test/{cases => stage1/behavior}/for.zig | 10 +- test/{cases => stage1/behavior}/generics.zig | 46 +- test/{cases => stage1/behavior}/if.zig | 4 +- test/stage1/behavior/import.zig | 10 + .../behavior}/import/a_namespace.zig | 0 .../behavior}/incomplete_struct_param_tld.zig | 4 +- test/{cases => stage1/behavior}/inttoptr.zig | 1 - .../behavior}/ir_block_deps.zig | 6 +- test/{cases => stage1/behavior}/math.zig | 256 +-- .../behavior}/merge_error_sets.zig | 0 test/{cases => stage1/behavior}/misc.zig | 350 ++-- .../namespace_depends_on_compile_var/a.zig | 0 .../namespace_depends_on_compile_var/b.zig | 0 .../index.zig | 6 +- .../behavior}/new_stack_call.zig | 10 +- test/{cases => stage1/behavior}/null.zig | 34 +- test/stage1/behavior/optional.zig | 81 + test/{cases => stage1/behavior}/pointers.zig | 24 +- test/stage1/behavior/popcount.zig | 25 + test/{cases => stage1/behavior}/ptrcast.zig | 0 .../behavior}/pub_enum/index.zig | 6 +- .../behavior}/pub_enum/other.zig | 0 ...ef_var_in_if_after_if_2nd_switch_prong.zig | 10 +- test/stage1/behavior/reflection.zig | 96 ++ test/stage1/behavior/sizeof_and_typeof.zig | 69 + test/{cases => stage1/behavior}/slice.zig | 16 +- test/{cases => stage1/behavior}/struct.zig | 186 +-- .../struct_contains_null_ptr_itself.zig | 4 +- .../struct_contains_slice_of_itself.zig | 26 +- test/{cases => stage1/behavior}/switch.zig | 66 +- .../behavior}/switch_prong_err_enum.zig | 6 +- .../behavior}/switch_prong_implicit_cast.zig | 4 +- test/{cases => stage1/behavior}/syntax.zig | 1 + test/{cases => stage1/behavior}/this.zig | 9 +- test/{cases => stage1/behavior}/truncate.zig | 4 +- test/{cases => stage1/behavior}/try.zig | 10 +- test/stage1/behavior/type_info.zig | 264 +++ test/{cases => stage1/behavior}/undefined.zig | 29 +- .../{cases => stage1/behavior}/underscore.zig | 2 +- test/{cases => stage1/behavior}/union.zig | 76 +- test/{cases => stage1/behavior}/var_args.zig | 34 +- test/{cases => stage1/behavior}/void.zig | 11 +- test/{cases => stage1/behavior}/while.zig | 45 +- test/{cases => stage1/behavior}/widening.zig | 9 +- 108 files changed, 3834 insertions(+), 2931 deletions(-) delete mode 100644 test/behavior.zig delete mode 100644 test/cases/array.zig delete mode 100644 test/cases/asm.zig delete mode 100644 test/cases/bitreverse.zig delete mode 100644 test/cases/bswap.zig delete mode 100644 test/cases/import.zig delete mode 100644 test/cases/optional.zig delete mode 100644 test/cases/popcount.zig delete mode 100644 test/cases/reflection.zig delete mode 100644 test/cases/sizeof_and_typeof.zig delete mode 100644 test/cases/type_info.zig create mode 100644 test/stage1/behavior.zig rename test/{cases => stage1/behavior}/align.zig (69%) rename test/{cases => stage1/behavior}/alignof.zig (61%) create mode 100644 test/stage1/behavior/array.zig create mode 100644 test/stage1/behavior/asm.zig rename test/{cases => stage1/behavior}/atomics.zig (65%) rename test/{cases => stage1/behavior}/bit_shifting.zig (90%) rename test/{cases => stage1/behavior}/bitcast.zig (78%) create mode 100644 test/stage1/behavior/bitreverse.zig rename test/{cases => stage1/behavior}/bool.zig (50%) create mode 100644 test/stage1/behavior/bswap.zig rename test/{cases => stage1/behavior}/bugs/1076.zig (75%) rename test/{cases => stage1/behavior}/bugs/1111.zig (100%) rename test/{cases => stage1/behavior}/bugs/1277.zig (100%) rename test/{cases => stage1/behavior}/bugs/1322.zig (66%) rename test/{cases => stage1/behavior}/bugs/1381.zig (100%) rename test/{cases => stage1/behavior}/bugs/1421.zig (70%) rename test/{cases => stage1/behavior}/bugs/1442.zig (100%) rename test/{cases => stage1/behavior}/bugs/1486.zig (51%) rename test/{cases => stage1/behavior}/bugs/394.zig (68%) rename test/{cases => stage1/behavior}/bugs/655.zig (67%) rename test/{cases => stage1/behavior}/bugs/655_other_file.zig (100%) rename test/{cases => stage1/behavior}/bugs/656.zig (84%) rename test/{cases => stage1/behavior}/bugs/726.zig (71%) rename test/{cases => stage1/behavior}/bugs/828.zig (100%) rename test/{cases => stage1/behavior}/bugs/920.zig (95%) rename test/{cases => stage1/behavior}/byval_arg_var.zig (70%) rename test/{cases => stage1/behavior}/cancel.zig (84%) rename test/{cases => stage1/behavior}/cast.zig (67%) rename test/{cases => stage1/behavior}/const_slice_child.zig (80%) rename test/{cases => stage1/behavior}/coroutine_await_struct.zig (86%) rename test/{cases => stage1/behavior}/coroutines.zig (86%) rename test/{cases => stage1/behavior}/defer.zig (72%) rename test/{cases => stage1/behavior}/enum.zig (84%) rename test/{cases => stage1/behavior}/enum_with_members.zig (63%) rename test/{cases => stage1/behavior}/error.zig (58%) rename test/{cases => stage1/behavior}/eval.zig (70%) rename test/{cases => stage1/behavior}/field_parent_ptr.zig (69%) rename test/{cases => stage1/behavior}/fn.zig (78%) rename test/{cases => stage1/behavior}/fn_in_struct_in_comptime.zig (72%) rename test/{cases => stage1/behavior}/for.zig (89%) rename test/{cases => stage1/behavior}/generics.zig (68%) rename test/{cases => stage1/behavior}/if.zig (85%) create mode 100644 test/stage1/behavior/import.zig rename test/{cases => stage1/behavior}/import/a_namespace.zig (100%) rename test/{cases => stage1/behavior}/incomplete_struct_param_tld.zig (78%) rename test/{cases => stage1/behavior}/inttoptr.zig (99%) rename test/{cases => stage1/behavior}/ir_block_deps.zig (64%) rename test/{cases => stage1/behavior}/math.zig (56%) rename test/{cases => stage1/behavior}/merge_error_sets.zig (100%) rename test/{cases => stage1/behavior}/misc.zig (61%) rename test/{cases => stage1/behavior}/namespace_depends_on_compile_var/a.zig (100%) rename test/{cases => stage1/behavior}/namespace_depends_on_compile_var/b.zig (100%) rename test/{cases => stage1/behavior}/namespace_depends_on_compile_var/index.zig (62%) rename test/{cases => stage1/behavior}/new_stack_call.zig (72%) rename test/{cases => stage1/behavior}/null.zig (80%) create mode 100644 test/stage1/behavior/optional.zig rename test/{cases => stage1/behavior}/pointers.zig (50%) create mode 100644 test/stage1/behavior/popcount.zig rename test/{cases => stage1/behavior}/ptrcast.zig (100%) rename test/{cases => stage1/behavior}/pub_enum/index.zig (54%) rename test/{cases => stage1/behavior}/pub_enum/other.zig (100%) rename test/{cases => stage1/behavior}/ref_var_in_if_after_if_2nd_switch_prong.zig (77%) create mode 100644 test/stage1/behavior/reflection.zig create mode 100644 test/stage1/behavior/sizeof_and_typeof.zig rename test/{cases => stage1/behavior}/slice.zig (62%) rename test/{cases => stage1/behavior}/struct.zig (64%) rename test/{cases => stage1/behavior}/struct_contains_null_ptr_itself.zig (81%) rename test/{cases => stage1/behavior}/struct_contains_slice_of_itself.zig (68%) rename test/{cases => stage1/behavior}/switch.zig (72%) rename test/{cases => stage1/behavior}/switch_prong_err_enum.zig (79%) rename test/{cases => stage1/behavior}/switch_prong_implicit_cast.zig (82%) rename test/{cases => stage1/behavior}/syntax.zig (99%) rename test/{cases => stage1/behavior}/this.zig (74%) rename test/{cases => stage1/behavior}/truncate.zig (65%) rename test/{cases => stage1/behavior}/try.zig (79%) create mode 100644 test/stage1/behavior/type_info.zig rename test/{cases => stage1/behavior}/undefined.zig (59%) rename test/{cases => stage1/behavior}/underscore.zig (91%) rename test/{cases => stage1/behavior}/union.zig (77%) rename test/{cases => stage1/behavior}/var_args.zig (56%) rename test/{cases => stage1/behavior}/void.zig (67%) rename test/{cases => stage1/behavior}/while.zig (83%) rename test/{cases => stage1/behavior}/widening.zig (75%) diff --git a/build.zig b/build.zig index 16185eebf4fdeb645d5c7379c4ae6ea9055a9cbd..d99165a6de51df7388639a8c60dc2738907f13a5 100644 --- a/build.zig +++ b/build.zig @@ -104,7 +104,7 @@ pub fn build(b: *Builder) !void { } const modes = chosen_modes[0..chosen_mode_index]; - test_step.dependOn(tests.addPkgTests(b, test_filter, "test/behavior.zig", "behavior", "Run the behavior tests", modes)); + test_step.dependOn(tests.addPkgTests(b, test_filter, "test/stage1/behavior.zig", "behavior", "Run the behavior tests", modes)); test_step.dependOn(tests.addPkgTests(b, test_filter, "std/index.zig", "std", "Run the standard library tests", modes)); @@ -299,8 +299,7 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void { } else if (exe.target.isFreeBSD()) { try addCxxKnownPath(b, ctx, exe, "libc++.a", null); exe.linkSystemLibrary("pthread"); - } - else if (exe.target.isDarwin()) { + } else if (exe.target.isDarwin()) { if (addCxxKnownPath(b, ctx, exe, "libgcc_eh.a", "")) { // Compiler is GCC. try addCxxKnownPath(b, ctx, exe, "libstdc++.a", null); diff --git a/doc/langref.html.in b/doc/langref.html.in index 6e03d3ec6d676d64a2ac30e2dcb60e3dffe84247..909c0f5817dc059cc31a3fc92a12bd4778380505 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4327,7 +4327,7 @@ fn gimmeTheBiggerInteger(a: u64, b: u64) u64 {

For example, if we were to introduce another function to the above snippet:

- {#code_begin|test_err|unable to evaluate constant expression#} + {#code_begin|test_err|values of type 'type' must be comptime known#} fn max(comptime T: type, a: T, b: T) T { return if (a > b) a else b; } @@ -5905,13 +5905,13 @@ fn add(a: i32, b: i32) i32 { return a + b; } This function is a low level intrinsic with no safety mechanisms. Most code should not use this function, instead using something like this:

-
{#syntax#}for (source[0...byte_count]) |b, i| dest[i] = b;{#endsyntax#}
+
{#syntax#}for (source[0..byte_count]) |b, i| dest[i] = b;{#endsyntax#}

The optimizer is intelligent enough to turn the above snippet into a memcpy.

There is also a standard library function for this:

{#syntax#}const mem = @import("std").mem;
-mem.copy(u8, dest[0...byte_count], source[0...byte_count]);{#endsyntax#}
+mem.copy(u8, dest[0..byte_count], source[0..byte_count]);{#endsyntax#} {#header_close#} {#header_open|@memset#} @@ -5923,7 +5923,7 @@ mem.copy(u8, dest[0...byte_count], source[0...byte_count]);{#endsyntax#} This function is a low level intrinsic with no safety mechanisms. Most code should not use this function, instead using something like this:

-
{#syntax#}for (dest[0...byte_count]) |*b| b.* = c;{#endsyntax#}
+
{#syntax#}for (dest[0..byte_count]) |*b| b.* = c;{#endsyntax#}

The optimizer is intelligent enough to turn the above snippet into a memset.

@@ -6592,9 +6592,10 @@ pub const TypeInfo = union(TypeId) { {#header_close#} {#header_open|@typeName#} -
{#syntax#}@typeName(T: type) []u8{#endsyntax#}
+
{#syntax#}@typeName(T: type) [N]u8{#endsyntax#}

- This function returns the string representation of a type. + This function returns the string representation of a type, as + an array. It is equivalent to a string literal of the type name.

{#header_close#} diff --git a/src/all_types.hpp b/src/all_types.hpp index 91b24e311059c83605cd8486002f1b841846f060..4b134361a39a62115364f2befab2a29f23b5cc06 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -56,9 +56,6 @@ struct IrExecutable { size_t next_debug_id; size_t *backward_branch_count; size_t backward_branch_quota; - bool invalid; - bool is_inline; - bool is_generic_instantiation; ZigFn *fn_entry; Buf *c_import_buf; AstNode *source_node; @@ -78,6 +75,10 @@ struct IrExecutable { IrBasicBlock *coro_suspend_block; IrBasicBlock *coro_final_cleanup_block; ZigVar *coro_allocator_var; + + bool invalid; + bool is_inline; + bool is_generic_instantiation; }; enum OutType { @@ -90,6 +91,9 @@ enum OutType { enum ConstParentId { ConstParentIdNone, ConstParentIdStruct, + ConstParentIdErrUnionCode, + ConstParentIdErrUnionPayload, + ConstParentIdOptionalPayload, ConstParentIdArray, ConstParentIdUnion, ConstParentIdScalar, @@ -107,6 +111,15 @@ struct ConstParent { ConstExprValue *struct_val; size_t field_index; } p_struct; + struct { + ConstExprValue *err_union_val; + } p_err_union_code; + struct { + ConstExprValue *err_union_val; + } p_err_union_payload; + struct { + ConstExprValue *optional_val; + } p_optional_payload; struct { ConstExprValue *union_val; } p_union; @@ -118,13 +131,11 @@ struct ConstParent { struct ConstStructValue { ConstExprValue *fields; - ConstParent parent; }; struct ConstUnionValue { BigInt tag; ConstExprValue *payload; - ConstParent parent; }; enum ConstArraySpecial { @@ -138,7 +149,6 @@ struct ConstArrayValue { union { struct { ConstExprValue *elements; - ConstParent parent; } s_none; Buf *s_buf; } data; @@ -153,19 +163,29 @@ enum ConstPtrSpecial { ConstPtrSpecialBaseArray, // The pointer points to a field in an underlying struct. ConstPtrSpecialBaseStruct, + // The pointer points to the error set field of an error union + ConstPtrSpecialBaseErrorUnionCode, + // The pointer points to the payload field of an error union + ConstPtrSpecialBaseErrorUnionPayload, + // The pointer points to the payload field of an optional + ConstPtrSpecialBaseOptionalPayload, // This means that we did a compile-time pointer reinterpret and we cannot // understand the value of pointee at compile time. However, we will still // emit a binary with a compile time known address. // In this case index is the numeric address value. - // We also use this for null pointer. We need the data layout for ConstCastOnly == true - // types to be the same, so all optionals of pointer types use x_ptr - // instead of x_optional ConstPtrSpecialHardCodedAddr, // This means that the pointer represents memory of assigning to _. // That is, storing discards the data, and loading is invalid. ConstPtrSpecialDiscard, // This is actually a function. ConstPtrSpecialFunction, + // This means the pointer is null. This is only allowed when the type is ?*T. + // We use this instead of ConstPtrSpecialHardCodedAddr because often we check + // for that value to avoid doing comptime work. + // We need the data layout for ConstCastOnly == true + // types to be the same, so all optionals of pointer types use x_ptr + // instead of x_optional. + ConstPtrSpecialNull, }; enum ConstPtrMut { @@ -199,6 +219,15 @@ struct ConstPtrValue { ConstExprValue *struct_val; size_t field_index; } base_struct; + struct { + ConstExprValue *err_union_val; + } base_err_union_code; + struct { + ConstExprValue *err_union_val; + } base_err_union_payload; + struct { + ConstExprValue *optional_val; + } base_optional_payload; struct { uint64_t addr; } hard_coded_addr; @@ -209,7 +238,7 @@ struct ConstPtrValue { }; struct ConstErrValue { - ErrorTableEntry *err; + ConstExprValue *error_set; ConstExprValue *payload; }; @@ -265,6 +294,7 @@ struct ConstGlobalRefs { struct ConstExprValue { ZigType *type; ConstValSpecial special; + ConstParent parent; ConstGlobalRefs *global_refs; union { @@ -433,7 +463,7 @@ enum NodeType { NodeTypeArrayType, NodeTypeErrorType, NodeTypeIfErrorExpr, - NodeTypeTestExpr, + NodeTypeIfOptional, NodeTypeErrorSetDecl, NodeTypeCancel, NodeTypeResume, @@ -677,7 +707,7 @@ struct AstNodeUse { AstNode *expr; TldResolution resolution; - IrInstruction *value; + ConstExprValue *value; }; struct AstNodeIfBoolExpr { @@ -1610,7 +1640,7 @@ struct CodeGen { HashMap fn_type_table; HashMap error_table; HashMap generic_table; - HashMap memoized_fn_eval_table; + HashMap memoized_fn_eval_table; HashMap llvm_fn_table; HashMap exported_symbol_names; HashMap external_prototypes; @@ -1802,10 +1832,9 @@ enum VarLinkage { struct ZigVar { Buf name; - ConstExprValue *value; + ConstExprValue *const_value; + ZigType *var_type; LLVMValueRef value_ref; - bool src_is_const; - bool gen_is_const; IrInstruction *is_comptime; // which node is the declaration of the variable AstNode *decl_node; @@ -1815,17 +1844,21 @@ struct ZigVar { Scope *parent_scope; Scope *child_scope; LLVMValueRef param_value_ref; - bool shadowable; size_t mem_slot_index; IrExecutable *owner_exec; size_t ref_count; - VarLinkage linkage; - uint32_t align_bytes; // In an inline loop, multiple variables may be created, // In this case, a reference to a variable should follow // this pointer to the redefined variable. ZigVar *next_var; + + uint32_t align_bytes; + VarLinkage linkage; + + bool shadowable; + bool src_is_const; + bool gen_is_const; }; struct ErrorTableEntry { @@ -1891,10 +1924,11 @@ struct ScopeBlock { ZigList *incoming_values; ZigList *incoming_blocks; - bool safety_off; AstNode *safety_set_node; - bool fast_math_on; AstNode *fast_math_set_node; + + bool safety_off; + bool fast_math_on; }; // This scope is created from every defer expression. @@ -2030,8 +2064,19 @@ struct IrBasicBlock { IrInstruction *must_be_comptime_source_instr; }; +// These instructions are in transition to having "pass 1" instructions +// and "pass 2" instructions. The pass 1 instructions are suffixed with Src +// and pass 2 are suffixed with Gen. +// Once all instructions are separated in this way, they'll have different +// base types for better type safety. +// Src instructions are generated by ir_gen_* functions in ir.cpp from AST. +// ir_analyze_* functions consume Src instructions and produce Gen instructions. +// ir_render_* functions in codegen.cpp consume Gen instructions and produce LLVM IR. +// Src instructions do not have type information; Gen instructions do. enum IrInstructionId { IrInstructionIdInvalid, + IrInstructionIdDeclVarSrc, + IrInstructionIdDeclVarGen, IrInstructionIdBr, IrInstructionIdCondBr, IrInstructionIdSwitchBr, @@ -2040,7 +2085,6 @@ enum IrInstructionId { IrInstructionIdPhi, IrInstructionIdUnOp, IrInstructionIdBinOp, - IrInstructionIdDeclVar, IrInstructionIdLoadPtr, IrInstructionIdStorePtr, IrInstructionIdFieldPtr, @@ -2069,7 +2113,7 @@ enum IrInstructionId { IrInstructionIdAsm, IrInstructionIdSizeOf, IrInstructionIdTestNonNull, - IrInstructionIdUnwrapOptional, + IrInstructionIdOptionalUnwrapPtr, IrInstructionIdOptionalWrap, IrInstructionIdUnionTag, IrInstructionIdClz, @@ -2085,7 +2129,8 @@ enum IrInstructionId { IrInstructionIdCompileLog, IrInstructionIdErrName, IrInstructionIdEmbedFile, - IrInstructionIdCmpxchg, + IrInstructionIdCmpxchgSrc, + IrInstructionIdCmpxchgGen, IrInstructionIdFence, IrInstructionIdTruncate, IrInstructionIdIntCast, @@ -2114,7 +2159,8 @@ enum IrInstructionId { IrInstructionIdErrWrapPayload, IrInstructionIdFnProto, IrInstructionIdTestComptime, - IrInstructionIdPtrCast, + IrInstructionIdPtrCastSrc, + IrInstructionIdPtrCastGen, IrInstructionIdBitCast, IrInstructionIdWidenOrShorten, IrInstructionIdIntToPtr, @@ -2194,6 +2240,22 @@ struct IrInstruction { bool is_gen; }; +struct IrInstructionDeclVarSrc { + IrInstruction base; + + ZigVar *var; + IrInstruction *var_type; + IrInstruction *align_value; + IrInstruction *init_value; +}; + +struct IrInstructionDeclVarGen { + IrInstruction base; + + ZigVar *var; + IrInstruction *init_value; +}; + struct IrInstructionCondBr { IrInstruction base; @@ -2302,20 +2364,11 @@ struct IrInstructionBinOp { IrInstruction base; IrInstruction *op1; + IrInstruction *op2; IrBinOp op_id; - IrInstruction *op2; bool safety_check_on; }; -struct IrInstructionDeclVar { - IrInstruction base; - - ZigVar *var; - IrInstruction *var_type; - IrInstruction *align_value; - IrInstruction *init_value; -}; - struct IrInstructionLoadPtr { IrInstruction base; @@ -2335,7 +2388,6 @@ struct IrInstructionFieldPtr { IrInstruction *container_ptr; Buf *field_name_buffer; IrInstruction *field_name_expr; - bool is_const; }; struct IrInstructionStructFieldPtr { @@ -2378,13 +2430,13 @@ struct IrInstructionCall { ZigFn *fn_entry; size_t arg_count; IrInstruction **args; - bool is_comptime; LLVMValueRef tmp_ptr; - FnInline fn_inline; - bool is_async; IrInstruction *async_allocator; IrInstruction *new_stack; + FnInline fn_inline; + bool is_async; + bool is_comptime; }; struct IrInstructionConst { @@ -2527,9 +2579,9 @@ struct IrInstructionSliceType { IrInstruction base; IrInstruction *align_value; + IrInstruction *child_type; bool is_const; bool is_volatile; - IrInstruction *child_type; }; struct IrInstructionAsm { @@ -2557,10 +2609,12 @@ struct IrInstructionTestNonNull { IrInstruction *value; }; -struct IrInstructionUnwrapOptional { +// Takes a pointer to an optional value, returns a pointer +// to the payload. +struct IrInstructionOptionalUnwrapPtr { IrInstruction base; - IrInstruction *value; + IrInstruction *base_ptr; bool safety_check_on; }; @@ -2651,7 +2705,7 @@ struct IrInstructionEmbedFile { IrInstruction *name; }; -struct IrInstructionCmpxchg { +struct IrInstructionCmpxchgSrc { IrInstruction base; IrInstruction *type_value; @@ -2661,14 +2715,19 @@ struct IrInstructionCmpxchg { IrInstruction *success_order_value; IrInstruction *failure_order_value; - // if this instruction gets to runtime then we know these values: - ZigType *type; + bool is_weak; +}; + +struct IrInstructionCmpxchgGen { + IrInstruction base; + + IrInstruction *ptr; + IrInstruction *cmp_value; + IrInstruction *new_value; + LLVMValueRef tmp_ptr; AtomicOrder success_order; AtomicOrder failure_order; - bool is_weak; - - LLVMValueRef tmp_ptr; }; struct IrInstructionFence { @@ -2851,7 +2910,7 @@ struct IrInstructionTestErr { struct IrInstructionUnwrapErrCode { IrInstruction base; - IrInstruction *value; + IrInstruction *err_union; }; struct IrInstructionUnwrapErrPayload { @@ -2899,13 +2958,19 @@ struct IrInstructionTestComptime { IrInstruction *value; }; -struct IrInstructionPtrCast { +struct IrInstructionPtrCastSrc { IrInstruction base; IrInstruction *dest_type; IrInstruction *ptr; }; +struct IrInstructionPtrCastGen { + IrInstruction base; + + IrInstruction *ptr; +}; + struct IrInstructionBitCast { IrInstruction base; diff --git a/src/analyze.cpp b/src/analyze.cpp index 15370983fcb5409eaa77ef502601a83811a9304a..194888068cf42f8eae2238ef34b64a0b148faea1 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -570,7 +570,7 @@ ZigType *get_optional_type(CodeGen *g, ZigType *child_type) { if (child_type->zero_bits) { entry->type_ref = LLVMInt1Type(); entry->di_type = g->builtin_types.entry_bool->di_type; - } else if (type_is_codegen_pointer(child_type)) { + } else if (type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet) { assert(child_type->di_type); // this is an optimization but also is necessary for calling C // functions where all pointers are maybe pointers @@ -1278,7 +1278,9 @@ ZigType *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind return entry; } -static IrInstruction *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry, Buf *type_name) { +static ConstExprValue *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, ZigType *type_entry, + Buf *type_name) +{ size_t backward_branch_count = 0; return ir_eval_const_value(g, scope, node, type_entry, &backward_branch_count, default_backward_branch_quota, @@ -1286,12 +1288,12 @@ static IrInstruction *analyze_const_value(CodeGen *g, Scope *scope, AstNode *nod } ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node) { - IrInstruction *result = analyze_const_value(g, scope, node, g->builtin_types.entry_type, nullptr); - if (result->value.type->id == ZigTypeIdInvalid) + ConstExprValue *result = analyze_const_value(g, scope, node, g->builtin_types.entry_type, nullptr); + if (type_is_invalid(result->type)) return g->builtin_types.entry_invalid; - assert(result->value.special != ConstValSpecialRuntime); - return result->value.data.x_type; + assert(result->special != ConstValSpecialRuntime); + return result->data.x_type; } ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id) { @@ -1342,11 +1344,11 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou } static bool analyze_const_align(CodeGen *g, Scope *scope, AstNode *node, uint32_t *result) { - IrInstruction *align_result = analyze_const_value(g, scope, node, get_align_amt_type(g), nullptr); - if (type_is_invalid(align_result->value.type)) + ConstExprValue *align_result = analyze_const_value(g, scope, node, get_align_amt_type(g), nullptr); + if (type_is_invalid(align_result->type)) return false; - uint32_t align_bytes = bigint_as_unsigned(&align_result->value.data.x_bigint); + uint32_t align_bytes = bigint_as_unsigned(&align_result->data.x_bigint); if (align_bytes == 0) { add_node_error(g, node, buf_sprintf("alignment must be >= 1")); return false; @@ -1364,12 +1366,12 @@ static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf ** ZigType *ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, PtrLenUnknown, 0, 0, 0); ZigType *str_type = get_slice_type(g, ptr_type); - IrInstruction *instr = analyze_const_value(g, scope, node, str_type, nullptr); - if (type_is_invalid(instr->value.type)) + ConstExprValue *result_val = analyze_const_value(g, scope, node, str_type, nullptr); + if (type_is_invalid(result_val->type)) return false; - ConstExprValue *ptr_field = &instr->value.data.x_struct.fields[slice_ptr_index]; - ConstExprValue *len_field = &instr->value.data.x_struct.fields[slice_len_index]; + ConstExprValue *ptr_field = &result_val->data.x_struct.fields[slice_ptr_index]; + ConstExprValue *len_field = &result_val->data.x_struct.fields[slice_len_index]; assert(ptr_field->data.x_ptr.special == ConstPtrSpecialBaseArray); ConstExprValue *array_val = ptr_field->data.x_ptr.data.base_array.array_val; @@ -2504,20 +2506,20 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { // In this first pass we resolve explicit tag values. // In a second pass we will fill in the unspecified ones. if (tag_value != nullptr) { - IrInstruction *result_inst = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr); - if (result_inst->value.type->id == ZigTypeIdInvalid) { + ConstExprValue *result = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr); + if (type_is_invalid(result->type)) { enum_type->data.enumeration.is_invalid = true; continue; } - assert(result_inst->value.special != ConstValSpecialRuntime); - assert(result_inst->value.type->id == ZigTypeIdInt || - result_inst->value.type->id == ZigTypeIdComptimeInt); - auto entry = occupied_tag_values.put_unique(result_inst->value.data.x_bigint, tag_value); + assert(result->special != ConstValSpecialRuntime); + assert(result->type->id == ZigTypeIdInt || + result->type->id == ZigTypeIdComptimeInt); + auto entry = occupied_tag_values.put_unique(result->data.x_bigint, tag_value); if (entry == nullptr) { - bigint_init_bigint(&type_enum_field->value, &result_inst->value.data.x_bigint); + bigint_init_bigint(&type_enum_field->value, &result->data.x_bigint); } else { Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, &result_inst->value.data.x_bigint, 10); + bigint_append_buf(val_buf, &result->data.x_bigint, 10); ErrorMsg *msg = add_node_error(g, tag_value, buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf))); @@ -2944,19 +2946,19 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) { // In a second pass we will fill in the unspecified ones. if (tag_value != nullptr) { ZigType *tag_int_type = tag_type->data.enumeration.tag_int_type; - IrInstruction *result_inst = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr); - if (result_inst->value.type->id == ZigTypeIdInvalid) { + ConstExprValue *result = analyze_const_value(g, scope, tag_value, tag_int_type, nullptr); + if (type_is_invalid(result->type)) { union_type->data.unionation.is_invalid = true; continue; } - assert(result_inst->value.special != ConstValSpecialRuntime); - assert(result_inst->value.type->id == ZigTypeIdInt); - auto entry = occupied_tag_values.put_unique(result_inst->value.data.x_bigint, tag_value); + assert(result->special != ConstValSpecialRuntime); + assert(result->type->id == ZigTypeIdInt); + auto entry = occupied_tag_values.put_unique(result->data.x_bigint, tag_value); if (entry == nullptr) { - bigint_init_bigint(&union_field->enum_field->value, &result_inst->value.data.x_bigint); + bigint_init_bigint(&union_field->enum_field->value, &result->data.x_bigint); } else { Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, &result_inst->value.data.x_bigint, 10); + bigint_append_buf(val_buf, &result->data.x_bigint, 10); ErrorMsg *msg = add_node_error(g, tag_value, buf_sprintf("enum tag value %s already taken", buf_ptr(val_buf))); @@ -3419,7 +3421,8 @@ void update_compile_var(CodeGen *g, Buf *name, ConstExprValue *value) { resolve_top_level_decl(g, tld, false, tld->source_node); assert(tld->id == TldIdVar); TldVar *tld_var = (TldVar *)tld; - tld_var->var->value = value; + tld_var->var->const_value = value; + tld_var->var->var_type = value->type; tld_var->var->align_bytes = get_abi_alignment(g, value->type); } @@ -3513,7 +3516,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { case NodeTypeArrayType: case NodeTypeErrorType: case NodeTypeIfErrorExpr: - case NodeTypeTestExpr: + case NodeTypeIfOptional: case NodeTypeErrorSetDecl: case NodeTypeCancel: case NodeTypeResume: @@ -3582,13 +3585,15 @@ ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry // Set name to nullptr to make the variable anonymous (not visible to programmer). // TODO merge with definition of add_local_var in ir.cpp ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf *name, - bool is_const, ConstExprValue *value, Tld *src_tld) + bool is_const, ConstExprValue *const_value, Tld *src_tld, ZigType *var_type) { Error err; - assert(value); + assert(const_value != nullptr); + assert(var_type != nullptr); ZigVar *variable_entry = allocate(1); - variable_entry->value = value; + variable_entry->const_value = const_value; + variable_entry->var_type = var_type; variable_entry->parent_scope = parent_scope; variable_entry->shadowable = false; variable_entry->mem_slot_index = SIZE_MAX; @@ -3597,23 +3602,23 @@ ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf assert(name); buf_init_from_buf(&variable_entry->name, name); - if ((err = type_resolve(g, value->type, ResolveStatusAlignmentKnown))) { - variable_entry->value->type = g->builtin_types.entry_invalid; + if ((err = type_resolve(g, var_type, ResolveStatusAlignmentKnown))) { + variable_entry->var_type = g->builtin_types.entry_invalid; } else { - variable_entry->align_bytes = get_abi_alignment(g, value->type); + variable_entry->align_bytes = get_abi_alignment(g, var_type); ZigVar *existing_var = find_variable(g, parent_scope, name, nullptr); if (existing_var && !existing_var->shadowable) { ErrorMsg *msg = add_node_error(g, source_node, buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); add_error_note(g, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); - variable_entry->value->type = g->builtin_types.entry_invalid; + variable_entry->var_type = g->builtin_types.entry_invalid; } else { ZigType *type; if (get_primitive_type(g, name, &type) != ErrorPrimitiveTypeNotFound) { add_node_error(g, source_node, buf_sprintf("variable shadows primitive type '%s'", buf_ptr(name))); - variable_entry->value->type = g->builtin_types.entry_invalid; + variable_entry->var_type = g->builtin_types.entry_invalid; } else { Scope *search_scope = nullptr; if (src_tld == nullptr) { @@ -3627,7 +3632,7 @@ ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf ErrorMsg *msg = add_node_error(g, source_node, buf_sprintf("redefinition of '%s'", buf_ptr(name))); add_error_note(g, msg, tld->source_node, buf_sprintf("previous definition is here")); - variable_entry->value->type = g->builtin_types.entry_invalid; + variable_entry->var_type = g->builtin_types.entry_invalid; } } } @@ -3677,7 +3682,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { linkage = VarLinkageInternal; } - IrInstruction *init_value = nullptr; + ConstExprValue *init_value = nullptr; // TODO more validation for types that can't be used for export/extern variables ZigType *implicit_type = nullptr; @@ -3686,7 +3691,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { } else if (var_decl->expr) { init_value = analyze_const_value(g, tld_var->base.parent_scope, var_decl->expr, explicit_type, var_decl->symbol); assert(init_value); - implicit_type = init_value->value.type; + implicit_type = init_value->type; if (implicit_type->id == ZigTypeIdUnreachable) { add_node_error(g, source_node, buf_sprintf("variable initialization is unreachable")); @@ -3704,7 +3709,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { add_node_error(g, source_node, buf_sprintf("variable of type 'type' must be constant")); implicit_type = g->builtin_types.entry_invalid; } - assert(implicit_type->id == ZigTypeIdInvalid || init_value->value.special != ConstValSpecialRuntime); + assert(implicit_type->id == ZigTypeIdInvalid || init_value->special != ConstValSpecialRuntime); } else if (linkage != VarLinkageExternal) { add_node_error(g, source_node, buf_sprintf("variables must be initialized")); implicit_type = g->builtin_types.entry_invalid; @@ -3713,19 +3718,19 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { ZigType *type = explicit_type ? explicit_type : implicit_type; assert(type != nullptr); // should have been caught by the parser - ConstExprValue *init_val = init_value ? &init_value->value : create_const_runtime(type); + ConstExprValue *init_val = (init_value != nullptr) ? init_value : create_const_runtime(type); tld_var->var = add_variable(g, source_node, tld_var->base.parent_scope, var_decl->symbol, - is_const, init_val, &tld_var->base); + is_const, init_val, &tld_var->base, type); tld_var->var->linkage = linkage; if (implicit_type != nullptr && type_is_invalid(implicit_type)) { - tld_var->var->value->type = g->builtin_types.entry_invalid; + tld_var->var->var_type = g->builtin_types.entry_invalid; } if (var_decl->align_expr != nullptr) { if (!analyze_const_align(g, tld_var->base.parent_scope, var_decl->align_expr, &tld_var->var->align_bytes)) { - tld_var->var->value->type = g->builtin_types.entry_invalid; + tld_var->var->var_type = g->builtin_types.entry_invalid; } } @@ -4090,7 +4095,7 @@ static void define_local_param_variables(CodeGen *g, ZigFn *fn_table_entry) { } ZigVar *var = add_variable(g, param_decl_node, fn_table_entry->child_scope, - param_name, true, create_const_runtime(param_type), nullptr); + param_name, true, create_const_runtime(param_type), nullptr, param_type); var->src_arg_index = i; fn_table_entry->child_scope = var->child_scope; var->shadowable = var->shadowable || is_var_args; @@ -4228,18 +4233,17 @@ static void add_symbols_from_import(CodeGen *g, AstNode *src_use_node, AstNode * preview_use_decl(g, src_use_node); } - IrInstruction *use_target_value = src_use_node->data.use.value; - if (use_target_value->value.type->id == ZigTypeIdInvalid) { + ConstExprValue *use_target_value = src_use_node->data.use.value; + if (type_is_invalid(use_target_value->type)) { dst_use_node->owner->any_imports_failed = true; return; } dst_use_node->data.use.resolution = TldResolutionOk; - ConstExprValue *const_val = &use_target_value->value; - assert(const_val->special != ConstValSpecialRuntime); + assert(use_target_value->special != ConstValSpecialRuntime); - ImportTableEntry *target_import = const_val->data.x_import; + ImportTableEntry *target_import = use_target_value->data.x_import; assert(target_import); if (target_import->any_imports_failed) { @@ -4302,10 +4306,10 @@ void preview_use_decl(CodeGen *g, AstNode *node) { } node->data.use.resolution = TldResolutionResolving; - IrInstruction *result = analyze_const_value(g, &node->owner->decls_scope->base, + ConstExprValue *result = analyze_const_value(g, &node->owner->decls_scope->base, node->data.use.expr, g->builtin_types.entry_namespace, nullptr); - if (result->value.type->id == ZigTypeIdInvalid) + if (type_is_invalid(result->type)) node->owner->any_imports_failed = true; node->data.use.value = result; @@ -4486,7 +4490,8 @@ bool handle_is_ptr(ZigType *type_entry) { return type_has_bits(type_entry->data.error_union.payload_type); case ZigTypeIdOptional: return type_has_bits(type_entry->data.maybe.child_type) && - !type_is_codegen_pointer(type_entry->data.maybe.child_type); + !type_is_codegen_pointer(type_entry->data.maybe.child_type) && + type_entry->data.maybe.child_type->id != ZigTypeIdErrorSet; case ZigTypeIdUnion: assert(type_entry->data.unionation.zero_bits_known); if (type_entry->data.unionation.gen_field_count == 0) @@ -4732,6 +4737,11 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) { return true; } +static uint32_t hash_const_val_error_set(ConstExprValue *const_val) { + assert(const_val->data.x_err_set != nullptr); + return const_val->data.x_err_set->value ^ 2630160122; +} + static uint32_t hash_const_val_ptr(ConstExprValue *const_val) { uint32_t hash_val = 0; switch (const_val->data.x_ptr.mut) { @@ -4763,6 +4773,18 @@ static uint32_t hash_const_val_ptr(ConstExprValue *const_val) { hash_val += hash_ptr(const_val->data.x_ptr.data.base_struct.struct_val); hash_val += hash_size(const_val->data.x_ptr.data.base_struct.field_index); return hash_val; + case ConstPtrSpecialBaseErrorUnionCode: + hash_val += (uint32_t)2994743799; + hash_val += hash_ptr(const_val->data.x_ptr.data.base_err_union_code.err_union_val); + return hash_val; + case ConstPtrSpecialBaseErrorUnionPayload: + hash_val += (uint32_t)3456080131; + hash_val += hash_ptr(const_val->data.x_ptr.data.base_err_union_payload.err_union_val); + return hash_val; + case ConstPtrSpecialBaseOptionalPayload: + hash_val += (uint32_t)3163140517; + hash_val += hash_ptr(const_val->data.x_ptr.data.base_optional_payload.optional_val); + return hash_val; case ConstPtrSpecialHardCodedAddr: hash_val += (uint32_t)4048518294; hash_val += hash_size(const_val->data.x_ptr.data.hard_coded_addr.addr); @@ -4774,6 +4796,9 @@ static uint32_t hash_const_val_ptr(ConstExprValue *const_val) { hash_val += (uint32_t)2590901619; hash_val += hash_ptr(const_val->data.x_ptr.data.fn.fn_entry); return hash_val; + case ConstPtrSpecialNull: + hash_val += (uint32_t)1486246455; + return hash_val; } zig_unreachable(); } @@ -4872,7 +4897,9 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { return 2709806591; case ZigTypeIdOptional: if (get_codegen_ptr_type(const_val->type) != nullptr) { - return hash_const_val(const_val) * 1992916303; + return hash_const_val_ptr(const_val) * 1992916303; + } else if (const_val->type->data.maybe.child_type->id == ZigTypeIdErrorSet) { + return hash_const_val_error_set(const_val) * 3147031929; } else { if (const_val->data.x_optional) { return hash_const_val(const_val->data.x_optional) * 1992916303; @@ -4884,8 +4911,7 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { // TODO better hashing algorithm return 3415065496; case ZigTypeIdErrorSet: - assert(const_val->data.x_err_set != nullptr); - return const_val->data.x_err_set->value ^ 2630160122; + return hash_const_val_error_set(const_val); case ZigTypeIdNamespace: return hash_ptr(const_val->data.x_import); case ZigTypeIdBoundFn: @@ -4987,7 +5013,7 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) { return can_mutate_comptime_var_state(value->data.x_optional); case ZigTypeIdErrorUnion: - if (value->data.x_err_union.err != nullptr) + if (value->data.x_err_union.error_set->data.x_err_set != nullptr) return false; assert(value->data.x_err_union.payload != nullptr); return can_mutate_comptime_var_state(value->data.x_err_union.payload); @@ -5048,9 +5074,9 @@ bool fn_eval_cacheable(Scope *scope, ZigType *return_type) { while (scope) { if (scope->id == ScopeIdVarDecl) { ScopeVarDecl *var_scope = (ScopeVarDecl *)scope; - if (type_is_invalid(var_scope->var->value->type)) + if (type_is_invalid(var_scope->var->var_type)) return false; - if (can_mutate_comptime_var_state(var_scope->var->value)) + if (can_mutate_comptime_var_state(var_scope->var->const_value)) return false; } else if (scope->id == ScopeIdFnDef) { return true; @@ -5068,7 +5094,7 @@ uint32_t fn_eval_hash(Scope* scope) { while (scope) { if (scope->id == ScopeIdVarDecl) { ScopeVarDecl *var_scope = (ScopeVarDecl *)scope; - result += hash_const_val(var_scope->var->value); + result += hash_const_val(var_scope->var->const_value); } else if (scope->id == ScopeIdFnDef) { ScopeFnDef *fn_scope = (ScopeFnDef *)scope; result += hash_ptr(fn_scope->fn_entry); @@ -5092,10 +5118,16 @@ bool fn_eval_eql(Scope *a, Scope *b) { if (a->id == ScopeIdVarDecl) { ScopeVarDecl *a_var_scope = (ScopeVarDecl *)a; ScopeVarDecl *b_var_scope = (ScopeVarDecl *)b; - if (a_var_scope->var->value->type != b_var_scope->var->value->type) - return false; - if (!const_values_equal(a->codegen, a_var_scope->var->value, b_var_scope->var->value)) + if (a_var_scope->var->var_type != b_var_scope->var->var_type) return false; + if (a_var_scope->var->var_type == a_var_scope->var->const_value->type && + b_var_scope->var->var_type == b_var_scope->var->const_value->type) + { + if (!const_values_equal(a->codegen, a_var_scope->var->const_value, b_var_scope->var->const_value)) + return false; + } else { + zig_panic("TODO comptime ptr reinterpret for fn_eval_eql"); + } } else if (a->id == ScopeIdFnDef) { ScopeFnDef *a_fn_scope = (ScopeFnDef *)a; ScopeFnDef *b_fn_scope = (ScopeFnDef *)b; @@ -5113,6 +5145,7 @@ bool fn_eval_eql(Scope *a, Scope *b) { return false; } +// Whether the type has bits at runtime. bool type_has_bits(ZigType *type_entry) { assert(type_entry); assert(!type_is_invalid(type_entry)); @@ -5120,6 +5153,65 @@ bool type_has_bits(ZigType *type_entry) { return !type_entry->zero_bits; } +// Whether you can infer the value based solely on the type. +OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) { + assert(type_entry != nullptr); + Error err; + if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown))) + return OnePossibleValueInvalid; + switch (type_entry->id) { + case ZigTypeIdInvalid: + zig_unreachable(); + case ZigTypeIdOpaque: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdMetaType: + case ZigTypeIdNamespace: + case ZigTypeIdBoundFn: + case ZigTypeIdArgTuple: + case ZigTypeIdOptional: + case ZigTypeIdFn: + case ZigTypeIdBool: + case ZigTypeIdFloat: + case ZigTypeIdPromise: + case ZigTypeIdErrorUnion: + return OnePossibleValueNo; + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdVoid: + case ZigTypeIdUnreachable: + return OnePossibleValueYes; + case ZigTypeIdArray: + if (type_entry->data.array.len == 0) + return OnePossibleValueYes; + return type_has_one_possible_value(g, type_entry->data.array.child_type); + case ZigTypeIdStruct: + for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) { + TypeStructField *field = &type_entry->data.structure.fields[i]; + switch (type_has_one_possible_value(g, field->type_entry)) { + case OnePossibleValueInvalid: + return OnePossibleValueInvalid; + case OnePossibleValueNo: + return OnePossibleValueNo; + case OnePossibleValueYes: + continue; + } + } + return OnePossibleValueYes; + case ZigTypeIdErrorSet: + case ZigTypeIdEnum: + case ZigTypeIdInt: + return type_has_bits(type_entry) ? OnePossibleValueNo : OnePossibleValueYes; + case ZigTypeIdPointer: + return type_has_one_possible_value(g, type_entry->data.pointer.child_type); + case ZigTypeIdUnion: + if (type_entry->data.unionation.src_field_count > 1) + return OnePossibleValueNo; + return type_has_one_possible_value(g, type_entry->data.unionation.fields[0].type_entry); + } + zig_unreachable(); +} + ReqCompTime type_requires_comptime(CodeGen *g, ZigType *type_entry) { Error err; if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown))) @@ -5574,6 +5666,33 @@ bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) { if (a->data.x_ptr.data.base_struct.field_index != b->data.x_ptr.data.base_struct.field_index) return false; return true; + case ConstPtrSpecialBaseErrorUnionCode: + if (a->data.x_ptr.data.base_err_union_code.err_union_val != + b->data.x_ptr.data.base_err_union_code.err_union_val && + a->data.x_ptr.data.base_err_union_code.err_union_val->global_refs != + b->data.x_ptr.data.base_err_union_code.err_union_val->global_refs) + { + return false; + } + return true; + case ConstPtrSpecialBaseErrorUnionPayload: + if (a->data.x_ptr.data.base_err_union_payload.err_union_val != + b->data.x_ptr.data.base_err_union_payload.err_union_val && + a->data.x_ptr.data.base_err_union_payload.err_union_val->global_refs != + b->data.x_ptr.data.base_err_union_payload.err_union_val->global_refs) + { + return false; + } + return true; + case ConstPtrSpecialBaseOptionalPayload: + if (a->data.x_ptr.data.base_optional_payload.optional_val != + b->data.x_ptr.data.base_optional_payload.optional_val && + a->data.x_ptr.data.base_optional_payload.optional_val->global_refs != + b->data.x_ptr.data.base_optional_payload.optional_val->global_refs) + { + return false; + } + return true; case ConstPtrSpecialHardCodedAddr: if (a->data.x_ptr.data.hard_coded_addr.addr != b->data.x_ptr.data.hard_coded_addr.addr) return false; @@ -5582,6 +5701,8 @@ bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) { return true; case ConstPtrSpecialFunction: return a->data.x_ptr.data.fn.fn_entry == b->data.x_ptr.data.fn.fn_entry; + case ConstPtrSpecialNull: + return true; } zig_unreachable(); } @@ -5750,7 +5871,7 @@ void eval_min_max_value(CodeGen *g, ZigType *type_entry, ConstExprValue *const_v } } -void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigType *type_entry) { +static void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigType *type_entry) { assert(type_entry->id == ZigTypeIdPointer); if (type_entry->data.pointer.child_type->id == ZigTypeIdOpaque) { @@ -5763,6 +5884,9 @@ void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigTy zig_unreachable(); case ConstPtrSpecialRef: case ConstPtrSpecialBaseStruct: + case ConstPtrSpecialBaseErrorUnionCode: + case ConstPtrSpecialBaseErrorUnionPayload: + case ConstPtrSpecialBaseOptionalPayload: buf_appendf(buf, "*"); // TODO we need a source node for const_ptr_pointee because it can generate compile errors render_const_value(g, buf, const_ptr_pointee(nullptr, g, const_val, nullptr)); @@ -5790,10 +5914,21 @@ void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigTy buf_appendf(buf, "@ptrCast(%s, %s)", buf_ptr(&const_val->type->name), buf_ptr(&fn_entry->symbol_name)); return; } + case ConstPtrSpecialNull: + buf_append_str(buf, "null"); + return; } zig_unreachable(); } +static void render_const_val_err_set(CodeGen *g, Buf *buf, ConstExprValue *const_val, ZigType *type_entry) { + if (const_val->data.x_err_set == nullptr) { + buf_append_str(buf, "null"); + } else { + buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(&const_val->data.x_err_set->name)); + } +} + void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { switch (const_val->special) { case ConstValSpecialRuntime: @@ -5921,6 +6056,8 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { { if (get_codegen_ptr_type(const_val->type) != nullptr) return render_const_val_ptr(g, buf, const_val, type_entry->data.maybe.child_type); + if (type_entry->data.maybe.child_type->id == ZigTypeIdErrorSet) + return render_const_val_err_set(g, buf, const_val, type_entry->data.maybe.child_type); if (const_val->data.x_optional) { render_const_value(g, buf, const_val->data.x_optional); } else { @@ -5958,11 +6095,12 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { case ZigTypeIdErrorUnion: { buf_appendf(buf, "%s(", buf_ptr(&type_entry->name)); - if (const_val->data.x_err_union.err == nullptr) { + ErrorTableEntry *err_set = const_val->data.x_err_union.error_set->data.x_err_set; + if (err_set == nullptr) { render_const_value(g, buf, const_val->data.x_err_union.payload); } else { buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->data.error_union.err_set_type->name), - buf_ptr(&const_val->data.x_err_union.err->name)); + buf_ptr(&err_set->name)); } buf_appendf(buf, ")"); return; @@ -5977,10 +6115,7 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { return; } case ZigTypeIdErrorSet: - { - buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(&const_val->data.x_err_set->name)); - return; - } + return render_const_val_err_set(g, buf, const_val, type_entry); case ZigTypeIdArgTuple: { buf_appendf(buf, "(args value)"); @@ -6172,6 +6307,10 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) { // Canonicalize the array value as ConstArraySpecialNone void expand_undef_array(CodeGen *g, ConstExprValue *const_val) { assert(const_val->type->id == ZigTypeIdArray); + if (const_val->special == ConstValSpecialUndef) { + const_val->special = ConstValSpecialStatic; + const_val->data.x_array.special = ConstArraySpecialUndef; + } switch (const_val->data.x_array.special) { case ConstArraySpecialNone: return; @@ -6215,17 +6354,7 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) { } ConstParent *get_const_val_parent(CodeGen *g, ConstExprValue *value) { - assert(value->type); - ZigType *type_entry = value->type; - if (type_entry->id == ZigTypeIdArray) { - expand_undef_array(g, value); - return &value->data.x_array.data.s_none.parent; - } else if (type_entry->id == ZigTypeIdStruct) { - return &value->data.x_struct.parent; - } else if (type_entry->id == ZigTypeIdUnion) { - return &value->data.x_union.parent; - } - return nullptr; + return &value->parent; } static const ZigTypeId all_type_ids[] = { @@ -6453,7 +6582,7 @@ ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) { resolve_top_level_decl(codegen, tld, false, nullptr); assert(tld->id == TldIdVar); TldVar *tld_var = (TldVar *)tld; - ConstExprValue *var_value = tld_var->var->value; + ConstExprValue *var_value = tld_var->var->const_value; assert(var_value != nullptr); return var_value; } diff --git a/src/analyze.hpp b/src/analyze.hpp index efbc065a633aac92738d6b124ec04b72e08847f5..1bac15ebcce7f60cb360448adcf44e2f303e62f3 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -81,7 +81,7 @@ ZigFn *scope_fn_entry(Scope *scope); ImportTableEntry *get_scope_import(Scope *scope); void init_tld(Tld *tld, TldId id, Buf *name, VisibMod visib_mod, AstNode *source_node, Scope *parent_scope); ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf *name, - bool is_const, ConstExprValue *init_value, Tld *src_tld); + bool is_const, ConstExprValue *init_value, Tld *src_tld, ZigType *var_type); ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node); ZigFn *create_fn(CodeGen *g, AstNode *proto_node); ZigFn *create_fn_raw(CodeGen *g, FnInline inline_value); @@ -222,6 +222,13 @@ enum ReqCompTime { }; ReqCompTime type_requires_comptime(CodeGen *g, ZigType *type_entry); +enum OnePossibleValue { + OnePossibleValueInvalid, + OnePossibleValueNo, + OnePossibleValueYes, +}; +OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry); + Error ensure_const_val_repr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, ConstExprValue *const_val, ZigType *wanted_type); diff --git a/src/ast_render.cpp b/src/ast_render.cpp index f7eb646e16d7111d1899d0fd7a046cfdefebd535..994ba5f5b139fe32e884a69f7837784c3dddc5a5 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -233,8 +233,8 @@ static const char *node_type_str(NodeType node_type) { return "ErrorType"; case NodeTypeIfErrorExpr: return "IfErrorExpr"; - case NodeTypeTestExpr: - return "TestExpr"; + case NodeTypeIfOptional: + return "IfOptional"; case NodeTypeErrorSetDecl: return "ErrorSetDecl"; case NodeTypeCancel: @@ -387,7 +387,7 @@ static bool statement_terminates_without_semicolon(AstNode *node) { if (node->data.if_err_expr.else_node) return statement_terminates_without_semicolon(node->data.if_err_expr.else_node); return node->data.if_err_expr.then_node->type == NodeTypeBlock; - case NodeTypeTestExpr: + case NodeTypeIfOptional: if (node->data.test_expr.else_node) return statement_terminates_without_semicolon(node->data.test_expr.else_node); return node->data.test_expr.then_node->type == NodeTypeBlock; @@ -974,7 +974,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { } break; } - case NodeTypeTestExpr: + case NodeTypeIfOptional: { fprintf(ar->f, "if ("); render_node_grouped(ar, node->data.test_expr.target_node); diff --git a/src/codegen.cpp b/src/codegen.cpp index 47f2aa103fdec2d3cd9a641f8f16833bda795941..2f360735dd0630f1a542129193d52c28c909c2de 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -313,6 +313,8 @@ static void render_const_val(CodeGen *g, ConstExprValue *const_val, const char * static void render_const_val_global(CodeGen *g, ConstExprValue *const_val, const char *name); static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name); static void generate_error_name_table(CodeGen *g); +static bool value_is_all_undef(ConstExprValue *const_val); +static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_type, LLVMValueRef ptr); static void addLLVMAttr(LLVMValueRef val, LLVMAttributeIndex attr_index, const char *attr_name) { unsigned kind_id = LLVMGetEnumAttributeKindForName(attr_name, strlen(attr_name)); @@ -461,6 +463,21 @@ static void maybe_import_dll(CodeGen *g, LLVMValueRef global_value, GlobalLinkag } } +static bool cc_want_sret_attr(CallingConvention cc) { + switch (cc) { + case CallingConventionNaked: + zig_unreachable(); + case CallingConventionC: + case CallingConventionCold: + case CallingConventionStdcall: + return true; + case CallingConventionAsync: + case CallingConventionUnspecified: + return false; + } + zig_unreachable(); +} + static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) { if (fn_table_entry->llvm_value) return fn_table_entry->llvm_value; @@ -598,9 +615,9 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) { } else if (type_is_codegen_pointer(return_type)) { addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull"); } else if (want_first_arg_sret(g, &fn_type->data.fn.fn_type_id)) { - addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret"); addLLVMArgAttr(fn_table_entry->llvm_value, 0, "nonnull"); - if (cc == CallingConventionC) { + addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret"); + if (cc_want_sret_attr(cc)) { addLLVMArgAttr(fn_table_entry->llvm_value, 0, "noalias"); } init_gen_i = 1; @@ -2200,10 +2217,10 @@ void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk) { assert(variable); assert(variable->value_ref); - if (!handle_is_ptr(variable->value->type)) { + if (!handle_is_ptr(variable->var_type)) { clear_debug_source_node(g); - gen_store_untyped(g, LLVMGetParam(llvm_fn, (unsigned)variable->gen_arg_index), variable->value_ref, - variable->align_bytes, false); + gen_store_untyped(g, LLVMGetParam(llvm_fn, (unsigned)variable->gen_arg_index), + variable->value_ref, variable->align_bytes, false); } if (variable->decl_node) { @@ -2961,7 +2978,7 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, } static LLVMValueRef ir_render_ptr_cast(CodeGen *g, IrExecutable *executable, - IrInstructionPtrCast *instruction) + IrInstructionPtrCastGen *instruction) { ZigType *wanted_type = instruction->base.value.type; if (!type_has_bits(wanted_type)) { @@ -3149,11 +3166,11 @@ static LLVMValueRef ir_render_bool_not(CodeGen *g, IrExecutable *executable, IrI } static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, - IrInstructionDeclVar *decl_var_instruction) + IrInstructionDeclVarGen *decl_var_instruction) { ZigVar *var = decl_var_instruction->var; - if (!type_has_bits(var->value->type)) + if (!type_has_bits(var->var_type)) return nullptr; if (var->ref_count == 0 && g->build_mode != BuildModeDebug) @@ -3161,34 +3178,16 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, IrInstruction *init_value = decl_var_instruction->init_value; - bool have_init_expr = false; - - ConstExprValue *const_val = &init_value->value; - if (const_val->special == ConstValSpecialRuntime || const_val->special == ConstValSpecialStatic) - have_init_expr = true; + bool have_init_expr = !value_is_all_undef(&init_value->value); if (have_init_expr) { - assert(var->value->type == init_value->value.type); - ZigType *var_ptr_type = get_pointer_to_type_extra(g, var->value->type, false, false, + ZigType *var_ptr_type = get_pointer_to_type_extra(g, var->var_type, false, false, PtrLenSingle, var->align_bytes, 0, 0); LLVMValueRef llvm_init_val = ir_llvm_value(g, init_value); gen_assign_raw(g, var->value_ref, var_ptr_type, llvm_init_val); - } else { - bool want_safe = ir_want_runtime_safety(g, &decl_var_instruction->base); - if (want_safe) { - ZigType *usize = g->builtin_types.entry_usize; - uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, var->value->type->type_ref); - assert(size_bytes > 0); - - assert(var->align_bytes > 0); - - // memset uninitialized memory to 0xa - LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); - LLVMValueRef fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false); - LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, var->value_ref, ptr_u8, ""); - LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_bytes, false); - ZigLLVMBuildMemSet(g->builder, dest_ptr, fill_char, byte_count, var->align_bytes, false); - } + } else if (ir_want_runtime_safety(g, &decl_var_instruction->base)) { + uint32_t align_bytes = (var->align_bytes == 0) ? get_abi_alignment(g, var->var_type) : var->align_bytes; + gen_undef_init(g, align_bytes, var->var_type, var->value_ref); } gen_var_debug_decl(g, var); @@ -3225,21 +3224,75 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI return LLVMBuildTrunc(g->builder, shifted_value, child_type->type_ref, ""); } +static bool value_is_all_undef(ConstExprValue *const_val) { + switch (const_val->special) { + case ConstValSpecialRuntime: + return false; + case ConstValSpecialUndef: + return true; + case ConstValSpecialStatic: + if (const_val->type->id == ZigTypeIdStruct) { + for (size_t i = 0; i < const_val->type->data.structure.src_field_count; i += 1) { + if (!value_is_all_undef(&const_val->data.x_struct.fields[i])) + return false; + } + return true; + } else if (const_val->type->id == ZigTypeIdArray) { + switch (const_val->data.x_array.special) { + case ConstArraySpecialUndef: + return true; + case ConstArraySpecialBuf: + return false; + case ConstArraySpecialNone: + for (size_t i = 0; i < const_val->type->data.array.len; i += 1) { + if (!value_is_all_undef(&const_val->data.x_array.data.s_none.elements[i])) + return false; + } + return true; + } + zig_unreachable(); + } else { + return false; + } + } + zig_unreachable(); +} + +static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_type, LLVMValueRef ptr) { + assert(type_has_bits(value_type)); + uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, value_type->type_ref); + assert(size_bytes > 0); + assert(ptr_align_bytes > 0); + // memset uninitialized memory to 0xaa + LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); + LLVMValueRef fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false); + LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, ptr, ptr_u8, ""); + ZigType *usize = g->builtin_types.entry_usize; + LLVMValueRef byte_count = LLVMConstInt(usize->type_ref, size_bytes, false); + ZigLLVMBuildMemSet(g->builder, dest_ptr, fill_char, byte_count, ptr_align_bytes, false); +} + static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, IrInstructionStorePtr *instruction) { - LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); - LLVMValueRef value = ir_llvm_value(g, instruction->value); - - assert(instruction->ptr->value.type->id == ZigTypeIdPointer); ZigType *ptr_type = instruction->ptr->value.type; + assert(ptr_type->id == ZigTypeIdPointer); + if (!type_has_bits(ptr_type)) + return nullptr; - gen_assign_raw(g, ptr, ptr_type, value); - + bool have_init_expr = !value_is_all_undef(&instruction->value->value); + if (have_init_expr) { + LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); + LLVMValueRef value = ir_llvm_value(g, instruction->value); + gen_assign_raw(g, ptr, ptr_type, value); + } else if (ir_want_runtime_safety(g, &instruction->base)) { + gen_undef_init(g, get_ptr_align(g, ptr_type), instruction->value->value.type, + ir_llvm_value(g, instruction->ptr)); + } return nullptr; } static LLVMValueRef ir_render_var_ptr(CodeGen *g, IrExecutable *executable, IrInstructionVarPtr *instruction) { ZigVar *var = instruction->var; - if (type_has_bits(var->value->type)) { + if (type_has_bits(var->var_type)) { assert(var->value_ref); return var->value_ref; } else { @@ -3553,7 +3606,8 @@ static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutable *executab LLVMPositionBuilderAtEnd(g->builder, ok_block); } - LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, union_type->data.unionation.gen_union_index, ""); + LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, + union_type->data.unionation.gen_union_index, ""); LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, field_type_ref, ""); return bitcasted_union_field_ptr; } @@ -3715,8 +3769,8 @@ static LLVMValueRef gen_non_null_bit(CodeGen *g, ZigType *maybe_type, LLVMValueR if (child_type->zero_bits) { return maybe_handle; } else { - bool maybe_is_ptr = type_is_codegen_pointer(child_type); - if (maybe_is_ptr) { + bool is_scalar = type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet; + if (is_scalar) { return LLVMBuildICmp(g->builder, LLVMIntNE, maybe_handle, LLVMConstNull(maybe_type->type_ref), ""); } else { LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_handle, maybe_null_index, ""); @@ -3731,17 +3785,17 @@ static LLVMValueRef ir_render_test_non_null(CodeGen *g, IrExecutable *executable return gen_non_null_bit(g, instruction->value->value.type, ir_llvm_value(g, instruction->value)); } -static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable, - IrInstructionUnwrapOptional *instruction) +static LLVMValueRef ir_render_optional_unwrap_ptr(CodeGen *g, IrExecutable *executable, + IrInstructionOptionalUnwrapPtr *instruction) { - ZigType *ptr_type = instruction->value->value.type; + ZigType *ptr_type = instruction->base_ptr->value.type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *maybe_type = ptr_type->data.pointer.child_type; assert(maybe_type->id == ZigTypeIdOptional); ZigType *child_type = maybe_type->data.maybe.child_type; - LLVMValueRef maybe_ptr = ir_llvm_value(g, instruction->value); - LLVMValueRef maybe_handle = get_handle_value(g, maybe_ptr, maybe_type, ptr_type); + LLVMValueRef maybe_ptr = ir_llvm_value(g, instruction->base_ptr); if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on) { + LLVMValueRef maybe_handle = get_handle_value(g, maybe_ptr, maybe_type, ptr_type); LLVMValueRef non_null_bit = gen_non_null_bit(g, maybe_type, maybe_handle); LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalOk"); LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalFail"); @@ -3755,8 +3809,8 @@ static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable, if (child_type->zero_bits) { return nullptr; } else { - bool maybe_is_ptr = type_is_codegen_pointer(child_type); - if (maybe_is_ptr) { + bool is_scalar = type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet; + if (is_scalar) { return maybe_ptr; } else { LLVMValueRef maybe_struct_ref = get_handle_value(g, maybe_ptr, maybe_type, ptr_type); @@ -4174,7 +4228,7 @@ static LLVMAtomicRMWBinOp to_LLVMAtomicRMWBinOp(AtomicRmwOp op, bool is_signed) zig_unreachable(); } -static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrInstructionCmpxchg *instruction) { +static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrInstructionCmpxchgGen *instruction) { LLVMValueRef ptr_val = ir_llvm_value(g, instruction->ptr); LLVMValueRef cmp_val = ir_llvm_value(g, instruction->cmp_value); LLVMValueRef new_val = ir_llvm_value(g, instruction->new_value); @@ -4189,18 +4243,18 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn assert(maybe_type->id == ZigTypeIdOptional); ZigType *child_type = maybe_type->data.maybe.child_type; - if (type_is_codegen_pointer(child_type)) { + if (!handle_is_ptr(maybe_type)) { LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, ""); LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, ""); return LLVMBuildSelect(g->builder, success_bit, LLVMConstNull(child_type->type_ref), payload_val, ""); } assert(instruction->tmp_ptr != nullptr); - assert(type_has_bits(instruction->type)); + assert(type_has_bits(child_type)); LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, ""); LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_child_index, ""); - gen_assign_raw(g, val_ptr, get_pointer_to_type(g, instruction->type, false), payload_val); + gen_assign_raw(g, val_ptr, get_pointer_to_type(g, child_type, false), payload_val); LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, ""); LLVMValueRef nonnull_bit = LLVMBuildNot(g->builder, success_bit, ""); @@ -4351,6 +4405,7 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst assert(array_type->data.structure.is_slice); assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind); + assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(tmp_struct_ptr))) == LLVMStructTypeKind); size_t ptr_index = array_type->data.structure.fields[slice_ptr_index].gen_index; assert(ptr_index != SIZE_MAX); @@ -4540,12 +4595,14 @@ static LLVMValueRef ir_render_test_err(CodeGen *g, IrExecutable *executable, IrI return LLVMBuildICmp(g->builder, LLVMIntNE, err_val, zero, ""); } -static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executable, IrInstructionUnwrapErrCode *instruction) { - ZigType *ptr_type = instruction->value->value.type; +static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executable, + IrInstructionUnwrapErrCode *instruction) +{ + ZigType *ptr_type = instruction->err_union->value.type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *err_union_type = ptr_type->data.pointer.child_type; ZigType *payload_type = err_union_type->data.error_union.payload_type; - LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value); + LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->err_union); LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, ptr_type); if (type_has_bits(payload_type)) { @@ -4556,7 +4613,13 @@ static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executab } } -static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *executable, IrInstructionUnwrapErrPayload *instruction) { +static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *executable, + IrInstructionUnwrapErrPayload *instruction) +{ + bool want_safety = ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on && + g->errors_by_index.length > 1; + if (!want_safety && !type_has_bits(instruction->base.value.type)) + return nullptr; ZigType *ptr_type = instruction->value->value.type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *err_union_type = ptr_type->data.pointer.child_type; @@ -4568,7 +4631,7 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu return err_union_handle; } - if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on && g->errors_by_index.length > 1) { + if (want_safety) { LLVMValueRef err_val; if (type_has_bits(payload_type)) { LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, ""); @@ -4607,7 +4670,7 @@ static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, I } LLVMValueRef payload_val = ir_llvm_value(g, instruction->value); - if (type_is_codegen_pointer(child_type)) { + if (type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet) { return payload_val; } @@ -5184,12 +5247,15 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdToBytes: case IrInstructionIdEnumToInt: case IrInstructionIdCheckRuntimeScope: + case IrInstructionIdDeclVarSrc: + case IrInstructionIdPtrCastSrc: + case IrInstructionIdCmpxchgSrc: zig_unreachable(); + case IrInstructionIdDeclVarGen: + return ir_render_decl_var(g, executable, (IrInstructionDeclVarGen *)instruction); case IrInstructionIdReturn: return ir_render_return(g, executable, (IrInstructionReturn *)instruction); - case IrInstructionIdDeclVar: - return ir_render_decl_var(g, executable, (IrInstructionDeclVar *)instruction); case IrInstructionIdBinOp: return ir_render_bin_op(g, executable, (IrInstructionBinOp *)instruction); case IrInstructionIdCast: @@ -5220,8 +5286,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_asm(g, executable, (IrInstructionAsm *)instruction); case IrInstructionIdTestNonNull: return ir_render_test_non_null(g, executable, (IrInstructionTestNonNull *)instruction); - case IrInstructionIdUnwrapOptional: - return ir_render_unwrap_maybe(g, executable, (IrInstructionUnwrapOptional *)instruction); + case IrInstructionIdOptionalUnwrapPtr: + return ir_render_optional_unwrap_ptr(g, executable, (IrInstructionOptionalUnwrapPtr *)instruction); case IrInstructionIdClz: return ir_render_clz(g, executable, (IrInstructionClz *)instruction); case IrInstructionIdCtz: @@ -5236,8 +5302,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_ref(g, executable, (IrInstructionRef *)instruction); case IrInstructionIdErrName: return ir_render_err_name(g, executable, (IrInstructionErrName *)instruction); - case IrInstructionIdCmpxchg: - return ir_render_cmpxchg(g, executable, (IrInstructionCmpxchg *)instruction); + case IrInstructionIdCmpxchgGen: + return ir_render_cmpxchg(g, executable, (IrInstructionCmpxchgGen *)instruction); case IrInstructionIdFence: return ir_render_fence(g, executable, (IrInstructionFence *)instruction); case IrInstructionIdTruncate: @@ -5278,8 +5344,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_struct_init(g, executable, (IrInstructionStructInit *)instruction); case IrInstructionIdUnionInit: return ir_render_union_init(g, executable, (IrInstructionUnionInit *)instruction); - case IrInstructionIdPtrCast: - return ir_render_ptr_cast(g, executable, (IrInstructionPtrCast *)instruction); + case IrInstructionIdPtrCastGen: + return ir_render_ptr_cast(g, executable, (IrInstructionPtrCastGen *)instruction); case IrInstructionIdBitCast: return ir_render_bit_cast(g, executable, (IrInstructionBitCast *)instruction); case IrInstructionIdWidenOrShorten: @@ -5377,6 +5443,9 @@ static void ir_render(CodeGen *g, ZigFn *fn_entry) { static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *struct_const_val, size_t field_index); static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *array_const_val, size_t index); static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *union_const_val); +static LLVMValueRef gen_const_ptr_err_union_code_recursive(CodeGen *g, ConstExprValue *err_union_const_val); +static LLVMValueRef gen_const_ptr_err_union_payload_recursive(CodeGen *g, ConstExprValue *err_union_const_val); +static LLVMValueRef gen_const_ptr_optional_payload_recursive(CodeGen *g, ConstExprValue *optional_const_val); static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent *parent) { switch (parent->id) { @@ -5387,6 +5456,12 @@ static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent case ConstParentIdStruct: return gen_const_ptr_struct_recursive(g, parent->data.p_struct.struct_val, parent->data.p_struct.field_index); + case ConstParentIdErrUnionCode: + return gen_const_ptr_err_union_code_recursive(g, parent->data.p_err_union_code.err_union_val); + case ConstParentIdErrUnionPayload: + return gen_const_ptr_err_union_payload_recursive(g, parent->data.p_err_union_payload.err_union_val); + case ConstParentIdOptionalPayload: + return gen_const_ptr_optional_payload_recursive(g, parent->data.p_optional_payload.optional_val); case ConstParentIdArray: return gen_const_ptr_array_recursive(g, parent->data.p_array.array_val, parent->data.p_array.elem_index); @@ -5402,7 +5477,7 @@ static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *array_const_val, size_t index) { expand_undef_array(g, array_const_val); - ConstParent *parent = &array_const_val->data.x_array.data.s_none.parent; + ConstParent *parent = &array_const_val->parent; LLVMValueRef base_ptr = gen_parent_ptr(g, array_const_val, parent); LLVMTypeKind el_type = LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(base_ptr))); @@ -5427,7 +5502,7 @@ static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *ar } static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *struct_const_val, size_t field_index) { - ConstParent *parent = &struct_const_val->data.x_struct.parent; + ConstParent *parent = &struct_const_val->parent; LLVMValueRef base_ptr = gen_parent_ptr(g, struct_const_val, parent); ZigType *u32 = g->builtin_types.entry_u32; @@ -5438,8 +5513,44 @@ static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *s return LLVMConstInBoundsGEP(base_ptr, indices, 2); } +static LLVMValueRef gen_const_ptr_err_union_code_recursive(CodeGen *g, ConstExprValue *err_union_const_val) { + ConstParent *parent = &err_union_const_val->parent; + LLVMValueRef base_ptr = gen_parent_ptr(g, err_union_const_val, parent); + + ZigType *u32 = g->builtin_types.entry_u32; + LLVMValueRef indices[] = { + LLVMConstNull(u32->type_ref), + LLVMConstInt(u32->type_ref, err_union_err_index, false), + }; + return LLVMConstInBoundsGEP(base_ptr, indices, 2); +} + +static LLVMValueRef gen_const_ptr_err_union_payload_recursive(CodeGen *g, ConstExprValue *err_union_const_val) { + ConstParent *parent = &err_union_const_val->parent; + LLVMValueRef base_ptr = gen_parent_ptr(g, err_union_const_val, parent); + + ZigType *u32 = g->builtin_types.entry_u32; + LLVMValueRef indices[] = { + LLVMConstNull(u32->type_ref), + LLVMConstInt(u32->type_ref, err_union_payload_index, false), + }; + return LLVMConstInBoundsGEP(base_ptr, indices, 2); +} + +static LLVMValueRef gen_const_ptr_optional_payload_recursive(CodeGen *g, ConstExprValue *optional_const_val) { + ConstParent *parent = &optional_const_val->parent; + LLVMValueRef base_ptr = gen_parent_ptr(g, optional_const_val, parent); + + ZigType *u32 = g->builtin_types.entry_u32; + LLVMValueRef indices[] = { + LLVMConstNull(u32->type_ref), + LLVMConstInt(u32->type_ref, maybe_child_index, false), + }; + return LLVMConstInBoundsGEP(base_ptr, indices, 2); +} + static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *union_const_val) { - ConstParent *parent = &union_const_val->data.x_union.parent; + ConstParent *parent = &union_const_val->parent; LLVMValueRef base_ptr = gen_parent_ptr(g, union_const_val, parent); ZigType *u32 = g->builtin_types.entry_u32; @@ -5609,6 +5720,63 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con render_const_val_global(g, const_val, ""); return ptr_val; } + case ConstPtrSpecialBaseErrorUnionCode: + { + render_const_val_global(g, const_val, name); + ConstExprValue *err_union_const_val = const_val->data.x_ptr.data.base_err_union_code.err_union_val; + assert(err_union_const_val->type->id == ZigTypeIdErrorUnion); + if (err_union_const_val->type->zero_bits) { + // make this a null pointer + ZigType *usize = g->builtin_types.entry_usize; + const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref), + const_val->type->type_ref); + render_const_val_global(g, const_val, ""); + return const_val->global_refs->llvm_value; + } + LLVMValueRef uncasted_ptr_val = gen_const_ptr_err_union_code_recursive(g, err_union_const_val); + LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref); + const_val->global_refs->llvm_value = ptr_val; + render_const_val_global(g, const_val, ""); + return ptr_val; + } + case ConstPtrSpecialBaseErrorUnionPayload: + { + render_const_val_global(g, const_val, name); + ConstExprValue *err_union_const_val = const_val->data.x_ptr.data.base_err_union_payload.err_union_val; + assert(err_union_const_val->type->id == ZigTypeIdErrorUnion); + if (err_union_const_val->type->zero_bits) { + // make this a null pointer + ZigType *usize = g->builtin_types.entry_usize; + const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref), + const_val->type->type_ref); + render_const_val_global(g, const_val, ""); + return const_val->global_refs->llvm_value; + } + LLVMValueRef uncasted_ptr_val = gen_const_ptr_err_union_payload_recursive(g, err_union_const_val); + LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref); + const_val->global_refs->llvm_value = ptr_val; + render_const_val_global(g, const_val, ""); + return ptr_val; + } + case ConstPtrSpecialBaseOptionalPayload: + { + render_const_val_global(g, const_val, name); + ConstExprValue *optional_const_val = const_val->data.x_ptr.data.base_optional_payload.optional_val; + assert(optional_const_val->type->id == ZigTypeIdOptional); + if (optional_const_val->type->zero_bits) { + // make this a null pointer + ZigType *usize = g->builtin_types.entry_usize; + const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref), + const_val->type->type_ref); + render_const_val_global(g, const_val, ""); + return const_val->global_refs->llvm_value; + } + LLVMValueRef uncasted_ptr_val = gen_const_ptr_optional_payload_recursive(g, optional_const_val); + LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref); + const_val->global_refs->llvm_value = ptr_val; + render_const_val_global(g, const_val, ""); + return ptr_val; + } case ConstPtrSpecialHardCodedAddr: { render_const_val_global(g, const_val, name); @@ -5621,10 +5789,17 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con } case ConstPtrSpecialFunction: return LLVMConstBitCast(fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry), const_val->type->type_ref); + case ConstPtrSpecialNull: + return LLVMConstNull(const_val->type->type_ref); } zig_unreachable(); } +static LLVMValueRef gen_const_val_err_set(CodeGen *g, ConstExprValue *const_val, const char *name) { + uint64_t value = (const_val->data.x_err_set == nullptr) ? 0 : const_val->data.x_err_set->value; + return LLVMConstInt(g->builtin_types.entry_global_error_set->type_ref, value, false); +} + static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name) { Error err; @@ -5644,9 +5819,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c case ZigTypeIdInt: return bigint_to_llvm_const(type_entry->type_ref, &const_val->data.x_bigint); case ZigTypeIdErrorSet: - assert(const_val->data.x_err_set != nullptr); - return LLVMConstInt(g->builtin_types.entry_global_error_set->type_ref, - const_val->data.x_err_set->value, false); + return gen_const_val_err_set(g, const_val, name); case ZigTypeIdFloat: switch (type_entry->data.floating.bit_count) { case 16: @@ -5680,6 +5853,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c return LLVMConstInt(LLVMInt1Type(), const_val->data.x_optional ? 1 : 0, false); } else if (type_is_codegen_pointer(child_type)) { return gen_const_val_ptr(g, const_val, name); + } else if (child_type->id == ZigTypeIdErrorSet) { + return gen_const_val_err_set(g, const_val, name); } else { LLVMValueRef child_val; LLVMValueRef maybe_val; @@ -5914,7 +6089,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c ZigType *err_set_type = type_entry->data.error_union.err_set_type; if (!type_has_bits(payload_type)) { assert(type_has_bits(err_set_type)); - uint64_t value = const_val->data.x_err_union.err ? const_val->data.x_err_union.err->value : 0; + ErrorTableEntry *err_set = const_val->data.x_err_union.error_set->data.x_err_set; + uint64_t value = (err_set == nullptr) ? 0 : err_set->value; return LLVMConstInt(g->err_tag_type->type_ref, value, false); } else if (!type_has_bits(err_set_type)) { assert(type_has_bits(payload_type)); @@ -5923,8 +6099,9 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c LLVMValueRef err_tag_value; LLVMValueRef err_payload_value; bool make_unnamed_struct; - if (const_val->data.x_err_union.err) { - err_tag_value = LLVMConstInt(g->err_tag_type->type_ref, const_val->data.x_err_union.err->value, false); + ErrorTableEntry *err_set = const_val->data.x_err_union.error_set->data.x_err_set; + if (err_set != nullptr) { + err_tag_value = LLVMConstInt(g->err_tag_type->type_ref, err_set->value, false); err_payload_value = LLVMConstNull(payload_type->type_ref); make_unnamed_struct = false; } else { @@ -6130,10 +6307,13 @@ static void do_code_gen(CodeGen *g) { TldVar *tld_var = g->global_vars.at(i); ZigVar *var = tld_var->var; - if (var->value->type->id == ZigTypeIdComptimeFloat) { + if (var->var_type->id == ZigTypeIdComptimeFloat) { // Generate debug info for it but that's it. - ConstExprValue *const_val = var->value; + ConstExprValue *const_val = var->const_value; assert(const_val->special != ConstValSpecialRuntime); + if (const_val->type != var->var_type) { + zig_panic("TODO debug info for var with ptr casted value"); + } ZigType *var_type = g->builtin_types.entry_f128; ConstExprValue coerced_value; coerced_value.special = ConstValSpecialStatic; @@ -6144,10 +6324,13 @@ static void do_code_gen(CodeGen *g) { continue; } - if (var->value->type->id == ZigTypeIdComptimeInt) { + if (var->var_type->id == ZigTypeIdComptimeInt) { // Generate debug info for it but that's it. - ConstExprValue *const_val = var->value; + ConstExprValue *const_val = var->const_value; assert(const_val->special != ConstValSpecialRuntime); + if (const_val->type != var->var_type) { + zig_panic("TODO debug info for var with ptr casted value"); + } size_t bits_needed = bigint_bits_needed(&const_val->data.x_bigint); if (bits_needed < 8) { bits_needed = 8; @@ -6158,7 +6341,7 @@ static void do_code_gen(CodeGen *g) { continue; } - if (!type_has_bits(var->value->type)) + if (!type_has_bits(var->var_type)) continue; assert(var->decl_node); @@ -6167,9 +6350,9 @@ static void do_code_gen(CodeGen *g) { if (var->linkage == VarLinkageExternal) { LLVMValueRef existing_llvm_var = LLVMGetNamedGlobal(g->module, buf_ptr(&var->name)); if (existing_llvm_var) { - global_value = LLVMConstBitCast(existing_llvm_var, LLVMPointerType(var->value->type->type_ref, 0)); + global_value = LLVMConstBitCast(existing_llvm_var, LLVMPointerType(var->var_type->type_ref, 0)); } else { - global_value = LLVMAddGlobal(g->module, var->value->type->type_ref, buf_ptr(&var->name)); + global_value = LLVMAddGlobal(g->module, var->var_type->type_ref, buf_ptr(&var->name)); // TODO debug info for the extern variable LLVMSetLinkage(global_value, LLVMExternalLinkage); @@ -6180,9 +6363,9 @@ static void do_code_gen(CodeGen *g) { } else { bool exported = (var->linkage == VarLinkageExport); const char *mangled_name = buf_ptr(get_mangled_name(g, &var->name, exported)); - render_const_val(g, var->value, mangled_name); - render_const_val_global(g, var->value, mangled_name); - global_value = var->value->global_refs->llvm_global; + render_const_val(g, var->const_value, mangled_name); + render_const_val_global(g, var->const_value, mangled_name); + global_value = var->const_value->global_refs->llvm_global; if (exported) { LLVMSetLinkage(global_value, LLVMExternalLinkage); @@ -6194,8 +6377,10 @@ static void do_code_gen(CodeGen *g) { LLVMSetAlignment(global_value, var->align_bytes); // TODO debug info for function pointers - if (var->gen_is_const && var->value->type->id != ZigTypeIdFn) { - gen_global_var(g, var, var->value->global_refs->llvm_value, var->value->type); + // Here we use const_value->type because that's the type of the llvm global, + // which we const ptr cast upon use to whatever it needs to be. + if (var->gen_is_const && var->const_value->type->id != ZigTypeIdFn) { + gen_global_var(g, var, var->const_value->global_refs->llvm_value, var->const_value->type); } LLVMSetGlobalConstant(global_value, var->gen_is_const); @@ -6281,8 +6466,8 @@ static void do_code_gen(CodeGen *g) { } else if (instruction->id == IrInstructionIdErrWrapCode) { IrInstructionErrWrapCode *err_wrap_code_instruction = (IrInstructionErrWrapCode *)instruction; slot = &err_wrap_code_instruction->tmp_ptr; - } else if (instruction->id == IrInstructionIdCmpxchg) { - IrInstructionCmpxchg *cmpxchg_instruction = (IrInstructionCmpxchg *)instruction; + } else if (instruction->id == IrInstructionIdCmpxchgGen) { + IrInstructionCmpxchgGen *cmpxchg_instruction = (IrInstructionCmpxchgGen *)instruction; slot = &cmpxchg_instruction->tmp_ptr; } else { zig_unreachable(); @@ -6304,12 +6489,12 @@ static void do_code_gen(CodeGen *g) { for (size_t var_i = 0; var_i < fn_table_entry->variable_list.length; var_i += 1) { ZigVar *var = fn_table_entry->variable_list.at(var_i); - if (!type_has_bits(var->value->type)) { + if (!type_has_bits(var->var_type)) { continue; } if (ir_get_var_is_comptime(var)) continue; - switch (type_requires_comptime(g, var->value->type)) { + switch (type_requires_comptime(g, var->var_type)) { case ReqCompTimeInvalid: zig_unreachable(); case ReqCompTimeYes: @@ -6319,11 +6504,11 @@ static void do_code_gen(CodeGen *g) { } if (var->src_arg_index == SIZE_MAX) { - var->value_ref = build_alloca(g, var->value->type, buf_ptr(&var->name), var->align_bytes); + var->value_ref = build_alloca(g, var->var_type, buf_ptr(&var->name), var->align_bytes); var->di_loc_var = ZigLLVMCreateAutoVariable(g->dbuilder, get_di_scope(g, var->parent_scope), buf_ptr(&var->name), import->di_file, (unsigned)(var->decl_node->line + 1), - var->value->type->di_type, !g->strip_debug_symbols, 0); + var->var_type->di_type, !g->strip_debug_symbols, 0); } else if (is_c_abi) { fn_walk_var.data.vars.var = var; @@ -6333,16 +6518,16 @@ static void do_code_gen(CodeGen *g) { ZigType *gen_type; FnGenParamInfo *gen_info = &fn_table_entry->type_entry->data.fn.gen_param_info[var->src_arg_index]; - if (handle_is_ptr(var->value->type)) { + if (handle_is_ptr(var->var_type)) { if (gen_info->is_byval) { - gen_type = var->value->type; + gen_type = var->var_type; } else { gen_type = gen_info->type; } var->value_ref = LLVMGetParam(fn, (unsigned)var->gen_arg_index); } else { - gen_type = var->value->type; - var->value_ref = build_alloca(g, var->value->type, buf_ptr(&var->name), var->align_bytes); + gen_type = var->var_type; + var->value_ref = build_alloca(g, var->var_type, buf_ptr(&var->name), var->align_bytes); } if (var->decl_node) { var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope), @@ -7458,9 +7643,9 @@ static void create_test_compile_var_and_add_test_runner(CodeGen *g) { ConstExprValue *this_val = &test_fn_array->data.x_array.data.s_none.elements[i]; this_val->special = ConstValSpecialStatic; this_val->type = struct_type; - this_val->data.x_struct.parent.id = ConstParentIdArray; - this_val->data.x_struct.parent.data.p_array.array_val = test_fn_array; - this_val->data.x_struct.parent.data.p_array.elem_index = i; + this_val->parent.id = ConstParentIdArray; + this_val->parent.data.p_array.array_val = test_fn_array; + this_val->parent.data.p_array.elem_index = i; this_val->data.x_struct.fields = create_const_vals(2); ConstExprValue *name_field = &this_val->data.x_struct.fields[0]; diff --git a/src/ir.cpp b/src/ir.cpp index b184252a2e83289557442d5ad39e56af1a714d64..06eb4a47f1cfe113df5633869e32508a01131c01 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -167,6 +167,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ ZigType *dest_type, IrInstruction *dest_type_src); static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed); static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs); +static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align); static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) { assert(get_src_ptr_type(const_val->type) != nullptr); @@ -178,15 +179,28 @@ static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *c case ConstPtrSpecialRef: result = const_val->data.x_ptr.data.ref.pointee; break; - case ConstPtrSpecialBaseArray: - expand_undef_array(g, const_val->data.x_ptr.data.base_array.array_val); - result = &const_val->data.x_ptr.data.base_array.array_val->data.x_array.data.s_none.elements[ - const_val->data.x_ptr.data.base_array.elem_index]; + case ConstPtrSpecialBaseArray: { + ConstExprValue *array_val = const_val->data.x_ptr.data.base_array.array_val; + expand_undef_array(g, array_val); + 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]; break; + case ConstPtrSpecialBaseErrorUnionCode: + result = const_val->data.x_ptr.data.base_err_union_code.err_union_val->data.x_err_union.error_set; + break; + case ConstPtrSpecialBaseErrorUnionPayload: + result = const_val->data.x_ptr.data.base_err_union_payload.err_union_val->data.x_err_union.payload; + break; + case ConstPtrSpecialBaseOptionalPayload: + result = const_val->data.x_ptr.data.base_optional_payload.optional_val->data.x_optional; + break; + case ConstPtrSpecialNull: + result = const_val; + break; case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialDiscard: @@ -198,6 +212,11 @@ static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *c return result; } +static bool is_opt_err_set(ZigType *ty) { + return ty->id == ZigTypeIdErrorSet || + (ty->id == ZigTypeIdOptional && ty->data.maybe.child_type->id == ZigTypeIdErrorSet); +} + static bool types_have_same_zig_comptime_repr(ZigType *a, ZigType *b) { if (a == b) return true; @@ -208,15 +227,10 @@ static bool types_have_same_zig_comptime_repr(ZigType *a, ZigType *b) { if (get_codegen_ptr_type(a) != nullptr && get_codegen_ptr_type(b) != nullptr) return true; - return false; -} + if (is_opt_err_set(a) && is_opt_err_set(b)) + return true; -ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) { - ConstExprValue *result = const_ptr_pointee_unchecked(g, const_val); - if (const_val->type->id == ZigTypeIdPointer) { - assert(types_have_same_zig_comptime_repr(const_val->type->data.pointer.child_type, result->type)); - } - return result; + return false; } static bool ir_should_inline(IrExecutable *exec, Scope *scope) { @@ -305,6 +319,14 @@ static IrBasicBlock *ir_build_bb_from(IrBuilder *irb, IrBasicBlock *other_bb) { return new_bb; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclVarSrc *) { + return IrInstructionIdDeclVarSrc; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclVarGen *) { + return IrInstructionIdDeclVarGen; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionCondBr *) { return IrInstructionIdCondBr; } @@ -337,10 +359,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionBinOp *) { return IrInstructionIdBinOp; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclVar *) { - return IrInstructionIdDeclVar; -} - static constexpr IrInstructionId ir_instruction_id(IrInstructionExport *) { return IrInstructionIdExport; } @@ -449,8 +467,8 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTestNonNull *) { return IrInstructionIdTestNonNull; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapOptional *) { - return IrInstructionIdUnwrapOptional; +static constexpr IrInstructionId ir_instruction_id(IrInstructionOptionalUnwrapPtr *) { + return IrInstructionIdOptionalUnwrapPtr; } static constexpr IrInstructionId ir_instruction_id(IrInstructionClz *) { @@ -517,8 +535,12 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionEmbedFile *) { return IrInstructionIdEmbedFile; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionCmpxchg *) { - return IrInstructionIdCmpxchg; +static constexpr IrInstructionId ir_instruction_id(IrInstructionCmpxchgSrc *) { + return IrInstructionIdCmpxchgSrc; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionCmpxchgGen *) { + return IrInstructionIdCmpxchgGen; } static constexpr IrInstructionId ir_instruction_id(IrInstructionFence *) { @@ -649,8 +671,12 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTestComptime *) return IrInstructionIdTestComptime; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrCast *) { - return IrInstructionIdPtrCast; +static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrCastSrc *) { + return IrInstructionIdPtrCastSrc; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrCastGen *) { + return IrInstructionIdPtrCastGen; } static constexpr IrInstructionId ir_instruction_id(IrInstructionBitCast *) { @@ -915,7 +941,7 @@ static IrInstruction *ir_build_cond_br(IrBuilder *irb, Scope *scope, AstNode *so ir_ref_instruction(condition, irb->current_basic_block); ir_ref_bb(then_block); ir_ref_bb(else_block); - if (is_comptime) ir_ref_instruction(is_comptime, irb->current_basic_block); + if (is_comptime != nullptr) ir_ref_instruction(is_comptime, irb->current_basic_block); return &cond_br_instruction->base; } @@ -931,16 +957,6 @@ static IrInstruction *ir_build_return(IrBuilder *irb, Scope *scope, AstNode *sou return &return_instruction->base; } -static IrInstruction *ir_create_const(IrBuilder *irb, Scope *scope, AstNode *source_node, - ZigType *type_entry) -{ - assert(type_entry); - IrInstructionConst *const_instruction = ir_create_instruction(irb, scope, source_node); - const_instruction->base.value.type = type_entry; - const_instruction->base.value.special = ConstValSpecialStatic; - return &const_instruction->base; -} - static IrInstruction *ir_build_const_void(IrBuilder *irb, Scope *scope, AstNode *source_node) { IrInstructionConst *const_instruction = ir_build_instruction(irb, scope, source_node); const_instruction->base.value.type = irb->codegen->builtin_types.entry_void; @@ -1188,14 +1204,11 @@ static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *sourc call_instruction->async_allocator = async_allocator; call_instruction->new_stack = new_stack; - if (fn_ref) - ir_ref_instruction(fn_ref, irb->current_basic_block); + if (fn_ref != nullptr) ir_ref_instruction(fn_ref, irb->current_basic_block); for (size_t i = 0; i < arg_count; i += 1) ir_ref_instruction(args[i], irb->current_basic_block); - if (async_allocator) - ir_ref_instruction(async_allocator, irb->current_basic_block); - if (new_stack != nullptr) - ir_ref_instruction(new_stack, irb->current_basic_block); + if (async_allocator != nullptr) ir_ref_instruction(async_allocator, irb->current_basic_block); + if (new_stack != nullptr) ir_ref_instruction(new_stack, irb->current_basic_block); return &call_instruction->base; } @@ -1280,7 +1293,7 @@ static IrInstruction *ir_build_container_init_list(IrBuilder *irb, Scope *scope, container_init_list_instruction->item_count = item_count; container_init_list_instruction->items = items; - ir_ref_instruction(container_type, irb->current_basic_block); + if (container_type != nullptr) ir_ref_instruction(container_type, irb->current_basic_block); for (size_t i = 0; i < item_count; i += 1) { ir_ref_instruction(items[i], irb->current_basic_block); } @@ -1355,10 +1368,10 @@ static IrInstruction *ir_build_store_ptr(IrBuilder *irb, Scope *scope, AstNode * return &instruction->base; } -static IrInstruction *ir_build_var_decl(IrBuilder *irb, Scope *scope, AstNode *source_node, +static IrInstruction *ir_build_var_decl_src(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigVar *var, IrInstruction *var_type, IrInstruction *align_value, IrInstruction *init_value) { - IrInstructionDeclVar *decl_var_instruction = ir_build_instruction(irb, scope, source_node); + IrInstructionDeclVarSrc *decl_var_instruction = ir_build_instruction(irb, scope, source_node); decl_var_instruction->base.value.special = ConstValSpecialStatic; decl_var_instruction->base.value.type = irb->codegen->builtin_types.entry_void; decl_var_instruction->var = var; @@ -1366,13 +1379,28 @@ static IrInstruction *ir_build_var_decl(IrBuilder *irb, Scope *scope, AstNode *s decl_var_instruction->align_value = align_value; decl_var_instruction->init_value = init_value; - if (var_type) ir_ref_instruction(var_type, irb->current_basic_block); - if (align_value) ir_ref_instruction(align_value, irb->current_basic_block); + if (var_type != nullptr) ir_ref_instruction(var_type, irb->current_basic_block); + if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block); ir_ref_instruction(init_value, irb->current_basic_block); return &decl_var_instruction->base; } +static IrInstruction *ir_build_var_decl_gen(IrAnalyze *ira, IrInstruction *source_instruction, + ZigVar *var, IrInstruction *init_value) +{ + IrInstructionDeclVarGen *decl_var_instruction = ir_build_instruction(&ira->new_irb, + source_instruction->scope, source_instruction->source_node); + decl_var_instruction->base.value.special = ConstValSpecialStatic; + decl_var_instruction->base.value.type = ira->codegen->builtin_types.entry_void; + decl_var_instruction->var = var; + decl_var_instruction->init_value = init_value; + + ir_ref_instruction(init_value, ira->new_irb.current_basic_block); + + return &decl_var_instruction->base; +} + static IrInstruction *ir_build_export(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name, IrInstruction *target, IrInstruction *linkage) { @@ -1542,14 +1570,14 @@ static IrInstruction *ir_build_test_nonnull(IrBuilder *irb, Scope *scope, AstNod return &instruction->base; } -static IrInstruction *ir_build_unwrap_maybe(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value, - bool safety_check_on) +static IrInstruction *ir_build_optional_unwrap_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *base_ptr, bool safety_check_on) { - IrInstructionUnwrapOptional *instruction = ir_build_instruction(irb, scope, source_node); - instruction->value = value; + IrInstructionOptionalUnwrapPtr *instruction = ir_build_instruction(irb, scope, source_node); + instruction->base_ptr = base_ptr; instruction->safety_check_on = safety_check_on; - ir_ref_instruction(value, irb->current_basic_block); + ir_ref_instruction(base_ptr, irb->current_basic_block); return &instruction->base; } @@ -1765,13 +1793,12 @@ static IrInstruction *ir_build_embed_file(IrBuilder *irb, Scope *scope, AstNode return &instruction->base; } -static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value, - IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value, +static IrInstruction *ir_build_cmpxchg_src(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *type_value, IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value, IrInstruction *success_order_value, IrInstruction *failure_order_value, - bool is_weak, - ZigType *type, AtomicOrder success_order, AtomicOrder failure_order) + bool is_weak) { - IrInstructionCmpxchg *instruction = ir_build_instruction(irb, scope, source_node); + IrInstructionCmpxchgSrc *instruction = ir_build_instruction(irb, scope, source_node); instruction->type_value = type_value; instruction->ptr = ptr; instruction->cmp_value = cmp_value; @@ -1779,16 +1806,33 @@ static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *so instruction->success_order_value = success_order_value; instruction->failure_order_value = failure_order_value; instruction->is_weak = is_weak; - instruction->type = type; - instruction->success_order = success_order; - instruction->failure_order = failure_order; - if (type_value != nullptr) ir_ref_instruction(type_value, irb->current_basic_block); + ir_ref_instruction(type_value, irb->current_basic_block); ir_ref_instruction(ptr, irb->current_basic_block); ir_ref_instruction(cmp_value, irb->current_basic_block); ir_ref_instruction(new_value, irb->current_basic_block); - if (type_value != nullptr) ir_ref_instruction(success_order_value, irb->current_basic_block); - if (type_value != nullptr) ir_ref_instruction(failure_order_value, irb->current_basic_block); + ir_ref_instruction(success_order_value, irb->current_basic_block); + ir_ref_instruction(failure_order_value, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_cmpxchg_gen(IrAnalyze *ira, IrInstruction *source_instruction, + IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value, + AtomicOrder success_order, AtomicOrder failure_order, bool is_weak) +{ + IrInstructionCmpxchgGen *instruction = ir_build_instruction(&ira->new_irb, + source_instruction->scope, source_instruction->source_node); + instruction->ptr = ptr; + instruction->cmp_value = cmp_value; + instruction->new_value = new_value; + instruction->success_order = success_order; + instruction->failure_order = failure_order; + instruction->is_weak = is_weak; + + ir_ref_instruction(ptr, ira->new_irb.current_basic_block); + ir_ref_instruction(cmp_value, ira->new_irb.current_basic_block); + ir_ref_instruction(new_value, ira->new_irb.current_basic_block); return &instruction->base; } @@ -2060,12 +2104,12 @@ static IrInstruction *ir_build_test_err(IrBuilder *irb, Scope *scope, AstNode *s } static IrInstruction *ir_build_unwrap_err_code(IrBuilder *irb, Scope *scope, AstNode *source_node, - IrInstruction *value) + IrInstruction *err_union) { IrInstructionUnwrapErrCode *instruction = ir_build_instruction(irb, scope, source_node); - instruction->value = value; + instruction->err_union = err_union; - ir_ref_instruction(value, irb->current_basic_block); + ir_ref_instruction(err_union, irb->current_basic_block); return &instruction->base; } @@ -2115,20 +2159,33 @@ static IrInstruction *ir_build_test_comptime(IrBuilder *irb, Scope *scope, AstNo return &instruction->base; } -static IrInstruction *ir_build_ptr_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, +static IrInstruction *ir_build_ptr_cast_src(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *ptr) { - IrInstructionPtrCast *instruction = ir_build_instruction( + IrInstructionPtrCastSrc *instruction = ir_build_instruction( irb, scope, source_node); instruction->dest_type = dest_type; instruction->ptr = ptr; - if (dest_type) ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(dest_type, irb->current_basic_block); ir_ref_instruction(ptr, irb->current_basic_block); return &instruction->base; } +static IrInstruction *ir_build_ptr_cast_gen(IrAnalyze *ira, IrInstruction *source_instruction, + ZigType *ptr_type, IrInstruction *ptr) +{ + IrInstructionPtrCastGen *instruction = ir_build_instruction( + &ira->new_irb, source_instruction->scope, source_instruction->source_node); + instruction->base.value.type = ptr_type; + instruction->ptr = ptr; + + ir_ref_instruction(ptr, ira->new_irb.current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_bit_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *value) { @@ -2807,10 +2864,13 @@ static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o Scope *defer_expr_scope = defer_node->data.defer.expr_scope; IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope); if (defer_expr_value != irb->codegen->invalid_instruction) { - if (defer_expr_value->value.type != nullptr && defer_expr_value->value.type->id == ZigTypeIdUnreachable) { + if (defer_expr_value->value.type != nullptr && + defer_expr_value->value.type->id == ZigTypeIdUnreachable) + { is_noreturn = true; } else { - ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value)); + ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, + defer_expr_value)); } } } @@ -3065,7 +3125,7 @@ static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_s variable_entry->mem_slot_index = SIZE_MAX; variable_entry->is_comptime = is_comptime; variable_entry->src_arg_index = SIZE_MAX; - variable_entry->value = create_const_vals(1); + variable_entry->const_value = create_const_vals(1); if (is_comptime != nullptr) { is_comptime->ref_count += 1; @@ -3080,20 +3140,20 @@ static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_s ErrorMsg *msg = add_node_error(codegen, node, buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); - variable_entry->value->type = codegen->builtin_types.entry_invalid; + variable_entry->var_type = codegen->builtin_types.entry_invalid; } else { ZigType *type; if (get_primitive_type(codegen, name, &type) != ErrorPrimitiveTypeNotFound) { add_node_error(codegen, node, buf_sprintf("variable shadows primitive type '%s'", buf_ptr(name))); - variable_entry->value->type = codegen->builtin_types.entry_invalid; + variable_entry->var_type = codegen->builtin_types.entry_invalid; } else { Tld *tld = find_decl(codegen, parent_scope, name); if (tld != nullptr) { ErrorMsg *msg = add_node_error(codegen, node, buf_sprintf("redefinition of '%s'", buf_ptr(name))); add_error_note(codegen, msg, tld->source_node, buf_sprintf("previous definition is here")); - variable_entry->value->type = codegen->builtin_types.entry_invalid; + variable_entry->var_type = codegen->builtin_types.entry_invalid; } } } @@ -3156,7 +3216,8 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode scope_block->incoming_blocks = &incoming_blocks; scope_block->incoming_values = &incoming_values; scope_block->end_block = ir_create_basic_block(irb, parent_scope, "BlockEnd"); - scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node, ir_should_inline(irb->exec, parent_scope)); + scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node, + ir_should_inline(irb->exec, parent_scope)); } bool is_continuation_unreachable = false; @@ -3174,9 +3235,9 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode // defer starts a new scope child_scope = statement_node->data.defer.child_scope; assert(child_scope); - } else if (statement_value->id == IrInstructionIdDeclVar) { + } else if (statement_value->id == IrInstructionIdDeclVarSrc) { // variable declarations start a new scope - IrInstructionDeclVar *decl_var_instruction = (IrInstructionDeclVar *)statement_value; + IrInstructionDeclVarSrc *decl_var_instruction = (IrInstructionDeclVarSrc *)statement_value; child_scope = decl_var_instruction->var->child_scope; } else if (statement_value != irb->codegen->invalid_instruction && !is_continuation_unreachable) { // this statement's value must be void @@ -3331,7 +3392,7 @@ static IrInstruction *ir_gen_bool_and(IrBuilder *irb, Scope *scope, AstNode *nod return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); } -static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, AstNode *node) { +static IrInstruction *ir_gen_orelse(IrBuilder *irb, Scope *parent_scope, AstNode *node) { assert(node->type == NodeTypeBinOpExpr); AstNode *op1_node = node->data.bin_op_expr.op1; @@ -3365,7 +3426,7 @@ static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, As ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime)); ir_set_cursor_at_end_and_append_block(irb, ok_block); - IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, parent_scope, node, maybe_ptr, false); + IrInstruction *unwrapped_ptr = ir_build_optional_unwrap_ptr(irb, parent_scope, node, maybe_ptr, false); IrInstruction *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr); IrBasicBlock *after_ok_block = irb->current_basic_block; ir_build_br(irb, parent_scope, node, end_block, is_comptime); @@ -3483,7 +3544,7 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) case BinOpTypeMergeErrorSets: return ir_gen_bin_op_id(irb, scope, node, IrBinOpMergeErrorSets); case BinOpTypeUnwrapOptional: - return ir_gen_maybe_ok_or(irb, scope, node); + return ir_gen_orelse(irb, scope, node); case BinOpTypeErrorUnion: return ir_gen_error_union(irb, scope, node); } @@ -3542,6 +3603,7 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, buf_ptr(variable_name))); return irb->codegen->invalid_instruction; } + assert(err == ErrorPrimitiveTypeNotFound); } else { IrInstruction *value = ir_build_const_type(irb, scope, node, primitive_type); if (lval == LValPtr) { @@ -3904,9 +3966,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg5_value == irb->codegen->invalid_instruction) return arg5_value; - IrInstruction *cmpxchg = ir_build_cmpxchg(irb, scope, node, arg0_value, arg1_value, - arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak), - nullptr, AtomicOrderUnordered, AtomicOrderUnordered); + IrInstruction *cmpxchg = ir_build_cmpxchg_src(irb, scope, node, arg0_value, arg1_value, + arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak)); return ir_lval_wrap(irb, scope, cmpxchg, lval); } case BuiltinFnIdFence: @@ -4346,7 +4407,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg1_value == irb->codegen->invalid_instruction) return arg1_value; - IrInstruction *ptr_cast = ir_build_ptr_cast(irb, scope, node, arg0_value, arg1_value); + IrInstruction *ptr_cast = ir_build_ptr_cast_src(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, ptr_cast, lval); } case BuiltinFnIdBitCast: @@ -4784,7 +4845,8 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node } } - IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator, nullptr); + IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, + is_async, async_allocator, nullptr); return ir_lval_wrap(irb, scope, fn_call, lval); } @@ -4793,7 +4855,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode IrInstruction *condition = ir_gen_node(irb, node->data.if_bool_expr.condition, scope); if (condition == irb->codegen->invalid_instruction) - return condition; + return irb->codegen->invalid_instruction; IrInstruction *is_comptime; if (ir_should_inline(irb->exec, scope)) { @@ -4816,7 +4878,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); IrInstruction *then_expr_result = ir_gen_node(irb, then_node, subexpr_scope); if (then_expr_result == irb->codegen->invalid_instruction) - return then_expr_result; + return irb->codegen->invalid_instruction; IrBasicBlock *after_then_block = irb->current_basic_block; if (!instr_is_unreachable(then_expr_result)) ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); @@ -4826,7 +4888,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode if (else_node) { else_expr_result = ir_gen_node(irb, else_node, subexpr_scope); if (else_expr_result == irb->codegen->invalid_instruction) - return else_expr_result; + return irb->codegen->invalid_instruction; } else { else_expr_result = ir_build_const_void(irb, scope, node); } @@ -4927,7 +4989,7 @@ static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode ptr_len, align_value, bit_offset_start, host_int_bytes); } -static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode *source_node, AstNode *expr_node, +static IrInstruction *ir_gen_catch_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node, AstNode *expr_node, LVal lval) { IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr); @@ -5053,11 +5115,11 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod ir_should_inline(irb->exec, scope) || variable_declaration->is_comptime); ZigVar *var = ir_create_var(irb, node, scope, variable_declaration->symbol, is_const, is_const, is_shadowable, is_comptime); - // we detect IrInstructionIdDeclVar in gen_block to make sure the next node + // we detect IrInstructionIdDeclVarSrc in gen_block to make sure the next node // is inside var->child_scope if (!is_extern && !variable_declaration->expr) { - var->value->type = irb->codegen->builtin_types.entry_invalid; + var->var_type = irb->codegen->builtin_types.entry_invalid; add_node_error(irb->codegen, node, buf_sprintf("variables must be initialized")); return irb->codegen->invalid_instruction; } @@ -5084,7 +5146,7 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod if (init_value == irb->codegen->invalid_instruction) return init_value; - return ir_build_var_decl(irb, scope, node, var, type_instruction, align_value, init_value); + return ir_build_var_decl_src(irb, scope, node, var, type_instruction, align_value, init_value); } static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *node) { @@ -5140,7 +5202,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n err_val_ptr, false); IrInstruction *var_value = node->data.while_expr.var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, payload_scope, symbol_node, var_ptr_value); - ir_build_var_decl(irb, payload_scope, symbol_node, payload_var, nullptr, nullptr, var_value); + ir_build_var_decl_src(irb, payload_scope, symbol_node, payload_var, nullptr, nullptr, var_value); } ZigList incoming_values = {0}; @@ -5180,7 +5242,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n true, false, false, is_comptime); Scope *err_scope = err_var->child_scope; IrInstruction *err_var_value = ir_build_unwrap_err_code(irb, err_scope, err_symbol_node, err_val_ptr); - ir_build_var_decl(irb, err_scope, symbol_node, err_var, nullptr, nullptr, err_var_value); + ir_build_var_decl_src(irb, err_scope, symbol_node, err_var, nullptr, nullptr, err_var_value); IrInstruction *else_result = ir_gen_node(irb, else_node, err_scope); if (else_result == irb->codegen->invalid_instruction) @@ -5220,10 +5282,10 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n } ir_set_cursor_at_end_and_append_block(irb, body_block); - IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, child_scope, symbol_node, maybe_val_ptr, false); + IrInstruction *var_ptr_value = ir_build_optional_unwrap_ptr(irb, child_scope, symbol_node, maybe_val_ptr, false); IrInstruction *var_value = node->data.while_expr.var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, child_scope, symbol_node, var_ptr_value); - ir_build_var_decl(irb, child_scope, symbol_node, payload_var, nullptr, nullptr, var_value); + ir_build_var_decl_src(irb, child_scope, symbol_node, payload_var, nullptr, nullptr, var_value); ZigList incoming_values = {0}; ZigList incoming_blocks = {0}; @@ -5380,7 +5442,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo Scope *child_scope = elem_var->child_scope; IrInstruction *undefined_value = ir_build_const_undefined(irb, child_scope, elem_node); - ir_build_var_decl(irb, child_scope, elem_node, elem_var, elem_var_type, nullptr, undefined_value); + ir_build_var_decl_src(irb, child_scope, elem_node, elem_var, elem_var_type, nullptr, undefined_value); IrInstruction *elem_var_ptr = ir_build_var_ptr(irb, child_scope, node, elem_var); AstNode *index_var_source_node; @@ -5398,7 +5460,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo IrInstruction *usize = ir_build_const_type(irb, child_scope, node, irb->codegen->builtin_types.entry_usize); IrInstruction *zero = ir_build_const_usize(irb, child_scope, node, 0); IrInstruction *one = ir_build_const_usize(irb, child_scope, node, 1); - ir_build_var_decl(irb, child_scope, index_var_source_node, index_var, usize, nullptr, zero); + ir_build_var_decl_src(irb, child_scope, index_var_source_node, index_var, usize, nullptr, zero); IrInstruction *index_ptr = ir_build_var_ptr(irb, child_scope, node, index_var); @@ -5622,8 +5684,8 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod return ir_build_asm(irb, scope, node, input_list, output_types, output_vars, return_count, is_volatile); } -static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeTestExpr); +static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeIfOptional); Buf *var_symbol = node->data.test_expr.var_symbol; AstNode *expr_node = node->data.test_expr.target_node; @@ -5661,9 +5723,9 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no ZigVar *var = ir_create_var(irb, node, subexpr_scope, var_symbol, is_const, is_const, is_shadowable, is_comptime); - IrInstruction *var_ptr_value = ir_build_unwrap_maybe(irb, subexpr_scope, node, maybe_val_ptr, false); + IrInstruction *var_ptr_value = ir_build_optional_unwrap_ptr(irb, subexpr_scope, node, maybe_val_ptr, false); IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, subexpr_scope, node, var_ptr_value); - ir_build_var_decl(irb, subexpr_scope, node, var, var_type, nullptr, var_value); + ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, var_value); var_scope = var->child_scope; } else { var_scope = subexpr_scope; @@ -5738,7 +5800,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, subexpr_scope, node, err_val_ptr, false); IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, subexpr_scope, node, var_ptr_value); - ir_build_var_decl(irb, subexpr_scope, node, var, var_type, nullptr, var_value); + ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, var_value); var_scope = var->child_scope; } else { var_scope = subexpr_scope; @@ -5763,7 +5825,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * err_symbol, is_const, is_const, is_shadowable, is_comptime); IrInstruction *var_value = ir_build_unwrap_err_code(irb, subexpr_scope, node, err_val_ptr); - ir_build_var_decl(irb, subexpr_scope, node, var, var_type, nullptr, var_value); + ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, var_value); err_var_scope = var->child_scope; } else { err_var_scope = subexpr_scope; @@ -5818,7 +5880,7 @@ static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *swit var_value = var_is_ptr ? target_value_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, target_value_ptr); } IrInstruction *var_type = nullptr; // infer the type - ir_build_var_decl(irb, scope, var_symbol_node, var, var_type, nullptr, var_value); + ir_build_var_decl_src(irb, scope, var_symbol_node, var, var_type, nullptr, var_value); } else { child_scope = scope; } @@ -6228,7 +6290,7 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node) return ir_build_slice(irb, scope, node, ptr_value, start_value, end_value, true); } -static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstNode *node) { +static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode *node) { assert(node->type == NodeTypeUnwrapErrorExpr); AstNode *op1_node = node->data.unwrap_err_expr.op1; @@ -6242,7 +6304,7 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN add_node_error(irb->codegen, var_node, buf_sprintf("unused variable: '%s'", buf_ptr(var_name))); return irb->codegen->invalid_instruction; } - return ir_gen_err_assert_ok(irb, parent_scope, node, op1_node, LValNone); + return ir_gen_catch_unreachable(irb, parent_scope, node, op1_node, LValNone); } @@ -6276,7 +6338,7 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN is_const, is_const, is_shadowable, is_comptime); err_scope = var->child_scope; IrInstruction *err_val = ir_build_unwrap_err_code(irb, err_scope, node, err_union_ptr); - ir_build_var_decl(irb, err_scope, var_node, var, nullptr, nullptr, err_val); + ir_build_var_decl_src(irb, err_scope, var_node, var, nullptr, nullptr, err_val); } else { err_scope = parent_scope; } @@ -6312,7 +6374,8 @@ static bool render_instance_name_recursive(CodeGen *codegen, Buf *name, Scope *o ScopeVarDecl *var_scope = (ScopeVarDecl *)inner_scope; if (need_comma) buf_append_char(name, ','); - render_const_value(codegen, name, var_scope->var->value); + // TODO: const ptr reinterpret here to make the var type agree with the value? + render_const_value(codegen, name, var_scope->var->const_value); return true; } @@ -6562,7 +6625,7 @@ static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010 // TODO relies on Zig not re-ordering fields - IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst); + IrInstruction *casted_target_inst = ir_build_ptr_cast_src(irb, scope, node, promise_T_type_val, target_inst); IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, @@ -6640,7 +6703,7 @@ static IrInstruction *ir_gen_resume_target(IrBuilder *irb, Scope *scope, AstNode get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void)); // TODO relies on Zig not re-ordering fields - IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst); + IrInstruction *casted_target_inst = ir_build_ptr_cast_src(irb, scope, node, promise_T_type_val, target_inst); IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, @@ -6756,7 +6819,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n IrInstruction *target_promise_type = ir_build_typeof(irb, scope, node, target_inst); IrInstruction *promise_result_type = ir_build_promise_result_type(irb, scope, node, target_promise_type); ir_build_await_bookkeeping(irb, scope, node, promise_result_type); - ir_build_var_decl(irb, scope, node, result_var, promise_result_type, nullptr, undefined_value); + ir_build_var_decl_src(irb, scope, node, result_var, promise_result_type, nullptr, undefined_value); IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, scope, node, result_var); ir_build_store_ptr(irb, scope, node, result_ptr_field_ptr, my_result_var_ptr); IrInstruction *save_token = ir_build_coro_save(irb, scope, node, irb->exec->coro_handle); @@ -7050,7 +7113,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop if (maybe_ptr == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, scope, node, maybe_ptr, true); + IrInstruction *unwrapped_ptr = ir_build_optional_unwrap_ptr(irb, scope, node, maybe_ptr, true); if (lval == LValPtr) return unwrapped_ptr; @@ -7074,8 +7137,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_lval_wrap(irb, scope, ir_gen_null_literal(irb, scope, node), lval); case NodeTypeIfErrorExpr: return ir_lval_wrap(irb, scope, ir_gen_if_err_expr(irb, scope, node), lval); - case NodeTypeTestExpr: - return ir_lval_wrap(irb, scope, ir_gen_test_expr(irb, scope, node), lval); + case NodeTypeIfOptional: + return ir_lval_wrap(irb, scope, ir_gen_if_optional_expr(irb, scope, node), lval); case NodeTypeSwitchExpr: return ir_lval_wrap(irb, scope, ir_gen_switch_expr(irb, scope, node), lval); case NodeTypeCompTime: @@ -7093,7 +7156,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeSliceExpr: return ir_lval_wrap(irb, scope, ir_gen_slice(irb, scope, node), lval); case NodeTypeUnwrapErrorExpr: - return ir_lval_wrap(irb, scope, ir_gen_err_ok_or(irb, scope, node), lval); + return ir_lval_wrap(irb, scope, ir_gen_catch(irb, scope, node), lval); case NodeTypeContainerDecl: return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval); case NodeTypeFnProto: @@ -7152,6 +7215,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ir_ref_bb(irb->current_basic_block); ZigFn *fn_entry = exec_fn_entry(irb->exec); + bool is_async = fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; IrInstruction *coro_id; IrInstruction *u8_ptr_type; @@ -7172,27 +7236,27 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ZigType *coro_frame_type = get_promise_frame_type(irb->codegen, return_type); IrInstruction *coro_frame_type_value = ir_build_const_type(irb, coro_scope, node, coro_frame_type); // TODO mark this var decl as "no safety" e.g. disable initializing the undef value to 0xaa - ir_build_var_decl(irb, coro_scope, node, promise_var, coro_frame_type_value, nullptr, undef); + ir_build_var_decl_src(irb, coro_scope, node, promise_var, coro_frame_type_value, nullptr, undef); coro_promise_ptr = ir_build_var_ptr(irb, coro_scope, node, promise_var); ZigVar *await_handle_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false); IrInstruction *null_value = ir_build_const_null(irb, coro_scope, node); IrInstruction *await_handle_type_val = ir_build_const_type(irb, coro_scope, node, get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); - ir_build_var_decl(irb, coro_scope, node, await_handle_var, await_handle_type_val, nullptr, null_value); + ir_build_var_decl_src(irb, coro_scope, node, await_handle_var, await_handle_type_val, nullptr, null_value); irb->exec->await_handle_var_ptr = ir_build_var_ptr(irb, coro_scope, node, await_handle_var); u8_ptr_type = ir_build_const_type(irb, coro_scope, node, get_pointer_to_type(irb->codegen, irb->codegen->builtin_types.entry_u8, false)); - IrInstruction *promise_as_u8_ptr = ir_build_ptr_cast(irb, coro_scope, node, u8_ptr_type, coro_promise_ptr); + IrInstruction *promise_as_u8_ptr = ir_build_ptr_cast_src(irb, coro_scope, node, u8_ptr_type, coro_promise_ptr); coro_id = ir_build_coro_id(irb, coro_scope, node, promise_as_u8_ptr); coro_size_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false); IrInstruction *coro_size = ir_build_coro_size(irb, coro_scope, node); - ir_build_var_decl(irb, coro_scope, node, coro_size_var, nullptr, nullptr, coro_size); + ir_build_var_decl_src(irb, coro_scope, node, coro_size_var, nullptr, nullptr, coro_size); IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, coro_scope, node, ImplicitAllocatorIdArg); irb->exec->coro_allocator_var = ir_create_var(irb, node, coro_scope, nullptr, true, true, true, const_bool_false); - ir_build_var_decl(irb, coro_scope, node, irb->exec->coro_allocator_var, nullptr, nullptr, implicit_allocator_ptr); + ir_build_var_decl_src(irb, coro_scope, node, irb->exec->coro_allocator_var, nullptr, nullptr, implicit_allocator_ptr); Buf *alloc_field_name = buf_create_from_str(ASYNC_ALLOC_FIELD_NAME); IrInstruction *alloc_fn_ptr = ir_build_field_ptr(irb, coro_scope, node, implicit_allocator_ptr, alloc_field_name); IrInstruction *alloc_fn = ir_build_load_ptr(irb, coro_scope, node, alloc_fn_ptr); @@ -7208,7 +7272,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec ir_build_return(irb, coro_scope, node, undef); ir_set_cursor_at_end_and_append_block(irb, alloc_ok_block); - IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr); + IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr); irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr); Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); @@ -7286,8 +7350,8 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8, false, false, PtrLenUnknown, 0, 0, 0)); IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr); - IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, result_ptr); - IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, + IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len, result_ptr); + IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len, irb->exec->coro_result_field_ptr); IrInstruction *return_type_inst = ir_build_const_type(irb, scope, node, fn_entry->type_entry->data.fn.fn_type_id.return_type); @@ -7303,7 +7367,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec // Before we destroy the coroutine frame, we need to load the target promise into // a register or local variable which does not get spilled into the frame, // otherwise llvm tries to access memory inside the destroyed frame. - IrInstruction *unwrapped_await_handle_ptr = ir_build_unwrap_maybe(irb, scope, node, + IrInstruction *unwrapped_await_handle_ptr = ir_build_optional_unwrap_ptr(irb, scope, node, irb->exec->await_handle_var_ptr, false); IrInstruction *await_handle_in_block = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr); ir_build_br(irb, scope, node, check_free_block, const_bool_false); @@ -7338,7 +7402,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node, get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8, false, false, PtrLenUnknown, 0, 0, 0)); - IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type_unknown_len, coro_mem_ptr_maybe); + IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len, coro_mem_ptr_maybe); IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false); IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var); IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr); @@ -7435,7 +7499,7 @@ ConstExprValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ConstExprVal return val; } -static IrInstruction *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec) { +static ConstExprValue *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec) { IrBasicBlock *bb = exec->basic_block_list.at(0); for (size_t i = 0; i < bb->instruction_list.length; i += 1) { IrInstruction *instruction = bb->instruction_list.at(i); @@ -7445,16 +7509,16 @@ static IrInstruction *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec) if (value->value.special == ConstValSpecialRuntime) { exec_add_error_node(codegen, exec, value->source_node, buf_sprintf("unable to evaluate constant expression")); - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; } - return value; + return &value->value; } else if (ir_has_side_effects(instruction)) { exec_add_error_node(codegen, exec, instruction->source_node, buf_sprintf("unable to evaluate constant expression")); - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; } } - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; } static bool ir_emit_global_runtime_side_effect(IrAnalyze *ira, IrInstruction *source_instruction) { @@ -8768,13 +8832,13 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT size_t errors_count = 0; ZigType *err_set_type = nullptr; if (prev_inst->value.type->id == ZigTypeIdErrorSet) { + if (!resolve_inferred_error_set(ira->codegen, prev_inst->value.type, prev_inst->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } if (type_is_global_error_set(prev_inst->value.type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; } else { err_set_type = prev_inst->value.type; - if (!resolve_inferred_error_set(ira->codegen, err_set_type, prev_inst->source_node)) { - return ira->codegen->builtin_types.entry_invalid; - } update_errors_helper(ira->codegen, &errors, &errors_count); for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { @@ -8933,6 +8997,9 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT if (prev_type->id == ZigTypeIdArray) { convert_to_const_slice = true; } + if (!resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } if (type_is_global_error_set(cur_type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; continue; @@ -8940,9 +9007,6 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT if (err_set_type != nullptr && type_is_global_error_set(err_set_type)) { continue; } - if (!resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->source_node)) { - return ira->codegen->builtin_types.entry_invalid; - } update_errors_helper(ira->codegen, &errors, &errors_count); @@ -9432,14 +9496,23 @@ static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_ } return true; } + +static IrInstruction *ir_const(IrAnalyze *ira, IrInstruction *old_instruction, ZigType *ty) { + IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, + old_instruction->scope, old_instruction->source_node); + IrInstruction *new_instruction = &const_instruction->base; + new_instruction->value.type = ty; + new_instruction->value.special = ConstValSpecialStatic; + return new_instruction; +} + static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type, CastOp cast_op, bool need_alloca) { if ((instr_is_comptime(value) || !type_has_bits(wanted_type)) && cast_op != CastOpResizeSlice) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); if (!eval_const_expr_implicit_cast(ira, source_instr, cast_op, &value->value, value->value.type, &result->value, wanted_type)) { @@ -9476,9 +9549,7 @@ static IrInstruction *ir_resolve_ptr_of_array_to_unknown_len_ptr(IrAnalyze *ira, if (pointee == nullptr) return ira->codegen->invalid_instruction; if (pointee->special != ConstValSpecialRuntime) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); - result->value.type = wanted_type; + IrInstruction *result = ir_const(ira, source_instr, wanted_type); result->value.data.x_ptr.special = ConstPtrSpecialBaseArray; result->value.data.x_ptr.mut = value->value.data.x_ptr.mut; result->value.data.x_ptr.data.base_array.array_val = pointee; @@ -9517,8 +9588,7 @@ static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruc assert(is_slice(wanted_type)); bool is_const = wanted_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); init_const_slice(ira->codegen, &result->value, pointee, 0, array_type->data.array.len, is_const); result->value.data.x_struct.fields[slice_ptr_index].data.x_ptr.mut = value->value.data.x_ptr.mut; @@ -9653,15 +9723,6 @@ static IrInstruction *ir_finish_anal(IrAnalyze *ira, IrInstruction *instruction) return instruction; } -static IrInstruction *ir_const(IrAnalyze *ira, IrInstruction *old_instruction, ZigType *ty) { - IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, - old_instruction->scope, old_instruction->source_node); - IrInstruction *new_instruction = &const_instruction->base; - new_instruction->value.type = ty; - new_instruction->value.special = ConstValSpecialStatic; - return new_instruction; -} - static IrInstruction *ir_const_type(IrAnalyze *ira, IrInstruction *source_instruction, ZigType *ty) { IrInstruction *result = ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_type); result->value.data.x_type = ty; @@ -9719,13 +9780,13 @@ static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, Un zig_unreachable(); } -IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, +ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, ZigType *expected_type, size_t *backward_branch_count, size_t backward_branch_quota, ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name, IrExecutable *parent_exec) { if (expected_type != nullptr && type_is_invalid(expected_type)) - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; IrExecutable *ir_executable = allocate(1); ir_executable->source_node = source_node; @@ -9738,13 +9799,13 @@ IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node ir_gen(codegen, node, scope, ir_executable); if (ir_executable->invalid) - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; if (codegen->verbose_ir) { fprintf(stderr, "\nSource: "); ast_render(codegen, stderr, node, 4); fprintf(stderr, "\n{ // (IR)\n"); - ir_print(codegen, stderr, ir_executable, 4); + ir_print(codegen, stderr, ir_executable, 2); fprintf(stderr, "}\n"); } IrExecutable *analyzed_executable = allocate(1); @@ -9760,11 +9821,11 @@ IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node analyzed_executable->begin_scope = scope; ZigType *result_type = ir_analyze(codegen, ir_executable, analyzed_executable, expected_type, node); if (type_is_invalid(result_type)) - return codegen->invalid_instruction; + return &codegen->invalid_instruction->value; if (codegen->verbose_ir) { fprintf(stderr, "{ // (analyzed)\n"); - ir_print(codegen, stderr, analyzed_executable, 4); + ir_print(codegen, stderr, analyzed_executable, 2); fprintf(stderr, "}\n"); } @@ -9838,7 +9899,9 @@ static ZigFn *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) { return const_val->data.x_ptr.data.fn.fn_entry; } -static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) { +static IrInstruction *ir_analyze_optional_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, + ZigType *wanted_type) +{ assert(wanted_type->id == ZigTypeIdOptional); if (instr_is_comptime(value)) { @@ -9854,7 +9917,7 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); const_instruction->base.value.special = ConstValSpecialStatic; - if (get_codegen_ptr_type(wanted_type) != nullptr) { + if (types_have_same_zig_comptime_repr(wanted_type, payload_type)) { copy_const_val(&const_instruction->base.value, val, val->data.x_ptr.mut == ConstPtrMutComptimeConst); } else { const_instruction->base.value.data.x_optional = val; @@ -9885,11 +9948,16 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction if (!val) return ira->codegen->invalid_instruction; + ConstExprValue *err_set_val = create_const_vals(1); + err_set_val->type = wanted_type->data.error_union.err_set_type; + err_set_val->special = ConstValSpecialStatic; + err_set_val->data.x_err_set = nullptr; + IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); const_instruction->base.value.type = wanted_type; const_instruction->base.value.special = ConstValSpecialStatic; - const_instruction->base.value.data.x_err_union.err = nullptr; + const_instruction->base.value.data.x_err_union.error_set = err_set_val; const_instruction->base.value.data.x_err_union.payload = val; return &const_instruction->base; } @@ -9954,11 +10022,16 @@ static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *so if (!val) return ira->codegen->invalid_instruction; + ConstExprValue *err_set_val = create_const_vals(1); + err_set_val->special = ConstValSpecialStatic; + err_set_val->type = wanted_type->data.error_union.err_set_type; + err_set_val->data.x_err_set = val->data.x_err_set; + IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); const_instruction->base.value.type = wanted_type; const_instruction->base.value.special = ConstValSpecialStatic; - const_instruction->base.value.data.x_err_union.err = val->data.x_err_set; + const_instruction->base.value.data.x_err_union.error_set = err_set_val; const_instruction->base.value.data.x_err_union.payload = nullptr; return &const_instruction->base; } @@ -9980,8 +10053,9 @@ static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *so IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); const_instruction->base.value.special = ConstValSpecialStatic; if (get_codegen_ptr_type(wanted_type) != nullptr) { - const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr; - const_instruction->base.value.data.x_ptr.data.hard_coded_addr.addr = 0; + const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialNull; + } else if (is_opt_err_set(wanted_type)) { + const_instruction->base.value.data.x_err_set = nullptr; } else { const_instruction->base.value.data.x_optional = nullptr; } @@ -10014,7 +10088,7 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi source_instruction->source_node, value, is_const, is_volatile); new_instruction->value.type = ptr_type; new_instruction->value.data.rh_ptr = RuntimeHintPtrStack; - if (type_has_bits(ptr_type)) { + if (type_has_bits(ptr_type) && !handle_is_ptr(value->value.type)) { ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); assert(fn_entry); fn_entry->alloca_list.append(new_instruction); @@ -10040,20 +10114,17 @@ static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *s ZigType *array_type = array->value.type; assert(array_type->id == ZigTypeIdArray); - if (instr_is_comptime(array)) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + if (instr_is_comptime(array) || array_type->data.array.len == 0) { + IrInstruction *result = ir_const(ira, source_instr, wanted_type); init_const_slice(ira->codegen, &result->value, &array->value, 0, array_type->data.array.len, true); result->value.type = wanted_type; return result; } - IrInstruction *start = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, ira->codegen->builtin_types.entry_usize); + IrInstruction *start = ir_const(ira, source_instr, ira->codegen->builtin_types.entry_usize); init_const_usize(ira->codegen, &start->value, 0); - IrInstruction *end = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, ira->codegen->builtin_types.entry_usize); + IrInstruction *end = ir_const(ira, source_instr, ira->codegen->builtin_types.entry_usize); init_const_usize(ira->codegen, &end->value, array_type->data.array.len); if (!array_ptr) array_ptr = ir_get_ref(ira, source_instr, array, true, false); @@ -10092,8 +10163,7 @@ static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *sour ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); init_const_bigint(&result->value, wanted_type, &val->data.x_enum_tag); return result; } @@ -10103,8 +10173,7 @@ static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *sour actual_type->data.enumeration.src_field_count == 1) { assert(wanted_type== ira->codegen->builtin_types.entry_num_lit_int); - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); init_const_bigint(&result->value, wanted_type, &actual_type->data.enumeration.fields[0].value); return result; @@ -10127,8 +10196,7 @@ static IrInstruction *ir_analyze_union_to_tag(IrAnalyze *ira, IrInstruction *sou ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); result->value.special = ConstValSpecialStatic; result->value.type = wanted_type; bigint_init_bigint(&result->value.data.x_enum_tag, &val->data.x_union.tag); @@ -10139,8 +10207,7 @@ static IrInstruction *ir_analyze_union_to_tag(IrAnalyze *ira, IrInstruction *sou if (wanted_type->data.enumeration.layout == ContainerLayoutAuto && wanted_type->data.enumeration.src_field_count == 1) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); result->value.special = ConstValSpecialStatic; result->value.type = wanted_type; TypeEnumField *enum_field = target->value.type->data.unionation.fields[0].enum_field; @@ -10157,8 +10224,7 @@ static IrInstruction *ir_analyze_union_to_tag(IrAnalyze *ira, IrInstruction *sou static IrInstruction *ir_analyze_undefined_to_anything(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, ZigType *wanted_type) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); init_const_undefined(ira->codegen, &result->value); return result; } @@ -10190,8 +10256,7 @@ static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *so buf_sprintf("field '%s' declared here", buf_ptr(union_field->name))); return ira->codegen->invalid_instruction; } - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); result->value.special = ConstValSpecialStatic; result->value.type = wanted_type; bigint_init_bigint(&result->value.data.x_union.tag, &val->data.x_enum_tag); @@ -10246,8 +10311,7 @@ static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction return ira->codegen->invalid_instruction; } } - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); result->value.type = wanted_type; if (wanted_type->id == ZigTypeIdInt) { bigint_init_bigint(&result->value.data.x_bigint, &val->data.x_bigint); @@ -10301,8 +10365,7 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour return ira->codegen->invalid_instruction; } - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); bigint_init_bigint(&result->value.data.x_enum_tag, &val->data.x_bigint); return result; } @@ -10320,8 +10383,7 @@ static IrInstruction *ir_analyze_number_to_literal(IrAnalyze *ira, IrInstruction if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); if (wanted_type->id == ZigTypeIdComptimeFloat) { float_init_float(&result->value, val); } else if (wanted_type->id == ZigTypeIdComptimeInt) { @@ -10344,8 +10406,7 @@ static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *sourc if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); if (!resolve_inferred_error_set(ira->codegen, wanted_type, source_instr->source_node)) { return ira->codegen->invalid_instruction; @@ -10409,12 +10470,11 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); + IrInstruction *result = ir_const(ira, source_instr, wanted_type); ErrorTableEntry *err; if (err_type->id == ZigTypeIdErrorUnion) { - err = val->data.x_err_union.err; + err = val->data.x_err_union.error_set->data.x_err_set; } else if (err_type->id == ZigTypeIdErrorSet) { err = val->data.x_err_set; } else { @@ -10449,15 +10509,11 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc return ira->codegen->invalid_instruction; } if (err_set_type->data.error_set.err_count == 0) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); - result->value.type = wanted_type; + IrInstruction *result = ir_const(ira, source_instr, wanted_type); bigint_init_unsigned(&result->value.data.x_bigint, 0); return result; } else if (err_set_type->data.error_set.err_count == 1) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, wanted_type); - result->value.type = wanted_type; + IrInstruction *result = ir_const(ira, source_instr, wanted_type); ErrorTableEntry *err = err_set_type->data.error_set.errors[0]; bigint_init_unsigned(&result->value.data.x_bigint, err->value); return result; @@ -10504,8 +10560,8 @@ static IrInstruction *ir_analyze_ptr_to_array(IrAnalyze *ira, IrInstruction *sou array_val->type = array_type; array_val->data.x_array.special = ConstArraySpecialNone; array_val->data.x_array.data.s_none.elements = pointee; - array_val->data.x_array.data.s_none.parent.id = ConstParentIdScalar; - array_val->data.x_array.data.s_none.parent.data.p_scalar.scalar_val = pointee; + array_val->parent.id = ConstParentIdScalar; + array_val->parent.data.p_scalar.scalar_val = pointee; IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); @@ -10653,12 +10709,12 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node, false).id == ConstCastResultIdOk) { - return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type); + return ir_analyze_optional_wrap(ira, source_instr, value, wanted_type); } else if (actual_type->id == ZigTypeIdComptimeInt || actual_type->id == ZigTypeIdComptimeFloat) { if (ir_num_lit_fits_in_other_type(ira, value, wanted_child_type, true)) { - return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type); + return ir_analyze_optional_wrap(ira, source_instr, value, wanted_type); } else { return ira->codegen->invalid_instruction; } @@ -10682,7 +10738,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst wanted_child_type); if (type_is_invalid(cast1->value.type)) return ira->codegen->invalid_instruction; - return ir_analyze_maybe_wrap(ira, source_instr, cast1, wanted_type); + return ir_analyze_optional_wrap(ira, source_instr, cast1, wanted_type); } } } @@ -10735,6 +10791,11 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst (wanted_type->id == ZigTypeIdInt || wanted_type->id == ZigTypeIdComptimeInt || wanted_type->id == ZigTypeIdFloat || wanted_type->id == ZigTypeIdComptimeFloat)) { + if (value->value.special == ConstValSpecialUndef) { + IrInstruction *result = ir_const(ira, source_instr, wanted_type); + result->value.special = ConstValSpecialUndef; + return result; + } if (ir_num_lit_fits_in_other_type(ira, value, wanted_type, true)) { if (wanted_type->id == ZigTypeIdComptimeInt || wanted_type->id == ZigTypeIdInt) { IrInstruction *result = ir_const(ira, source_instr, wanted_type); @@ -10788,6 +10849,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // cast from [N]T to []const T + // TODO: once https://github.com/ziglang/zig/issues/265 lands, remove this if (is_slice(wanted_type) && actual_type->id == ZigTypeIdArray) { ZigType *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == ZigTypeIdPointer); @@ -10800,6 +10862,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } // cast from [N]T to ?[]const T + // TODO: once https://github.com/ziglang/zig/issues/265 lands, remove this if (wanted_type->id == ZigTypeIdOptional && is_slice(wanted_type->data.maybe.child_type) && actual_type->id == ZigTypeIdArray) @@ -10894,7 +10957,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // cast from error set to error union type + // cast from E to E!T if (wanted_type->id == ZigTypeIdErrorUnion && actual_type->id == ZigTypeIdErrorSet) { @@ -11046,8 +11109,7 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc ZigType *child_type = type_entry->data.pointer.child_type; // dereferencing a *u0 is comptime known to be 0 if (child_type->id == ZigTypeIdInt && child_type->data.integral.bit_count == 0) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope, - source_instruction->source_node, child_type); + IrInstruction *result = ir_const(ira, source_instruction, child_type); init_const_unsigned_negative(&result->value, child_type, 0, false); return result; } @@ -11061,8 +11123,7 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc { ConstExprValue *pointee = const_ptr_pointee_unchecked(ira->codegen, &ptr->value); if (pointee->special != ConstValSpecialRuntime) { - IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope, - source_instruction->source_node, child_type); + IrInstruction *result = ir_const(ira, source_instruction, child_type); if ((err = ir_read_const_ptr(ira, ira->codegen, source_instruction->source_node, &result->value, &ptr->value))) @@ -11074,7 +11135,11 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc } } } - // TODO if the instruction is a const ref instruction we can skip it + // if the instruction is a const ref instruction we can skip it + if (ptr->id == IrInstructionIdRef) { + IrInstructionRef *ref_inst = reinterpret_cast(ptr); + return ref_inst->value; + } IrInstruction *load_ptr_instruction = ir_build_load_ptr(&ira->new_irb, source_instruction->scope, source_instruction->source_node, ptr); load_ptr_instruction->value.type = child_type; @@ -11321,8 +11386,7 @@ static IrInstruction *ir_analyze_instruction_return(IrAnalyze *ira, IrInstructio static IrInstruction *ir_analyze_instruction_const(IrAnalyze *ira, IrInstructionConst *instruction) { IrInstruction *result = ir_const(ira, &instruction->base, nullptr); - // TODO determine if we need to use copy_const_val here - result->value = instruction->base.value; + copy_const_val(&result->value, &instruction->base.value, true); return result; } @@ -11397,6 +11461,8 @@ static bool optional_value_is_null(ConstExprValue *val) { if (get_codegen_ptr_type(val->type) != nullptr) { return val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && val->data.x_ptr.data.hard_coded_addr.addr == 0; + } else if (is_opt_err_set(val->type)) { + return val->data.x_err_set == nullptr; } else { return val->data.x_optional == nullptr; } @@ -11596,19 +11662,18 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp * if (casted_op2 == ira->codegen->invalid_instruction) return ira->codegen->invalid_instruction; - bool requires_comptime; - switch (type_requires_comptime(ira->codegen, resolved_type)) { - case ReqCompTimeYes: - requires_comptime = true; - break; - case ReqCompTimeNo: - requires_comptime = false; - break; - case ReqCompTimeInvalid: + bool one_possible_value; + switch (type_has_one_possible_value(ira->codegen, resolved_type)) { + case OnePossibleValueInvalid: return ira->codegen->invalid_instruction; + case OnePossibleValueYes: + one_possible_value = true; + break; + case OnePossibleValueNo: + one_possible_value = false; + break; } - bool one_possible_value = !requires_comptime && !type_has_bits(resolved_type); if (one_possible_value || (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2))) { ConstExprValue *op1_val = one_possible_value ? &casted_op1->value : ir_resolve_const(ira, casted_op1, UndefBad); if (op1_val == nullptr) @@ -12497,13 +12562,15 @@ static IrInstruction *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructio zig_unreachable(); } -static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstructionDeclVar *decl_var_instruction) { +static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, + IrInstructionDeclVarSrc *decl_var_instruction) +{ Error err; ZigVar *var = decl_var_instruction->var; IrInstruction *init_value = decl_var_instruction->init_value->child; if (type_is_invalid(init_value->value.type)) { - var->value->type = ira->codegen->builtin_types.entry_invalid; + var->var_type = ira->codegen->builtin_types.entry_invalid; return ira->codegen->invalid_instruction; } @@ -12514,7 +12581,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct ZigType *proposed_type = ir_resolve_type(ira, var_type); explicit_type = validate_var_type(ira->codegen, var_type->source_node, proposed_type); if (type_is_invalid(explicit_type)) { - var->value->type = ira->codegen->builtin_types.entry_invalid; + var->var_type = ira->codegen->builtin_types.entry_invalid; return ira->codegen->invalid_instruction; } } @@ -12539,7 +12606,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct case ReqCompTimeInvalid: result_type = ira->codegen->builtin_types.entry_invalid; break; - case ReqCompTimeYes: { + case ReqCompTimeYes: var_class_requires_const = true; if (!var->gen_is_const && !is_comptime_var) { ir_add_error_node(ira, source_node, @@ -12548,7 +12615,6 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct result_type = ira->codegen->builtin_types.entry_invalid; } break; - } case ReqCompTimeNo: if (casted_init_value->value.special == ConstValSpecialStatic && casted_init_value->value.type->id == ZigTypeIdFn && @@ -12567,7 +12633,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct break; } - if (var->value->type != nullptr && !is_comptime_var) { + if (var->var_type != nullptr && !is_comptime_var) { // This is at least the second time we've seen this variable declaration during analysis. // This means that this is actually a different variable due to, e.g. an inline while loop. // We make a new variable so that it can hold a different type, and so the debug info can @@ -12589,8 +12655,8 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct // This must be done after possibly creating a new variable above var->ref_count = 0; - var->value->type = result_type; - assert(var->value->type); + var->var_type = result_type; + assert(var->var_type); if (type_is_invalid(result_type)) { return ir_const_void(ira, &decl_var_instruction->base); @@ -12598,13 +12664,13 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct if (decl_var_instruction->align_value == nullptr) { if ((err = type_resolve(ira->codegen, result_type, ResolveStatusAlignmentKnown))) { - var->value->type = ira->codegen->builtin_types.entry_invalid; + var->var_type = ira->codegen->builtin_types.entry_invalid; return ir_const_void(ira, &decl_var_instruction->base); } var->align_bytes = get_abi_alignment(ira->codegen, result_type); } else { if (!ir_resolve_align(ira, decl_var_instruction->align_value->child, &var->align_bytes)) { - var->value->type = ira->codegen->builtin_types.entry_invalid; + var->var_type = ira->codegen->builtin_types.entry_invalid; } } @@ -12621,7 +12687,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct } else if (is_comptime_var) { ir_add_error(ira, &decl_var_instruction->base, buf_sprintf("cannot store runtime value in compile time variable")); - var->value->type = ira->codegen->builtin_types.entry_invalid; + var->var_type = ira->codegen->builtin_types.entry_invalid; return ira->codegen->invalid_instruction; } @@ -12629,11 +12695,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruct if (fn_entry) fn_entry->variable_list.append(var); - IrInstruction *result = ir_build_var_decl(&ira->new_irb, - decl_var_instruction->base.scope, decl_var_instruction->base.source_node, - var, var_type, nullptr, casted_init_value); - result->value.type = ira->codegen->builtin_types.entry_void; - return result; + return ir_build_var_decl_gen(ira, &decl_var_instruction->base, var, casted_init_value); } static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructionExport *instruction) { @@ -12963,7 +13025,7 @@ static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node Buf *param_name = param_decl_node->data.param_decl.name; ZigVar *var = add_variable(ira->codegen, param_decl_node, - *exec_scope, param_name, true, arg_val, nullptr); + *exec_scope, param_name, true, arg_val, nullptr, arg_val->type); *exec_scope = var->child_scope; *next_proto_i += 1; @@ -13021,7 +13083,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod if (!param_name) return false; if (!is_var_args) { ZigVar *var = add_variable(ira->codegen, param_decl_node, - *child_scope, param_name, true, arg_val, nullptr); + *child_scope, param_name, true, arg_val, nullptr, arg_val->type); *child_scope = var->child_scope; var->shadowable = !comptime_arg; @@ -13075,9 +13137,7 @@ static ZigVar *get_fn_var_by_index(ZigFn *fn_entry, size_t index) { return fn_entry->variable_list.at(next_var_i); } -static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, - ZigVar *var) -{ +static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, ZigVar *var) { while (var->next_var != nullptr) { var = var->next_var; } @@ -13086,14 +13146,14 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, assert(ira->codegen->errors.length != 0); return ira->codegen->invalid_instruction; } - if (var->value->type == nullptr || type_is_invalid(var->value->type)) + if (var->var_type == nullptr || type_is_invalid(var->var_type)) return ira->codegen->invalid_instruction; bool comptime_var_mem = ir_get_var_is_comptime(var); ConstExprValue *mem_slot = nullptr; - if (var->value->special == ConstValSpecialStatic) { - mem_slot = var->value; + if (var->const_value->special == ConstValSpecialStatic) { + mem_slot = var->const_value; } else { if (var->mem_slot_index != SIZE_MAX && (comptime_var_mem || var->gen_is_const)) { // find the relevant exec_context @@ -13122,7 +13182,7 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, assert(!comptime_var_mem); ptr_mut = ConstPtrMutRuntimeVar; } - return ir_get_const_ptr(ira, instruction, mem_slot, var->value->type, + return ir_get_const_ptr(ira, instruction, mem_slot, var->var_type, ptr_mut, is_const, is_volatile, var->align_bytes); } } @@ -13133,7 +13193,7 @@ no_mem_slot: IrInstruction *var_ptr_instruction = ir_build_var_ptr(&ira->new_irb, instruction->scope, instruction->source_node, var); - var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->value->type, + var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->var_type, var->src_is_const, is_volatile, PtrLenSingle, var->align_bytes, 0, 0); bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr); @@ -13142,6 +13202,96 @@ no_mem_slot: return var_ptr_instruction; } +static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *ptr, IrInstruction *uncasted_value) +{ + if (ptr->value.type->id != ZigTypeIdPointer) { + ir_add_error(ira, ptr, + buf_sprintf("attempt to dereference non pointer type '%s'", buf_ptr(&ptr->value.type->name))); + return ira->codegen->invalid_instruction; + } + + if (ptr->value.data.x_ptr.special == ConstPtrSpecialDiscard) { + return ir_const_void(ira, source_instr); + } + + if (ptr->value.type->data.pointer.is_const && !source_instr->is_gen) { + ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); + return ira->codegen->invalid_instruction; + } + + ZigType *child_type = ptr->value.type->data.pointer.child_type; + IrInstruction *value = ir_implicit_cast(ira, uncasted_value, child_type); + if (value == ira->codegen->invalid_instruction) + return ira->codegen->invalid_instruction; + + if (instr_is_comptime(ptr) && ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { + if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst) { + ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); + return ira->codegen->invalid_instruction; + } + if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar) { + if (instr_is_comptime(value)) { + ConstExprValue *dest_val = const_ptr_pointee(ira, ira->codegen, &ptr->value, source_instr->source_node); + if (dest_val == nullptr) + return ira->codegen->invalid_instruction; + if (dest_val->special != ConstValSpecialRuntime) { + // TODO this allows a value stored to have the original value modified and then + // have that affect what should be a copy. We need some kind of advanced copy-on-write + // system to make these two tests pass at the same time: + // * "string literal used as comptime slice is memoized" + // * "comptime modification of const struct field" - except modified to avoid + // ConstPtrMutComptimeVar, thus defeating the logic below. + bool same_global_refs = ptr->value.data.x_ptr.mut != ConstPtrMutComptimeVar; + copy_const_val(dest_val, &value->value, same_global_refs); + if (!ira->new_irb.current_basic_block->must_be_comptime_source_instr) { + switch (type_has_one_possible_value(ira->codegen, child_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_instruction; + case OnePossibleValueNo: + ira->new_irb.current_basic_block->must_be_comptime_source_instr = source_instr; + break; + case OnePossibleValueYes: + break; + } + } + return ir_const_void(ira, source_instr); + } + } + ir_add_error(ira, source_instr, + buf_sprintf("cannot store runtime value in compile time variable")); + ConstExprValue *dest_val = const_ptr_pointee_unchecked(ira->codegen, &ptr->value); + dest_val->type = ira->codegen->builtin_types.entry_invalid; + + return ira->codegen->invalid_instruction; + } + } + + switch (type_requires_comptime(ira->codegen, child_type)) { + case ReqCompTimeInvalid: + return ira->codegen->invalid_instruction; + case ReqCompTimeYes: + switch (type_has_one_possible_value(ira->codegen, ptr->value.type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_instruction; + case OnePossibleValueNo: + ir_add_error(ira, source_instr, + buf_sprintf("cannot store runtime value in type '%s'", buf_ptr(&child_type->name))); + return ira->codegen->invalid_instruction; + case OnePossibleValueYes: + return ir_const_void(ira, source_instr); + } + zig_unreachable(); + case ReqCompTimeNo: + break; + } + + IrInstruction *result = ir_build_store_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, + ptr, value); + result->value.type = ira->codegen->builtin_types.entry_void; + return result; +} + static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instruction, ZigFn *fn_entry, ZigType *fn_type, IrInstruction *fn_ref, IrInstruction *first_arg_ptr, bool comptime_fn_call, FnInline fn_inline) @@ -13286,7 +13436,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call } bool cacheable = fn_eval_cacheable(exec_scope, return_type); - IrInstruction *result = nullptr; + ConstExprValue *result = nullptr; if (cacheable) { auto entry = ira->codegen->memoized_fn_eval_table.maybe_get(exec_scope); if (entry) @@ -13302,18 +13452,19 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call if (inferred_err_set_type != nullptr) { inferred_err_set_type->data.error_set.infer_fn = nullptr; - if (result->value.type->id == ZigTypeIdErrorUnion) { - if (result->value.data.x_err_union.err != nullptr) { + if (result->type->id == ZigTypeIdErrorUnion) { + ErrorTableEntry *err = result->data.x_err_union.error_set->data.x_err_set; + if (err != nullptr) { inferred_err_set_type->data.error_set.err_count = 1; inferred_err_set_type->data.error_set.errors = allocate(1); - inferred_err_set_type->data.error_set.errors[0] = result->value.data.x_err_union.err; + inferred_err_set_type->data.error_set.errors[0] = err; } - ZigType *fn_inferred_err_set_type = result->value.type->data.error_union.err_set_type; + ZigType *fn_inferred_err_set_type = result->type->data.error_union.err_set_type; inferred_err_set_type->data.error_set.err_count = fn_inferred_err_set_type->data.error_set.err_count; inferred_err_set_type->data.error_set.errors = fn_inferred_err_set_type->data.error_set.errors; - } else if (result->value.type->id == ZigTypeIdErrorSet) { - inferred_err_set_type->data.error_set.err_count = result->value.type->data.error_set.err_count; - inferred_err_set_type->data.error_set.errors = result->value.type->data.error_set.errors; + } else if (result->type->id == ZigTypeIdErrorSet) { + inferred_err_set_type->data.error_set.err_count = result->type->data.error_set.err_count; + inferred_err_set_type->data.error_set.errors = result->type->data.error_set.errors; } } @@ -13321,13 +13472,12 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call ira->codegen->memoized_fn_eval_table.put(exec_scope, result); } - if (type_is_invalid(result->value.type)) + if (type_is_invalid(result->type)) return ira->codegen->invalid_instruction; } - IrInstruction *new_instruction = ir_const(ira, &call_instruction->base, result->value.type); - // TODO should we use copy_const_val? - new_instruction->value = result->value; + IrInstruction *new_instruction = ir_const(ira, &call_instruction->base, result->type); + copy_const_val(&new_instruction->value, result, true); new_instruction->value.type = return_type; return ir_finish_anal(ira, new_instruction); } @@ -13486,18 +13636,21 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call ConstExprValue *var_args_val = create_const_arg_tuple(ira->codegen, first_var_arg, inst_fn_type_id.param_count); ZigVar *var = add_variable(ira->codegen, param_decl_node, - impl_fn->child_scope, param_name, true, var_args_val, nullptr); + impl_fn->child_scope, param_name, true, var_args_val, nullptr, var_args_val->type); impl_fn->child_scope = var->child_scope; } if (fn_proto_node->data.fn_proto.align_expr != nullptr) { - IrInstruction *align_result = ir_eval_const_value(ira->codegen, impl_fn->child_scope, + ConstExprValue *align_result = ir_eval_const_value(ira->codegen, impl_fn->child_scope, fn_proto_node->data.fn_proto.align_expr, get_align_amt_type(ira->codegen), ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr, nullptr, fn_proto_node->data.fn_proto.align_expr, nullptr, ira->new_irb.exec); + IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, + impl_fn->child_scope, fn_proto_node->data.fn_proto.align_expr); + const_instruction->base.value = *align_result; uint32_t align_bytes = 0; - ir_resolve_align(ira, align_result, &align_bytes); + ir_resolve_align(ira, &const_instruction->base, &align_bytes); impl_fn->align_bytes = align_bytes; inst_fn_type_id.alignment = align_bytes; } @@ -13575,12 +13728,12 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call ira->codegen->fn_defs.append(impl_fn); } - ZigType *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type; - if (fn_type_can_fail(&impl_fn->type_entry->data.fn.fn_type_id)) { + FnTypeId *impl_fn_type_id = &impl_fn->type_entry->data.fn.fn_type_id; + if (fn_type_can_fail(impl_fn_type_id)) { parent_fn_entry->calls_or_awaits_errorable_fn = true; } - size_t impl_param_count = impl_fn->type_entry->data.fn.fn_type_id.param_count; + size_t impl_param_count = impl_fn_type_id->param_count; if (call_instruction->is_async) { IrInstruction *result = ir_analyze_async_call(ira, call_instruction, impl_fn, impl_fn->type_entry, fn_ref, casted_args, impl_param_count, async_allocator_inst); @@ -13593,9 +13746,9 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call call_instruction->base.scope, call_instruction->base.source_node, impl_fn, nullptr, impl_param_count, casted_args, false, fn_inline, call_instruction->is_async, nullptr, casted_new_stack); - new_call_instruction->value.type = return_type; + new_call_instruction->value.type = impl_fn_type_id->return_type; - ir_add_alloca(ira, new_call_instruction, return_type); + ir_add_alloca(ira, new_call_instruction, impl_fn_type_id->return_type); return ir_finish_anal(ira, new_call_instruction); } @@ -13790,6 +13943,13 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source switch (ptr_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: zig_unreachable(); + case ConstPtrSpecialNull: + if (dst_size == 0) + return ErrorNone; + opt_ir_add_error_node(ira, codegen, source_node, + buf_sprintf("attempt to read %zu bytes from null pointer", + dst_size)); + return ErrorSemanticAnalyzeFail; case ConstPtrSpecialRef: { opt_ir_add_error_node(ira, codegen, source_node, buf_sprintf("attempt to read %zu bytes from pointer to %s which is %zu bytes", @@ -13822,6 +13982,9 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source return ErrorNone; } case ConstPtrSpecialBaseStruct: + case ConstPtrSpecialBaseErrorUnionCode: + case ConstPtrSpecialBaseErrorUnionPayload: + case ConstPtrSpecialBaseOptionalPayload: case ConstPtrSpecialDiscard: case ConstPtrSpecialHardCodedAddr: case ConstPtrSpecialFunction: @@ -14017,9 +14180,14 @@ static IrInstruction *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructi if (!ir_resolve_comptime(ira, cond_br_instruction->is_comptime->child, &is_comptime)) return ir_unreach_error(ira); - if (is_comptime || instr_is_comptime(condition)) { + ZigType *bool_type = ira->codegen->builtin_types.entry_bool; + IrInstruction *casted_condition = ir_implicit_cast(ira, condition, bool_type); + if (type_is_invalid(casted_condition->value.type)) + return ir_unreach_error(ira); + + if (is_comptime || instr_is_comptime(casted_condition)) { bool cond_is_true; - if (!ir_resolve_bool(ira, condition, &cond_is_true)) + if (!ir_resolve_bool(ira, casted_condition, &cond_is_true)) return ir_unreach_error(ira); IrBasicBlock *old_dest_block = cond_is_true ? @@ -14038,11 +14206,6 @@ static IrInstruction *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructi return ir_finish_anal(ira, result); } - ZigType *bool_type = ira->codegen->builtin_types.entry_bool; - IrInstruction *casted_condition = ir_implicit_cast(ira, condition, bool_type); - if (casted_condition == ira->codegen->invalid_instruction) - return ir_unreach_error(ira); - assert(cond_br_instruction->then_block != cond_br_instruction->else_block); IrBasicBlock *new_then_block = ir_get_new_bb_runtime(ira, cond_br_instruction->then_block, &cond_br_instruction->base); if (new_then_block == nullptr) @@ -14081,8 +14244,7 @@ static IrInstruction *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPh if (value->value.special != ConstValSpecialRuntime) { IrInstruction *result = ir_const(ira, &phi_instruction->base, nullptr); - // TODO use copy_const_val? - result->value = value->value; + copy_const_val(&result->value, &value->value, true); return result; } else { return value; @@ -14131,14 +14293,24 @@ static IrInstruction *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPh if (type_is_invalid(resolved_type)) return ira->codegen->invalid_instruction; - if (resolved_type->id == ZigTypeIdComptimeFloat || - resolved_type->id == ZigTypeIdComptimeInt || - resolved_type->id == ZigTypeIdNull || - resolved_type->id == ZigTypeIdUndefined) - { + switch (type_has_one_possible_value(ira->codegen, resolved_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_instruction; + case OnePossibleValueYes: + return ir_const(ira, &phi_instruction->base, resolved_type); + case OnePossibleValueNo: + break; + } + + switch (type_requires_comptime(ira->codegen, resolved_type)) { + case ReqCompTimeInvalid: + return ira->codegen->invalid_instruction; + case ReqCompTimeYes: ir_add_error_node(ira, phi_instruction->base.source_node, - buf_sprintf("unable to infer expression type")); + buf_sprintf("values of type '%s' must be comptime known", buf_ptr(&resolved_type->name))); return ira->codegen->invalid_instruction; + case ReqCompTimeNo: + break; } bool all_stack_ptrs = (resolved_type->id == ZigTypeIdPointer); @@ -14428,10 +14600,18 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct } case ConstPtrSpecialBaseStruct: zig_panic("TODO elem ptr on a const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO elem ptr on a const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO elem ptr on a const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO elem ptr on a const inner optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO element ptr of a function casted to a ptr"); + case ConstPtrSpecialNull: + zig_panic("TODO elem ptr on a null pointer"); } if (new_index >= mem_size) { ir_add_error_node(ira, elem_ptr_instruction->base.source_node, @@ -14481,10 +14661,18 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct } case ConstPtrSpecialBaseStruct: zig_panic("TODO elem ptr on a slice backed by const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO elem ptr on a slice backed by const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO elem ptr on a slice backed by const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO elem ptr on a slice backed by const optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO elem ptr on a slice that was ptrcast from a function"); + case ConstPtrSpecialNull: + zig_panic("TODO elem ptr on a slice has a null pointer"); } return result; } else if (array_type->id == ZigTypeIdArray) { @@ -15171,74 +15359,23 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc } } -static IrInstruction *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *load_ptr_instruction) { - IrInstruction *ptr = load_ptr_instruction->ptr->child; +static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstructionStorePtr *instruction) { + IrInstruction *ptr = instruction->ptr->child; if (type_is_invalid(ptr->value.type)) return ira->codegen->invalid_instruction; - return ir_get_deref(ira, &load_ptr_instruction->base, ptr); -} -static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstructionStorePtr *store_ptr_instruction) { - IrInstruction *ptr = store_ptr_instruction->ptr->child; - if (type_is_invalid(ptr->value.type)) - return ira->codegen->invalid_instruction; - - IrInstruction *value = store_ptr_instruction->value->child; + IrInstruction *value = instruction->value->child; if (type_is_invalid(value->value.type)) return ira->codegen->invalid_instruction; - if (ptr->value.type->id != ZigTypeIdPointer) { - ir_add_error(ira, ptr, - buf_sprintf("attempt to dereference non pointer type '%s'", buf_ptr(&ptr->value.type->name))); - return ira->codegen->invalid_instruction; - } - - if (ptr->value.data.x_ptr.special == ConstPtrSpecialDiscard) { - return ir_const_void(ira, &store_ptr_instruction->base); - } - - if (ptr->value.type->data.pointer.is_const && !store_ptr_instruction->base.is_gen) { - ir_add_error(ira, &store_ptr_instruction->base, buf_sprintf("cannot assign to constant")); - return ira->codegen->invalid_instruction; - } + return ir_analyze_store_ptr(ira, &instruction->base, ptr, value); +} - ZigType *child_type = ptr->value.type->data.pointer.child_type; - IrInstruction *casted_value = ir_implicit_cast(ira, value, child_type); - if (casted_value == ira->codegen->invalid_instruction) +static IrInstruction *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *instruction) { + IrInstruction *ptr = instruction->ptr->child; + if (type_is_invalid(ptr->value.type)) return ira->codegen->invalid_instruction; - - if (instr_is_comptime(ptr) && ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { - if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst) { - ir_add_error(ira, &store_ptr_instruction->base, buf_sprintf("cannot assign to constant")); - return ira->codegen->invalid_instruction; - } - if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar) { - if (instr_is_comptime(casted_value)) { - ConstExprValue *dest_val = const_ptr_pointee(ira, ira->codegen, &ptr->value, store_ptr_instruction->base.source_node); - if (dest_val == nullptr) - return ira->codegen->invalid_instruction; - if (dest_val->special != ConstValSpecialRuntime) { - *dest_val = casted_value->value; - if (!ira->new_irb.current_basic_block->must_be_comptime_source_instr) { - ira->new_irb.current_basic_block->must_be_comptime_source_instr = &store_ptr_instruction->base; - } - return ir_const_void(ira, &store_ptr_instruction->base); - } - } - ir_add_error(ira, &store_ptr_instruction->base, - buf_sprintf("cannot store runtime value in compile time variable")); - ConstExprValue *dest_val = const_ptr_pointee_unchecked(ira->codegen, &ptr->value); - dest_val->type = ira->codegen->builtin_types.entry_invalid; - - return ira->codegen->invalid_instruction; - } - } - - IrInstruction *result = ir_build_store_ptr(&ira->new_irb, - store_ptr_instruction->base.scope, store_ptr_instruction->base.source_node, - ptr, casted_value); - result->value.type = ira->codegen->builtin_types.entry_void; - return result; + return ir_get_deref(ira, &instruction->base, ptr); } static IrInstruction *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructionTypeOf *typeof_instruction) { @@ -15709,11 +15846,7 @@ static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira, zig_unreachable(); } -static IrInstruction *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrInstructionTestNonNull *instruction) { - IrInstruction *value = instruction->value->child; - if (type_is_invalid(value->value.type)) - return ira->codegen->invalid_instruction; - +static IrInstruction *ir_analyze_test_non_null(IrAnalyze *ira, IrInstruction *source_inst, IrInstruction *value) { ZigType *type_entry = value->value.type; if (type_entry->id == ZigTypeIdOptional) { @@ -15722,60 +15855,66 @@ static IrInstruction *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrIns if (!maybe_val) return ira->codegen->invalid_instruction; - return ir_const_bool(ira, &instruction->base, !optional_value_is_null(maybe_val)); + return ir_const_bool(ira, source_inst, !optional_value_is_null(maybe_val)); } IrInstruction *result = ir_build_test_nonnull(&ira->new_irb, - instruction->base.scope, instruction->base.source_node, value); + source_inst->scope, source_inst->source_node, value); result->value.type = ira->codegen->builtin_types.entry_bool; return result; } else if (type_entry->id == ZigTypeIdNull) { - return ir_const_bool(ira, &instruction->base, false); + return ir_const_bool(ira, source_inst, false); } else { - return ir_const_bool(ira, &instruction->base, true); + return ir_const_bool(ira, source_inst, true); } } -static IrInstruction *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, - IrInstructionUnwrapOptional *unwrap_maybe_instruction) -{ - IrInstruction *value = unwrap_maybe_instruction->value->child; +static IrInstruction *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrInstructionTestNonNull *instruction) { + IrInstruction *value = instruction->value->child; if (type_is_invalid(value->value.type)) return ira->codegen->invalid_instruction; - ZigType *ptr_type = value->value.type; + return ir_analyze_test_non_null(ira, &instruction->base, value); +} + +static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *base_ptr, bool safety_check_on) +{ + ZigType *ptr_type = base_ptr->value.type; assert(ptr_type->id == ZigTypeIdPointer); ZigType *type_entry = ptr_type->data.pointer.child_type; - if (type_is_invalid(type_entry)) { + if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; - } else if (type_entry->id != ZigTypeIdOptional) { - ir_add_error_node(ira, unwrap_maybe_instruction->value->source_node, + + if (type_entry->id != ZigTypeIdOptional) { + ir_add_error_node(ira, base_ptr->source_node, buf_sprintf("expected optional type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->invalid_instruction; } + ZigType *child_type = type_entry->data.maybe.child_type; ZigType *result_type = get_pointer_to_type_extra(ira->codegen, child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0); - if (instr_is_comptime(value)) { - ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); + if (instr_is_comptime(base_ptr)) { + ConstExprValue *val = ir_resolve_const(ira, base_ptr, UndefBad); if (!val) return ira->codegen->invalid_instruction; - ConstExprValue *maybe_val = const_ptr_pointee(ira, ira->codegen, val, unwrap_maybe_instruction->base.source_node); + ConstExprValue *maybe_val = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node); if (maybe_val == nullptr) return ira->codegen->invalid_instruction; if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { if (optional_value_is_null(maybe_val)) { - ir_add_error(ira, &unwrap_maybe_instruction->base, buf_sprintf("unable to unwrap null")); + ir_add_error(ira, source_instr, buf_sprintf("unable to unwrap null")); return ira->codegen->invalid_instruction; } - IrInstruction *result = ir_const(ira, &unwrap_maybe_instruction->base, result_type); + IrInstruction *result = ir_const(ira, source_instr, result_type); ConstExprValue *out_val = &result->value; out_val->data.x_ptr.special = ConstPtrSpecialRef; out_val->data.x_ptr.mut = val->data.x_ptr.mut; - if (type_is_codegen_pointer(child_type)) { + if (types_have_same_zig_comptime_repr(type_entry, child_type)) { out_val->data.x_ptr.data.ref.pointee = maybe_val; } else { out_val->data.x_ptr.data.ref.pointee = maybe_val->data.x_optional; @@ -15784,13 +15923,22 @@ static IrInstruction *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, } } - IrInstruction *result = ir_build_unwrap_maybe(&ira->new_irb, - unwrap_maybe_instruction->base.scope, unwrap_maybe_instruction->base.source_node, - value, unwrap_maybe_instruction->safety_check_on); + IrInstruction *result = ir_build_optional_unwrap_ptr(&ira->new_irb, source_instr->scope, + source_instr->source_node, base_ptr, safety_check_on); result->value.type = result_type; return result; } +static IrInstruction *ir_analyze_instruction_optional_unwrap_ptr(IrAnalyze *ira, + IrInstructionOptionalUnwrapPtr *instruction) +{ + IrInstruction *base_ptr = instruction->base_ptr->child; + if (type_is_invalid(base_ptr->value.type)) + return ira->codegen->invalid_instruction; + + return ir_analyze_unwrap_optional_payload(ira, &instruction->base, base_ptr, instruction->safety_check_on); +} + static IrInstruction *ir_analyze_instruction_ctz(IrAnalyze *ira, IrInstructionCtz *ctz_instruction) { IrInstruction *value = ctz_instruction->value->child; if (type_is_invalid(value->value.type)) { @@ -16091,9 +16239,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, return result; } - IrInstruction *result = ir_build_load_ptr(&ira->new_irb, - switch_target_instruction->base.scope, switch_target_instruction->base.source_node, - target_value_ptr); + IrInstruction *result = ir_get_deref(ira, &switch_target_instruction->base, target_value_ptr); result->value.type = target_type; return result; } @@ -16123,8 +16269,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, return result; } - IrInstruction *union_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, - switch_target_instruction->base.source_node, target_value_ptr); + IrInstruction *union_value = ir_get_deref(ira, &switch_target_instruction->base, target_value_ptr); union_value->value.type = target_type; IrInstruction *union_tag_inst = ir_build_union_tag(&ira->new_irb, switch_target_instruction->base.scope, @@ -16148,8 +16293,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, return result; } - IrInstruction *enum_value = ir_build_load_ptr(&ira->new_irb, switch_target_instruction->base.scope, - switch_target_instruction->base.source_node, target_value_ptr); + IrInstruction *enum_value = ir_get_deref(ira, &switch_target_instruction->base, target_value_ptr); enum_value->value.type = target_type; return enum_value; } @@ -16306,7 +16450,7 @@ static IrInstruction *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrI Error err; assert(container_type->id == ZigTypeIdUnion); - if ((err = ensure_complete_type(ira->codegen, container_type))) + if ((err = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_instruction; if (instr_field_count != 1) { @@ -16350,12 +16494,8 @@ static IrInstruction *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrI ConstExprValue *out_val = &result->value; out_val->data.x_union.payload = field_val; out_val->data.x_union.tag = type_field->enum_field->value; - - ConstParent *parent = get_const_val_parent(ira->codegen, field_val); - if (parent != nullptr) { - parent->id = ConstParentIdUnion; - parent->data.p_union.union_val = out_val; - } + out_val->parent.id = ConstParentIdUnion; + out_val->parent.data.p_union.union_val = out_val; return result; } @@ -16382,7 +16522,7 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc return ira->codegen->invalid_instruction; } - if ((err = ensure_complete_type(ira->codegen, container_type))) + if ((err = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_instruction; size_t actual_field_count = container_type->data.structure.src_field_count; @@ -16461,9 +16601,8 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc if (const_val.special == ConstValSpecialStatic) { IrInstruction *result = ir_const(ira, instruction, nullptr); ConstExprValue *out_val = &result->value; - // TODO copy_const_val? - *out_val = const_val; - result->value.type = container_type; + copy_const_val(out_val, &const_val, true); + out_val->type = container_type; for (size_t i = 0; i < instr_field_count; i += 1) { ConstExprValue *field_val = &out_val->data.x_struct.fields[i]; @@ -16495,127 +16634,119 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira, IrInstructionContainerInitList *instruction) { - IrInstruction *container_type_value = instruction->container_type->child; - if (type_is_invalid(container_type_value->value.type)) + ZigType *container_type = ir_resolve_type(ira, instruction->container_type->child); + if (type_is_invalid(container_type)) return ira->codegen->invalid_instruction; size_t elem_count = instruction->item_count; - if (container_type_value->value.type->id == ZigTypeIdMetaType) { - ZigType *container_type = ir_resolve_type(ira, container_type_value); - if (type_is_invalid(container_type)) - return ira->codegen->invalid_instruction; - if (container_type->id == ZigTypeIdStruct && !is_slice(container_type) && elem_count == 0) { - return ir_analyze_container_init_fields(ira, &instruction->base, container_type, - 0, nullptr); - } else if (is_slice(container_type) || container_type->id == ZigTypeIdArray) { - // array is same as slice init but we make a compile error if the length is wrong - ZigType *child_type; - if (container_type->id == ZigTypeIdArray) { - child_type = container_type->data.array.child_type; - if (container_type->data.array.len != elem_count) { - ZigType *literal_type = get_array_type(ira->codegen, child_type, elem_count); - - ir_add_error(ira, &instruction->base, - buf_sprintf("expected %s literal, found %s literal", - buf_ptr(&container_type->name), buf_ptr(&literal_type->name))); - return ira->codegen->invalid_instruction; - } - } else { - ZigType *pointer_type = container_type->data.structure.fields[slice_ptr_index].type_entry; - assert(pointer_type->id == ZigTypeIdPointer); - child_type = pointer_type->data.pointer.child_type; + if (container_type->id == ZigTypeIdStruct && !is_slice(container_type) && elem_count == 0) { + return ir_analyze_container_init_fields(ira, &instruction->base, container_type, + 0, nullptr); + } else if (is_slice(container_type) || container_type->id == ZigTypeIdArray) { + // array is same as slice init but we make a compile error if the length is wrong + ZigType *child_type; + if (container_type->id == ZigTypeIdArray) { + child_type = container_type->data.array.child_type; + if (container_type->data.array.len != elem_count) { + ZigType *literal_type = get_array_type(ira->codegen, child_type, elem_count); + + ir_add_error(ira, &instruction->base, + buf_sprintf("expected %s literal, found %s literal", + buf_ptr(&container_type->name), buf_ptr(&literal_type->name))); + return ira->codegen->invalid_instruction; } + } else { + ZigType *pointer_type = container_type->data.structure.fields[slice_ptr_index].type_entry; + assert(pointer_type->id == ZigTypeIdPointer); + child_type = pointer_type->data.pointer.child_type; + } - ZigType *fixed_size_array_type = get_array_type(ira->codegen, child_type, elem_count); - - ConstExprValue const_val = {}; - const_val.special = ConstValSpecialStatic; - const_val.type = fixed_size_array_type; - const_val.data.x_array.data.s_none.elements = create_const_vals(elem_count); + ZigType *fixed_size_array_type = get_array_type(ira->codegen, child_type, elem_count); - bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->base.scope); + ConstExprValue const_val = {}; + const_val.special = ConstValSpecialStatic; + const_val.type = fixed_size_array_type; + const_val.data.x_array.data.s_none.elements = create_const_vals(elem_count); - IrInstruction **new_items = allocate(elem_count); + bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->base.scope); - IrInstruction *first_non_const_instruction = nullptr; + IrInstruction **new_items = allocate(elem_count); - for (size_t i = 0; i < elem_count; i += 1) { - IrInstruction *arg_value = instruction->items[i]->child; - if (type_is_invalid(arg_value->value.type)) - return ira->codegen->invalid_instruction; + IrInstruction *first_non_const_instruction = nullptr; - IrInstruction *casted_arg = ir_implicit_cast(ira, arg_value, child_type); - if (casted_arg == ira->codegen->invalid_instruction) - return ira->codegen->invalid_instruction; + for (size_t i = 0; i < elem_count; i += 1) { + IrInstruction *arg_value = instruction->items[i]->child; + if (type_is_invalid(arg_value->value.type)) + return ira->codegen->invalid_instruction; - new_items[i] = casted_arg; + IrInstruction *casted_arg = ir_implicit_cast(ira, arg_value, child_type); + if (casted_arg == ira->codegen->invalid_instruction) + return ira->codegen->invalid_instruction; - if (const_val.special == ConstValSpecialStatic) { - if (is_comptime || casted_arg->value.special != ConstValSpecialRuntime) { - ConstExprValue *elem_val = ir_resolve_const(ira, casted_arg, UndefBad); - if (!elem_val) - return ira->codegen->invalid_instruction; - - copy_const_val(&const_val.data.x_array.data.s_none.elements[i], elem_val, true); - } else { - first_non_const_instruction = casted_arg; - const_val.special = ConstValSpecialRuntime; - } - } - } + new_items[i] = casted_arg; if (const_val.special == ConstValSpecialStatic) { - IrInstruction *result = ir_const(ira, &instruction->base, nullptr); - ConstExprValue *out_val = &result->value; - // TODO copy_const_val? - *out_val = const_val; - result->value.type = fixed_size_array_type; - for (size_t i = 0; i < elem_count; i += 1) { - ConstExprValue *elem_val = &out_val->data.x_array.data.s_none.elements[i]; - ConstParent *parent = get_const_val_parent(ira->codegen, elem_val); - if (parent != nullptr) { - parent->id = ConstParentIdArray; - parent->data.p_array.array_val = out_val; - parent->data.p_array.elem_index = i; - } + if (is_comptime || casted_arg->value.special != ConstValSpecialRuntime) { + ConstExprValue *elem_val = ir_resolve_const(ira, casted_arg, UndefBad); + if (!elem_val) + return ira->codegen->invalid_instruction; + + copy_const_val(&const_val.data.x_array.data.s_none.elements[i], elem_val, true); + } else { + first_non_const_instruction = casted_arg; + const_val.special = ConstValSpecialRuntime; } - return result; } + } - if (is_comptime) { - ir_add_error_node(ira, first_non_const_instruction->source_node, - buf_sprintf("unable to evaluate constant expression")); - return ira->codegen->invalid_instruction; + if (const_val.special == ConstValSpecialStatic) { + IrInstruction *result = ir_const(ira, &instruction->base, nullptr); + ConstExprValue *out_val = &result->value; + copy_const_val(out_val, &const_val, true); + result->value.type = fixed_size_array_type; + for (size_t i = 0; i < elem_count; i += 1) { + ConstExprValue *elem_val = &out_val->data.x_array.data.s_none.elements[i]; + ConstParent *parent = get_const_val_parent(ira->codegen, elem_val); + if (parent != nullptr) { + parent->id = ConstParentIdArray; + parent->data.p_array.array_val = out_val; + parent->data.p_array.elem_index = i; + } } + return result; + } - IrInstruction *new_instruction = ir_build_container_init_list(&ira->new_irb, - instruction->base.scope, instruction->base.source_node, - container_type_value, elem_count, new_items); - new_instruction->value.type = fixed_size_array_type; - ir_add_alloca(ira, new_instruction, fixed_size_array_type); - return new_instruction; - } else if (container_type->id == ZigTypeIdVoid) { - if (elem_count != 0) { - ir_add_error_node(ira, instruction->base.source_node, - buf_sprintf("void expression expects no arguments")); - return ira->codegen->invalid_instruction; - } - return ir_const_void(ira, &instruction->base); - } else { + if (is_comptime) { + ir_add_error_node(ira, first_non_const_instruction->source_node, + buf_sprintf("unable to evaluate constant expression")); + return ira->codegen->invalid_instruction; + } + + IrInstruction *new_instruction = ir_build_container_init_list(&ira->new_irb, + instruction->base.scope, instruction->base.source_node, + nullptr, elem_count, new_items); + new_instruction->value.type = fixed_size_array_type; + ir_add_alloca(ira, new_instruction, fixed_size_array_type); + return new_instruction; + } else if (container_type->id == ZigTypeIdVoid) { + if (elem_count != 0) { ir_add_error_node(ira, instruction->base.source_node, - buf_sprintf("type '%s' does not support array initialization", - buf_ptr(&container_type->name))); + buf_sprintf("void expression expects no arguments")); return ira->codegen->invalid_instruction; } + return ir_const_void(ira, &instruction->base); } else { - ir_add_error(ira, container_type_value, - buf_sprintf("expected type, found '%s' value", buf_ptr(&container_type_value->value.type->name))); + ir_add_error_node(ira, instruction->base.source_node, + buf_sprintf("type '%s' does not support array initialization", + buf_ptr(&container_type->name))); return ira->codegen->invalid_instruction; } } -static IrInstruction *ir_analyze_instruction_container_init_fields(IrAnalyze *ira, IrInstructionContainerInitFields *instruction) { +static IrInstruction *ir_analyze_instruction_container_init_fields(IrAnalyze *ira, + IrInstructionContainerInitFields *instruction) +{ IrInstruction *container_type_value = instruction->container_type->child; ZigType *container_type = ir_resolve_type(ira, container_type_value); if (type_is_invalid(container_type)) @@ -16675,7 +16806,7 @@ static IrInstruction *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstruct if (type_is_invalid(value->value.type)) return ira->codegen->invalid_instruction; - IrInstruction *casted_value = ir_implicit_cast(ira, value, value->value.type); + IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->codegen->builtin_types.entry_global_error_set); if (type_is_invalid(casted_value->value.type)) return ira->codegen->invalid_instruction; @@ -16936,10 +17067,11 @@ static ZigType *ir_type_info_get_type(IrAnalyze *ira, const char *type_name, Zig ZigVar *var = tld->var; - if ((err = ensure_complete_type(ira->codegen, var->value->type))) + if ((err = ensure_complete_type(ira->codegen, var->const_value->type))) return ira->codegen->builtin_types.entry_invalid; - assert(var->value->type->id == ZigTypeIdMetaType); - return var->value->data.x_type; + + assert(var->const_value->type->id == ZigTypeIdMetaType); + return var->const_value->data.x_type; } static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, ScopeDecls *decls_scope) { @@ -16994,7 +17126,6 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco definition_array->special = ConstValSpecialStatic; definition_array->type = get_array_type(ira->codegen, type_info_definition_type, definition_count); definition_array->data.x_array.special = ConstArraySpecialNone; - definition_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; definition_array->data.x_array.data.s_none.elements = create_const_vals(definition_count); init_const_slice(ira->codegen, out_val, definition_array, 0, definition_count, false); @@ -17025,33 +17156,30 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco inner_fields[1].data.x_bool = curr_entry->value->visib_mod == VisibModPub; inner_fields[2].special = ConstValSpecialStatic; inner_fields[2].type = type_info_definition_data_type; - inner_fields[2].data.x_union.parent.id = ConstParentIdStruct; - inner_fields[2].data.x_union.parent.data.p_struct.struct_val = definition_val; - inner_fields[2].data.x_union.parent.data.p_struct.field_index = 1; + inner_fields[2].parent.id = ConstParentIdStruct; + inner_fields[2].parent.data.p_struct.struct_val = definition_val; + inner_fields[2].parent.data.p_struct.field_index = 1; switch (curr_entry->value->id) { case TldIdVar: { ZigVar *var = ((TldVar *)curr_entry->value)->var; - if ((err = ensure_complete_type(ira->codegen, var->value->type))) + if ((err = ensure_complete_type(ira->codegen, var->const_value->type))) return ErrorSemanticAnalyzeFail; - if (var->value->type->id == ZigTypeIdMetaType) - { + if (var->const_value->type->id == ZigTypeIdMetaType) { // We have a variable of type 'type', so it's actually a type definition. // 0: Data.Type: type bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 0); - inner_fields[2].data.x_union.payload = var->value; - } - else - { + inner_fields[2].data.x_union.payload = var->const_value; + } else { // We have a variable of another type, so we store the type of the variable. // 1: Data.Var: type bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 1); ConstExprValue *payload = create_const_vals(1); payload->type = ira->codegen->builtin_types.entry_type; - payload->data.x_type = var->value->type; + payload->data.x_type = var->const_value->type; inner_fields[2].data.x_union.payload = payload; } @@ -17071,8 +17199,8 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco ConstExprValue *fn_def_val = create_const_vals(1); fn_def_val->special = ConstValSpecialStatic; fn_def_val->type = type_info_fn_def_type; - fn_def_val->data.x_struct.parent.id = ConstParentIdUnion; - fn_def_val->data.x_struct.parent.data.p_union.union_val = &inner_fields[2]; + fn_def_val->parent.id = ConstParentIdUnion; + fn_def_val->parent.data.p_union.union_val = &inner_fields[2]; ConstExprValue *fn_def_fields = create_const_vals(9); fn_def_val->data.x_struct.fields = fn_def_fields; @@ -17136,20 +17264,18 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco fn_arg_name_array->type = get_array_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr), fn_arg_count); fn_arg_name_array->data.x_array.special = ConstArraySpecialNone; - fn_arg_name_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; fn_arg_name_array->data.x_array.data.s_none.elements = create_const_vals(fn_arg_count); init_const_slice(ira->codegen, &fn_def_fields[8], fn_arg_name_array, 0, fn_arg_count, false); - for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++) - { + for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++) { ZigVar *arg_var = fn_entry->variable_list.at(fn_arg_index); ConstExprValue *fn_arg_name_val = &fn_arg_name_array->data.x_array.data.s_none.elements[fn_arg_index]; ConstExprValue *arg_name = create_const_str_lit(ira->codegen, &arg_var->name); init_const_slice(ira->codegen, fn_arg_name_val, arg_name, 0, buf_len(&arg_var->name), true); - fn_arg_name_val->data.x_struct.parent.id = ConstParentIdArray; - fn_arg_name_val->data.x_struct.parent.data.p_array.array_val = fn_arg_name_array; - fn_arg_name_val->data.x_struct.parent.data.p_array.elem_index = fn_arg_index; + fn_arg_name_val->parent.id = ConstParentIdArray; + fn_arg_name_val->parent.data.p_array.array_val = fn_arg_name_array; + fn_arg_name_val->parent.data.p_array.elem_index = fn_arg_index; } inner_fields[2].data.x_union.payload = fn_def_val; @@ -17442,7 +17568,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE enum_field_array->special = ConstValSpecialStatic; enum_field_array->type = get_array_type(ira->codegen, type_info_enum_field_type, enum_field_count); enum_field_array->data.x_array.special = ConstArraySpecialNone; - enum_field_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; enum_field_array->data.x_array.data.s_none.elements = create_const_vals(enum_field_count); init_const_slice(ira->codegen, &fields[2], enum_field_array, 0, enum_field_count, false); @@ -17452,9 +17577,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum_field_index]; ConstExprValue *enum_field_val = &enum_field_array->data.x_array.data.s_none.elements[enum_field_index]; make_enum_field_val(ira, enum_field_val, enum_field, type_info_enum_field_type); - enum_field_val->data.x_struct.parent.id = ConstParentIdArray; - enum_field_val->data.x_struct.parent.data.p_array.array_val = enum_field_array; - enum_field_val->data.x_struct.parent.data.p_array.elem_index = enum_field_index; + enum_field_val->parent.id = ConstParentIdArray; + enum_field_val->parent.data.p_array.array_val = enum_field_array; + enum_field_val->parent.data.p_array.elem_index = enum_field_index; } // defs: []TypeInfo.Definition ensure_field_index(result->type, "defs", 3); @@ -17481,7 +17606,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE error_array->special = ConstValSpecialStatic; error_array->type = get_array_type(ira->codegen, type_info_error_type, error_count); error_array->data.x_array.special = ConstArraySpecialNone; - error_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; error_array->data.x_array.data.s_none.elements = create_const_vals(error_count); init_const_slice(ira->codegen, &fields[0], error_array, 0, error_count, false); @@ -17505,9 +17629,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE bigint_init_unsigned(&inner_fields[1].data.x_bigint, error->value); error_val->data.x_struct.fields = inner_fields; - error_val->data.x_struct.parent.id = ConstParentIdArray; - error_val->data.x_struct.parent.data.p_array.array_val = error_array; - error_val->data.x_struct.parent.data.p_array.elem_index = error_index; + error_val->parent.id = ConstParentIdArray; + error_val->parent.data.p_array.array_val = error_array; + error_val->parent.data.p_array.elem_index = error_index; } break; @@ -17576,7 +17700,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE union_field_array->special = ConstValSpecialStatic; union_field_array->type = get_array_type(ira->codegen, type_info_union_field_type, union_field_count); union_field_array->data.x_array.special = ConstArraySpecialNone; - union_field_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; union_field_array->data.x_array.data.s_none.elements = create_const_vals(union_field_count); init_const_slice(ira->codegen, &fields[2], union_field_array, 0, union_field_count, false); @@ -17609,9 +17732,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(union_field->name), true); union_field_val->data.x_struct.fields = inner_fields; - union_field_val->data.x_struct.parent.id = ConstParentIdArray; - union_field_val->data.x_struct.parent.data.p_array.array_val = union_field_array; - union_field_val->data.x_struct.parent.data.p_array.elem_index = union_field_index; + union_field_val->parent.id = ConstParentIdArray; + union_field_val->parent.data.p_array.array_val = union_field_array; + union_field_val->parent.data.p_array.elem_index = union_field_index; } // defs: []TypeInfo.Definition ensure_field_index(result->type, "defs", 3); @@ -17651,7 +17774,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE struct_field_array->special = ConstValSpecialStatic; struct_field_array->type = get_array_type(ira->codegen, type_info_struct_field_type, struct_field_count); struct_field_array->data.x_array.special = ConstArraySpecialNone; - struct_field_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; struct_field_array->data.x_array.data.s_none.elements = create_const_vals(struct_field_count); init_const_slice(ira->codegen, &fields[1], struct_field_array, 0, struct_field_count, false); @@ -17685,9 +17807,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(struct_field->name), true); struct_field_val->data.x_struct.fields = inner_fields; - struct_field_val->data.x_struct.parent.id = ConstParentIdArray; - struct_field_val->data.x_struct.parent.data.p_array.array_val = struct_field_array; - struct_field_val->data.x_struct.parent.data.p_array.elem_index = struct_field_index; + struct_field_val->parent.id = ConstParentIdArray; + struct_field_val->parent.data.p_array.array_val = struct_field_array; + struct_field_val->parent.data.p_array.elem_index = struct_field_index; } // defs: []TypeInfo.Definition ensure_field_index(result->type, "defs", 2); @@ -17757,7 +17879,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE fn_arg_array->special = ConstValSpecialStatic; fn_arg_array->type = get_array_type(ira->codegen, type_info_fn_arg_type, fn_arg_count); fn_arg_array->data.x_array.special = ConstArraySpecialNone; - fn_arg_array->data.x_array.data.s_none.parent.id = ConstParentIdNone; fn_arg_array->data.x_array.data.s_none.elements = create_const_vals(fn_arg_count); init_const_slice(ira->codegen, &fields[5], fn_arg_array, 0, fn_arg_count, false); @@ -17794,9 +17915,9 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE } fn_arg_val->data.x_struct.fields = inner_fields; - fn_arg_val->data.x_struct.parent.id = ConstParentIdArray; - fn_arg_val->data.x_struct.parent.data.p_array.array_val = fn_arg_array; - fn_arg_val->data.x_struct.parent.data.p_array.elem_index = fn_arg_index; + fn_arg_val->parent.id = ConstParentIdArray; + fn_arg_val->parent.data.p_array.array_val = fn_arg_array; + fn_arg_val->parent.data.p_array.elem_index = fn_arg_index; } break; @@ -17840,8 +17961,8 @@ static IrInstruction *ir_analyze_instruction_type_info(IrAnalyze *ira, if (payload != nullptr) { assert(payload->type->id == ZigTypeIdStruct); - payload->data.x_struct.parent.id = ConstParentIdUnion; - payload->data.x_struct.parent.data.p_union.union_val = out_val; + payload->parent.id = ConstParentIdUnion; + payload->parent.data.p_union.union_val = out_val; } return result; @@ -17913,10 +18034,10 @@ static IrInstruction *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruct // Execute the C import block like an inline function ZigType *void_type = ira->codegen->builtin_types.entry_void; - IrInstruction *cimport_result = ir_eval_const_value(ira->codegen, &cimport_scope->base, block_node, void_type, + ConstExprValue *cimport_result = ir_eval_const_value(ira->codegen, &cimport_scope->base, block_node, void_type, ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr, &cimport_scope->buf, block_node, nullptr, nullptr); - if (type_is_invalid(cimport_result->value.type)) + if (type_is_invalid(cimport_result->type)) return ira->codegen->invalid_instruction; find_libc_include_path(ira->codegen); @@ -18066,7 +18187,7 @@ static IrInstruction *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstru return result; } -static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructionCmpxchg *instruction) { +static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructionCmpxchgSrc *instruction) { ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->type_value->child); if (type_is_invalid(operand_type)) return ira->codegen->invalid_instruction; @@ -18138,9 +18259,9 @@ static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructi zig_panic("TODO compile-time execution of cmpxchg"); } - IrInstruction *result = ir_build_cmpxchg(&ira->new_irb, instruction->base.scope, instruction->base.source_node, - nullptr, casted_ptr, casted_cmp_value, casted_new_value, nullptr, nullptr, instruction->is_weak, - operand_type, success_order, failure_order); + IrInstruction *result = ir_build_cmpxchg_gen(ira, &instruction->base, + casted_ptr, casted_cmp_value, casted_new_value, + success_order, failure_order, instruction->is_weak); result->value.type = get_optional_type(ira->codegen, operand_type); ir_add_alloca(ira, result, result->value.type); return result; @@ -18312,18 +18433,6 @@ static IrInstruction *ir_analyze_instruction_err_set_cast(IrAnalyze *ira, IrInst return ir_analyze_err_set_cast(ira, &instruction->base, target, dest_type); } -static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align) { - Error err; - - if (ty->id == ZigTypeIdPointer) { - if ((err = type_resolve(ira->codegen, ty->data.pointer.child_type, ResolveStatusAlignmentKnown))) - return err; - } - - *result_align = get_ptr_align(ira->codegen, ty); - return ErrorNone; -} - static IrInstruction *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstructionFromBytes *instruction) { Error err; @@ -18442,6 +18551,20 @@ static IrInstruction *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstruct return ir_resolve_cast(ira, &instruction->base, target, dest_slice_type, CastOpResizeSlice, true); } +static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align) { + Error err; + + ZigType *ptr_type = get_src_ptr_type(ty); + assert(ptr_type != nullptr); + if (ptr_type->id == ZigTypeIdPointer) { + if ((err = type_resolve(ira->codegen, ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) + return err; + } + + *result_align = get_ptr_align(ira->codegen, ty); + return ErrorNone; +} + static IrInstruction *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrInstructionIntToFloat *instruction) { ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); if (type_is_invalid(dest_type)) @@ -18646,10 +18769,18 @@ static IrInstruction *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructio } case ConstPtrSpecialBaseStruct: zig_panic("TODO memset on const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO memset on const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO memset on const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO memset on const inner optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO memset on ptr cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO memset on null ptr"); } size_t count = bigint_as_unsigned(&casted_count->value.data.x_bigint); @@ -18761,10 +18892,18 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio } case ConstPtrSpecialBaseStruct: zig_panic("TODO memcpy on const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO memcpy on const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO memcpy on const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO memcpy on const inner optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO memcpy on ptr cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO memcpy on null ptr"); } if (dest_start + count > dest_end) { @@ -18797,10 +18936,18 @@ static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructio } case ConstPtrSpecialBaseStruct: zig_panic("TODO memcpy on const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO memcpy on const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO memcpy on const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO memcpy on const inner optional payload"); case ConstPtrSpecialHardCodedAddr: zig_unreachable(); case ConstPtrSpecialFunction: zig_panic("TODO memcpy on ptr cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO memcpy on null ptr"); } if (src_start + count > src_end) { @@ -18828,9 +18975,9 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction if (type_is_invalid(ptr_ptr->value.type)) return ira->codegen->invalid_instruction; - ZigType *ptr_type = ptr_ptr->value.type; - assert(ptr_type->id == ZigTypeIdPointer); - ZigType *array_type = ptr_type->data.pointer.child_type; + ZigType *ptr_ptr_type = ptr_ptr->value.type; + assert(ptr_ptr_type->id == ZigTypeIdPointer); + ZigType *array_type = ptr_ptr_type->data.pointer.child_type; IrInstruction *start = instruction->start->child; if (type_is_invalid(start->value.type)) @@ -18859,10 +19006,10 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction bool is_comptime_const = ptr_ptr->value.special == ConstValSpecialStatic && ptr_ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst; ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.array.child_type, - ptr_type->data.pointer.is_const || is_comptime_const, - ptr_type->data.pointer.is_volatile, + ptr_ptr_type->data.pointer.is_const || is_comptime_const, + ptr_ptr_type->data.pointer.is_volatile, PtrLenUnknown, - ptr_type->data.pointer.explicit_alignment, 0, 0); + ptr_ptr_type->data.pointer.explicit_alignment, 0, 0); return_type = get_slice_type(ira->codegen, slice_ptr_type); } else if (array_type->id == ZigTypeIdPointer) { if (array_type->data.pointer.ptr_len == PtrLenSingle) { @@ -18960,6 +19107,12 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction break; case ConstPtrSpecialBaseStruct: zig_panic("TODO slice const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO slice const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO slice const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO slice const inner optional payload"); case ConstPtrSpecialHardCodedAddr: array_val = nullptr; abs_offset = 0; @@ -18967,6 +19120,8 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction break; case ConstPtrSpecialFunction: zig_panic("TODO slice of ptr cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO slice of null ptr"); } } else if (is_slice(array_type)) { ConstExprValue *slice_ptr = const_ptr_pointee(ira, ira->codegen, &ptr_ptr->value, instruction->base.source_node); @@ -18997,6 +19152,12 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction break; case ConstPtrSpecialBaseStruct: zig_panic("TODO slice const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO slice const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO slice const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO slice const inner optional payload"); case ConstPtrSpecialHardCodedAddr: array_val = nullptr; abs_offset = 0; @@ -19004,6 +19165,8 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction break; case ConstPtrSpecialFunction: zig_panic("TODO slice of slice cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO slice of null"); } } else { zig_unreachable(); @@ -19069,6 +19232,12 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction zig_unreachable(); case ConstPtrSpecialBaseStruct: zig_panic("TODO"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO"); case ConstPtrSpecialHardCodedAddr: init_const_ptr_hard_coded_addr(ira->codegen, ptr_val, parent_ptr->type->data.pointer.child_type, @@ -19077,6 +19246,8 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction break; case ConstPtrSpecialFunction: zig_panic("TODO"); + case ConstPtrSpecialNull: + zig_panic("TODO"); } ConstExprValue *len_val = &out_val->data.x_struct.fields[slice_len_index]; @@ -19432,7 +19603,8 @@ static IrInstruction *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruct return ira->codegen->invalid_instruction; if (err_union_val->special != ConstValSpecialRuntime) { - return ir_const_bool(ira, &instruction->base, (err_union_val->data.x_err_union.err != nullptr)); + ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set; + return ir_const_bool(ira, &instruction->base, (err != nullptr)); } } @@ -19458,48 +19630,47 @@ static IrInstruction *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruct } } -static IrInstruction *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira, - IrInstructionUnwrapErrCode *instruction) -{ - IrInstruction *value = instruction->value->child; - if (type_is_invalid(value->value.type)) +static IrInstruction *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira, IrInstructionUnwrapErrCode *instruction) { + IrInstruction *base_ptr = instruction->err_union->child; + if (type_is_invalid(base_ptr->value.type)) return ira->codegen->invalid_instruction; - ZigType *ptr_type = value->value.type; + ZigType *ptr_type = base_ptr->value.type; // This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing. assert(ptr_type->id == ZigTypeIdPointer); ZigType *type_entry = ptr_type->data.pointer.child_type; - if (type_is_invalid(type_entry)) { + if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; - } else if (type_entry->id == ZigTypeIdErrorUnion) { - if (instr_is_comptime(value)) { - ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad); - if (!ptr_val) - return ira->codegen->invalid_instruction; - ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, instruction->base.source_node); - if (err_union_val == nullptr) - return ira->codegen->invalid_instruction; - if (err_union_val->special != ConstValSpecialRuntime) { - ErrorTableEntry *err = err_union_val->data.x_err_union.err; - assert(err); - IrInstruction *result = ir_const(ira, &instruction->base, - type_entry->data.error_union.err_set_type); - result->value.data.x_err_set = err; - return result; - } - } - - IrInstruction *result = ir_build_unwrap_err_code(&ira->new_irb, - instruction->base.scope, instruction->base.source_node, value); - result->value.type = type_entry->data.error_union.err_set_type; - return result; - } else { - ir_add_error(ira, value, + if (type_entry->id != ZigTypeIdErrorUnion) { + ir_add_error(ira, base_ptr, buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->invalid_instruction; } + + if (instr_is_comptime(base_ptr)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad); + if (!ptr_val) + return ira->codegen->invalid_instruction; + ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, instruction->base.source_node); + if (err_union_val == nullptr) + return ira->codegen->invalid_instruction; + if (err_union_val->special != ConstValSpecialRuntime) { + ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set; + assert(err); + + IrInstruction *result = ir_const(ira, &instruction->base, + type_entry->data.error_union.err_set_type); + result->value.data.x_err_set = err; + return result; + } + } + + IrInstruction *result = ir_build_unwrap_err_code(&ira->new_irb, + instruction->base.scope, instruction->base.source_node, base_ptr); + result->value.type = type_entry->data.error_union.err_set_type; + return result; } static IrInstruction *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, @@ -19515,48 +19686,48 @@ static IrInstruction *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, assert(ptr_type->id == ZigTypeIdPointer); ZigType *type_entry = ptr_type->data.pointer.child_type; - if (type_is_invalid(type_entry)) { + if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; - } else if (type_entry->id == ZigTypeIdErrorUnion) { - ZigType *payload_type = type_entry->data.error_union.payload_type; - if (type_is_invalid(payload_type)) { - return ira->codegen->invalid_instruction; - } - ZigType *result_type = get_pointer_to_type_extra(ira->codegen, payload_type, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, - PtrLenSingle, 0, 0, 0); - if (instr_is_comptime(value)) { - ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad); - if (!ptr_val) - return ira->codegen->invalid_instruction; - ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, instruction->base.source_node); - if (err_union_val == nullptr) - return ira->codegen->invalid_instruction; - if (err_union_val->special != ConstValSpecialRuntime) { - ErrorTableEntry *err = err_union_val->data.x_err_union.err; - if (err != nullptr) { - ir_add_error(ira, &instruction->base, - buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name))); - return ira->codegen->invalid_instruction; - } - IrInstruction *result = ir_const(ira, &instruction->base, result_type); - result->value.data.x_ptr.special = ConstPtrSpecialRef; - result->value.data.x_ptr.data.ref.pointee = err_union_val->data.x_err_union.payload; - return result; - } - } - - IrInstruction *result = ir_build_unwrap_err_payload(&ira->new_irb, - instruction->base.scope, instruction->base.source_node, value, instruction->safety_check_on); - result->value.type = result_type; - return result; - } else { + if (type_entry->id != ZigTypeIdErrorUnion) { ir_add_error(ira, value, buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); return ira->codegen->invalid_instruction; } + ZigType *payload_type = type_entry->data.error_union.payload_type; + if (type_is_invalid(payload_type)) + return ira->codegen->invalid_instruction; + + ZigType *result_type = get_pointer_to_type_extra(ira->codegen, payload_type, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + PtrLenSingle, 0, 0, 0); + if (instr_is_comptime(value)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad); + if (!ptr_val) + return ira->codegen->invalid_instruction; + ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, instruction->base.source_node); + if (err_union_val == nullptr) + return ira->codegen->invalid_instruction; + if (err_union_val->special != ConstValSpecialRuntime) { + ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set; + if (err != nullptr) { + ir_add_error(ira, &instruction->base, + buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name))); + return ira->codegen->invalid_instruction; + } + + IrInstruction *result = ir_const(ira, &instruction->base, result_type); + result->value.data.x_ptr.special = ConstPtrSpecialRef; + result->value.data.x_ptr.data.ref.pointee = err_union_val->data.x_err_union.payload; + return result; + } + } + + IrInstruction *result = ir_build_unwrap_err_payload(&ira->new_irb, + instruction->base.scope, instruction->base.source_node, value, instruction->safety_check_on); + result->value.type = result_type; + return result; } static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstructionFnProto *instruction) { @@ -19973,7 +20144,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3 return ira->codegen->invalid_instruction; } - IrInstruction *result = ir_create_const(&ira->new_irb, target->scope, target->source_node, result_type); + IrInstruction *result = ir_const(ira, target, result_type); copy_const_val(&result->value, val, false); result->value.type = result_type; return result; @@ -20021,8 +20192,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ if (!val) return ira->codegen->invalid_instruction; - IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, - dest_type); + IrInstruction *result = ir_const(ira, source_instr, dest_type); copy_const_val(&result->value, val, false); result->value.type = dest_type; return result; @@ -20045,9 +20215,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ return ira->codegen->invalid_instruction; } - IrInstruction *casted_ptr = ir_build_ptr_cast(&ira->new_irb, source_instr->scope, - source_instr->source_node, nullptr, ptr); - casted_ptr->value.type = dest_type; + IrInstruction *casted_ptr = ir_build_ptr_cast_gen(ira, source_instr, dest_type, ptr); if (type_has_bits(dest_type) && !type_has_bits(src_type)) { ErrorMsg *msg = ir_add_error(ira, source_instr, @@ -20073,7 +20241,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ return result; } -static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtrCast *instruction) { +static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtrCastSrc *instruction) { IrInstruction *dest_type_value = instruction->dest_type->child; ZigType *dest_type = ir_resolve_type(ira, dest_type_value); if (type_is_invalid(dest_type)) @@ -20211,15 +20379,29 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem))) return err; } - break; + return ErrorNone; case ConstArraySpecialUndef: zig_panic("TODO buf_read_value_bytes ConstArraySpecialUndef array type"); case ConstArraySpecialBuf: zig_panic("TODO buf_read_value_bytes ConstArraySpecialBuf array type"); } - - return ErrorNone; + zig_unreachable(); } + case ZigTypeIdEnum: + switch (val->type->data.enumeration.layout) { + case ContainerLayoutAuto: + zig_panic("TODO buf_read_value_bytes enum auto"); + case ContainerLayoutPacked: + zig_panic("TODO buf_read_value_bytes enum packed"); + case ContainerLayoutExtern: { + ZigType *tag_int_type = val->type->data.enumeration.tag_int_type; + assert(tag_int_type->id == ZigTypeIdInt); + bigint_read_twos_complement(&val->data.x_enum_tag, buf, tag_int_type->data.integral.bit_count, + codegen->is_big_endian, tag_int_type->data.integral.is_signed); + return ErrorNone; + } + } + zig_unreachable(); case ZigTypeIdStruct: switch (val->type->data.structure.layout) { case ContainerLayoutAuto: { @@ -20258,8 +20440,6 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou zig_panic("TODO buf_read_value_bytes error union"); case ZigTypeIdErrorSet: zig_panic("TODO buf_read_value_bytes pure error type"); - case ZigTypeIdEnum: - zig_panic("TODO buf_read_value_bytes enum type"); case ZigTypeIdFn: zig_panic("TODO buf_read_value_bytes fn type"); case ZigTypeIdUnion: @@ -20426,8 +20606,7 @@ static IrInstruction *ir_analyze_instruction_decl_ref(IrAnalyze *ira, case TldIdContainer: case TldIdCompTime: zig_unreachable(); - case TldIdVar: - { + case TldIdVar: { TldVar *tld_var = (TldVar *)tld; ZigVar *var = tld_var->var; @@ -20445,8 +20624,7 @@ static IrInstruction *ir_analyze_instruction_decl_ref(IrAnalyze *ira, return ir_get_deref(ira, &instruction->base, var_ptr); } } - case TldIdFn: - { + case TldIdFn: { TldFn *tld_fn = (TldFn *)tld; ZigFn *fn_entry = tld_fn->fn_entry; assert(fn_entry->type_entry); @@ -20492,8 +20670,7 @@ static IrInstruction *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstru if (!val) return ira->codegen->invalid_instruction; if (val->type->id == ZigTypeIdPointer && val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { - IrInstruction *result = ir_create_const(&ira->new_irb, instruction->base.scope, - instruction->base.source_node, usize); + IrInstruction *result = ir_const(ira, &instruction->base, usize); bigint_init_unsigned(&result->value.data.x_bigint, val->data.x_ptr.data.hard_coded_addr.addr); result->value.type = usize; return result; @@ -21331,6 +21508,9 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio case IrInstructionIdErrWrapCode: case IrInstructionIdErrWrapPayload: case IrInstructionIdCast: + case IrInstructionIdDeclVarGen: + case IrInstructionIdPtrCastGen: + case IrInstructionIdCmpxchgGen: zig_unreachable(); case IrInstructionIdReturn: @@ -21341,8 +21521,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_un_op(ira, (IrInstructionUnOp *)instruction); case IrInstructionIdBinOp: return ir_analyze_instruction_bin_op(ira, (IrInstructionBinOp *)instruction); - case IrInstructionIdDeclVar: - return ir_analyze_instruction_decl_var(ira, (IrInstructionDeclVar *)instruction); + case IrInstructionIdDeclVarSrc: + return ir_analyze_instruction_decl_var(ira, (IrInstructionDeclVarSrc *)instruction); case IrInstructionIdLoadPtr: return ir_analyze_instruction_load_ptr(ira, (IrInstructionLoadPtr *)instruction); case IrInstructionIdStorePtr: @@ -21387,8 +21567,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_size_of(ira, (IrInstructionSizeOf *)instruction); case IrInstructionIdTestNonNull: return ir_analyze_instruction_test_non_null(ira, (IrInstructionTestNonNull *)instruction); - case IrInstructionIdUnwrapOptional: - return ir_analyze_instruction_unwrap_maybe(ira, (IrInstructionUnwrapOptional *)instruction); + case IrInstructionIdOptionalUnwrapPtr: + return ir_analyze_instruction_optional_unwrap_ptr(ira, (IrInstructionOptionalUnwrapPtr *)instruction); case IrInstructionIdClz: return ir_analyze_instruction_clz(ira, (IrInstructionClz *)instruction); case IrInstructionIdCtz: @@ -21429,8 +21609,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_c_undef(ira, (IrInstructionCUndef *)instruction); case IrInstructionIdEmbedFile: return ir_analyze_instruction_embed_file(ira, (IrInstructionEmbedFile *)instruction); - case IrInstructionIdCmpxchg: - return ir_analyze_instruction_cmpxchg(ira, (IrInstructionCmpxchg *)instruction); + case IrInstructionIdCmpxchgSrc: + return ir_analyze_instruction_cmpxchg(ira, (IrInstructionCmpxchgSrc *)instruction); case IrInstructionIdFence: return ir_analyze_instruction_fence(ira, (IrInstructionFence *)instruction); case IrInstructionIdTruncate: @@ -21497,8 +21677,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_decl_ref(ira, (IrInstructionDeclRef *)instruction); case IrInstructionIdPanic: return ir_analyze_instruction_panic(ira, (IrInstructionPanic *)instruction); - case IrInstructionIdPtrCast: - return ir_analyze_instruction_ptr_cast(ira, (IrInstructionPtrCast *)instruction); + case IrInstructionIdPtrCastSrc: + return ir_analyze_instruction_ptr_cast(ira, (IrInstructionPtrCastSrc *)instruction); case IrInstructionIdBitCast: return ir_analyze_instruction_bit_cast(ira, (IrInstructionBitCast *)instruction); case IrInstructionIdIntToPtr: @@ -21682,7 +21862,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdBr: case IrInstructionIdCondBr: case IrInstructionIdSwitchBr: - case IrInstructionIdDeclVar: + case IrInstructionIdDeclVarSrc: + case IrInstructionIdDeclVarGen: case IrInstructionIdStorePtr: case IrInstructionIdCall: case IrInstructionIdReturn: @@ -21697,7 +21878,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdCInclude: case IrInstructionIdCDefine: case IrInstructionIdCUndef: - case IrInstructionIdCmpxchg: case IrInstructionIdFence: case IrInstructionIdMemset: case IrInstructionIdMemcpy: @@ -21725,6 +21905,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdMergeErrRetTraces: case IrInstructionIdMarkErrRetTracePtr: case IrInstructionIdAtomicRmw: + case IrInstructionIdCmpxchgGen: + case IrInstructionIdCmpxchgSrc: return true; case IrInstructionIdPhi: @@ -21750,7 +21932,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdSliceType: case IrInstructionIdSizeOf: case IrInstructionIdTestNonNull: - case IrInstructionIdUnwrapOptional: + case IrInstructionIdOptionalUnwrapPtr: case IrInstructionIdClz: case IrInstructionIdCtz: case IrInstructionIdPopCount: @@ -21777,7 +21959,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdErrWrapPayload: case IrInstructionIdFnProto: case IrInstructionIdTestComptime: - case IrInstructionIdPtrCast: + case IrInstructionIdPtrCastSrc: + case IrInstructionIdPtrCastGen: case IrInstructionIdBitCast: case IrInstructionIdWidenOrShorten: case IrInstructionIdPtrToInt: diff --git a/src/ir.hpp b/src/ir.hpp index 7af1d7f52ba8b05e0c8185a47142e3812912d92b..0a7c614812b424c817c98b1b8ce2919a6f56df5c 100644 --- a/src/ir.hpp +++ b/src/ir.hpp @@ -13,7 +13,7 @@ bool ir_gen(CodeGen *g, AstNode *node, Scope *scope, IrExecutable *ir_executable); bool ir_gen_fn(CodeGen *g, ZigFn *fn_entry); -IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, +ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, ZigType *expected_type, size_t *backward_branch_count, size_t backward_branch_quota, ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name, IrExecutable *parent_exec); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index b5099db86aaf868658627c3dab4c2eab3a65684b..a3ec8e9d3589d5098d603d351552dde5daa06071 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -172,7 +172,7 @@ static void ir_print_bin_op(IrPrint *irp, IrInstructionBinOp *bin_op_instruction } } -static void ir_print_decl_var(IrPrint *irp, IrInstructionDeclVar *decl_var_instruction) { +static void ir_print_decl_var_src(IrPrint *irp, IrInstructionDeclVarSrc *decl_var_instruction) { const char *var_or_const = decl_var_instruction->var->gen_is_const ? "const" : "var"; const char *name = buf_ptr(&decl_var_instruction->var->name); if (decl_var_instruction->var_type) { @@ -332,8 +332,8 @@ static void ir_print_var_ptr(IrPrint *irp, IrInstructionVarPtr *instruction) { } static void ir_print_load_ptr(IrPrint *irp, IrInstructionLoadPtr *instruction) { - fprintf(irp->f, "*"); ir_print_other_instruction(irp, instruction->ptr); + fprintf(irp->f, ".*"); } static void ir_print_store_ptr(IrPrint *irp, IrInstructionStorePtr *instruction) { @@ -479,15 +479,15 @@ static void ir_print_size_of(IrPrint *irp, IrInstructionSizeOf *instruction) { fprintf(irp->f, ")"); } -static void ir_print_test_null(IrPrint *irp, IrInstructionTestNonNull *instruction) { - fprintf(irp->f, "*"); +static void ir_print_test_non_null(IrPrint *irp, IrInstructionTestNonNull *instruction) { ir_print_other_instruction(irp, instruction->value); fprintf(irp->f, " != null"); } -static void ir_print_unwrap_maybe(IrPrint *irp, IrInstructionUnwrapOptional *instruction) { - fprintf(irp->f, "&??*"); - ir_print_other_instruction(irp, instruction->value); +static void ir_print_optional_unwrap_ptr(IrPrint *irp, IrInstructionOptionalUnwrapPtr *instruction) { + fprintf(irp->f, "&"); + ir_print_other_instruction(irp, instruction->base_ptr); + fprintf(irp->f, ".*.?"); if (!instruction->safety_check_on) { fprintf(irp->f, " // no safety"); } @@ -613,7 +613,7 @@ static void ir_print_embed_file(IrPrint *irp, IrInstructionEmbedFile *instructio fprintf(irp->f, ")"); } -static void ir_print_cmpxchg(IrPrint *irp, IrInstructionCmpxchg *instruction) { +static void ir_print_cmpxchg_src(IrPrint *irp, IrInstructionCmpxchgSrc *instruction) { fprintf(irp->f, "@cmpxchg("); ir_print_other_instruction(irp, instruction->ptr); fprintf(irp->f, ", "); @@ -627,6 +627,16 @@ static void ir_print_cmpxchg(IrPrint *irp, IrInstructionCmpxchg *instruction) { fprintf(irp->f, ")"); } +static void ir_print_cmpxchg_gen(IrPrint *irp, IrInstructionCmpxchgGen *instruction) { + fprintf(irp->f, "@cmpxchg("); + ir_print_other_instruction(irp, instruction->ptr); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->cmp_value); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->new_value); + fprintf(irp->f, ", TODO print atomic orders)"); +} + static void ir_print_fence(IrPrint *irp, IrInstructionFence *instruction) { fprintf(irp->f, "@fence("); ir_print_other_instruction(irp, instruction->order_value); @@ -820,13 +830,13 @@ static void ir_print_test_err(IrPrint *irp, IrInstructionTestErr *instruction) { } static void ir_print_unwrap_err_code(IrPrint *irp, IrInstructionUnwrapErrCode *instruction) { - fprintf(irp->f, "@unwrapErrorCode("); - ir_print_other_instruction(irp, instruction->value); + fprintf(irp->f, "UnwrapErrorCode("); + ir_print_other_instruction(irp, instruction->err_union); fprintf(irp->f, ")"); } static void ir_print_unwrap_err_payload(IrPrint *irp, IrInstructionUnwrapErrPayload *instruction) { - fprintf(irp->f, "@unwrapErrorPayload("); + fprintf(irp->f, "ErrorUnionFieldPayload("); ir_print_other_instruction(irp, instruction->value); fprintf(irp->f, ")"); if (!instruction->safety_check_on) { @@ -879,7 +889,7 @@ static void ir_print_test_comptime(IrPrint *irp, IrInstructionTestComptime *inst fprintf(irp->f, ")"); } -static void ir_print_ptr_cast(IrPrint *irp, IrInstructionPtrCast *instruction) { +static void ir_print_ptr_cast_src(IrPrint *irp, IrInstructionPtrCastSrc *instruction) { fprintf(irp->f, "@ptrCast("); if (instruction->dest_type) { ir_print_other_instruction(irp, instruction->dest_type); @@ -889,6 +899,12 @@ static void ir_print_ptr_cast(IrPrint *irp, IrInstructionPtrCast *instruction) { fprintf(irp->f, ")"); } +static void ir_print_ptr_cast_gen(IrPrint *irp, IrInstructionPtrCastGen *instruction) { + fprintf(irp->f, "@ptrCast("); + ir_print_other_instruction(irp, instruction->ptr); + fprintf(irp->f, ")"); +} + static void ir_print_bit_cast(IrPrint *irp, IrInstructionBitCast *instruction) { fprintf(irp->f, "@bitCast("); if (instruction->dest_type) { @@ -900,7 +916,7 @@ static void ir_print_bit_cast(IrPrint *irp, IrInstructionBitCast *instruction) { } static void ir_print_widen_or_shorten(IrPrint *irp, IrInstructionWidenOrShorten *instruction) { - fprintf(irp->f, "@widenOrShorten("); + fprintf(irp->f, "WidenOrShorten("); ir_print_other_instruction(irp, instruction->target); fprintf(irp->f, ")"); } @@ -1323,6 +1339,20 @@ static void ir_print_sqrt(IrPrint *irp, IrInstructionSqrt *instruction) { fprintf(irp->f, ")"); } +static void ir_print_decl_var_gen(IrPrint *irp, IrInstructionDeclVarGen *decl_var_instruction) { + ZigVar *var = decl_var_instruction->var; + const char *var_or_const = decl_var_instruction->var->gen_is_const ? "const" : "var"; + const char *name = buf_ptr(&decl_var_instruction->var->name); + fprintf(irp->f, "%s %s: %s align(%u) = ", var_or_const, name, buf_ptr(&var->var_type->name), + var->align_bytes); + + ir_print_other_instruction(irp, decl_var_instruction->init_value); + if (decl_var_instruction->var->is_comptime != nullptr) { + fprintf(irp->f, " // comptime = "); + ir_print_other_instruction(irp, decl_var_instruction->var->is_comptime); + } +} + static void ir_print_bswap(IrPrint *irp, IrInstructionBswap *instruction) { fprintf(irp->f, "@bswap("); if (instruction->type != nullptr) { @@ -1361,8 +1391,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdBinOp: ir_print_bin_op(irp, (IrInstructionBinOp *)instruction); break; - case IrInstructionIdDeclVar: - ir_print_decl_var(irp, (IrInstructionDeclVar *)instruction); + case IrInstructionIdDeclVarSrc: + ir_print_decl_var_src(irp, (IrInstructionDeclVarSrc *)instruction); break; case IrInstructionIdCast: ir_print_cast(irp, (IrInstructionCast *)instruction); @@ -1452,10 +1482,10 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_size_of(irp, (IrInstructionSizeOf *)instruction); break; case IrInstructionIdTestNonNull: - ir_print_test_null(irp, (IrInstructionTestNonNull *)instruction); + ir_print_test_non_null(irp, (IrInstructionTestNonNull *)instruction); break; - case IrInstructionIdUnwrapOptional: - ir_print_unwrap_maybe(irp, (IrInstructionUnwrapOptional *)instruction); + case IrInstructionIdOptionalUnwrapPtr: + ir_print_optional_unwrap_ptr(irp, (IrInstructionOptionalUnwrapPtr *)instruction); break; case IrInstructionIdCtz: ir_print_ctz(irp, (IrInstructionCtz *)instruction); @@ -1508,8 +1538,11 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdEmbedFile: ir_print_embed_file(irp, (IrInstructionEmbedFile *)instruction); break; - case IrInstructionIdCmpxchg: - ir_print_cmpxchg(irp, (IrInstructionCmpxchg *)instruction); + case IrInstructionIdCmpxchgSrc: + ir_print_cmpxchg_src(irp, (IrInstructionCmpxchgSrc *)instruction); + break; + case IrInstructionIdCmpxchgGen: + ir_print_cmpxchg_gen(irp, (IrInstructionCmpxchgGen *)instruction); break; case IrInstructionIdFence: ir_print_fence(irp, (IrInstructionFence *)instruction); @@ -1607,8 +1640,11 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdTestComptime: ir_print_test_comptime(irp, (IrInstructionTestComptime *)instruction); break; - case IrInstructionIdPtrCast: - ir_print_ptr_cast(irp, (IrInstructionPtrCast *)instruction); + case IrInstructionIdPtrCastSrc: + ir_print_ptr_cast_src(irp, (IrInstructionPtrCastSrc *)instruction); + break; + case IrInstructionIdPtrCastGen: + ir_print_ptr_cast_gen(irp, (IrInstructionPtrCastGen *)instruction); break; case IrInstructionIdBitCast: ir_print_bit_cast(irp, (IrInstructionBitCast *)instruction); @@ -1775,6 +1811,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdCheckRuntimeScope: ir_print_check_runtime_scope(irp, (IrInstructionCheckRuntimeScope *)instruction); break; + case IrInstructionIdDeclVarGen: + ir_print_decl_var_gen(irp, (IrInstructionDeclVarGen *)instruction); + break; } fprintf(irp->f, "\n"); } diff --git a/src/parser.cpp b/src/parser.cpp index 077365995e87d5d38f491d87ef121f68cb0f90c9..81bd469d1c084bbd4c2b21c41c0608c0b4dd785d 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -381,7 +381,7 @@ static AstNode *ast_parse_if_expr_helper(ParseContext *pc, AstNode *(*body_parse else_body = ast_expect(pc, body_parser); } - assert(res->type == NodeTypeTestExpr); + assert(res->type == NodeTypeIfOptional); if (err_payload != nullptr) { AstNodeTestExpr old = res->data.test_expr; res->type = NodeTypeIfErrorExpr; @@ -990,7 +990,7 @@ static AstNode *ast_parse_if_statement(ParseContext *pc) { if (requires_semi && else_body == nullptr) expect_token(pc, TokenIdSemicolon); - assert(res->type == NodeTypeTestExpr); + assert(res->type == NodeTypeIfOptional); if (err_payload != nullptr) { AstNodeTestExpr old = res->data.test_expr; res->type = NodeTypeIfErrorExpr; @@ -2204,7 +2204,7 @@ static AstNode *ast_parse_if_prefix(ParseContext *pc) { Optional opt_payload = ast_parse_ptr_payload(pc); PtrPayload payload; - AstNode *res = ast_create_node(pc, NodeTypeTestExpr, first); + AstNode *res = ast_create_node(pc, NodeTypeIfOptional, first); res->data.test_expr.target_node = condition; if (opt_payload.unwrap(&payload)) { res->data.test_expr.var_symbol = token_buf(payload.payload); @@ -2999,7 +2999,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont visit_field(&node->data.if_err_expr.then_node, visit, context); visit_field(&node->data.if_err_expr.else_node, visit, context); break; - case NodeTypeTestExpr: + case NodeTypeIfOptional: visit_field(&node->data.test_expr.target_node, visit, context); visit_field(&node->data.test_expr.then_node, visit, context); visit_field(&node->data.test_expr.else_node, visit, context); diff --git a/std/event/fs.zig b/std/event/fs.zig index 1b8e1aa5dc0c19fe703fd62c4c9208150899e2a5..7e77b3e6e2de0fda045efc267e58910b22ef6be0 100644 --- a/std/event/fs.zig +++ b/std/event/fs.zig @@ -1307,32 +1307,29 @@ pub fn Watch(comptime V: type) type { const test_tmp_dir = "std_event_fs_test"; -test "write a file, watch it, write it again" { - if (builtin.os == builtin.Os.windows) { - // TODO this test is disabled on windows until the coroutine rewrite is finished. - // https://github.com/ziglang/zig/issues/1363 - return error.SkipZigTest; - } - var da = std.heap.DirectAllocator.init(); - defer da.deinit(); - - const allocator = &da.allocator; - - // TODO move this into event loop too - try os.makePath(allocator, test_tmp_dir); - defer os.deleteTree(allocator, test_tmp_dir) catch {}; - - var loop: Loop = undefined; - try loop.initMultiThreaded(allocator); - defer loop.deinit(); - - var result: anyerror!void = error.ResultNeverWritten; - const handle = try async testFsWatchCantFail(&loop, &result); - defer cancel handle; - - loop.run(); - return result; -} +// TODO this test is disabled until the coroutine rewrite is finished. +//test "write a file, watch it, write it again" { +// return error.SkipZigTest; +// var da = std.heap.DirectAllocator.init(); +// defer da.deinit(); +// +// const allocator = &da.allocator; +// +// // TODO move this into event loop too +// try os.makePath(allocator, test_tmp_dir); +// defer os.deleteTree(allocator, test_tmp_dir) catch {}; +// +// var loop: Loop = undefined; +// try loop.initMultiThreaded(allocator); +// defer loop.deinit(); +// +// var result: anyerror!void = error.ResultNeverWritten; +// const handle = try async testFsWatchCantFail(&loop, &result); +// defer cancel handle; +// +// loop.run(); +// return result; +//} async fn testFsWatchCantFail(loop: *Loop, result: *(anyerror!void)) void { result.* = await (async testFsWatch(loop) catch unreachable); diff --git a/test/behavior.zig b/test/behavior.zig deleted file mode 100644 index 10cd08dad717eb75f3aa68eabd2ad684d56a292b..0000000000000000000000000000000000000000 --- a/test/behavior.zig +++ /dev/null @@ -1,82 +0,0 @@ -const builtin = @import("builtin"); - -comptime { - _ = @import("cases/align.zig"); - _ = @import("cases/alignof.zig"); - _ = @import("cases/array.zig"); - _ = @import("cases/asm.zig"); - _ = @import("cases/atomics.zig"); - _ = @import("cases/bitcast.zig"); - _ = @import("cases/bool.zig"); - _ = @import("cases/bswap.zig"); - _ = @import("cases/bitreverse.zig"); - _ = @import("cases/bugs/1076.zig"); - _ = @import("cases/bugs/1111.zig"); - _ = @import("cases/bugs/1277.zig"); - _ = @import("cases/bugs/1322.zig"); - _ = @import("cases/bugs/1381.zig"); - _ = @import("cases/bugs/1421.zig"); - _ = @import("cases/bugs/1442.zig"); - _ = @import("cases/bugs/1486.zig"); - _ = @import("cases/bugs/394.zig"); - _ = @import("cases/bugs/655.zig"); - _ = @import("cases/bugs/656.zig"); - _ = @import("cases/bugs/726.zig"); - _ = @import("cases/bugs/828.zig"); - _ = @import("cases/bugs/920.zig"); - _ = @import("cases/byval_arg_var.zig"); - _ = @import("cases/cancel.zig"); - _ = @import("cases/cast.zig"); - _ = @import("cases/const_slice_child.zig"); - _ = @import("cases/coroutine_await_struct.zig"); - _ = @import("cases/coroutines.zig"); - _ = @import("cases/defer.zig"); - _ = @import("cases/enum.zig"); - _ = @import("cases/enum_with_members.zig"); - _ = @import("cases/error.zig"); - _ = @import("cases/eval.zig"); - _ = @import("cases/field_parent_ptr.zig"); - _ = @import("cases/fn.zig"); - _ = @import("cases/fn_in_struct_in_comptime.zig"); - _ = @import("cases/for.zig"); - _ = @import("cases/generics.zig"); - _ = @import("cases/if.zig"); - _ = @import("cases/import.zig"); - _ = @import("cases/incomplete_struct_param_tld.zig"); - _ = @import("cases/inttoptr.zig"); - _ = @import("cases/ir_block_deps.zig"); - _ = @import("cases/math.zig"); - _ = @import("cases/merge_error_sets.zig"); - _ = @import("cases/misc.zig"); - _ = @import("cases/namespace_depends_on_compile_var/index.zig"); - _ = @import("cases/new_stack_call.zig"); - _ = @import("cases/null.zig"); - _ = @import("cases/optional.zig"); - _ = @import("cases/pointers.zig"); - _ = @import("cases/popcount.zig"); - _ = @import("cases/ptrcast.zig"); - _ = @import("cases/pub_enum/index.zig"); - _ = @import("cases/ref_var_in_if_after_if_2nd_switch_prong.zig"); - _ = @import("cases/reflection.zig"); - _ = @import("cases/sizeof_and_typeof.zig"); - _ = @import("cases/slice.zig"); - _ = @import("cases/struct.zig"); - _ = @import("cases/struct_contains_null_ptr_itself.zig"); - _ = @import("cases/struct_contains_slice_of_itself.zig"); - _ = @import("cases/switch.zig"); - _ = @import("cases/switch_prong_err_enum.zig"); - _ = @import("cases/switch_prong_implicit_cast.zig"); - _ = @import("cases/syntax.zig"); - _ = @import("cases/this.zig"); - _ = @import("cases/truncate.zig"); - _ = @import("cases/try.zig"); - _ = @import("cases/type_info.zig"); - _ = @import("cases/undefined.zig"); - _ = @import("cases/underscore.zig"); - _ = @import("cases/union.zig"); - _ = @import("cases/var_args.zig"); - _ = @import("cases/void.zig"); - _ = @import("cases/while.zig"); - _ = @import("cases/widening.zig"); - _ = @import("cases/bit_shifting.zig"); -} diff --git a/test/cases/array.zig b/test/cases/array.zig deleted file mode 100644 index 7c63a649a8f322fe335039aa6717fea1333b3fcd..0000000000000000000000000000000000000000 --- a/test/cases/array.zig +++ /dev/null @@ -1,173 +0,0 @@ -const assert = @import("std").debug.assert; -const mem = @import("std").mem; - -test "arrays" { - var array: [5]u32 = undefined; - - var i: u32 = 0; - while (i < 5) { - array[i] = i + 1; - i = array[i]; - } - - i = 0; - var accumulator = u32(0); - while (i < 5) { - accumulator += array[i]; - - i += 1; - } - - assert(accumulator == 15); - assert(getArrayLen(array) == 5); -} -fn getArrayLen(a: []const u32) usize { - return a.len; -} - -test "void arrays" { - var array: [4]void = undefined; - array[0] = void{}; - array[1] = array[2]; - assert(@sizeOf(@typeOf(array)) == 0); - assert(array.len == 4); -} - -test "array literal" { - const hex_mult = []u16{ - 4096, - 256, - 16, - 1, - }; - - assert(hex_mult.len == 4); - assert(hex_mult[1] == 256); -} - -test "array dot len const expr" { - assert(comptime x: { - break :x some_array.len == 4; - }); -} - -const ArrayDotLenConstExpr = struct { - y: [some_array.len]u8, -}; -const some_array = []u8{ - 0, - 1, - 2, - 3, -}; - -test "nested arrays" { - const array_of_strings = [][]const u8{ - "hello", - "this", - "is", - "my", - "thing", - }; - for (array_of_strings) |s, i| { - if (i == 0) assert(mem.eql(u8, s, "hello")); - if (i == 1) assert(mem.eql(u8, s, "this")); - if (i == 2) assert(mem.eql(u8, s, "is")); - if (i == 3) assert(mem.eql(u8, s, "my")); - if (i == 4) assert(mem.eql(u8, s, "thing")); - } -} - -var s_array: [8]Sub = undefined; -const Sub = struct { - b: u8, -}; -const Str = struct { - a: []Sub, -}; -test "set global var array via slice embedded in struct" { - var s = Str{ .a = s_array[0..] }; - - s.a[0].b = 1; - s.a[1].b = 2; - s.a[2].b = 3; - - assert(s_array[0].b == 1); - assert(s_array[1].b == 2); - assert(s_array[2].b == 3); -} - -test "array literal with specified size" { - var array = [2]u8{ - 1, - 2, - }; - assert(array[0] == 1); - assert(array[1] == 2); -} - -test "array child property" { - var x: [5]i32 = undefined; - assert(@typeOf(x).Child == i32); -} - -test "array len property" { - var x: [5]i32 = undefined; - assert(@typeOf(x).len == 5); -} - -test "array len field" { - var arr = [4]u8{ 0, 0, 0, 0 }; - var ptr = &arr; - assert(arr.len == 4); - comptime assert(arr.len == 4); - assert(ptr.len == 4); - comptime assert(ptr.len == 4); -} - -test "single-item pointer to array indexing and slicing" { - testSingleItemPtrArrayIndexSlice(); - comptime testSingleItemPtrArrayIndexSlice(); -} - -fn testSingleItemPtrArrayIndexSlice() void { - var array = "aaaa"; - doSomeMangling(&array); - assert(mem.eql(u8, "azya", array)); -} - -fn doSomeMangling(array: *[4]u8) void { - array[1] = 'z'; - array[2..3][0] = 'y'; -} - -test "implicit cast single-item pointer" { - testImplicitCastSingleItemPtr(); - comptime testImplicitCastSingleItemPtr(); -} - -fn testImplicitCastSingleItemPtr() void { - var byte: u8 = 100; - const slice = (*[1]u8)(&byte)[0..]; - slice[0] += 1; - assert(byte == 101); -} - -fn testArrayByValAtComptime(b: [2]u8) u8 { - return b[0]; -} - -test "comptime evalutating function that takes array by value" { - const arr = []u8{ 0, 1 }; - _ = comptime testArrayByValAtComptime(arr); - _ = comptime testArrayByValAtComptime(arr); -} - -test "implicit comptime in array type size" { - var arr: [plusOne(10)]bool = undefined; - assert(arr.len == 11); -} - -fn plusOne(x: u32) u32 { - return x + 1; -} diff --git a/test/cases/asm.zig b/test/cases/asm.zig deleted file mode 100644 index 63e37c857c1dd1440e5c2ceed002c2ac86743dc7..0000000000000000000000000000000000000000 --- a/test/cases/asm.zig +++ /dev/null @@ -1,48 +0,0 @@ -const config = @import("builtin"); -const assert = @import("std").debug.assert; - -comptime { - if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) { - asm volatile ( - \\.globl aoeu; - \\.type aoeu, @function; - \\.set aoeu, derp; - ); - } -} - -test "module level assembly" { - if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) { - assert(aoeu() == 1234); - } -} - -test "output constraint modifiers" { - // This is only testing compilation. - var a: u32 = 3; - asm volatile ("" : [_]"=m,r"(a) : : ""); - asm volatile ("" : [_]"=r,m"(a) : : ""); -} - -test "alternative constraints" { - // Make sure we allow commas as a separator for alternative constraints. - var a: u32 = 3; - asm volatile ("" : [_]"=r,m"(a) : [_]"r,m"(a) : ""); -} - -test "sized integer/float in asm input" { - asm volatile ("" : : [_]"m"(usize(3)) : ""); - asm volatile ("" : : [_]"m"(i15(-3)) : ""); - asm volatile ("" : : [_]"m"(u3(3)) : ""); - asm volatile ("" : : [_]"m"(i3(3)) : ""); - asm volatile ("" : : [_]"m"(u121(3)) : ""); - asm volatile ("" : : [_]"m"(i121(3)) : ""); - asm volatile ("" : : [_]"m"(f32(3.17)) : ""); - asm volatile ("" : : [_]"m"(f64(3.17)) : ""); -} - -extern fn aoeu() i32; - -export fn derp() i32 { - return 1234; -} diff --git a/test/cases/bitreverse.zig b/test/cases/bitreverse.zig deleted file mode 100644 index 3721e68a9431fa4fec723ff1ca53fc5f411f344d..0000000000000000000000000000000000000000 --- a/test/cases/bitreverse.zig +++ /dev/null @@ -1,81 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; -const minInt = std.math.minInt; - -test "@bitreverse" { - comptime testBitReverse(); - testBitReverse(); -} - -fn testBitReverse() void { - // using comptime_ints, unsigned - assert(@bitreverse(u0, 0) == 0); - assert(@bitreverse(u5, 0x12) == 0x9); - assert(@bitreverse(u8, 0x12) == 0x48); - assert(@bitreverse(u16, 0x1234) == 0x2c48); - assert(@bitreverse(u24, 0x123456) == 0x6a2c48); - assert(@bitreverse(u32, 0x12345678) == 0x1e6a2c48); - assert(@bitreverse(u40, 0x123456789a) == 0x591e6a2c48); - assert(@bitreverse(u48, 0x123456789abc) == 0x3d591e6a2c48); - assert(@bitreverse(u56, 0x123456789abcde) == 0x7b3d591e6a2c48); - assert(@bitreverse(u64, 0x123456789abcdef1) == 0x8f7b3d591e6a2c48); - assert(@bitreverse(u128, 0x123456789abcdef11121314151617181) == 0x818e868a828c84888f7b3d591e6a2c48); - - // using runtime uints, unsigned - var num0: u0 = 0; - assert(@bitreverse(u0, num0) == 0); - var num5: u5 = 0x12; - assert(@bitreverse(u5, num5) == 0x9); - var num8: u8 = 0x12; - assert(@bitreverse(u8, num8) == 0x48); - var num16: u16 = 0x1234; - assert(@bitreverse(u16, num16) == 0x2c48); - var num24: u24 = 0x123456; - assert(@bitreverse(u24, num24) == 0x6a2c48); - var num32: u32 = 0x12345678; - assert(@bitreverse(u32, num32) == 0x1e6a2c48); - var num40: u40 = 0x123456789a; - assert(@bitreverse(u40, num40) == 0x591e6a2c48); - var num48: u48 = 0x123456789abc; - assert(@bitreverse(u48, num48) == 0x3d591e6a2c48); - var num56: u56 = 0x123456789abcde; - assert(@bitreverse(u56, num56) == 0x7b3d591e6a2c48); - var num64: u64 = 0x123456789abcdef1; - assert(@bitreverse(u64, num64) == 0x8f7b3d591e6a2c48); - var num128: u128 = 0x123456789abcdef11121314151617181; - assert(@bitreverse(u128, num128) == 0x818e868a828c84888f7b3d591e6a2c48); - - // using comptime_ints, signed, positive - assert(@bitreverse(i0, 0) == 0); - assert(@bitreverse(i8, @bitCast(i8, u8(0x92))) == @bitCast(i8, u8( 0x49))); - assert(@bitreverse(i16, @bitCast(i16, u16(0x1234))) == @bitCast(i16, u16( 0x2c48))); - assert(@bitreverse(i24, @bitCast(i24, u24(0x123456))) == @bitCast(i24, u24( 0x6a2c48))); - assert(@bitreverse(i32, @bitCast(i32, u32(0x12345678))) == @bitCast(i32, u32( 0x1e6a2c48))); - assert(@bitreverse(i40, @bitCast(i40, u40(0x123456789a))) == @bitCast(i40, u40( 0x591e6a2c48))); - assert(@bitreverse(i48, @bitCast(i48, u48(0x123456789abc))) == @bitCast(i48, u48( 0x3d591e6a2c48))); - assert(@bitreverse(i56, @bitCast(i56, u56(0x123456789abcde))) == @bitCast(i56, u56( 0x7b3d591e6a2c48))); - assert(@bitreverse(i64, @bitCast(i64, u64(0x123456789abcdef1))) == @bitCast(i64,u64(0x8f7b3d591e6a2c48))); - assert(@bitreverse(i128, @bitCast(i128,u128(0x123456789abcdef11121314151617181))) == @bitCast(i128,u128(0x818e868a828c84888f7b3d591e6a2c48))); - - // using comptime_ints, signed, negative. Compare to runtime ints returned from llvm. - var neg5: i5 = minInt(i5) + 1; - assert(@bitreverse(i5, minInt(i5) + 1) == @bitreverse(i5, neg5)); - var neg8: i8 = -18; - assert(@bitreverse(i8, -18) == @bitreverse(i8, neg8)); - var neg16: i16 = -32694; - assert(@bitreverse(i16, -32694) == @bitreverse(i16, neg16)); - var neg24: i24 = -6773785; - assert(@bitreverse(i24, -6773785) == @bitreverse(i24, neg24)); - var neg32: i32 = -16773785; - assert(@bitreverse(i32, -16773785) == @bitreverse(i32, neg32)); - var neg40: i40 = minInt(i40) + 12345; - assert(@bitreverse(i40, minInt(i40) + 12345) == @bitreverse(i40, neg40)); - var neg48: i48 = minInt(i48) + 12345; - assert(@bitreverse(i48, minInt(i48) + 12345) == @bitreverse(i48, neg48)); - var neg56: i56 = minInt(i56) + 12345; - assert(@bitreverse(i56, minInt(i56) + 12345) == @bitreverse(i56, neg56)); - var neg64: i64 = minInt(i64) + 12345; - assert(@bitreverse(i64, minInt(i64) + 12345) == @bitreverse(i64, neg64)); - var neg128: i128 = minInt(i128) + 12345; - assert(@bitreverse(i128, minInt(i128) + 12345) == @bitreverse(i128, neg128)); -} diff --git a/test/cases/bswap.zig b/test/cases/bswap.zig deleted file mode 100644 index 57993077e1a855fa9af487b136132d8bc0fa86ea..0000000000000000000000000000000000000000 --- a/test/cases/bswap.zig +++ /dev/null @@ -1,32 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; - -test "@bswap" { - comptime testByteSwap(); - testByteSwap(); -} - -fn testByteSwap() void { - assert(@bswap(u0, 0) == 0); - assert(@bswap(u8, 0x12) == 0x12); - assert(@bswap(u16, 0x1234) == 0x3412); - assert(@bswap(u24, 0x123456) == 0x563412); - assert(@bswap(u32, 0x12345678) == 0x78563412); - assert(@bswap(u40, 0x123456789a) == 0x9a78563412); - assert(@bswap(u48, 0x123456789abc) == 0xbc9a78563412); - assert(@bswap(u56, 0x123456789abcde) == 0xdebc9a78563412); - assert(@bswap(u64, 0x123456789abcdef1) == 0xf1debc9a78563412); - assert(@bswap(u128, 0x123456789abcdef11121314151617181) == 0x8171615141312111f1debc9a78563412); - - assert(@bswap(i0, 0) == 0); - assert(@bswap(i8, -50) == -50); - assert(@bswap(i16, @bitCast(i16, u16(0x1234))) == @bitCast(i16, u16(0x3412))); - assert(@bswap(i24, @bitCast(i24, u24(0x123456))) == @bitCast(i24, u24(0x563412))); - assert(@bswap(i32, @bitCast(i32, u32(0x12345678))) == @bitCast(i32, u32(0x78563412))); - assert(@bswap(i40, @bitCast(i40, u40(0x123456789a))) == @bitCast(i40, u40(0x9a78563412))); - assert(@bswap(i48, @bitCast(i48, u48(0x123456789abc))) == @bitCast(i48, u48(0xbc9a78563412))); - assert(@bswap(i56, @bitCast(i56, u56(0x123456789abcde))) == @bitCast(i56, u56(0xdebc9a78563412))); - assert(@bswap(i64, @bitCast(i64, u64(0x123456789abcdef1))) == @bitCast(i64, u64(0xf1debc9a78563412))); - assert(@bswap(i128, @bitCast(i128, u128(0x123456789abcdef11121314151617181))) == - @bitCast(i128, u128(0x8171615141312111f1debc9a78563412))); -} diff --git a/test/cases/import.zig b/test/cases/import.zig deleted file mode 100644 index 6d6d4b020860be13787cd862624e4bfdb4644bb3..0000000000000000000000000000000000000000 --- a/test/cases/import.zig +++ /dev/null @@ -1,10 +0,0 @@ -const assert = @import("std").debug.assert; -const a_namespace = @import("import/a_namespace.zig"); - -test "call fn via namespace lookup" { - assert(a_namespace.foo() == 1234); -} - -test "importing the same thing gives the same import" { - assert(@import("std") == @import("std")); -} diff --git a/test/cases/optional.zig b/test/cases/optional.zig deleted file mode 100644 index d43682bbec3c676163007eb7b49677c3cc8a2a44..0000000000000000000000000000000000000000 --- a/test/cases/optional.zig +++ /dev/null @@ -1,30 +0,0 @@ -const assert = @import("std").debug.assert; - -pub const EmptyStruct = struct {}; - -test "optional pointer to size zero struct" { - var e = EmptyStruct{}; - var o: ?*EmptyStruct = &e; - assert(o != null); -} - -test "equality compare nullable pointers" { - testNullPtrsEql(); - comptime testNullPtrsEql(); -} - -fn testNullPtrsEql() void { - var number: i32 = 1234; - - var x: ?*i32 = null; - var y: ?*i32 = null; - assert(x == y); - y = &number; - assert(x != y); - assert(x != &number); - assert(&number != x); - x = &number; - assert(x == y); - assert(x == &number); - assert(&number == x); -} diff --git a/test/cases/popcount.zig b/test/cases/popcount.zig deleted file mode 100644 index 7dc7f28c0e9cf48733267aa5b6cf961c33880e73..0000000000000000000000000000000000000000 --- a/test/cases/popcount.zig +++ /dev/null @@ -1,24 +0,0 @@ -const assert = @import("std").debug.assert; - -test "@popCount" { - comptime testPopCount(); - testPopCount(); -} - -fn testPopCount() void { - { - var x: u32 = 0xaa; - assert(@popCount(x) == 4); - } - { - var x: u32 = 0xaaaaaaaa; - assert(@popCount(x) == 16); - } - { - var x: i16 = -1; - assert(@popCount(x) == 16); - } - comptime { - assert(@popCount(0b11111111000110001100010000100001000011000011100101010001) == 24); - } -} diff --git a/test/cases/reflection.zig b/test/cases/reflection.zig deleted file mode 100644 index b9b8aff4e16ad47a56d7dc486e4a96688d815af4..0000000000000000000000000000000000000000 --- a/test/cases/reflection.zig +++ /dev/null @@ -1,95 +0,0 @@ -const assert = @import("std").debug.assert; -const mem = @import("std").mem; -const reflection = @This(); - -test "reflection: array, pointer, optional, error union type child" { - comptime { - assert(([10]u8).Child == u8); - assert((*u8).Child == u8); - assert((anyerror!u8).Payload == u8); - assert((?u8).Child == u8); - } -} - -test "reflection: function return type, var args, and param types" { - comptime { - assert(@typeOf(dummy).ReturnType == i32); - assert(!@typeOf(dummy).is_var_args); - assert(@typeOf(dummy_varargs).is_var_args); - assert(@typeOf(dummy).arg_count == 3); - assert(@ArgType(@typeOf(dummy), 0) == bool); - assert(@ArgType(@typeOf(dummy), 1) == i32); - assert(@ArgType(@typeOf(dummy), 2) == f32); - } -} - -fn dummy(a: bool, b: i32, c: f32) i32 { - return 1234; -} -fn dummy_varargs(args: ...) void {} - -test "reflection: struct member types and names" { - comptime { - assert(@memberCount(Foo) == 3); - - assert(@memberType(Foo, 0) == i32); - assert(@memberType(Foo, 1) == bool); - assert(@memberType(Foo, 2) == void); - - assert(mem.eql(u8, @memberName(Foo, 0), "one")); - assert(mem.eql(u8, @memberName(Foo, 1), "two")); - assert(mem.eql(u8, @memberName(Foo, 2), "three")); - } -} - -test "reflection: enum member types and names" { - comptime { - assert(@memberCount(Bar) == 4); - - assert(@memberType(Bar, 0) == void); - assert(@memberType(Bar, 1) == i32); - assert(@memberType(Bar, 2) == bool); - assert(@memberType(Bar, 3) == f64); - - assert(mem.eql(u8, @memberName(Bar, 0), "One")); - assert(mem.eql(u8, @memberName(Bar, 1), "Two")); - assert(mem.eql(u8, @memberName(Bar, 2), "Three")); - assert(mem.eql(u8, @memberName(Bar, 3), "Four")); - } -} - -test "reflection: @field" { - var f = Foo{ - .one = 42, - .two = true, - .three = void{}, - }; - - assert(f.one == f.one); - assert(@field(f, "o" ++ "ne") == f.one); - assert(@field(f, "t" ++ "wo") == f.two); - assert(@field(f, "th" ++ "ree") == f.three); - assert(@field(Foo, "const" ++ "ant") == Foo.constant); - assert(@field(Bar, "O" ++ "ne") == Bar.One); - assert(@field(Bar, "T" ++ "wo") == Bar.Two); - assert(@field(Bar, "Th" ++ "ree") == Bar.Three); - assert(@field(Bar, "F" ++ "our") == Bar.Four); - assert(@field(reflection, "dum" ++ "my")(true, 1, 2) == dummy(true, 1, 2)); - @field(f, "o" ++ "ne") = 4; - assert(f.one == 4); -} - -const Foo = struct { - const constant = 52; - - one: i32, - two: bool, - three: void, -}; - -const Bar = union(enum) { - One: void, - Two: i32, - Three: bool, - Four: f64, -}; diff --git a/test/cases/sizeof_and_typeof.zig b/test/cases/sizeof_and_typeof.zig deleted file mode 100644 index 11c6b2f6ba36c9c355b50dae4c724bc9d14ede34..0000000000000000000000000000000000000000 --- a/test/cases/sizeof_and_typeof.zig +++ /dev/null @@ -1,69 +0,0 @@ -const builtin = @import("builtin"); -const assert = @import("std").debug.assert; - -test "@sizeOf and @typeOf" { - const y: @typeOf(x) = 120; - assert(@sizeOf(@typeOf(y)) == 2); -} -const x: u16 = 13; -const z: @typeOf(x) = 19; - -const A = struct { - a: u8, - b: u32, - c: u8, - d: u3, - e: u5, - f: u16, - g: u16, -}; - -const P = packed struct { - a: u8, - b: u32, - c: u8, - d: u3, - e: u5, - f: u16, - g: u16, -}; - -test "@byteOffsetOf" { - // Packed structs have fixed memory layout - assert(@byteOffsetOf(P, "a") == 0); - assert(@byteOffsetOf(P, "b") == 1); - assert(@byteOffsetOf(P, "c") == 5); - assert(@byteOffsetOf(P, "d") == 6); - assert(@byteOffsetOf(P, "e") == 6); - assert(@byteOffsetOf(P, "f") == 7); - assert(@byteOffsetOf(P, "g") == 9); - - // Normal struct fields can be moved/padded - var a: A = undefined; - assert(@ptrToInt(&a.a) - @ptrToInt(&a) == @byteOffsetOf(A, "a")); - assert(@ptrToInt(&a.b) - @ptrToInt(&a) == @byteOffsetOf(A, "b")); - assert(@ptrToInt(&a.c) - @ptrToInt(&a) == @byteOffsetOf(A, "c")); - assert(@ptrToInt(&a.d) - @ptrToInt(&a) == @byteOffsetOf(A, "d")); - assert(@ptrToInt(&a.e) - @ptrToInt(&a) == @byteOffsetOf(A, "e")); - assert(@ptrToInt(&a.f) - @ptrToInt(&a) == @byteOffsetOf(A, "f")); - assert(@ptrToInt(&a.g) - @ptrToInt(&a) == @byteOffsetOf(A, "g")); -} - -test "@bitOffsetOf" { - // Packed structs have fixed memory layout - assert(@bitOffsetOf(P, "a") == 0); - assert(@bitOffsetOf(P, "b") == 8); - assert(@bitOffsetOf(P, "c") == 40); - assert(@bitOffsetOf(P, "d") == 48); - assert(@bitOffsetOf(P, "e") == 51); - assert(@bitOffsetOf(P, "f") == 56); - assert(@bitOffsetOf(P, "g") == 72); - - assert(@byteOffsetOf(A, "a") * 8 == @bitOffsetOf(A, "a")); - assert(@byteOffsetOf(A, "b") * 8 == @bitOffsetOf(A, "b")); - assert(@byteOffsetOf(A, "c") * 8 == @bitOffsetOf(A, "c")); - assert(@byteOffsetOf(A, "d") * 8 == @bitOffsetOf(A, "d")); - assert(@byteOffsetOf(A, "e") * 8 == @bitOffsetOf(A, "e")); - assert(@byteOffsetOf(A, "f") * 8 == @bitOffsetOf(A, "f")); - assert(@byteOffsetOf(A, "g") * 8 == @bitOffsetOf(A, "g")); -} diff --git a/test/cases/type_info.zig b/test/cases/type_info.zig deleted file mode 100644 index cec532d5d30419b4de264c17b268c032c659ee01..0000000000000000000000000000000000000000 --- a/test/cases/type_info.zig +++ /dev/null @@ -1,264 +0,0 @@ -const assert = @import("std").debug.assert; -const mem = @import("std").mem; -const TypeInfo = @import("builtin").TypeInfo; -const TypeId = @import("builtin").TypeId; - -test "type info: tag type, void info" { - testBasic(); - comptime testBasic(); -} - -fn testBasic() void { - assert(@TagType(TypeInfo) == TypeId); - const void_info = @typeInfo(void); - assert(TypeId(void_info) == TypeId.Void); - assert(void_info.Void == {}); -} - -test "type info: integer, floating point type info" { - testIntFloat(); - comptime testIntFloat(); -} - -fn testIntFloat() void { - const u8_info = @typeInfo(u8); - assert(TypeId(u8_info) == TypeId.Int); - assert(!u8_info.Int.is_signed); - assert(u8_info.Int.bits == 8); - - const f64_info = @typeInfo(f64); - assert(TypeId(f64_info) == TypeId.Float); - assert(f64_info.Float.bits == 64); -} - -test "type info: pointer type info" { - testPointer(); - comptime testPointer(); -} - -fn testPointer() void { - const u32_ptr_info = @typeInfo(*u32); - assert(TypeId(u32_ptr_info) == TypeId.Pointer); - assert(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.One); - assert(u32_ptr_info.Pointer.is_const == false); - assert(u32_ptr_info.Pointer.is_volatile == false); - assert(u32_ptr_info.Pointer.alignment == @alignOf(u32)); - assert(u32_ptr_info.Pointer.child == u32); -} - -test "type info: unknown length pointer type info" { - testUnknownLenPtr(); - comptime testUnknownLenPtr(); -} - -fn testUnknownLenPtr() void { - const u32_ptr_info = @typeInfo([*]const volatile f64); - assert(TypeId(u32_ptr_info) == TypeId.Pointer); - assert(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many); - assert(u32_ptr_info.Pointer.is_const == true); - assert(u32_ptr_info.Pointer.is_volatile == true); - assert(u32_ptr_info.Pointer.alignment == @alignOf(f64)); - assert(u32_ptr_info.Pointer.child == f64); -} - -test "type info: slice type info" { - testSlice(); - comptime testSlice(); -} - -fn testSlice() void { - const u32_slice_info = @typeInfo([]u32); - assert(TypeId(u32_slice_info) == TypeId.Pointer); - assert(u32_slice_info.Pointer.size == TypeInfo.Pointer.Size.Slice); - assert(u32_slice_info.Pointer.is_const == false); - assert(u32_slice_info.Pointer.is_volatile == false); - assert(u32_slice_info.Pointer.alignment == 4); - assert(u32_slice_info.Pointer.child == u32); -} - -test "type info: array type info" { - testArray(); - comptime testArray(); -} - -fn testArray() void { - const arr_info = @typeInfo([42]bool); - assert(TypeId(arr_info) == TypeId.Array); - assert(arr_info.Array.len == 42); - assert(arr_info.Array.child == bool); -} - -test "type info: optional type info" { - testOptional(); - comptime testOptional(); -} - -fn testOptional() void { - const null_info = @typeInfo(?void); - assert(TypeId(null_info) == TypeId.Optional); - assert(null_info.Optional.child == void); -} - -test "type info: promise info" { - testPromise(); - comptime testPromise(); -} - -fn testPromise() void { - const null_promise_info = @typeInfo(promise); - assert(TypeId(null_promise_info) == TypeId.Promise); - assert(null_promise_info.Promise.child == null); - - const promise_info = @typeInfo(promise->usize); - assert(TypeId(promise_info) == TypeId.Promise); - assert(promise_info.Promise.child.? == usize); -} - -test "type info: error set, error union info" { - testErrorSet(); - comptime testErrorSet(); -} - -fn testErrorSet() void { - const TestErrorSet = error{ - First, - Second, - Third, - }; - - const error_set_info = @typeInfo(TestErrorSet); - assert(TypeId(error_set_info) == TypeId.ErrorSet); - assert(error_set_info.ErrorSet.errors.len == 3); - assert(mem.eql(u8, error_set_info.ErrorSet.errors[0].name, "First")); - assert(error_set_info.ErrorSet.errors[2].value == @errorToInt(TestErrorSet.Third)); - - const error_union_info = @typeInfo(TestErrorSet!usize); - assert(TypeId(error_union_info) == TypeId.ErrorUnion); - assert(error_union_info.ErrorUnion.error_set == TestErrorSet); - assert(error_union_info.ErrorUnion.payload == usize); -} - -test "type info: enum info" { - testEnum(); - comptime testEnum(); -} - -fn testEnum() void { - const Os = enum { - Windows, - Macos, - Linux, - FreeBSD, - }; - - const os_info = @typeInfo(Os); - assert(TypeId(os_info) == TypeId.Enum); - assert(os_info.Enum.layout == TypeInfo.ContainerLayout.Auto); - assert(os_info.Enum.fields.len == 4); - assert(mem.eql(u8, os_info.Enum.fields[1].name, "Macos")); - assert(os_info.Enum.fields[3].value == 3); - assert(os_info.Enum.tag_type == u2); - assert(os_info.Enum.defs.len == 0); -} - -test "type info: union info" { - testUnion(); - comptime testUnion(); -} - -fn testUnion() void { - const typeinfo_info = @typeInfo(TypeInfo); - assert(TypeId(typeinfo_info) == TypeId.Union); - assert(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto); - assert(typeinfo_info.Union.tag_type.? == TypeId); - assert(typeinfo_info.Union.fields.len == 24); - assert(typeinfo_info.Union.fields[4].enum_field != null); - assert(typeinfo_info.Union.fields[4].enum_field.?.value == 4); - assert(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int)); - assert(typeinfo_info.Union.defs.len == 20); - - const TestNoTagUnion = union { - Foo: void, - Bar: u32, - }; - - const notag_union_info = @typeInfo(TestNoTagUnion); - assert(TypeId(notag_union_info) == TypeId.Union); - assert(notag_union_info.Union.tag_type == null); - assert(notag_union_info.Union.layout == TypeInfo.ContainerLayout.Auto); - assert(notag_union_info.Union.fields.len == 2); - assert(notag_union_info.Union.fields[0].enum_field == null); - assert(notag_union_info.Union.fields[1].field_type == u32); - - const TestExternUnion = extern union { - foo: *c_void, - }; - - const extern_union_info = @typeInfo(TestExternUnion); - assert(extern_union_info.Union.layout == TypeInfo.ContainerLayout.Extern); - assert(extern_union_info.Union.tag_type == null); - assert(extern_union_info.Union.fields[0].enum_field == null); - assert(extern_union_info.Union.fields[0].field_type == *c_void); -} - -test "type info: struct info" { - testStruct(); - comptime testStruct(); -} - -fn testStruct() void { - const struct_info = @typeInfo(TestStruct); - assert(TypeId(struct_info) == TypeId.Struct); - assert(struct_info.Struct.layout == TypeInfo.ContainerLayout.Packed); - assert(struct_info.Struct.fields.len == 3); - assert(struct_info.Struct.fields[1].offset == null); - assert(struct_info.Struct.fields[2].field_type == *TestStruct); - assert(struct_info.Struct.defs.len == 2); - assert(struct_info.Struct.defs[0].is_pub); - assert(!struct_info.Struct.defs[0].data.Fn.is_extern); - assert(struct_info.Struct.defs[0].data.Fn.lib_name == null); - assert(struct_info.Struct.defs[0].data.Fn.return_type == void); - assert(struct_info.Struct.defs[0].data.Fn.fn_type == fn (*const TestStruct) void); -} - -const TestStruct = packed struct { - const Self = @This(); - - fieldA: usize, - fieldB: void, - fieldC: *Self, - - pub fn foo(self: *const Self) void {} -}; - -test "type info: function type info" { - testFunction(); - comptime testFunction(); -} - -fn testFunction() void { - const fn_info = @typeInfo(@typeOf(foo)); - assert(TypeId(fn_info) == TypeId.Fn); - assert(fn_info.Fn.calling_convention == TypeInfo.CallingConvention.Unspecified); - assert(fn_info.Fn.is_generic); - assert(fn_info.Fn.args.len == 2); - assert(fn_info.Fn.is_var_args); - assert(fn_info.Fn.return_type == null); - assert(fn_info.Fn.async_allocator_type == null); - - const test_instance: TestStruct = undefined; - const bound_fn_info = @typeInfo(@typeOf(test_instance.foo)); - assert(TypeId(bound_fn_info) == TypeId.BoundFn); - assert(bound_fn_info.BoundFn.args[0].arg_type.? == *const TestStruct); -} - -fn foo(comptime a: usize, b: bool, args: ...) usize { - return 0; -} - -test "typeInfo with comptime parameter in struct fn def" { - const S = struct { - pub fn func(comptime x: f32) void {} - }; - comptime var info = @typeInfo(S); -} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 0754f223e82730bceb3da1ed33e6df4587fd8851..bc1ef660c3a6528a8749fc4362071ce684c07191 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -3220,7 +3220,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return 2; \\} , - ".tmp_source.zig:2:15: error: unable to infer expression type", + ".tmp_source.zig:2:15: error: values of type 'comptime_int' must be comptime known", ); cases.add( @@ -3566,7 +3566,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(a)); } , - ".tmp_source.zig:2:11: error: expected type, found 'i32'", + ".tmp_source.zig:2:11: error: expected type 'type', found 'i32'", ); cases.add( diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig new file mode 100644 index 0000000000000000000000000000000000000000..e545a4c4184d3f6587b5ead0c453fe3c54e8ef11 --- /dev/null +++ b/test/stage1/behavior.zig @@ -0,0 +1,80 @@ +comptime { + _ = @import("behavior/align.zig"); + _ = @import("behavior/alignof.zig"); + _ = @import("behavior/array.zig"); + _ = @import("behavior/asm.zig"); + _ = @import("behavior/atomics.zig"); + _ = @import("behavior/bit_shifting.zig"); + _ = @import("behavior/bitcast.zig"); + _ = @import("behavior/bitreverse.zig"); + _ = @import("behavior/bool.zig"); + _ = @import("behavior/bswap.zig"); + _ = @import("behavior/bugs/1076.zig"); + _ = @import("behavior/bugs/1111.zig"); + _ = @import("behavior/bugs/1277.zig"); + _ = @import("behavior/bugs/1322.zig"); + _ = @import("behavior/bugs/1381.zig"); + _ = @import("behavior/bugs/1421.zig"); + _ = @import("behavior/bugs/1442.zig"); + _ = @import("behavior/bugs/1486.zig"); + _ = @import("behavior/bugs/394.zig"); + _ = @import("behavior/bugs/655.zig"); + _ = @import("behavior/bugs/656.zig"); + _ = @import("behavior/bugs/726.zig"); + _ = @import("behavior/bugs/828.zig"); + _ = @import("behavior/bugs/920.zig"); + _ = @import("behavior/byval_arg_var.zig"); + _ = @import("behavior/cancel.zig"); + _ = @import("behavior/cast.zig"); + _ = @import("behavior/const_slice_child.zig"); + _ = @import("behavior/coroutine_await_struct.zig"); + _ = @import("behavior/coroutines.zig"); + _ = @import("behavior/defer.zig"); + _ = @import("behavior/enum.zig"); + _ = @import("behavior/enum_with_members.zig"); + _ = @import("behavior/error.zig"); + _ = @import("behavior/eval.zig"); + _ = @import("behavior/field_parent_ptr.zig"); + _ = @import("behavior/fn.zig"); + _ = @import("behavior/fn_in_struct_in_comptime.zig"); + _ = @import("behavior/for.zig"); + _ = @import("behavior/generics.zig"); + _ = @import("behavior/if.zig"); + _ = @import("behavior/import.zig"); + _ = @import("behavior/incomplete_struct_param_tld.zig"); + _ = @import("behavior/inttoptr.zig"); + _ = @import("behavior/ir_block_deps.zig"); + _ = @import("behavior/math.zig"); + _ = @import("behavior/merge_error_sets.zig"); + _ = @import("behavior/misc.zig"); + _ = @import("behavior/namespace_depends_on_compile_var/index.zig"); + _ = @import("behavior/new_stack_call.zig"); + _ = @import("behavior/null.zig"); + _ = @import("behavior/optional.zig"); + _ = @import("behavior/pointers.zig"); + _ = @import("behavior/popcount.zig"); + _ = @import("behavior/ptrcast.zig"); + _ = @import("behavior/pub_enum/index.zig"); + _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); + _ = @import("behavior/reflection.zig"); + _ = @import("behavior/sizeof_and_typeof.zig"); + _ = @import("behavior/slice.zig"); + _ = @import("behavior/struct.zig"); + _ = @import("behavior/struct_contains_null_ptr_itself.zig"); + _ = @import("behavior/struct_contains_slice_of_itself.zig"); + _ = @import("behavior/switch.zig"); + _ = @import("behavior/switch_prong_err_enum.zig"); + _ = @import("behavior/switch_prong_implicit_cast.zig"); + _ = @import("behavior/syntax.zig"); + _ = @import("behavior/this.zig"); + _ = @import("behavior/truncate.zig"); + _ = @import("behavior/try.zig"); + _ = @import("behavior/type_info.zig"); + _ = @import("behavior/undefined.zig"); + _ = @import("behavior/underscore.zig"); + _ = @import("behavior/union.zig"); + _ = @import("behavior/var_args.zig"); + _ = @import("behavior/void.zig"); + _ = @import("behavior/while.zig"); + _ = @import("behavior/widening.zig"); +} diff --git a/test/cases/align.zig b/test/stage1/behavior/align.zig similarity index 69% rename from test/cases/align.zig rename to test/stage1/behavior/align.zig index 3dff57feb882141a8f9e7cabc1f710542310beb1..aa7a93ad848210b088ab33ab63d284c1e7a1ae56 100644 --- a/test/cases/align.zig +++ b/test/stage1/behavior/align.zig @@ -1,13 +1,13 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const builtin = @import("builtin"); var foo: u8 align(4) = 100; test "global variable alignment" { - assert(@typeOf(&foo).alignment == 4); - assert(@typeOf(&foo) == *align(4) u8); + assertOrPanic(@typeOf(&foo).alignment == 4); + assertOrPanic(@typeOf(&foo) == *align(4) u8); const slice = (*[1]u8)(&foo)[0..]; - assert(@typeOf(slice) == []align(4) u8); + assertOrPanic(@typeOf(slice) == []align(4) u8); } fn derp() align(@sizeOf(usize) * 2) i32 { @@ -17,9 +17,9 @@ fn noop1() align(1) void {} fn noop4() align(4) void {} test "function alignment" { - assert(derp() == 1234); - assert(@typeOf(noop1) == fn () align(1) void); - assert(@typeOf(noop4) == fn () align(4) void); + assertOrPanic(derp() == 1234); + assertOrPanic(@typeOf(noop1) == fn () align(1) void); + assertOrPanic(@typeOf(noop4) == fn () align(4) void); noop1(); noop4(); } @@ -30,7 +30,7 @@ var baz: packed struct { } = undefined; test "packed struct alignment" { - assert(@typeOf(&baz.b) == *align(1) u32); + assertOrPanic(@typeOf(&baz.b) == *align(1) u32); } const blah: packed struct { @@ -40,17 +40,17 @@ const blah: packed struct { } = undefined; test "bit field alignment" { - assert(@typeOf(&blah.b) == *align(1:3:1) const u3); + assertOrPanic(@typeOf(&blah.b) == *align(1:3:1) const u3); } test "default alignment allows unspecified in type syntax" { - assert(*u32 == *align(@alignOf(u32)) u32); + assertOrPanic(*u32 == *align(@alignOf(u32)) u32); } test "implicitly decreasing pointer alignment" { const a: u32 align(4) = 3; const b: u32 align(8) = 4; - assert(addUnaligned(&a, &b) == 7); + assertOrPanic(addUnaligned(&a, &b) == 7); } fn addUnaligned(a: *align(1) const u32, b: *align(1) const u32) u32 { @@ -60,7 +60,7 @@ fn addUnaligned(a: *align(1) const u32, b: *align(1) const u32) u32 { test "implicitly decreasing slice alignment" { const a: u32 align(4) = 3; const b: u32 align(8) = 4; - assert(addUnalignedSlice((*[1]u32)(&a)[0..], (*[1]u32)(&b)[0..]) == 7); + assertOrPanic(addUnalignedSlice((*[1]u32)(&a)[0..], (*[1]u32)(&b)[0..]) == 7); } fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 { return a[0] + b[0]; @@ -77,7 +77,7 @@ fn testBytesAlign(b: u8) void { b, }; const ptr = @ptrCast(*u32, &bytes[0]); - assert(ptr.* == 0x33333333); + assertOrPanic(ptr.* == 0x33333333); } test "specifying alignment allows slice cast" { @@ -91,13 +91,13 @@ fn testBytesAlignSlice(b: u8) void { b, }; const slice: []u32 = @bytesToSlice(u32, bytes[0..]); - assert(slice[0] == 0x33333333); + assertOrPanic(slice[0] == 0x33333333); } test "@alignCast pointers" { var x: u32 align(4) = 1; expectsOnly1(&x); - assert(x == 2); + assertOrPanic(x == 2); } fn expectsOnly1(x: *align(1) u32) void { expects4(@alignCast(4, x)); @@ -113,7 +113,7 @@ test "@alignCast slices" { }; const slice = array[0..]; sliceExpectsOnly1(slice); - assert(slice[0] == 2); + assertOrPanic(slice[0] == 2); } fn sliceExpectsOnly1(slice: []align(1) u32) void { sliceExpects4(@alignCast(4, slice)); @@ -128,7 +128,7 @@ test "implicitly decreasing fn alignment" { } fn testImplicitlyDecreaseFnAlign(ptr: fn () align(1) i32, answer: i32) void { - assert(ptr() == answer); + assertOrPanic(ptr() == answer); } fn alignedSmall() align(8) i32 { @@ -139,7 +139,7 @@ fn alignedBig() align(16) i32 { } test "@alignCast functions" { - assert(fnExpectsOnly1(simple4) == 0x19); + assertOrPanic(fnExpectsOnly1(simple4) == 0x19); } fn fnExpectsOnly1(ptr: fn () align(1) i32) i32 { return fnExpects4(@alignCast(4, ptr)); @@ -152,9 +152,9 @@ fn simple4() align(4) i32 { } test "generic function with align param" { - assert(whyWouldYouEverDoThis(1) == 0x1); - assert(whyWouldYouEverDoThis(4) == 0x1); - assert(whyWouldYouEverDoThis(8) == 0x1); + assertOrPanic(whyWouldYouEverDoThis(1) == 0x1); + assertOrPanic(whyWouldYouEverDoThis(4) == 0x1); + assertOrPanic(whyWouldYouEverDoThis(8) == 0x1); } fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 { @@ -164,28 +164,28 @@ fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 { test "@ptrCast preserves alignment of bigger source" { var x: u32 align(16) = 1234; const ptr = @ptrCast(*u8, &x); - assert(@typeOf(ptr) == *align(16) u8); + assertOrPanic(@typeOf(ptr) == *align(16) u8); } test "runtime known array index has best alignment possible" { // take full advantage of over-alignment var array align(4) = []u8{ 1, 2, 3, 4 }; - assert(@typeOf(&array[0]) == *align(4) u8); - assert(@typeOf(&array[1]) == *u8); - assert(@typeOf(&array[2]) == *align(2) u8); - assert(@typeOf(&array[3]) == *u8); + assertOrPanic(@typeOf(&array[0]) == *align(4) u8); + assertOrPanic(@typeOf(&array[1]) == *u8); + assertOrPanic(@typeOf(&array[2]) == *align(2) u8); + assertOrPanic(@typeOf(&array[3]) == *u8); // because align is too small but we still figure out to use 2 var bigger align(2) = []u64{ 1, 2, 3, 4 }; - assert(@typeOf(&bigger[0]) == *align(2) u64); - assert(@typeOf(&bigger[1]) == *align(2) u64); - assert(@typeOf(&bigger[2]) == *align(2) u64); - assert(@typeOf(&bigger[3]) == *align(2) u64); + assertOrPanic(@typeOf(&bigger[0]) == *align(2) u64); + assertOrPanic(@typeOf(&bigger[1]) == *align(2) u64); + assertOrPanic(@typeOf(&bigger[2]) == *align(2) u64); + assertOrPanic(@typeOf(&bigger[3]) == *align(2) u64); // because pointer is align 2 and u32 align % 2 == 0 we can assume align 2 var smaller align(2) = []u32{ 1, 2, 3, 4 }; - comptime assert(@typeOf(smaller[0..]) == []align(2) u32); - comptime assert(@typeOf(smaller[0..].ptr) == [*]align(2) u32); + comptime assertOrPanic(@typeOf(smaller[0..]) == []align(2) u32); + comptime assertOrPanic(@typeOf(smaller[0..].ptr) == [*]align(2) u32); testIndex(smaller[0..].ptr, 0, *align(2) u32); testIndex(smaller[0..].ptr, 1, *align(2) u32); testIndex(smaller[0..].ptr, 2, *align(2) u32); @@ -198,14 +198,14 @@ test "runtime known array index has best alignment possible" { testIndex2(array[0..].ptr, 3, *u8); } fn testIndex(smaller: [*]align(2) u32, index: usize, comptime T: type) void { - comptime assert(@typeOf(&smaller[index]) == T); + comptime assertOrPanic(@typeOf(&smaller[index]) == T); } fn testIndex2(ptr: [*]align(4) u8, index: usize, comptime T: type) void { - comptime assert(@typeOf(&ptr[index]) == T); + comptime assertOrPanic(@typeOf(&ptr[index]) == T); } test "alignstack" { - assert(fnWithAlignedStack() == 1234); + assertOrPanic(fnWithAlignedStack() == 1234); } fn fnWithAlignedStack() i32 { @@ -214,7 +214,7 @@ fn fnWithAlignedStack() i32 { } test "alignment of structs" { - assert(@alignOf(struct { + assertOrPanic(@alignOf(struct { a: i32, b: *i32, }) == @alignOf(usize)); diff --git a/test/cases/alignof.zig b/test/stage1/behavior/alignof.zig similarity index 61% rename from test/cases/alignof.zig rename to test/stage1/behavior/alignof.zig index 433e86e45edfa3c1d3d41b4a519f24cf2f19a579..98c805908b724e01907d24ba540a8434ebe91007 100644 --- a/test/cases/alignof.zig +++ b/test/stage1/behavior/alignof.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const builtin = @import("builtin"); const maxInt = std.math.maxInt; @@ -10,8 +10,9 @@ const Foo = struct { }; test "@alignOf(T) before referencing T" { - comptime assert(@alignOf(Foo) != maxInt(usize)); + comptime assertOrPanic(@alignOf(Foo) != maxInt(usize)); if (builtin.arch == builtin.Arch.x86_64) { - comptime assert(@alignOf(Foo) == 4); + comptime assertOrPanic(@alignOf(Foo) == 4); } } + diff --git a/test/stage1/behavior/array.zig b/test/stage1/behavior/array.zig new file mode 100644 index 0000000000000000000000000000000000000000..118330520986bfc055fb640fa2793c239f9703f6 --- /dev/null +++ b/test/stage1/behavior/array.zig @@ -0,0 +1,270 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; +const mem = @import("std").mem; + +test "arrays" { + var array: [5]u32 = undefined; + + var i: u32 = 0; + while (i < 5) { + array[i] = i + 1; + i = array[i]; + } + + i = 0; + var accumulator = u32(0); + while (i < 5) { + accumulator += array[i]; + + i += 1; + } + + assertOrPanic(accumulator == 15); + assertOrPanic(getArrayLen(array) == 5); +} +fn getArrayLen(a: []const u32) usize { + return a.len; +} + +test "void arrays" { + var array: [4]void = undefined; + array[0] = void{}; + array[1] = array[2]; + assertOrPanic(@sizeOf(@typeOf(array)) == 0); + assertOrPanic(array.len == 4); +} + +test "array literal" { + const hex_mult = []u16{ + 4096, + 256, + 16, + 1, + }; + + assertOrPanic(hex_mult.len == 4); + assertOrPanic(hex_mult[1] == 256); +} + +test "array dot len const expr" { + assertOrPanic(comptime x: { + break :x some_array.len == 4; + }); +} + +const ArrayDotLenConstExpr = struct { + y: [some_array.len]u8, +}; +const some_array = []u8{ + 0, + 1, + 2, + 3, +}; + +test "nested arrays" { + const array_of_strings = [][]const u8{ + "hello", + "this", + "is", + "my", + "thing", + }; + for (array_of_strings) |s, i| { + if (i == 0) assertOrPanic(mem.eql(u8, s, "hello")); + if (i == 1) assertOrPanic(mem.eql(u8, s, "this")); + if (i == 2) assertOrPanic(mem.eql(u8, s, "is")); + if (i == 3) assertOrPanic(mem.eql(u8, s, "my")); + if (i == 4) assertOrPanic(mem.eql(u8, s, "thing")); + } +} + +var s_array: [8]Sub = undefined; +const Sub = struct { + b: u8, +}; +const Str = struct { + a: []Sub, +}; +test "set global var array via slice embedded in struct" { + var s = Str{ .a = s_array[0..] }; + + s.a[0].b = 1; + s.a[1].b = 2; + s.a[2].b = 3; + + assertOrPanic(s_array[0].b == 1); + assertOrPanic(s_array[1].b == 2); + assertOrPanic(s_array[2].b == 3); +} + +test "array literal with specified size" { + var array = [2]u8{ + 1, + 2, + }; + assertOrPanic(array[0] == 1); + assertOrPanic(array[1] == 2); +} + +test "array child property" { + var x: [5]i32 = undefined; + assertOrPanic(@typeOf(x).Child == i32); +} + +test "array len property" { + var x: [5]i32 = undefined; + assertOrPanic(@typeOf(x).len == 5); +} + +test "array len field" { + var arr = [4]u8{ 0, 0, 0, 0 }; + var ptr = &arr; + assertOrPanic(arr.len == 4); + comptime assertOrPanic(arr.len == 4); + assertOrPanic(ptr.len == 4); + comptime assertOrPanic(ptr.len == 4); +} + +test "single-item pointer to array indexing and slicing" { + testSingleItemPtrArrayIndexSlice(); + comptime testSingleItemPtrArrayIndexSlice(); +} + +fn testSingleItemPtrArrayIndexSlice() void { + var array = "aaaa"; + doSomeMangling(&array); + assertOrPanic(mem.eql(u8, "azya", array)); +} + +fn doSomeMangling(array: *[4]u8) void { + array[1] = 'z'; + array[2..3][0] = 'y'; +} + +test "implicit cast single-item pointer" { + testImplicitCastSingleItemPtr(); + comptime testImplicitCastSingleItemPtr(); +} + +fn testImplicitCastSingleItemPtr() void { + var byte: u8 = 100; + const slice = (*[1]u8)(&byte)[0..]; + slice[0] += 1; + assertOrPanic(byte == 101); +} + +fn testArrayByValAtComptime(b: [2]u8) u8 { + return b[0]; +} + +test "comptime evalutating function that takes array by value" { + const arr = []u8{ 0, 1 }; + _ = comptime testArrayByValAtComptime(arr); + _ = comptime testArrayByValAtComptime(arr); +} + +test "implicit comptime in array type size" { + var arr: [plusOne(10)]bool = undefined; + assertOrPanic(arr.len == 11); +} + +fn plusOne(x: u32) u32 { + return x + 1; +} + +test "array literal as argument to function" { + const S = struct { + fn entry(two: i32) void { + foo([]i32{ + 1, + 2, + 3, + }); + foo([]i32{ + 1, + two, + 3, + }); + foo2(true, []i32{ + 1, + 2, + 3, + }); + foo2(true, []i32{ + 1, + two, + 3, + }); + } + fn foo(x: []const i32) void { + assertOrPanic(x[0] == 1); + assertOrPanic(x[1] == 2); + assertOrPanic(x[2] == 3); + } + fn foo2(trash: bool, x: []const i32) void { + assertOrPanic(trash); + assertOrPanic(x[0] == 1); + assertOrPanic(x[1] == 2); + assertOrPanic(x[2] == 3); + } + }; + S.entry(2); + comptime S.entry(2); +} + +test "double nested array to const slice cast in array literal" { + const S = struct { + fn entry(two: i32) void { + const cases = [][]const []const i32{ + [][]const i32{[]i32{1}}, + [][]const i32{[]i32{ 2, 3 }}, + [][]const i32{ + []i32{4}, + []i32{ 5, 6, 7 }, + }, + }; + check(cases); + + const cases2 = [][]const i32{ + []i32{1}, + []i32{ two, 3 }, + }; + assertOrPanic(cases2.len == 2); + assertOrPanic(cases2[0].len == 1); + assertOrPanic(cases2[0][0] == 1); + assertOrPanic(cases2[1].len == 2); + assertOrPanic(cases2[1][0] == 2); + assertOrPanic(cases2[1][1] == 3); + + const cases3 = [][]const []const i32{ + [][]const i32{[]i32{1}}, + [][]const i32{[]i32{ two, 3 }}, + [][]const i32{ + []i32{4}, + []i32{ 5, 6, 7 }, + }, + }; + check(cases3); + } + + fn check(cases: []const []const []const i32) void { + assertOrPanic(cases.len == 3); + assertOrPanic(cases[0].len == 1); + assertOrPanic(cases[0][0].len == 1); + assertOrPanic(cases[0][0][0] == 1); + assertOrPanic(cases[1].len == 1); + assertOrPanic(cases[1][0].len == 2); + assertOrPanic(cases[1][0][0] == 2); + assertOrPanic(cases[1][0][1] == 3); + assertOrPanic(cases[2].len == 2); + assertOrPanic(cases[2][0].len == 1); + assertOrPanic(cases[2][0][0] == 4); + assertOrPanic(cases[2][1].len == 3); + assertOrPanic(cases[2][1][0] == 5); + assertOrPanic(cases[2][1][1] == 6); + assertOrPanic(cases[2][1][2] == 7); + } + }; + S.entry(2); + comptime S.entry(2); +} diff --git a/test/stage1/behavior/asm.zig b/test/stage1/behavior/asm.zig new file mode 100644 index 0000000000000000000000000000000000000000..48701c58362181a187436e997128208878f42b99 --- /dev/null +++ b/test/stage1/behavior/asm.zig @@ -0,0 +1,92 @@ +const config = @import("builtin"); +const assertOrPanic = @import("std").debug.assertOrPanic; + +comptime { + if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) { + asm volatile ( + \\.globl aoeu; + \\.type aoeu, @function; + \\.set aoeu, derp; + ); + } +} + +test "module level assembly" { + if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) { + assertOrPanic(aoeu() == 1234); + } +} + +test "output constraint modifiers" { + // This is only testing compilation. + var a: u32 = 3; + asm volatile ("" + : [_] "=m,r" (a) + : + : "" + ); + asm volatile ("" + : [_] "=r,m" (a) + : + : "" + ); +} + +test "alternative constraints" { + // Make sure we allow commas as a separator for alternative constraints. + var a: u32 = 3; + asm volatile ("" + : [_] "=r,m" (a) + : [_] "r,m" (a) + : "" + ); +} + +test "sized integer/float in asm input" { + asm volatile ("" + : + : [_] "m" (usize(3)) + : "" + ); + asm volatile ("" + : + : [_] "m" (i15(-3)) + : "" + ); + asm volatile ("" + : + : [_] "m" (u3(3)) + : "" + ); + asm volatile ("" + : + : [_] "m" (i3(3)) + : "" + ); + asm volatile ("" + : + : [_] "m" (u121(3)) + : "" + ); + asm volatile ("" + : + : [_] "m" (i121(3)) + : "" + ); + asm volatile ("" + : + : [_] "m" (f32(3.17)) + : "" + ); + asm volatile ("" + : + : [_] "m" (f64(3.17)) + : "" + ); +} + +extern fn aoeu() i32; + +export fn derp() i32 { + return 1234; +} diff --git a/test/cases/atomics.zig b/test/stage1/behavior/atomics.zig similarity index 65% rename from test/cases/atomics.zig rename to test/stage1/behavior/atomics.zig index 67c9ab3dd117be1faf552e66fc1670a69b717fe2..fa3c5f29a603a337e94eb4cfc0d74070e48f6726 100644 --- a/test/cases/atomics.zig +++ b/test/stage1/behavior/atomics.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const builtin = @import("builtin"); const AtomicRmwOp = builtin.AtomicRmwOp; const AtomicOrder = builtin.AtomicOrder; @@ -7,18 +7,18 @@ const AtomicOrder = builtin.AtomicOrder; test "cmpxchg" { var x: i32 = 1234; if (@cmpxchgWeak(i32, &x, 99, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { - assert(x1 == 1234); + assertOrPanic(x1 == 1234); } else { @panic("cmpxchg should have failed"); } while (@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { - assert(x1 == 1234); + assertOrPanic(x1 == 1234); } - assert(x == 5678); + assertOrPanic(x == 5678); - assert(@cmpxchgStrong(i32, &x, 5678, 42, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); - assert(x == 42); + assertOrPanic(@cmpxchgStrong(i32, &x, 5678, 42, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); + assertOrPanic(x == 42); } test "fence" { @@ -30,24 +30,24 @@ test "fence" { test "atomicrmw and atomicload" { var data: u8 = 200; testAtomicRmw(&data); - assert(data == 42); + assertOrPanic(data == 42); testAtomicLoad(&data); } fn testAtomicRmw(ptr: *u8) void { const prev_value = @atomicRmw(u8, ptr, AtomicRmwOp.Xchg, 42, AtomicOrder.SeqCst); - assert(prev_value == 200); + assertOrPanic(prev_value == 200); comptime { var x: i32 = 1234; const y: i32 = 12345; - assert(@atomicLoad(i32, &x, AtomicOrder.SeqCst) == 1234); - assert(@atomicLoad(i32, &y, AtomicOrder.SeqCst) == 12345); + assertOrPanic(@atomicLoad(i32, &x, AtomicOrder.SeqCst) == 1234); + assertOrPanic(@atomicLoad(i32, &y, AtomicOrder.SeqCst) == 12345); } } fn testAtomicLoad(ptr: *u8) void { const x = @atomicLoad(u8, ptr, AtomicOrder.SeqCst); - assert(x == 42); + assertOrPanic(x == 42); } test "cmpxchg with ptr" { @@ -56,16 +56,16 @@ test "cmpxchg with ptr" { var data3: i32 = 9101; var x: *i32 = &data1; if (@cmpxchgWeak(*i32, &x, &data2, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { - assert(x1 == &data1); + assertOrPanic(x1 == &data1); } else { @panic("cmpxchg should have failed"); } while (@cmpxchgWeak(*i32, &x, &data1, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { - assert(x1 == &data1); + assertOrPanic(x1 == &data1); } - assert(x == &data3); + assertOrPanic(x == &data3); - assert(@cmpxchgStrong(*i32, &x, &data3, &data2, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); - assert(x == &data2); + assertOrPanic(@cmpxchgStrong(*i32, &x, &data3, &data2, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); + assertOrPanic(x == &data2); } diff --git a/test/cases/bit_shifting.zig b/test/stage1/behavior/bit_shifting.zig similarity index 90% rename from test/cases/bit_shifting.zig rename to test/stage1/behavior/bit_shifting.zig index 325e765bb0a5e19400450982dfa0389462cdc2e3..3290688358feac2159cdc205227bdb9ed91c1f72 100644 --- a/test/cases/bit_shifting.zig +++ b/test/stage1/behavior/bit_shifting.zig @@ -1,9 +1,9 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; fn ShardedTable(comptime Key: type, comptime mask_bit_count: comptime_int, comptime V: type) type { - assert(Key == @IntType(false, Key.bit_count)); - assert(Key.bit_count >= mask_bit_count); + assertOrPanic(Key == @IntType(false, Key.bit_count)); + assertOrPanic(Key.bit_count >= mask_bit_count); const ShardKey = @IntType(false, mask_bit_count); const shift_amount = Key.bit_count - ShardKey.bit_count; return struct { @@ -77,12 +77,12 @@ fn testShardedTable(comptime Key: type, comptime mask_bit_count: comptime_int, c var node_buffer: [node_count]Table.Node = undefined; for (node_buffer) |*node, i| { const key = @intCast(Key, i); - assert(table.get(key) == null); + assertOrPanic(table.get(key) == null); node.init(key, {}); table.put(node); } for (node_buffer) |*node, i| { - assert(table.get(@intCast(Key, i)) == node); + assertOrPanic(table.get(@intCast(Key, i)) == node); } } diff --git a/test/cases/bitcast.zig b/test/stage1/behavior/bitcast.zig similarity index 78% rename from test/cases/bitcast.zig rename to test/stage1/behavior/bitcast.zig index d85a84ed225ea1f5b660952bbd7f80f69f0465c0..19030255e4f71fe2330e0027a194c8e815ebcf20 100644 --- a/test/cases/bitcast.zig +++ b/test/stage1/behavior/bitcast.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const maxInt = std.math.maxInt; test "@bitCast i32 -> u32" { @@ -8,8 +8,8 @@ test "@bitCast i32 -> u32" { } fn testBitCast_i32_u32() void { - assert(conv(-1) == maxInt(u32)); - assert(conv2(maxInt(u32)) == -1); + assertOrPanic(conv(-1) == maxInt(u32)); + assertOrPanic(conv2(maxInt(u32)) == -1); } fn conv(x: i32) u32 { @@ -27,11 +27,10 @@ test "@bitCast extern enum to its integer type" { fn testBitCastExternEnum() void { var SOCK_DGRAM = @This().B; var sock_dgram = @bitCast(c_int, SOCK_DGRAM); - assert(sock_dgram == 1); + assertOrPanic(sock_dgram == 1); } }; SOCK.testBitCastExternEnum(); comptime SOCK.testBitCastExternEnum(); } - diff --git a/test/stage1/behavior/bitreverse.zig b/test/stage1/behavior/bitreverse.zig new file mode 100644 index 0000000000000000000000000000000000000000..97787ace84f20f45d2b01d1f5239ae773d2970a2 --- /dev/null +++ b/test/stage1/behavior/bitreverse.zig @@ -0,0 +1,81 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; +const minInt = std.math.minInt; + +test "@bitreverse" { + comptime testBitReverse(); + testBitReverse(); +} + +fn testBitReverse() void { + // using comptime_ints, unsigned + assertOrPanic(@bitreverse(u0, 0) == 0); + assertOrPanic(@bitreverse(u5, 0x12) == 0x9); + assertOrPanic(@bitreverse(u8, 0x12) == 0x48); + assertOrPanic(@bitreverse(u16, 0x1234) == 0x2c48); + assertOrPanic(@bitreverse(u24, 0x123456) == 0x6a2c48); + assertOrPanic(@bitreverse(u32, 0x12345678) == 0x1e6a2c48); + assertOrPanic(@bitreverse(u40, 0x123456789a) == 0x591e6a2c48); + assertOrPanic(@bitreverse(u48, 0x123456789abc) == 0x3d591e6a2c48); + assertOrPanic(@bitreverse(u56, 0x123456789abcde) == 0x7b3d591e6a2c48); + assertOrPanic(@bitreverse(u64, 0x123456789abcdef1) == 0x8f7b3d591e6a2c48); + assertOrPanic(@bitreverse(u128, 0x123456789abcdef11121314151617181) == 0x818e868a828c84888f7b3d591e6a2c48); + + // using runtime uints, unsigned + var num0: u0 = 0; + assertOrPanic(@bitreverse(u0, num0) == 0); + var num5: u5 = 0x12; + assertOrPanic(@bitreverse(u5, num5) == 0x9); + var num8: u8 = 0x12; + assertOrPanic(@bitreverse(u8, num8) == 0x48); + var num16: u16 = 0x1234; + assertOrPanic(@bitreverse(u16, num16) == 0x2c48); + var num24: u24 = 0x123456; + assertOrPanic(@bitreverse(u24, num24) == 0x6a2c48); + var num32: u32 = 0x12345678; + assertOrPanic(@bitreverse(u32, num32) == 0x1e6a2c48); + var num40: u40 = 0x123456789a; + assertOrPanic(@bitreverse(u40, num40) == 0x591e6a2c48); + var num48: u48 = 0x123456789abc; + assertOrPanic(@bitreverse(u48, num48) == 0x3d591e6a2c48); + var num56: u56 = 0x123456789abcde; + assertOrPanic(@bitreverse(u56, num56) == 0x7b3d591e6a2c48); + var num64: u64 = 0x123456789abcdef1; + assertOrPanic(@bitreverse(u64, num64) == 0x8f7b3d591e6a2c48); + var num128: u128 = 0x123456789abcdef11121314151617181; + assertOrPanic(@bitreverse(u128, num128) == 0x818e868a828c84888f7b3d591e6a2c48); + + // using comptime_ints, signed, positive + assertOrPanic(@bitreverse(i0, 0) == 0); + assertOrPanic(@bitreverse(i8, @bitCast(i8, u8(0x92))) == @bitCast(i8, u8(0x49))); + assertOrPanic(@bitreverse(i16, @bitCast(i16, u16(0x1234))) == @bitCast(i16, u16(0x2c48))); + assertOrPanic(@bitreverse(i24, @bitCast(i24, u24(0x123456))) == @bitCast(i24, u24(0x6a2c48))); + assertOrPanic(@bitreverse(i32, @bitCast(i32, u32(0x12345678))) == @bitCast(i32, u32(0x1e6a2c48))); + assertOrPanic(@bitreverse(i40, @bitCast(i40, u40(0x123456789a))) == @bitCast(i40, u40(0x591e6a2c48))); + assertOrPanic(@bitreverse(i48, @bitCast(i48, u48(0x123456789abc))) == @bitCast(i48, u48(0x3d591e6a2c48))); + assertOrPanic(@bitreverse(i56, @bitCast(i56, u56(0x123456789abcde))) == @bitCast(i56, u56(0x7b3d591e6a2c48))); + assertOrPanic(@bitreverse(i64, @bitCast(i64, u64(0x123456789abcdef1))) == @bitCast(i64, u64(0x8f7b3d591e6a2c48))); + assertOrPanic(@bitreverse(i128, @bitCast(i128, u128(0x123456789abcdef11121314151617181))) == @bitCast(i128, u128(0x818e868a828c84888f7b3d591e6a2c48))); + + // using comptime_ints, signed, negative. Compare to runtime ints returned from llvm. + var neg5: i5 = minInt(i5) + 1; + assertOrPanic(@bitreverse(i5, minInt(i5) + 1) == @bitreverse(i5, neg5)); + var neg8: i8 = -18; + assertOrPanic(@bitreverse(i8, -18) == @bitreverse(i8, neg8)); + var neg16: i16 = -32694; + assertOrPanic(@bitreverse(i16, -32694) == @bitreverse(i16, neg16)); + var neg24: i24 = -6773785; + assertOrPanic(@bitreverse(i24, -6773785) == @bitreverse(i24, neg24)); + var neg32: i32 = -16773785; + assertOrPanic(@bitreverse(i32, -16773785) == @bitreverse(i32, neg32)); + var neg40: i40 = minInt(i40) + 12345; + assertOrPanic(@bitreverse(i40, minInt(i40) + 12345) == @bitreverse(i40, neg40)); + var neg48: i48 = minInt(i48) + 12345; + assertOrPanic(@bitreverse(i48, minInt(i48) + 12345) == @bitreverse(i48, neg48)); + var neg56: i56 = minInt(i56) + 12345; + assertOrPanic(@bitreverse(i56, minInt(i56) + 12345) == @bitreverse(i56, neg56)); + var neg64: i64 = minInt(i64) + 12345; + assertOrPanic(@bitreverse(i64, minInt(i64) + 12345) == @bitreverse(i64, neg64)); + var neg128: i128 = minInt(i128) + 12345; + assertOrPanic(@bitreverse(i128, minInt(i128) + 12345) == @bitreverse(i128, neg128)); +} diff --git a/test/cases/bool.zig b/test/stage1/behavior/bool.zig similarity index 50% rename from test/cases/bool.zig rename to test/stage1/behavior/bool.zig index 3e4ac9c1cfe4d30f72189c27d47e6b4949ead971..2d7241526f429e138e577a22c589a849eb1926d4 100644 --- a/test/cases/bool.zig +++ b/test/stage1/behavior/bool.zig @@ -1,25 +1,25 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "bool literals" { - assert(true); - assert(!false); + assertOrPanic(true); + assertOrPanic(!false); } test "cast bool to int" { const t = true; const f = false; - assert(@boolToInt(t) == u32(1)); - assert(@boolToInt(f) == u32(0)); + assertOrPanic(@boolToInt(t) == u32(1)); + assertOrPanic(@boolToInt(f) == u32(0)); nonConstCastBoolToInt(t, f); } fn nonConstCastBoolToInt(t: bool, f: bool) void { - assert(@boolToInt(t) == u32(1)); - assert(@boolToInt(f) == u32(0)); + assertOrPanic(@boolToInt(t) == u32(1)); + assertOrPanic(@boolToInt(f) == u32(0)); } test "bool cmp" { - assert(testBoolCmp(true, false) == false); + assertOrPanic(testBoolCmp(true, false) == false); } fn testBoolCmp(a: bool, b: bool) bool { return a == b; @@ -30,6 +30,6 @@ const global_t = true; const not_global_f = !global_f; const not_global_t = !global_t; test "compile time bool not" { - assert(not_global_f); - assert(!not_global_t); + assertOrPanic(not_global_f); + assertOrPanic(!not_global_t); } diff --git a/test/stage1/behavior/bswap.zig b/test/stage1/behavior/bswap.zig new file mode 100644 index 0000000000000000000000000000000000000000..8084538e03c02f5a6621b51ed8b0c52666cefaac --- /dev/null +++ b/test/stage1/behavior/bswap.zig @@ -0,0 +1,32 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; + +test "@bswap" { + comptime testByteSwap(); + testByteSwap(); +} + +fn testByteSwap() void { + assertOrPanic(@bswap(u0, 0) == 0); + assertOrPanic(@bswap(u8, 0x12) == 0x12); + assertOrPanic(@bswap(u16, 0x1234) == 0x3412); + assertOrPanic(@bswap(u24, 0x123456) == 0x563412); + assertOrPanic(@bswap(u32, 0x12345678) == 0x78563412); + assertOrPanic(@bswap(u40, 0x123456789a) == 0x9a78563412); + assertOrPanic(@bswap(u48, 0x123456789abc) == 0xbc9a78563412); + assertOrPanic(@bswap(u56, 0x123456789abcde) == 0xdebc9a78563412); + assertOrPanic(@bswap(u64, 0x123456789abcdef1) == 0xf1debc9a78563412); + assertOrPanic(@bswap(u128, 0x123456789abcdef11121314151617181) == 0x8171615141312111f1debc9a78563412); + + assertOrPanic(@bswap(i0, 0) == 0); + assertOrPanic(@bswap(i8, -50) == -50); + assertOrPanic(@bswap(i16, @bitCast(i16, u16(0x1234))) == @bitCast(i16, u16(0x3412))); + assertOrPanic(@bswap(i24, @bitCast(i24, u24(0x123456))) == @bitCast(i24, u24(0x563412))); + assertOrPanic(@bswap(i32, @bitCast(i32, u32(0x12345678))) == @bitCast(i32, u32(0x78563412))); + assertOrPanic(@bswap(i40, @bitCast(i40, u40(0x123456789a))) == @bitCast(i40, u40(0x9a78563412))); + assertOrPanic(@bswap(i48, @bitCast(i48, u48(0x123456789abc))) == @bitCast(i48, u48(0xbc9a78563412))); + assertOrPanic(@bswap(i56, @bitCast(i56, u56(0x123456789abcde))) == @bitCast(i56, u56(0xdebc9a78563412))); + assertOrPanic(@bswap(i64, @bitCast(i64, u64(0x123456789abcdef1))) == @bitCast(i64, u64(0xf1debc9a78563412))); + assertOrPanic(@bswap(i128, @bitCast(i128, u128(0x123456789abcdef11121314151617181))) == + @bitCast(i128, u128(0x8171615141312111f1debc9a78563412))); +} diff --git a/test/cases/bugs/1076.zig b/test/stage1/behavior/bugs/1076.zig similarity index 75% rename from test/cases/bugs/1076.zig rename to test/stage1/behavior/bugs/1076.zig index 7b843123104e5a2268e5a47081b44473e7ec4391..69a7e70f7dbb3b424c0740300703b12654acc807 100644 --- a/test/cases/bugs/1076.zig +++ b/test/stage1/behavior/bugs/1076.zig @@ -1,6 +1,6 @@ const std = @import("std"); const mem = std.mem; -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; test "comptime code should not modify constant data" { testCastPtrOfArrayToSliceAndPtr(); @@ -11,6 +11,6 @@ fn testCastPtrOfArrayToSliceAndPtr() void { var array = "aoeu"; const x: [*]u8 = &array; x[0] += 1; - assert(mem.eql(u8, array[0..], "boeu")); + assertOrPanic(mem.eql(u8, array[0..], "boeu")); } diff --git a/test/cases/bugs/1111.zig b/test/stage1/behavior/bugs/1111.zig similarity index 100% rename from test/cases/bugs/1111.zig rename to test/stage1/behavior/bugs/1111.zig diff --git a/test/cases/bugs/1277.zig b/test/stage1/behavior/bugs/1277.zig similarity index 100% rename from test/cases/bugs/1277.zig rename to test/stage1/behavior/bugs/1277.zig diff --git a/test/cases/bugs/1322.zig b/test/stage1/behavior/bugs/1322.zig similarity index 66% rename from test/cases/bugs/1322.zig rename to test/stage1/behavior/bugs/1322.zig index 2de92191ec17f36c0857ae320184412a9b7faee9..2e67f4473fecd47e54015be00e94350dfce83101 100644 --- a/test/cases/bugs/1322.zig +++ b/test/stage1/behavior/bugs/1322.zig @@ -13,7 +13,7 @@ const C = struct {}; test "tagged union with all void fields but a meaningful tag" { var a: A = A{ .b = B{ .c = C{} } }; - std.debug.assert(@TagType(B)(a.b) == @TagType(B).c); + std.debug.assertOrPanic(@TagType(B)(a.b) == @TagType(B).c); a = A{ .b = B.None }; - std.debug.assert(@TagType(B)(a.b) == @TagType(B).None); + std.debug.assertOrPanic(@TagType(B)(a.b) == @TagType(B).None); } diff --git a/test/cases/bugs/1381.zig b/test/stage1/behavior/bugs/1381.zig similarity index 100% rename from test/cases/bugs/1381.zig rename to test/stage1/behavior/bugs/1381.zig diff --git a/test/cases/bugs/1421.zig b/test/stage1/behavior/bugs/1421.zig similarity index 70% rename from test/cases/bugs/1421.zig rename to test/stage1/behavior/bugs/1421.zig index fcbb8b70e4f8483fd8037025e6f6f155d75d7d42..fbc932781ab9662e8820a0621940306cf0dfbe9e 100644 --- a/test/cases/bugs/1421.zig +++ b/test/stage1/behavior/bugs/1421.zig @@ -1,6 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const S = struct { fn method() builtin.TypeInfo { @@ -10,5 +10,5 @@ const S = struct { test "functions with return type required to be comptime are generic" { const ti = S.method(); - assert(builtin.TypeId(ti) == builtin.TypeId.Struct); + assertOrPanic(builtin.TypeId(ti) == builtin.TypeId.Struct); } diff --git a/test/cases/bugs/1442.zig b/test/stage1/behavior/bugs/1442.zig similarity index 100% rename from test/cases/bugs/1442.zig rename to test/stage1/behavior/bugs/1442.zig diff --git a/test/cases/bugs/1486.zig b/test/stage1/behavior/bugs/1486.zig similarity index 51% rename from test/cases/bugs/1486.zig rename to test/stage1/behavior/bugs/1486.zig index 98fae36d3a0f58223f4c4a62d310fab044a05f2e..0483e3828cde39dc0d245c4aa7690ee3ceb2c5ac 100644 --- a/test/cases/bugs/1486.zig +++ b/test/stage1/behavior/bugs/1486.zig @@ -1,11 +1,11 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const ptr = &global; var global: u64 = 123; test "constant pointer to global variable causes runtime load" { global = 1234; - assert(&global == ptr); - assert(ptr.* == 1234); + assertOrPanic(&global == ptr); + assertOrPanic(ptr.* == 1234); } diff --git a/test/cases/bugs/394.zig b/test/stage1/behavior/bugs/394.zig similarity index 68% rename from test/cases/bugs/394.zig rename to test/stage1/behavior/bugs/394.zig index b0afec23572a912a8185d871b733376ea448f25c..766ad9e15761335b3ce42fed34ba788349deddba 100644 --- a/test/cases/bugs/394.zig +++ b/test/stage1/behavior/bugs/394.zig @@ -7,12 +7,12 @@ const S = struct { y: E, }; -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "bug 394 fixed" { const x = S{ .x = 3, .y = E{ .B = 1 }, }; - assert(x.x == 3); + assertOrPanic(x.x == 3); } diff --git a/test/cases/bugs/655.zig b/test/stage1/behavior/bugs/655.zig similarity index 67% rename from test/cases/bugs/655.zig rename to test/stage1/behavior/bugs/655.zig index ebb8da0658d4c8d6bed4b281b386a2f7e122c828..67ba6a231f33d4ffa50f63e77b86c5bee7cd947f 100644 --- a/test/cases/bugs/655.zig +++ b/test/stage1/behavior/bugs/655.zig @@ -3,10 +3,10 @@ const other_file = @import("655_other_file.zig"); test "function with *const parameter with type dereferenced by namespace" { const x: other_file.Integer = 1234; - comptime std.debug.assert(@typeOf(&x) == *const other_file.Integer); + comptime std.debug.assertOrPanic(@typeOf(&x) == *const other_file.Integer); foo(&x); } fn foo(x: *const other_file.Integer) void { - std.debug.assert(x.* == 1234); + std.debug.assertOrPanic(x.* == 1234); } diff --git a/test/cases/bugs/655_other_file.zig b/test/stage1/behavior/bugs/655_other_file.zig similarity index 100% rename from test/cases/bugs/655_other_file.zig rename to test/stage1/behavior/bugs/655_other_file.zig diff --git a/test/cases/bugs/656.zig b/test/stage1/behavior/bugs/656.zig similarity index 84% rename from test/cases/bugs/656.zig rename to test/stage1/behavior/bugs/656.zig index f93f0ac4d51f4cc9acb1c304183dbe3519d1b935..cb37fe67fe84a4dda7e46952ac4200d989fa3791 100644 --- a/test/cases/bugs/656.zig +++ b/test/stage1/behavior/bugs/656.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const PrefixOp = union(enum) { Return, @@ -22,7 +22,7 @@ fn foo(a: bool, b: bool) void { PrefixOp.AddrOf => |addr_of_info| { if (b) {} if (addr_of_info.align_expr) |align_expr| { - assert(align_expr == 1234); + assertOrPanic(align_expr == 1234); } }, PrefixOp.Return => {}, diff --git a/test/cases/bugs/726.zig b/test/stage1/behavior/bugs/726.zig similarity index 71% rename from test/cases/bugs/726.zig rename to test/stage1/behavior/bugs/726.zig index 2acc91eb26c17d98239b1650cdae614296012526..ce20480c63325f23c4ae2bdb5218807887d00f58 100644 --- a/test/cases/bugs/726.zig +++ b/test/stage1/behavior/bugs/726.zig @@ -1,9 +1,9 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "@ptrCast from const to nullable" { const c: u8 = 4; var x: ?*const u8 = @ptrCast(?*const u8, &c); - assert(x.?.* == 4); + assertOrPanic(x.?.* == 4); } test "@ptrCast from var in empty struct to nullable" { @@ -11,6 +11,6 @@ test "@ptrCast from var in empty struct to nullable" { var c: u8 = 4; }; var x: ?*const u8 = @ptrCast(?*const u8, &container.c); - assert(x.?.* == 4); + assertOrPanic(x.?.* == 4); } diff --git a/test/cases/bugs/828.zig b/test/stage1/behavior/bugs/828.zig similarity index 100% rename from test/cases/bugs/828.zig rename to test/stage1/behavior/bugs/828.zig diff --git a/test/cases/bugs/920.zig b/test/stage1/behavior/bugs/920.zig similarity index 95% rename from test/cases/bugs/920.zig rename to test/stage1/behavior/bugs/920.zig index 2903f05a29abcb0a0d85ca4bb262e8a6455e83cf..e29c5c4acf5a31a97ef06afdf1c5c4c6ececc83b 100644 --- a/test/cases/bugs/920.zig +++ b/test/stage1/behavior/bugs/920.zig @@ -60,6 +60,6 @@ test "bug 920 fixed" { }; for (NormalDist1.f) |_, i| { - std.debug.assert(NormalDist1.f[i] == NormalDist.f[i]); + std.debug.assertOrPanic(NormalDist1.f[i] == NormalDist.f[i]); } } diff --git a/test/cases/byval_arg_var.zig b/test/stage1/behavior/byval_arg_var.zig similarity index 70% rename from test/cases/byval_arg_var.zig rename to test/stage1/behavior/byval_arg_var.zig index 826b9cc9e59334d4c67300ac5740929539c6217d..14ee212ce07cb45cfcfcd27b0202e67d4ca808df 100644 --- a/test/cases/byval_arg_var.zig +++ b/test/stage1/behavior/byval_arg_var.zig @@ -2,11 +2,11 @@ const std = @import("std"); var result: []const u8 = "wrong"; -test "aoeu" { +test "pass string literal byvalue to a generic var param" { start(); blowUpStack(10); - std.debug.assert(std.mem.eql(u8, result, "string literal")); + std.debug.assertOrPanic(std.mem.eql(u8, result, "string literal")); } fn start() void { diff --git a/test/cases/cancel.zig b/test/stage1/behavior/cancel.zig similarity index 84% rename from test/cases/cancel.zig rename to test/stage1/behavior/cancel.zig index c0f74fd34f4430b1a145aa51230cdb26c4a7a45f..863da4bdb831b9015d67a9fcf5284733b9c33e91 100644 --- a/test/cases/cancel.zig +++ b/test/stage1/behavior/cancel.zig @@ -10,9 +10,9 @@ test "cancel forwards" { const p = async<&da.allocator> f1() catch unreachable; cancel p; - std.debug.assert(defer_f1); - std.debug.assert(defer_f2); - std.debug.assert(defer_f3); + std.debug.assertOrPanic(defer_f1); + std.debug.assertOrPanic(defer_f2); + std.debug.assertOrPanic(defer_f3); } async fn f1() void { @@ -47,10 +47,10 @@ test "cancel backwards" { const p = async<&da.allocator> b1() catch unreachable; cancel p; - std.debug.assert(defer_b1); - std.debug.assert(defer_b2); - std.debug.assert(defer_b3); - std.debug.assert(defer_b4); + std.debug.assertOrPanic(defer_b1); + std.debug.assertOrPanic(defer_b2); + std.debug.assertOrPanic(defer_b3); + std.debug.assertOrPanic(defer_b4); } async fn b1() void { diff --git a/test/cases/cast.zig b/test/stage1/behavior/cast.zig similarity index 67% rename from test/cases/cast.zig rename to test/stage1/behavior/cast.zig index bd45bbc00f089ee1998b4327fe1ced55efc49e60..61ddcd81355653c60e45c6ffdd401051a8b7c033 100644 --- a/test/cases/cast.zig +++ b/test/stage1/behavior/cast.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; const maxInt = std.math.maxInt; @@ -7,12 +7,12 @@ test "int to ptr cast" { const x = usize(13); const y = @intToPtr(*u8, x); const z = @ptrToInt(y); - assert(z == 13); + assertOrPanic(z == 13); } test "integer literal to pointer cast" { const vga_mem = @intToPtr(*u16, 0xB8000); - assert(@ptrToInt(vga_mem) == 0xB8000); + assertOrPanic(@ptrToInt(vga_mem) == 0xB8000); } test "pointer reinterpret const float to int" { @@ -20,7 +20,7 @@ test "pointer reinterpret const float to int" { const float_ptr = &float; const int_ptr = @ptrCast(*const i32, float_ptr); const int_val = int_ptr.*; - assert(int_val == 858993411); + assertOrPanic(int_val == 858993411); } test "implicitly cast indirect pointer to maybe-indirect pointer" { @@ -44,10 +44,10 @@ test "implicitly cast indirect pointer to maybe-indirect pointer" { const p = &s; const q = &p; const r = &q; - assert(42 == S.constConst(q)); - assert(42 == S.maybeConstConst(q)); - assert(42 == S.constConstConst(r)); - assert(42 == S.maybeConstConstConst(r)); + assertOrPanic(42 == S.constConst(q)); + assertOrPanic(42 == S.maybeConstConst(q)); + assertOrPanic(42 == S.constConstConst(r)); + assertOrPanic(42 == S.maybeConstConstConst(r)); } test "explicit cast from integer to error type" { @@ -57,14 +57,14 @@ test "explicit cast from integer to error type" { fn testCastIntToErr(err: anyerror) void { const x = @errorToInt(err); const y = @intToError(x); - assert(error.ItBroke == y); + assertOrPanic(error.ItBroke == y); } test "peer resolve arrays of different size to const slice" { - assert(mem.eql(u8, boolToStr(true), "true")); - assert(mem.eql(u8, boolToStr(false), "false")); - comptime assert(mem.eql(u8, boolToStr(true), "true")); - comptime assert(mem.eql(u8, boolToStr(false), "false")); + assertOrPanic(mem.eql(u8, boolToStr(true), "true")); + assertOrPanic(mem.eql(u8, boolToStr(false), "false")); + comptime assertOrPanic(mem.eql(u8, boolToStr(true), "true")); + comptime assertOrPanic(mem.eql(u8, boolToStr(false), "false")); } fn boolToStr(b: bool) []const u8 { return if (b) "true" else "false"; @@ -77,28 +77,29 @@ test "peer resolve array and const slice" { fn testPeerResolveArrayConstSlice(b: bool) void { const value1 = if (b) "aoeu" else ([]const u8)("zz"); const value2 = if (b) ([]const u8)("zz") else "aoeu"; - assert(mem.eql(u8, value1, "aoeu")); - assert(mem.eql(u8, value2, "zz")); + assertOrPanic(mem.eql(u8, value1, "aoeu")); + assertOrPanic(mem.eql(u8, value2, "zz")); } test "implicitly cast from T to anyerror!?T" { castToOptionalTypeError(1); comptime castToOptionalTypeError(1); } + const A = struct { a: i32, }; fn castToOptionalTypeError(z: i32) void { const x = i32(1); const y: anyerror!?i32 = x; - assert((try y).? == 1); + assertOrPanic((try y).? == 1); const f = z; const g: anyerror!?i32 = f; const a = A{ .a = z }; const b: anyerror!?A = a; - assert((b catch unreachable).?.a == 1); + assertOrPanic((b catch unreachable).?.a == 1); } test "implicitly cast from int to anyerror!?T" { @@ -113,7 +114,7 @@ fn implicitIntLitToOptional() void { test "return null from fn() anyerror!?&T" { const a = returnNullFromOptionalTypeErrorRef(); const b = returnNullLitFromOptionalTypeErrorRef(); - assert((try a) == null and (try b) == null); + assertOrPanic((try a) == null and (try b) == null); } fn returnNullFromOptionalTypeErrorRef() anyerror!?*A { const a: ?*A = null; @@ -124,11 +125,11 @@ fn returnNullLitFromOptionalTypeErrorRef() anyerror!?*A { } test "peer type resolution: ?T and T" { - assert(peerTypeTAndOptionalT(true, false).? == 0); - assert(peerTypeTAndOptionalT(false, false).? == 3); + assertOrPanic(peerTypeTAndOptionalT(true, false).? == 0); + assertOrPanic(peerTypeTAndOptionalT(false, false).? == 3); comptime { - assert(peerTypeTAndOptionalT(true, false).? == 0); - assert(peerTypeTAndOptionalT(false, false).? == 3); + assertOrPanic(peerTypeTAndOptionalT(true, false).? == 0); + assertOrPanic(peerTypeTAndOptionalT(false, false).? == 3); } } fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize { @@ -140,11 +141,11 @@ fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize { } test "peer type resolution: [0]u8 and []const u8" { - assert(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); - assert(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); + assertOrPanic(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); + assertOrPanic(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); comptime { - assert(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); - assert(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); + assertOrPanic(peerTypeEmptyArrayAndSlice(true, "hi").len == 0); + assertOrPanic(peerTypeEmptyArrayAndSlice(false, "hi").len == 1); } } fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { @@ -156,8 +157,8 @@ fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { } test "implicitly cast from [N]T to ?[]const T" { - assert(mem.eql(u8, castToOptionalSlice().?, "hi")); - comptime assert(mem.eql(u8, castToOptionalSlice().?, "hi")); + assertOrPanic(mem.eql(u8, castToOptionalSlice().?, "hi")); + comptime assertOrPanic(mem.eql(u8, castToOptionalSlice().?, "hi")); } fn castToOptionalSlice() ?[]const u8 { @@ -170,7 +171,7 @@ test "implicitly cast from [0]T to anyerror![]T" { } fn testCastZeroArrayToErrSliceMut() void { - assert((gimmeErrOrSlice() catch unreachable).len == 0); + assertOrPanic((gimmeErrOrSlice() catch unreachable).len == 0); } fn gimmeErrOrSlice() anyerror![]u8 { @@ -181,14 +182,14 @@ test "peer type resolution: [0]u8, []const u8, and anyerror![]u8" { { var data = "hi"; const slice = data[0..]; - assert((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); - assert((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); + assertOrPanic((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); + assertOrPanic((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); } comptime { var data = "hi"; const slice = data[0..]; - assert((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); - assert((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); + assertOrPanic((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); + assertOrPanic((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); } } fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 { @@ -206,7 +207,7 @@ test "resolve undefined with integer" { fn testResolveUndefWithInt(b: bool, x: i32) void { const value = if (b) x else undefined; if (b) { - assert(value == x); + assertOrPanic(value == x); } } @@ -218,17 +219,17 @@ test "implicit cast from &const [N]T to []const T" { fn testCastConstArrayRefToConstSlice() void { const blah = "aoeu"; const const_array_ref = &blah; - assert(@typeOf(const_array_ref) == *const [4]u8); + assertOrPanic(@typeOf(const_array_ref) == *const [4]u8); const slice: []const u8 = const_array_ref; - assert(mem.eql(u8, slice, "aoeu")); + assertOrPanic(mem.eql(u8, slice, "aoeu")); } test "peer type resolution: error and [N]T" { // TODO: implicit error!T to error!U where T can implicitly cast to U - //assert(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); - //comptime assert(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); - assert(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); - comptime assert(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); + //assertOrPanic(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); + //comptime assertOrPanic(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); + assertOrPanic(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); + comptime assertOrPanic(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); } //fn testPeerErrorAndArray(x: u8) error![]const u8 { @@ -252,9 +253,9 @@ test "@floatToInt" { fn testFloatToInts() void { const x = i32(1e4); - assert(x == 10000); + assertOrPanic(x == 10000); const y = @floatToInt(i32, f32(1e4)); - assert(y == 10000); + assertOrPanic(y == 10000); expectFloatToInt(f16, 255.1, u8, 255); expectFloatToInt(f16, 127.2, i8, 127); expectFloatToInt(f16, -128.2, i8, -128); @@ -265,7 +266,7 @@ fn testFloatToInts() void { } fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) void { - assert(@floatToInt(I, f) == i); + assertOrPanic(@floatToInt(I, f) == i); } test "cast u128 to f128 and back" { @@ -274,7 +275,7 @@ test "cast u128 to f128 and back" { } fn testCast128() void { - assert(cast128Int(cast128Float(0x7fff0000000000000000000000000000)) == 0x7fff0000000000000000000000000000); + assertOrPanic(cast128Int(cast128Float(0x7fff0000000000000000000000000000)) == 0x7fff0000000000000000000000000000); } fn cast128Int(x: f128) u128 { @@ -294,9 +295,9 @@ test "const slice widen cast" { }; const u32_value = @bytesToSlice(u32, bytes[0..])[0]; - assert(u32_value == 0x12121212); + assertOrPanic(u32_value == 0x12121212); - assert(@bitCast(u32, bytes) == 0x12121212); + assertOrPanic(@bitCast(u32, bytes) == 0x12121212); } test "single-item pointer of array to slice and to unknown length pointer" { @@ -308,76 +309,76 @@ fn testCastPtrOfArrayToSliceAndPtr() void { var array = "aoeu"; const x: [*]u8 = &array; x[0] += 1; - assert(mem.eql(u8, array[0..], "boeu")); + assertOrPanic(mem.eql(u8, array[0..], "boeu")); const y: []u8 = &array; y[0] += 1; - assert(mem.eql(u8, array[0..], "coeu")); + assertOrPanic(mem.eql(u8, array[0..], "coeu")); } test "cast *[1][*]const u8 to [*]const ?[*]const u8" { const window_name = [1][*]const u8{c"window name"}; const x: [*]const ?[*]const u8 = &window_name; - assert(mem.eql(u8, std.cstr.toSliceConst(x[0].?), "window name")); + assertOrPanic(mem.eql(u8, std.cstr.toSliceConst(x[0].?), "window name")); } test "@intCast comptime_int" { const result = @intCast(i32, 1234); - assert(@typeOf(result) == i32); - assert(result == 1234); + assertOrPanic(@typeOf(result) == i32); + assertOrPanic(result == 1234); } test "@floatCast comptime_int and comptime_float" { { const result = @floatCast(f16, 1234); - assert(@typeOf(result) == f16); - assert(result == 1234.0); + assertOrPanic(@typeOf(result) == f16); + assertOrPanic(result == 1234.0); } { const result = @floatCast(f16, 1234.0); - assert(@typeOf(result) == f16); - assert(result == 1234.0); + assertOrPanic(@typeOf(result) == f16); + assertOrPanic(result == 1234.0); } { const result = @floatCast(f32, 1234); - assert(@typeOf(result) == f32); - assert(result == 1234.0); + assertOrPanic(@typeOf(result) == f32); + assertOrPanic(result == 1234.0); } { const result = @floatCast(f32, 1234.0); - assert(@typeOf(result) == f32); - assert(result == 1234.0); + assertOrPanic(@typeOf(result) == f32); + assertOrPanic(result == 1234.0); } } test "comptime_int @intToFloat" { { const result = @intToFloat(f16, 1234); - assert(@typeOf(result) == f16); - assert(result == 1234.0); + assertOrPanic(@typeOf(result) == f16); + assertOrPanic(result == 1234.0); } { const result = @intToFloat(f32, 1234); - assert(@typeOf(result) == f32); - assert(result == 1234.0); + assertOrPanic(@typeOf(result) == f32); + assertOrPanic(result == 1234.0); } } test "@bytesToSlice keeps pointer alignment" { var bytes = []u8{ 0x01, 0x02, 0x03, 0x04 }; const numbers = @bytesToSlice(u32, bytes[0..]); - comptime assert(@typeOf(numbers) == []align(@alignOf(@typeOf(bytes))) u32); + comptime assertOrPanic(@typeOf(numbers) == []align(@alignOf(@typeOf(bytes))) u32); } test "@intCast i32 to u7" { var x: u128 = maxInt(u128); var y: i32 = 120; var z = x >> @intCast(u7, y); - assert(z == 0xff); + assertOrPanic(z == 0xff); } test "implicit cast undefined to optional" { - assert(MakeType(void).getNull() == null); - assert(MakeType(void).getNonNull() != null); + assertOrPanic(MakeType(void).getNull() == null); + assertOrPanic(MakeType(void).getNonNull() != null); } fn MakeType(comptime T: type) type { @@ -397,16 +398,16 @@ test "implicit cast from *[N]T to ?[*]T" { var y: [4]u16 = [4]u16{ 0, 1, 2, 3 }; x = &y; - assert(std.mem.eql(u16, x.?[0..4], y[0..4])); + assertOrPanic(std.mem.eql(u16, x.?[0..4], y[0..4])); x.?[0] = 8; y[3] = 6; - assert(std.mem.eql(u16, x.?[0..4], y[0..4])); + assertOrPanic(std.mem.eql(u16, x.?[0..4], y[0..4])); } test "implicit cast from *T to ?*c_void" { var a: u8 = 1; incrementVoidPtrValue(&a); - std.debug.assert(a == 2); + std.debug.assertOrPanic(a == 2); } fn incrementVoidPtrValue(value: ?*c_void) void { @@ -416,7 +417,7 @@ fn incrementVoidPtrValue(value: ?*c_void) void { test "implicit cast from [*]T to ?*c_void" { var a = []u8{ 3, 2, 1 }; incrementVoidPtrArray(a[0..].ptr, 3); - assert(std.mem.eql(u8, a, []u8{ 4, 3, 2 })); + assertOrPanic(std.mem.eql(u8, a, []u8{ 4, 3, 2 })); } fn incrementVoidPtrArray(array: ?*c_void, len: usize) void { @@ -440,27 +441,27 @@ pub const FUNCTION_CONSTANT = @intToPtr(PFN_void, maxInt(usize)); pub const PFN_void = extern fn (*c_void) void; fn foobar(func: PFN_void) void { - std.debug.assert(@ptrToInt(func) == maxInt(usize)); + std.debug.assertOrPanic(@ptrToInt(func) == maxInt(usize)); } test "implicit ptr to *c_void" { var a: u32 = 1; var ptr: *c_void = &a; var b: *u32 = @ptrCast(*u32, ptr); - assert(b.* == 1); + assertOrPanic(b.* == 1); var ptr2: ?*c_void = &a; var c: *u32 = @ptrCast(*u32, ptr2.?); - assert(c.* == 1); + assertOrPanic(c.* == 1); } test "@intCast to comptime_int" { - assert(@intCast(comptime_int, 0) == 0); + assertOrPanic(@intCast(comptime_int, 0) == 0); } test "implicit cast comptime numbers to any type when the value fits" { const a: u64 = 255; var b: u8 = a; - assert(b == 255); + assertOrPanic(b == 255); } test "@intToEnum passed a comptime_int to an enum with one item" { @@ -468,5 +469,5 @@ test "@intToEnum passed a comptime_int to an enum with one item" { A, }; const x = @intToEnum(E, 0); - assert(x == E.A); + assertOrPanic(x == E.A); } diff --git a/test/cases/const_slice_child.zig b/test/stage1/behavior/const_slice_child.zig similarity index 80% rename from test/cases/const_slice_child.zig rename to test/stage1/behavior/const_slice_child.zig index 07d02d5df06fd7e0f32d2f03acbf0a0a99c1990a..5b9b70a558017b04cc2a0e805963bb5c05ae445e 100644 --- a/test/cases/const_slice_child.zig +++ b/test/stage1/behavior/const_slice_child.zig @@ -1,5 +1,5 @@ const debug = @import("std").debug; -const assert = debug.assert; +const assertOrPanic = debug.assertOrPanic; var argv: [*]const [*]const u8 = undefined; @@ -15,10 +15,10 @@ test "const slice child" { } fn foo(args: [][]const u8) void { - assert(args.len == 3); - assert(streql(args[0], "one")); - assert(streql(args[1], "two")); - assert(streql(args[2], "three")); + assertOrPanic(args.len == 3); + assertOrPanic(streql(args[0], "one")); + assertOrPanic(streql(args[1], "two")); + assertOrPanic(streql(args[2], "three")); } fn bar(argc: usize) void { diff --git a/test/cases/coroutine_await_struct.zig b/test/stage1/behavior/coroutine_await_struct.zig similarity index 86% rename from test/cases/coroutine_await_struct.zig rename to test/stage1/behavior/coroutine_await_struct.zig index 79168715d8e25e43550e7ee770ebcba0be9be2f6..6ca2a301eca5218568e8f9f375f026351a9ac708 100644 --- a/test/cases/coroutine_await_struct.zig +++ b/test/stage1/behavior/coroutine_await_struct.zig @@ -1,6 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const Foo = struct { x: i32, @@ -18,8 +18,8 @@ test "coroutine await struct" { await_seq('f'); resume await_a_promise; await_seq('i'); - assert(await_final_result.x == 1234); - assert(std.mem.eql(u8, await_points, "abcdefghi")); + assertOrPanic(await_final_result.x == 1234); + assertOrPanic(std.mem.eql(u8, await_points, "abcdefghi")); } async fn await_amain() void { await_seq('b'); diff --git a/test/cases/coroutines.zig b/test/stage1/behavior/coroutines.zig similarity index 86% rename from test/cases/coroutines.zig rename to test/stage1/behavior/coroutines.zig index 89490ebc2c1c01e28fccadcab16ccf69c4c3bd3b..a2327c506011df0892e72279bb8b5f2fc2c86b19 100644 --- a/test/cases/coroutines.zig +++ b/test/stage1/behavior/coroutines.zig @@ -1,6 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; var x: i32 = 1; @@ -9,9 +9,9 @@ test "create a coroutine and cancel it" { defer da.deinit(); const p = try async<&da.allocator> simpleAsyncFn(); - comptime assert(@typeOf(p) == promise->void); + comptime assertOrPanic(@typeOf(p) == promise->void); cancel p; - assert(x == 2); + assertOrPanic(x == 2); } async fn simpleAsyncFn() void { x += 1; @@ -31,7 +31,7 @@ test "coroutine suspend, resume, cancel" { cancel p; seq('g'); - assert(std.mem.eql(u8, points, "abcdefg")); + assertOrPanic(std.mem.eql(u8, points, "abcdefg")); } async fn testAsyncSeq() void { defer seq('e'); @@ -53,9 +53,9 @@ test "coroutine suspend with block" { defer da.deinit(); const p = try async<&da.allocator> testSuspendBlock(); - std.debug.assert(!result); + std.debug.assertOrPanic(!result); resume a_promise; - std.debug.assert(result); + std.debug.assertOrPanic(result); cancel p; } @@ -63,13 +63,13 @@ var a_promise: promise = undefined; var result = false; async fn testSuspendBlock() void { suspend { - comptime assert(@typeOf(@handle()) == promise->void); + comptime assertOrPanic(@typeOf(@handle()) == promise->void); a_promise = @handle(); } //Test to make sure that @handle() works as advertised (issue #1296) //var our_handle: promise = @handle(); - assert( a_promise == @handle() ); + assertOrPanic(a_promise == @handle()); result = true; } @@ -86,8 +86,8 @@ test "coroutine await" { await_seq('f'); resume await_a_promise; await_seq('i'); - assert(await_final_result == 1234); - assert(std.mem.eql(u8, await_points, "abcdefghi")); + assertOrPanic(await_final_result == 1234); + assertOrPanic(std.mem.eql(u8, await_points, "abcdefghi")); } async fn await_amain() void { await_seq('b'); @@ -123,8 +123,8 @@ test "coroutine await early return" { early_seq('a'); const p = async<&da.allocator> early_amain() catch @panic("out of memory"); early_seq('f'); - assert(early_final_result == 1234); - assert(std.mem.eql(u8, early_points, "abcdef")); + assertOrPanic(early_final_result == 1234); + assertOrPanic(std.mem.eql(u8, early_points, "abcdef")); } async fn early_amain() void { early_seq('b'); @@ -170,7 +170,7 @@ test "async function with dot syntax" { defer da.deinit(); const p = try async<&da.allocator> S.foo(); cancel p; - assert(S.y == 2); + assertOrPanic(S.y == 2); } test "async fn pointer in a struct field" { @@ -182,9 +182,9 @@ test "async fn pointer in a struct field" { var da = std.heap.DirectAllocator.init(); defer da.deinit(); const p = (async<&da.allocator> foo.bar(&data)) catch unreachable; - assert(data == 2); + assertOrPanic(data == 2); cancel p; - assert(data == 4); + assertOrPanic(data == 4); } async<*std.mem.Allocator> fn simpleAsyncFn2(y: *i32) void { defer y.* += 2; @@ -199,6 +199,7 @@ test "async fn with inferred error set" { resume p; cancel p; } + async fn failing() !void { suspend; return error.Fail; @@ -220,8 +221,7 @@ test "error return trace across suspend points - async return" { cancel p2; } -// TODO https://github.com/ziglang/zig/issues/760 -fn nonFailing() promise->(anyerror!void) { +fn nonFailing() (promise->anyerror!void) { return async suspendThenFail() catch unreachable; } async fn suspendThenFail() anyerror!void { @@ -230,9 +230,9 @@ async fn suspendThenFail() anyerror!void { } async fn printTrace(p: promise->(anyerror!void)) void { (await p) catch |e| { - std.debug.assert(e == error.Fail); + std.debug.assertOrPanic(e == error.Fail); if (@errorReturnTrace()) |trace| { - assert(trace.index == 1); + assertOrPanic(trace.index == 1); } else switch (builtin.mode) { builtin.Mode.Debug, builtin.Mode.ReleaseSafe => @panic("expected return trace"), builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => {}, @@ -246,7 +246,7 @@ test "break from suspend" { var my_result: i32 = 1; const p = try async testBreakFromSuspend(&my_result); cancel p; - std.debug.assert(my_result == 2); + std.debug.assertOrPanic(my_result == 2); } async fn testBreakFromSuspend(my_result: *i32) void { suspend { diff --git a/test/cases/defer.zig b/test/stage1/behavior/defer.zig similarity index 72% rename from test/cases/defer.zig rename to test/stage1/behavior/defer.zig index f9a2b69cd94b7513f0eb220653c7bbc562f73ea9..6c6c60311e67bf3a0b8f45c0e0d71106927c36a3 100644 --- a/test/cases/defer.zig +++ b/test/stage1/behavior/defer.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; var result: [3]u8 = undefined; var index: usize = undefined; @@ -21,18 +21,18 @@ fn runSomeErrorDefers(x: bool) !bool { } test "mixing normal and error defers" { - assert(runSomeErrorDefers(true) catch unreachable); - assert(result[0] == 'c'); - assert(result[1] == 'a'); + assertOrPanic(runSomeErrorDefers(true) catch unreachable); + assertOrPanic(result[0] == 'c'); + assertOrPanic(result[1] == 'a'); const ok = runSomeErrorDefers(false) catch |err| x: { - assert(err == error.FalseNotAllowed); + assertOrPanic(err == error.FalseNotAllowed); break :x true; }; - assert(ok); - assert(result[0] == 'c'); - assert(result[1] == 'b'); - assert(result[2] == 'a'); + assertOrPanic(ok); + assertOrPanic(result[0] == 'c'); + assertOrPanic(result[1] == 'b'); + assertOrPanic(result[2] == 'a'); } test "break and continue inside loop inside defer expression" { @@ -47,7 +47,7 @@ fn testBreakContInDefer(x: usize) void { if (i < 5) continue; if (i == 5) break; } - assert(i == 5); + assertOrPanic(i == 5); } } @@ -59,11 +59,11 @@ test "defer and labeled break" { break :blk; } - assert(i == 1); + assertOrPanic(i == 1); } test "errdefer does not apply to fn inside fn" { - if (testNestedFnErrDefer()) |_| @panic("expected error") else |e| assert(e == error.Bad); + if (testNestedFnErrDefer()) |_| @panic("expected error") else |e| assertOrPanic(e == error.Bad); } fn testNestedFnErrDefer() anyerror!void { diff --git a/test/cases/enum.zig b/test/stage1/behavior/enum.zig similarity index 84% rename from test/cases/enum.zig rename to test/stage1/behavior/enum.zig index 2dd552488c9461f81b3c4f138a7de36bb8640be8..9de138ef78cd40707c66ae7ea208b44f4ca5fbfd 100644 --- a/test/cases/enum.zig +++ b/test/stage1/behavior/enum.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const mem = @import("std").mem; test "enum type" { @@ -11,16 +11,16 @@ test "enum type" { }; const bar = Bar.B; - assert(bar == Bar.B); - assert(@memberCount(Foo) == 3); - assert(@memberCount(Bar) == 4); - assert(@sizeOf(Foo) == @sizeOf(FooNoVoid)); - assert(@sizeOf(Bar) == 1); + assertOrPanic(bar == Bar.B); + assertOrPanic(@memberCount(Foo) == 3); + assertOrPanic(@memberCount(Bar) == 4); + assertOrPanic(@sizeOf(Foo) == @sizeOf(FooNoVoid)); + assertOrPanic(@sizeOf(Bar) == 1); } test "enum as return value" { switch (returnAnInt(13)) { - Foo.One => |value| assert(value == 13), + Foo.One => |value| assertOrPanic(value == 13), else => unreachable, } } @@ -92,14 +92,14 @@ test "enum to int" { } fn shouldEqual(n: Number, expected: u3) void { - assert(@enumToInt(n) == expected); + assertOrPanic(@enumToInt(n) == expected); } test "int to enum" { testIntToEnumEval(3); } fn testIntToEnumEval(x: i32) void { - assert(@intToEnum(IntToEnumNumber, @intCast(u3, x)) == IntToEnumNumber.Three); + assertOrPanic(@intToEnum(IntToEnumNumber, @intCast(u3, x)) == IntToEnumNumber.Three); } const IntToEnumNumber = enum { Zero, @@ -110,8 +110,8 @@ const IntToEnumNumber = enum { }; test "@tagName" { - assert(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); - comptime assert(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); + assertOrPanic(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); + comptime assertOrPanic(mem.eql(u8, testEnumTagNameBare(BareNumber.Three), "Three")); } fn testEnumTagNameBare(n: BareNumber) []const u8 { @@ -126,8 +126,8 @@ const BareNumber = enum { test "enum alignment" { comptime { - assert(@alignOf(AlignTestEnum) >= @alignOf([9]u8)); - assert(@alignOf(AlignTestEnum) >= @alignOf(u64)); + assertOrPanic(@alignOf(AlignTestEnum) >= @alignOf([9]u8)); + assertOrPanic(@alignOf(AlignTestEnum) >= @alignOf(u64)); } } @@ -663,10 +663,10 @@ const ValueCount257 = enum { test "enum sizes" { comptime { - assert(@sizeOf(ValueCount1) == 0); - assert(@sizeOf(ValueCount2) == 1); - assert(@sizeOf(ValueCount256) == 1); - assert(@sizeOf(ValueCount257) == 2); + assertOrPanic(@sizeOf(ValueCount1) == 0); + assertOrPanic(@sizeOf(ValueCount2) == 1); + assertOrPanic(@sizeOf(ValueCount256) == 1); + assertOrPanic(@sizeOf(ValueCount257) == 2); } } @@ -685,12 +685,12 @@ test "set enum tag type" { { var x = Small.One; x = Small.Two; - comptime assert(@TagType(Small) == u2); + comptime assertOrPanic(@TagType(Small) == u2); } { var x = Small2.One; x = Small2.Two; - comptime assert(@TagType(Small2) == u2); + comptime assertOrPanic(@TagType(Small2) == u2); } } @@ -737,17 +737,17 @@ const bit_field_1 = BitFieldOfEnums{ test "bit field access with enum fields" { var data = bit_field_1; - assert(getA(&data) == A.Two); - assert(getB(&data) == B.Three3); - assert(getC(&data) == C.Four4); - comptime assert(@sizeOf(BitFieldOfEnums) == 1); + assertOrPanic(getA(&data) == A.Two); + assertOrPanic(getB(&data) == B.Three3); + assertOrPanic(getC(&data) == C.Four4); + comptime assertOrPanic(@sizeOf(BitFieldOfEnums) == 1); data.b = B.Four3; - assert(data.b == B.Four3); + assertOrPanic(data.b == B.Four3); data.a = A.Three; - assert(data.a == A.Three); - assert(data.b == B.Four3); + assertOrPanic(data.a == A.Three); + assertOrPanic(data.b == B.Four3); } fn getA(data: *const BitFieldOfEnums) A { @@ -768,7 +768,7 @@ test "casting enum to its tag type" { } fn testCastEnumToTagType(value: Small2) void { - assert(@enumToInt(value) == 1); + assertOrPanic(@enumToInt(value) == 1); } const MultipleChoice = enum(u32) { @@ -784,8 +784,8 @@ test "enum with specified tag values" { } fn testEnumWithSpecifiedTagValues(x: MultipleChoice) void { - assert(@enumToInt(x) == 60); - assert(1234 == switch (x) { + assertOrPanic(@enumToInt(x) == 60); + assertOrPanic(1234 == switch (x) { MultipleChoice.A => 1, MultipleChoice.B => 2, MultipleChoice.C => u32(1234), @@ -811,8 +811,8 @@ test "enum with specified and unspecified tag values" { } fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { - assert(@enumToInt(x) == 1000); - assert(1234 == switch (x) { + assertOrPanic(@enumToInt(x) == 1000); + assertOrPanic(1234 == switch (x) { MultipleChoice2.A => 1, MultipleChoice2.B => 2, MultipleChoice2.C => 3, @@ -826,8 +826,8 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { } test "cast integer literal to enum" { - assert(@intToEnum(MultipleChoice2, 0) == MultipleChoice2.Unspecified1); - assert(@intToEnum(MultipleChoice2, 40) == MultipleChoice2.B); + assertOrPanic(@intToEnum(MultipleChoice2, 0) == MultipleChoice2.Unspecified1); + assertOrPanic(@intToEnum(MultipleChoice2, 40) == MultipleChoice2.B); } const EnumWithOneMember = enum { @@ -865,14 +865,14 @@ const EnumWithTagValues = enum(u4) { D = 1 << 3, }; test "enum with tag values don't require parens" { - assert(@enumToInt(EnumWithTagValues.C) == 0b0100); + assertOrPanic(@enumToInt(EnumWithTagValues.C) == 0b0100); } test "enum with 1 field but explicit tag type should still have the tag type" { const Enum = enum(u8) { B = 2, }; - comptime @import("std").debug.assert(@sizeOf(Enum) == @sizeOf(u8)); + comptime @import("std").debug.assertOrPanic(@sizeOf(Enum) == @sizeOf(u8)); } test "empty extern enum with members" { @@ -881,14 +881,14 @@ test "empty extern enum with members" { B, C, }; - assert(@sizeOf(E) == @sizeOf(c_int)); + assertOrPanic(@sizeOf(E) == @sizeOf(c_int)); } -test "aoeu" { +test "tag name with assigned enum values" { const LocalFoo = enum { A = 1, B = 0, }; var b = LocalFoo.B; - assert(mem.eql(u8, @tagName(b), "B")); + assertOrPanic(mem.eql(u8, @tagName(b), "B")); } diff --git a/test/cases/enum_with_members.zig b/test/stage1/behavior/enum_with_members.zig similarity index 63% rename from test/cases/enum_with_members.zig rename to test/stage1/behavior/enum_with_members.zig index 088496bd2f414128e74596c172df2cc3c2aab429..49af1ceae78eef7a5566e589d9d39abd027d7d40 100644 --- a/test/cases/enum_with_members.zig +++ b/test/stage1/behavior/enum_with_members.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const mem = @import("std").mem; const fmt = @import("std").fmt; @@ -19,9 +19,9 @@ test "enum with members" { const b = ET{ .UINT = 42 }; var buf: [20]u8 = undefined; - assert((a.print(buf[0..]) catch unreachable) == 3); - assert(mem.eql(u8, buf[0..3], "-42")); + assertOrPanic((a.print(buf[0..]) catch unreachable) == 3); + assertOrPanic(mem.eql(u8, buf[0..3], "-42")); - assert((b.print(buf[0..]) catch unreachable) == 2); - assert(mem.eql(u8, buf[0..2], "42")); + assertOrPanic((b.print(buf[0..]) catch unreachable) == 2); + assertOrPanic(mem.eql(u8, buf[0..2], "42")); } diff --git a/test/cases/error.zig b/test/stage1/behavior/error.zig similarity index 58% rename from test/cases/error.zig rename to test/stage1/behavior/error.zig index a731f39021f73f5e8e0f3baee83b5832738834d4..c7e38712bcdf5b8e7ff0a4eb8d8eba7692ec768c 100644 --- a/test/cases/error.zig +++ b/test/stage1/behavior/error.zig @@ -1,5 +1,6 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; +const assertError = std.debug.assertError; const mem = std.mem; const builtin = @import("builtin"); @@ -18,7 +19,7 @@ pub fn baz() anyerror!i32 { } test "error wrapping" { - assert((baz() catch unreachable) == 15); + assertOrPanic((baz() catch unreachable) == 15); } fn gimmeItBroke() []const u8 { @@ -26,14 +27,14 @@ fn gimmeItBroke() []const u8 { } test "@errorName" { - assert(mem.eql(u8, @errorName(error.AnError), "AnError")); - assert(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName")); + assertOrPanic(mem.eql(u8, @errorName(error.AnError), "AnError")); + assertOrPanic(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName")); } test "error values" { const a = @errorToInt(error.err1); const b = @errorToInt(error.err2); - assert(a != b); + assertOrPanic(a != b); } test "redefinition of error values allowed" { @@ -46,8 +47,8 @@ fn shouldBeNotEqual(a: anyerror, b: anyerror) void { test "error binary operator" { const a = errBinaryOperatorG(true) catch 3; const b = errBinaryOperatorG(false) catch 3; - assert(a == 3); - assert(b == 10); + assertOrPanic(a == 3); + assertOrPanic(b == 10); } fn errBinaryOperatorG(x: bool) anyerror!isize { return if (x) error.ItBroke else isize(10); @@ -55,7 +56,7 @@ fn errBinaryOperatorG(x: bool) anyerror!isize { test "unwrap simple value from error" { const i = unwrapSimpleValueFromErrorDo() catch unreachable; - assert(i == 13); + assertOrPanic(i == 13); } fn unwrapSimpleValueFromErrorDo() anyerror!isize { return 13; @@ -81,13 +82,13 @@ test "error union type " { fn testErrorUnionType() void { const x: anyerror!i32 = 1234; - if (x) |value| assert(value == 1234) else |_| unreachable; - assert(@typeId(@typeOf(x)) == builtin.TypeId.ErrorUnion); - assert(@typeId(@typeOf(x).ErrorSet) == builtin.TypeId.ErrorSet); - assert(@typeOf(x).ErrorSet == anyerror); + if (x) |value| assertOrPanic(value == 1234) else |_| unreachable; + assertOrPanic(@typeId(@typeOf(x)) == builtin.TypeId.ErrorUnion); + assertOrPanic(@typeId(@typeOf(x).ErrorSet) == builtin.TypeId.ErrorSet); + assertOrPanic(@typeOf(x).ErrorSet == anyerror); } -test "error set type " { +test "error set type" { testErrorSetType(); comptime testErrorSetType(); } @@ -98,12 +99,12 @@ const MyErrSet = error{ }; fn testErrorSetType() void { - assert(@memberCount(MyErrSet) == 2); + assertOrPanic(@memberCount(MyErrSet) == 2); const a: MyErrSet!i32 = 5678; const b: MyErrSet!i32 = MyErrSet.OutOfMemory; - if (a) |value| assert(value == 5678) else |err| switch (err) { + if (a) |value| assertOrPanic(value == 5678) else |err| switch (err) { error.OutOfMemory => unreachable, error.FileNotFound => unreachable, } @@ -126,7 +127,7 @@ const Set2 = error{ fn testExplicitErrorSetCast(set1: Set1) void { var x = @errSetCast(Set2, set1); var y = @errSetCast(Set1, x); - assert(y == error.A); + assertOrPanic(y == error.A); } test "comptime test error for empty error set" { @@ -137,12 +138,12 @@ test "comptime test error for empty error set" { const EmptyErrorSet = error{}; fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) void { - if (x) |v| assert(v == 1234) else |err| @compileError("bad"); + if (x) |v| assertOrPanic(v == 1234) else |err| @compileError("bad"); } test "syntax: optional operator in front of error union operator" { comptime { - assert(?(anyerror!i32) == ?(anyerror!i32)); + assertOrPanic(?(anyerror!i32) == ?(anyerror!i32)); } } @@ -161,7 +162,6 @@ fn testErrToIntWithOnePossibleValue( test "error union peer type resolution" { testErrorUnionPeerTypeResolution(1); - comptime testErrorUnionPeerTypeResolution(1); } fn testErrorUnionPeerTypeResolution(x: i32) void { @@ -170,6 +170,11 @@ fn testErrorUnionPeerTypeResolution(x: i32) void { 2 => baz_1(), else => quux_1(), }; + if (y) |_| { + @panic("expected error"); + } else |e| { + assertOrPanic(e == error.A); + } } fn bar_1() anyerror { @@ -243,3 +248,85 @@ fn intLiteral(str: []const u8) !?i64 { return error.T; } + +test "nested error union function call in optional unwrap" { + const S = struct { + const Foo = struct { + a: i32, + }; + + fn errorable() !i32 { + var x: Foo = (try getFoo()) orelse return error.Other; + return x.a; + } + + fn errorable2() !i32 { + var x: Foo = (try getFoo2()) orelse return error.Other; + return x.a; + } + + fn errorable3() !i32 { + var x: Foo = (try getFoo3()) orelse return error.Other; + return x.a; + } + + fn getFoo() anyerror!?Foo { + return Foo{ .a = 1234 }; + } + + fn getFoo2() anyerror!?Foo { + return error.Failure; + } + + fn getFoo3() anyerror!?Foo { + return null; + } + }; + assertOrPanic((try S.errorable()) == 1234); + assertError(S.errorable2(), error.Failure); + assertError(S.errorable3(), error.Other); + comptime { + assertOrPanic((try S.errorable()) == 1234); + assertError(S.errorable2(), error.Failure); + assertError(S.errorable3(), error.Other); + } +} + +test "widen cast integer payload of error union function call" { + const S = struct { + fn errorable() !u64 { + var x = u64(try number()); + return x; + } + + fn number() anyerror!u32 { + return 1234; + } + }; + assertOrPanic((try S.errorable()) == 1234); +} + +test "return function call to error set from error union function" { + const S = struct { + fn errorable() anyerror!i32 { + return fail(); + } + + fn fail() anyerror { + return error.Failure; + } + }; + assertError(S.errorable(), error.Failure); + comptime assertError(S.errorable(), error.Failure); +} + +test "optional error set is the same size as error set" { + comptime assertOrPanic(@sizeOf(?anyerror) == @sizeOf(anyerror)); + const S = struct { + fn returnsOptErrSet() ?anyerror { + return null; + } + }; + assertOrPanic(S.returnsOptErrSet() == null); + comptime assertOrPanic(S.returnsOptErrSet() == null); +} diff --git a/test/cases/eval.zig b/test/stage1/behavior/eval.zig similarity index 70% rename from test/cases/eval.zig rename to test/stage1/behavior/eval.zig index a9eded151ebc8f77f92bd174cb04553815fda3e2..2d8494eb0b6db4fe27a6622a008d27d2774701c0 100644 --- a/test/cases/eval.zig +++ b/test/stage1/behavior/eval.zig @@ -1,9 +1,9 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const builtin = @import("builtin"); test "compile time recursion" { - assert(some_data.len == 21); + assertOrPanic(some_data.len == 21); } var some_data: [@intCast(usize, fibonacci(7))]u8 = undefined; fn fibonacci(x: i32) i32 { @@ -16,7 +16,7 @@ fn unwrapAndAddOne(blah: ?i32) i32 { } const should_be_1235 = unwrapAndAddOne(1234); test "static add one" { - assert(should_be_1235 == 1235); + assertOrPanic(should_be_1235 == 1235); } test "inlined loop" { @@ -24,7 +24,7 @@ test "inlined loop" { comptime var sum = 0; inline while (i <= 5) : (i += 1) sum += i; - assert(sum == 15); + assertOrPanic(sum == 15); } fn gimme1or2(comptime a: bool) i32 { @@ -34,12 +34,12 @@ fn gimme1or2(comptime a: bool) i32 { return z; } test "inline variable gets result of const if" { - assert(gimme1or2(true) == 1); - assert(gimme1or2(false) == 2); + assertOrPanic(gimme1or2(true) == 1); + assertOrPanic(gimme1or2(false) == 2); } test "static function evaluation" { - assert(statically_added_number == 3); + assertOrPanic(statically_added_number == 3); } const statically_added_number = staticAdd(1, 2); fn staticAdd(a: i32, b: i32) i32 { @@ -47,7 +47,8 @@ fn staticAdd(a: i32, b: i32) i32 { } test "const expr eval on single expr blocks" { - assert(constExprEvalOnSingleExprBlocksFn(1, true) == 3); + assertOrPanic(constExprEvalOnSingleExprBlocksFn(1, true) == 3); + comptime assertOrPanic(constExprEvalOnSingleExprBlocksFn(1, true) == 3); } fn constExprEvalOnSingleExprBlocksFn(x: i32, b: bool) i32 { @@ -63,10 +64,10 @@ fn constExprEvalOnSingleExprBlocksFn(x: i32, b: bool) i32 { } test "statically initialized list" { - assert(static_point_list[0].x == 1); - assert(static_point_list[0].y == 2); - assert(static_point_list[1].x == 3); - assert(static_point_list[1].y == 4); + assertOrPanic(static_point_list[0].x == 1); + assertOrPanic(static_point_list[0].y == 2); + assertOrPanic(static_point_list[1].x == 3); + assertOrPanic(static_point_list[1].y == 4); } const Point = struct { x: i32, @@ -84,8 +85,8 @@ fn makePoint(x: i32, y: i32) Point { } test "static eval list init" { - assert(static_vec3.data[2] == 1.0); - assert(vec3(0.0, 0.0, 3.0).data[2] == 3.0); + assertOrPanic(static_vec3.data[2] == 1.0); + assertOrPanic(vec3(0.0, 0.0, 3.0).data[2] == 3.0); } const static_vec3 = vec3(0.0, 0.0, 1.0); pub const Vec3 = struct { @@ -101,12 +102,12 @@ pub fn vec3(x: f32, y: f32, z: f32) Vec3 { test "constant expressions" { var array: [array_size]u8 = undefined; - assert(@sizeOf(@typeOf(array)) == 20); + assertOrPanic(@sizeOf(@typeOf(array)) == 20); } const array_size: u8 = 20; test "constant struct with negation" { - assert(vertices[0].x == -0.6); + assertOrPanic(vertices[0].x == -0.6); } const Vertex = struct { x: f32, @@ -141,7 +142,7 @@ const vertices = []Vertex{ test "statically initialized struct" { st_init_str_foo.x += 1; - assert(st_init_str_foo.x == 14); + assertOrPanic(st_init_str_foo.x == 14); } const StInitStrFoo = struct { x: i32, @@ -154,7 +155,7 @@ var st_init_str_foo = StInitStrFoo{ test "statically initalized array literal" { const y: [4]u8 = st_init_arr_lit_x; - assert(y[3] == 4); + assertOrPanic(y[3] == 4); } const st_init_arr_lit_x = []u8{ 1, @@ -166,15 +167,15 @@ const st_init_arr_lit_x = []u8{ test "const slice" { comptime { const a = "1234567890"; - assert(a.len == 10); + assertOrPanic(a.len == 10); const b = a[1..2]; - assert(b.len == 1); - assert(b[0] == '2'); + assertOrPanic(b.len == 1); + assertOrPanic(b[0] == '2'); } } test "try to trick eval with runtime if" { - assert(testTryToTrickEvalWithRuntimeIf(true) == 10); + assertOrPanic(testTryToTrickEvalWithRuntimeIf(true) == 10); } fn testTryToTrickEvalWithRuntimeIf(b: bool) usize { @@ -200,16 +201,16 @@ fn letsTryToCompareBools(a: bool, b: bool) bool { return max(bool, a, b); } test "inlined block and runtime block phi" { - assert(letsTryToCompareBools(true, true)); - assert(letsTryToCompareBools(true, false)); - assert(letsTryToCompareBools(false, true)); - assert(!letsTryToCompareBools(false, false)); + assertOrPanic(letsTryToCompareBools(true, true)); + assertOrPanic(letsTryToCompareBools(true, false)); + assertOrPanic(letsTryToCompareBools(false, true)); + assertOrPanic(!letsTryToCompareBools(false, false)); comptime { - assert(letsTryToCompareBools(true, true)); - assert(letsTryToCompareBools(true, false)); - assert(letsTryToCompareBools(false, true)); - assert(!letsTryToCompareBools(false, false)); + assertOrPanic(letsTryToCompareBools(true, true)); + assertOrPanic(letsTryToCompareBools(true, false)); + assertOrPanic(letsTryToCompareBools(false, true)); + assertOrPanic(!letsTryToCompareBools(false, false)); } } @@ -254,14 +255,14 @@ fn performFn(comptime prefix_char: u8, start_value: i32) i32 { } test "comptime iterate over fn ptr list" { - assert(performFn('t', 1) == 6); - assert(performFn('o', 0) == 1); - assert(performFn('w', 99) == 99); + assertOrPanic(performFn('t', 1) == 6); + assertOrPanic(performFn('o', 0) == 1); + assertOrPanic(performFn('w', 99) == 99); } test "eval @setRuntimeSafety at compile-time" { const result = comptime fnWithSetRuntimeSafety(); - assert(result == 1234); + assertOrPanic(result == 1234); } fn fnWithSetRuntimeSafety() i32 { @@ -271,7 +272,7 @@ fn fnWithSetRuntimeSafety() i32 { test "eval @setFloatMode at compile-time" { const result = comptime fnWithFloatMode(); - assert(result == 1234.0); + assertOrPanic(result == 1234.0); } fn fnWithFloatMode() f32 { @@ -292,15 +293,15 @@ var simple_struct = SimpleStruct{ .field = 1234 }; const bound_fn = simple_struct.method; test "call method on bound fn referring to var instance" { - assert(bound_fn() == 1237); + assertOrPanic(bound_fn() == 1237); } test "ptr to local array argument at comptime" { comptime { var bytes: [10]u8 = undefined; modifySomeBytes(bytes[0..]); - assert(bytes[0] == 'a'); - assert(bytes[9] == 'b'); + assertOrPanic(bytes[0] == 'a'); + assertOrPanic(bytes[9] == 'b'); } } @@ -328,9 +329,9 @@ fn testCompTimeUIntComparisons(x: u32) void { } test "const ptr to variable data changes at runtime" { - assert(foo_ref.name[0] == 'a'); + assertOrPanic(foo_ref.name[0] == 'a'); foo_ref.name = "b"; - assert(foo_ref.name[0] == 'b'); + assertOrPanic(foo_ref.name[0] == 'b'); } const Foo = struct { @@ -341,8 +342,8 @@ var foo_contents = Foo{ .name = "a" }; const foo_ref = &foo_contents; test "create global array with for loop" { - assert(global_array[5] == 5 * 5); - assert(global_array[9] == 9 * 9); + assertOrPanic(global_array[5] == 5 * 5); + assertOrPanic(global_array[9] == 9 * 9); } const global_array = x: { @@ -357,7 +358,7 @@ test "compile-time downcast when the bits fit" { comptime { const spartan_count: u16 = 255; const byte = @intCast(u8, spartan_count); - assert(byte == 255); + assertOrPanic(byte == 255); } } @@ -365,44 +366,45 @@ const hi1 = "hi"; const hi2 = hi1; test "const global shares pointer with other same one" { assertEqualPtrs(&hi1[0], &hi2[0]); - comptime assert(&hi1[0] == &hi2[0]); + comptime assertOrPanic(&hi1[0] == &hi2[0]); } fn assertEqualPtrs(ptr1: *const u8, ptr2: *const u8) void { - assert(ptr1 == ptr2); + assertOrPanic(ptr1 == ptr2); } test "@setEvalBranchQuota" { comptime { - // 1001 for the loop and then 1 more for the assert fn call + // 1001 for the loop and then 1 more for the assertOrPanic fn call @setEvalBranchQuota(1002); var i = 0; var sum = 0; while (i < 1001) : (i += 1) { sum += i; } - assert(sum == 500500); + assertOrPanic(sum == 500500); } } // TODO test "float literal at compile time not lossy" { -// TODO assert(16777216.0 + 1.0 == 16777217.0); -// TODO assert(9007199254740992.0 + 1.0 == 9007199254740993.0); +// TODO assertOrPanic(16777216.0 + 1.0 == 16777217.0); +// TODO assertOrPanic(9007199254740992.0 + 1.0 == 9007199254740993.0); // TODO } test "f32 at compile time is lossy" { - assert(f32(1 << 24) + 1 == 1 << 24); + assertOrPanic(f32(1 << 24) + 1 == 1 << 24); } test "f64 at compile time is lossy" { - assert(f64(1 << 53) + 1 == 1 << 53); + assertOrPanic(f64(1 << 53) + 1 == 1 << 53); } test "f128 at compile time is lossy" { - assert(f128(10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0); + assertOrPanic(f128(10384593717069655257060992658440192.0) + 1 == 10384593717069655257060992658440192.0); } -// TODO need a better implementation of bigfloat_init_bigint -// assert(f128(1 << 113) == 10384593717069655257060992658440192); +comptime { + assertOrPanic(f128(1 << 113) == 10384593717069655257060992658440192); +} pub fn TypeWithCompTimeSlice(comptime field_name: []const u8) type { return struct { @@ -413,15 +415,15 @@ pub fn TypeWithCompTimeSlice(comptime field_name: []const u8) type { test "string literal used as comptime slice is memoized" { const a = "link"; const b = "link"; - comptime assert(TypeWithCompTimeSlice(a).Node == TypeWithCompTimeSlice(b).Node); - comptime assert(TypeWithCompTimeSlice("link").Node == TypeWithCompTimeSlice("link").Node); + comptime assertOrPanic(TypeWithCompTimeSlice(a).Node == TypeWithCompTimeSlice(b).Node); + comptime assertOrPanic(TypeWithCompTimeSlice("link").Node == TypeWithCompTimeSlice("link").Node); } test "comptime slice of undefined pointer of length 0" { const slice1 = ([*]i32)(undefined)[0..0]; - assert(slice1.len == 0); + assertOrPanic(slice1.len == 0); const slice2 = ([*]i32)(undefined)[100..100]; - assert(slice2.len == 0); + assertOrPanic(slice2.len == 0); } fn copyWithPartialInline(s: []u32, b: []u8) void { @@ -443,16 +445,16 @@ test "binary math operator in partially inlined function" { r.* = @intCast(u8, i + 1); copyWithPartialInline(s[0..], b[0..]); - assert(s[0] == 0x1020304); - assert(s[1] == 0x5060708); - assert(s[2] == 0x90a0b0c); - assert(s[3] == 0xd0e0f10); + assertOrPanic(s[0] == 0x1020304); + assertOrPanic(s[1] == 0x5060708); + assertOrPanic(s[2] == 0x90a0b0c); + assertOrPanic(s[3] == 0xd0e0f10); } test "comptime function with the same args is memoized" { comptime { - assert(MakeType(i32) == MakeType(i32)); - assert(MakeType(i32) != MakeType(f64)); + assertOrPanic(MakeType(i32) == MakeType(i32)); + assertOrPanic(MakeType(i32) != MakeType(f64)); } } @@ -468,7 +470,7 @@ test "comptime function with mutable pointer is not memoized" { const ptr = &x; increment(ptr); increment(ptr); - assert(x == 3); + assertOrPanic(x == 3); } } @@ -494,14 +496,14 @@ fn doesAlotT(comptime T: type, value: usize) T { } test "@setEvalBranchQuota at same scope as generic function call" { - assert(doesAlotT(u32, 2) == 2); + assertOrPanic(doesAlotT(u32, 2) == 2); } test "comptime slice of slice preserves comptime var" { comptime { var buff: [10]u8 = undefined; buff[0..][0..][0] = 1; - assert(buff[0..][0..][0] == 1); + assertOrPanic(buff[0..][0..][0] == 1); } } @@ -510,7 +512,7 @@ test "comptime slice of pointer preserves comptime var" { var buff: [10]u8 = undefined; var a = buff[0..].ptr; a[0..1][0] = 1; - assert(buff[0..][0..][0] == 1); + assertOrPanic(buff[0..][0..][0] == 1); } } @@ -524,9 +526,9 @@ const SingleFieldStruct = struct { test "const ptr to comptime mutable data is not memoized" { comptime { var foo = SingleFieldStruct{ .x = 1 }; - assert(foo.read_x() == 1); + assertOrPanic(foo.read_x() == 1); foo.x = 2; - assert(foo.read_x() == 2); + assertOrPanic(foo.read_x() == 2); } } @@ -535,7 +537,7 @@ test "array concat of slices gives slice" { var a: []const u8 = "aoeu"; var b: []const u8 = "asdf"; const c = a ++ b; - assert(std.mem.eql(u8, c, "aoeuasdf")); + assertOrPanic(std.mem.eql(u8, c, "aoeuasdf")); } } @@ -552,14 +554,14 @@ test "comptime shlWithOverflow" { break :amt amt; }; - assert(ct_shifted == rt_shifted); + assertOrPanic(ct_shifted == rt_shifted); } test "runtime 128 bit integer division" { var a: u128 = 152313999999999991610955792383; var b: u128 = 10000000000000000000; var c = a / b; - assert(c == 15231399999); + assertOrPanic(c == 15231399999); } pub const Info = struct { @@ -572,20 +574,20 @@ test "comptime modification of const struct field" { comptime { var res = diamond_info; res.version = 1; - assert(diamond_info.version == 0); - assert(res.version == 1); + assertOrPanic(diamond_info.version == 0); + assertOrPanic(res.version == 1); } } test "pointer to type" { comptime { var T: type = i32; - assert(T == i32); + assertOrPanic(T == i32); var ptr = &T; - assert(@typeOf(ptr) == *type); + assertOrPanic(@typeOf(ptr) == *type); ptr.* = f32; - assert(T == f32); - assert(*T == *f32); + assertOrPanic(T == f32); + assertOrPanic(*T == *f32); } } @@ -594,17 +596,17 @@ test "slice of type" { var types_array = []type{ i32, f64, type }; for (types_array) |T, i| { switch (i) { - 0 => assert(T == i32), - 1 => assert(T == f64), - 2 => assert(T == type), + 0 => assertOrPanic(T == i32), + 1 => assertOrPanic(T == f64), + 2 => assertOrPanic(T == type), else => unreachable, } } for (types_array[0..]) |T, i| { switch (i) { - 0 => assert(T == i32), - 1 => assert(T == f64), - 2 => assert(T == type), + 0 => assertOrPanic(T == i32), + 1 => assertOrPanic(T == f64), + 2 => assertOrPanic(T == type), else => unreachable, } } @@ -621,7 +623,7 @@ fn wrap(comptime T: type) Wrapper { test "function which returns struct with type field causes implicit comptime" { const ty = wrap(i32).T; - assert(ty == i32); + assertOrPanic(ty == i32); } test "call method with comptime pass-by-non-copying-value self parameter" { @@ -635,12 +637,12 @@ test "call method with comptime pass-by-non-copying-value self parameter" { const s = S{ .a = 2 }; var b = s.b(); - assert(b == 2); + assertOrPanic(b == 2); } test "@tagName of @typeId" { const str = @tagName(@typeId(u8)); - assert(std.mem.eql(u8, str, "Int")); + assertOrPanic(std.mem.eql(u8, str, "Int")); } test "setting backward branch quota just before a generic fn call" { @@ -661,8 +663,8 @@ fn testVarInsideInlineLoop(args: ...) void { comptime var i = 0; inline while (i < args.len) : (i += 1) { const x = args[i]; - if (i == 0) assert(x); - if (i == 1) assert(x == 42); + if (i == 0) assertOrPanic(x); + if (i == 1) assertOrPanic(x == 42); } } @@ -672,7 +674,7 @@ test "inline for with same type but different values" { var a: T = undefined; res += a.len; } - assert(res == 5); + assertOrPanic(res == 5); } test "refer to the type of a generic function" { @@ -686,19 +688,19 @@ fn doNothingWithType(comptime T: type) void {} test "zero extend from u0 to u1" { var zero_u0: u0 = 0; var zero_u1: u1 = zero_u0; - assert(zero_u1 == 0); + assertOrPanic(zero_u1 == 0); } test "bit shift a u1" { var x: u1 = 1; var y = x << 0; - assert(y == 1); + assertOrPanic(y == 1); } test "@intCast to a u0" { var x: u8 = 0; var y: u0 = @intCast(u0, x); - assert(y == 0); + assertOrPanic(y == 0); } test "@bytesToslice on a packed struct" { @@ -708,7 +710,7 @@ test "@bytesToslice on a packed struct" { var b = [1]u8{9}; var f = @bytesToSlice(F, b); - assert(f[0].a == 9); + assertOrPanic(f[0].a == 9); } test "comptime pointer cast array and then slice" { @@ -720,8 +722,8 @@ test "comptime pointer cast array and then slice" { const ptrB: [*]const u8 = &array; const sliceB: []const u8 = ptrB[0..2]; - assert(sliceA[1] == 2); - assert(sliceB[1] == 2); + assertOrPanic(sliceA[1] == 2); + assertOrPanic(sliceB[1] == 2); } test "slice bounds in comptime concatenation" { @@ -730,47 +732,47 @@ test "slice bounds in comptime concatenation" { break :blk b[0..1]; }; const str = "" ++ bs; - assert(str.len == 1); - assert(std.mem.eql(u8, str, "1")); + assertOrPanic(str.len == 1); + assertOrPanic(std.mem.eql(u8, str, "1")); const str2 = bs ++ ""; - assert(str2.len == 1); - assert(std.mem.eql(u8, str2, "1")); + assertOrPanic(str2.len == 1); + assertOrPanic(std.mem.eql(u8, str2, "1")); } test "comptime bitwise operators" { comptime { - assert(3 & 1 == 1); - assert(3 & -1 == 3); - assert(-3 & -1 == -3); - assert(3 | -1 == -1); - assert(-3 | -1 == -1); - assert(3 ^ -1 == -4); - assert(-3 ^ -1 == 2); - assert(~i8(-1) == 0); - assert(~i128(-1) == 0); - assert(18446744073709551615 & 18446744073709551611 == 18446744073709551611); - assert(-18446744073709551615 & -18446744073709551611 == -18446744073709551615); - assert(~u128(0) == 0xffffffffffffffffffffffffffffffff); + assertOrPanic(3 & 1 == 1); + assertOrPanic(3 & -1 == 3); + assertOrPanic(-3 & -1 == -3); + assertOrPanic(3 | -1 == -1); + assertOrPanic(-3 | -1 == -1); + assertOrPanic(3 ^ -1 == -4); + assertOrPanic(-3 ^ -1 == 2); + assertOrPanic(~i8(-1) == 0); + assertOrPanic(~i128(-1) == 0); + assertOrPanic(18446744073709551615 & 18446744073709551611 == 18446744073709551611); + assertOrPanic(-18446744073709551615 & -18446744073709551611 == -18446744073709551615); + assertOrPanic(~u128(0) == 0xffffffffffffffffffffffffffffffff); } } test "*align(1) u16 is the same as *align(1:0:2) u16" { comptime { - assert(*align(1:0:2) u16 == *align(1) u16); + assertOrPanic(*align(1:0:2) u16 == *align(1) u16); // TODO add parsing support for this syntax - //assert(*align(:0:2) u16 == *u16); + //assertOrPanic(*align(:0:2) u16 == *u16); } } test "array concatenation forces comptime" { var a = oneItem(3) ++ oneItem(4); - assert(std.mem.eql(i32, a, []i32{3, 4})); + assertOrPanic(std.mem.eql(i32, a, []i32{ 3, 4 })); } test "array multiplication forces comptime" { var a = oneItem(3) ** scalar(2); - assert(std.mem.eql(i32, a, []i32{3, 3})); + assertOrPanic(std.mem.eql(i32, a, []i32{ 3, 3 })); } fn oneItem(x: i32) [1]i32 { diff --git a/test/cases/field_parent_ptr.zig b/test/stage1/behavior/field_parent_ptr.zig similarity index 69% rename from test/cases/field_parent_ptr.zig rename to test/stage1/behavior/field_parent_ptr.zig index 00d4e0f367217fe7a38fdeabedad3f6b7b03a8a4..ed2487c02081d059431989d21c31c5511a69bc54 100644 --- a/test/cases/field_parent_ptr.zig +++ b/test/stage1/behavior/field_parent_ptr.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "@fieldParentPtr non-first field" { testParentFieldPtr(&foo.c); @@ -25,17 +25,17 @@ const foo = Foo{ }; fn testParentFieldPtr(c: *const i32) void { - assert(c == &foo.c); + assertOrPanic(c == &foo.c); const base = @fieldParentPtr(Foo, "c", c); - assert(base == &foo); - assert(&base.c == c); + assertOrPanic(base == &foo); + assertOrPanic(&base.c == c); } fn testParentFieldPtrFirst(a: *const bool) void { - assert(a == &foo.a); + assertOrPanic(a == &foo.a); const base = @fieldParentPtr(Foo, "a", a); - assert(base == &foo); - assert(&base.a == a); + assertOrPanic(base == &foo); + assertOrPanic(&base.a == a); } diff --git a/test/cases/fn.zig b/test/stage1/behavior/fn.zig similarity index 78% rename from test/cases/fn.zig rename to test/stage1/behavior/fn.zig index 8908bd785475e01bff8951d5fc42ea3ad2721f9a..3011bc41d0746b316f816e94c3374bbc8b554a5f 100644 --- a/test/cases/fn.zig +++ b/test/stage1/behavior/fn.zig @@ -1,7 +1,7 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "params" { - assert(testParamsAdd(22, 11) == 33); + assertOrPanic(testParamsAdd(22, 11) == 33); } fn testParamsAdd(a: i32, b: i32) i32 { return a + b; @@ -21,32 +21,32 @@ test "void parameters" { fn voidFun(a: i32, b: void, c: i32, d: void) void { const v = b; const vv: void = if (a == 1) v else {}; - assert(a + c == 3); + assertOrPanic(a + c == 3); return vv; } test "mutable local variables" { var zero: i32 = 0; - assert(zero == 0); + assertOrPanic(zero == 0); var i = i32(0); while (i != 3) { i += 1; } - assert(i == 3); + assertOrPanic(i == 3); } test "separate block scopes" { { const no_conflict: i32 = 5; - assert(no_conflict == 5); + assertOrPanic(no_conflict == 5); } const c = x: { const no_conflict = i32(10); break :x no_conflict; }; - assert(c == 10); + assertOrPanic(c == 10); } test "call function with empty string" { @@ -59,7 +59,7 @@ fn @"weird function name"() i32 { return 1234; } test "weird function name" { - assert(@"weird function name"() == 1234); + assertOrPanic(@"weird function name"() == 1234); } test "implicit cast function unreachable return" { @@ -80,7 +80,7 @@ test "function pointers" { fn4, }; for (fns) |f, i| { - assert(f() == @intCast(u32, i) + 5); + assertOrPanic(f() == @intCast(u32, i) + 5); } } fn fn1() u32 { @@ -97,7 +97,7 @@ fn fn4() u32 { } test "inline function call" { - assert(@inlineCall(add, 3, 9) == 12); + assertOrPanic(@inlineCall(add, 3, 9) == 12); } fn add(a: i32, b: i32) i32 { @@ -110,7 +110,7 @@ test "number literal as an argument" { } fn numberLiteralArg(a: var) void { - assert(a == 3); + assertOrPanic(a == 3); } test "assign inline fn to const variable" { @@ -121,7 +121,7 @@ test "assign inline fn to const variable" { inline fn inlineFn() void {} test "pass by non-copying value" { - assert(addPointCoords(Point{ .x = 1, .y = 2 }) == 3); + assertOrPanic(addPointCoords(Point{ .x = 1, .y = 2 }) == 3); } const Point = struct { @@ -134,17 +134,17 @@ fn addPointCoords(pt: Point) i32 { } test "pass by non-copying value through var arg" { - assert(addPointCoordsVar(Point{ .x = 1, .y = 2 }) == 3); + assertOrPanic(addPointCoordsVar(Point{ .x = 1, .y = 2 }) == 3); } fn addPointCoordsVar(pt: var) i32 { - comptime assert(@typeOf(pt) == Point); + comptime assertOrPanic(@typeOf(pt) == Point); return pt.x + pt.y; } test "pass by non-copying value as method" { var pt = Point2{ .x = 1, .y = 2 }; - assert(pt.addPointCoords() == 3); + assertOrPanic(pt.addPointCoords() == 3); } const Point2 = struct { @@ -158,7 +158,7 @@ const Point2 = struct { test "pass by non-copying value as method, which is generic" { var pt = Point3{ .x = 1, .y = 2 }; - assert(pt.addPointCoords(i32) == 3); + assertOrPanic(pt.addPointCoords(i32) == 3); } const Point3 = struct { @@ -173,7 +173,7 @@ const Point3 = struct { test "pass by non-copying value as method, at comptime" { comptime { var pt = Point2{ .x = 1, .y = 2 }; - assert(pt.addPointCoords() == 3); + assertOrPanic(pt.addPointCoords() == 3); } } @@ -189,7 +189,7 @@ fn outer(y: u32) fn (u32) u32 { test "return inner function which references comptime variable of outer function" { var func = outer(10); - assert(func(3) == 7); + assertOrPanic(func(3) == 7); } test "extern struct with stdcallcc fn pointer" { @@ -203,5 +203,6 @@ test "extern struct with stdcallcc fn pointer" { var s: S = undefined; s.ptr = S.foo; - assert(s.ptr() == 1234); + assertOrPanic(s.ptr() == 1234); } + diff --git a/test/cases/fn_in_struct_in_comptime.zig b/test/stage1/behavior/fn_in_struct_in_comptime.zig similarity index 72% rename from test/cases/fn_in_struct_in_comptime.zig rename to test/stage1/behavior/fn_in_struct_in_comptime.zig index fabb57e9cbcbb7110d284b0a14a0da7be93370bf..0af076d40acfd848a05f6785243eeaecd2b56351 100644 --- a/test/cases/fn_in_struct_in_comptime.zig +++ b/test/stage1/behavior/fn_in_struct_in_comptime.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; fn get_foo() fn (*u8) usize { comptime { @@ -13,5 +13,5 @@ fn get_foo() fn (*u8) usize { test "define a function in an anonymous struct in comptime" { const foo = get_foo(); - assert(foo(@intToPtr(*u8, 12345)) == 12345); + assertOrPanic(foo(@intToPtr(*u8, 12345)) == 12345); } diff --git a/test/cases/for.zig b/test/stage1/behavior/for.zig similarity index 89% rename from test/cases/for.zig rename to test/stage1/behavior/for.zig index aecd8b9a078488015de99fb6aed25ac005c11cc1..b6d1ef24c42f58b3d349192267d494d64538d1ef 100644 --- a/test/cases/for.zig +++ b/test/stage1/behavior/for.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; test "continue in for loop" { @@ -26,7 +26,7 @@ test "for loop with pointer elem var" { var target: [source.len]u8 = undefined; mem.copy(u8, target[0..], source); mangleString(target[0..]); - assert(mem.eql(u8, target, "bcdefgh")); + assertOrPanic(mem.eql(u8, target, "bcdefgh")); } fn mangleString(s: []u8) void { for (s) |*c| { @@ -68,7 +68,7 @@ test "basic for loop" { buf_index += 1; } - assert(mem.eql(u8, buffer[0..buf_index], expected_result)); + assertOrPanic(mem.eql(u8, buffer[0..buf_index], expected_result)); } test "break from outer for loop" { @@ -85,7 +85,7 @@ fn testBreakOuter() void { break :outer; } } - assert(count == 1); + assertOrPanic(count == 1); } test "continue outer for loop" { @@ -102,5 +102,5 @@ fn testContinueOuter() void { continue :outer; } } - assert(counter == array.len); + assertOrPanic(counter == array.len); } diff --git a/test/cases/generics.zig b/test/stage1/behavior/generics.zig similarity index 68% rename from test/cases/generics.zig rename to test/stage1/behavior/generics.zig index 52aa013989e137bfb68890cb010c42da2f413fab..a0928634a7c3180411f22137ddffd2dffce30a13 100644 --- a/test/cases/generics.zig +++ b/test/stage1/behavior/generics.zig @@ -1,9 +1,9 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "simple generic fn" { - assert(max(i32, 3, -1) == 3); - assert(max(f32, 0.123, 0.456) == 0.456); - assert(add(2, 3) == 5); + assertOrPanic(max(i32, 3, -1) == 3); + assertOrPanic(max(f32, 0.123, 0.456) == 0.456); + assertOrPanic(add(2, 3) == 5); } fn max(comptime T: type, a: T, b: T) T { @@ -16,7 +16,7 @@ fn add(comptime a: i32, b: i32) i32 { const the_max = max(u32, 1234, 5678); test "compile time generic eval" { - assert(the_max == 5678); + assertOrPanic(the_max == 5678); } fn gimmeTheBigOne(a: u32, b: u32) u32 { @@ -32,19 +32,19 @@ fn sameButWithFloats(a: f64, b: f64) f64 { } test "fn with comptime args" { - assert(gimmeTheBigOne(1234, 5678) == 5678); - assert(shouldCallSameInstance(34, 12) == 34); - assert(sameButWithFloats(0.43, 0.49) == 0.49); + assertOrPanic(gimmeTheBigOne(1234, 5678) == 5678); + assertOrPanic(shouldCallSameInstance(34, 12) == 34); + assertOrPanic(sameButWithFloats(0.43, 0.49) == 0.49); } test "var params" { - assert(max_i32(12, 34) == 34); - assert(max_f64(1.2, 3.4) == 3.4); + assertOrPanic(max_i32(12, 34) == 34); + assertOrPanic(max_f64(1.2, 3.4) == 3.4); } comptime { - assert(max_i32(12, 34) == 34); - assert(max_f64(1.2, 3.4) == 3.4); + assertOrPanic(max_i32(12, 34) == 34); + assertOrPanic(max_f64(1.2, 3.4) == 3.4); } fn max_var(a: var, b: var) @typeOf(a + b) { @@ -76,8 +76,8 @@ test "function with return type type" { var list2: List(i32) = undefined; list.length = 10; list2.length = 10; - assert(list.prealloc_items.len == 8); - assert(list2.prealloc_items.len == 8); + assertOrPanic(list.prealloc_items.len == 8); + assertOrPanic(list2.prealloc_items.len == 8); } test "generic struct" { @@ -89,9 +89,9 @@ test "generic struct" { .value = true, .next = null, }; - assert(a1.value == 13); - assert(a1.value == a1.getVal()); - assert(b1.getVal()); + assertOrPanic(a1.value == 13); + assertOrPanic(a1.value == a1.getVal()); + assertOrPanic(b1.getVal()); } fn GenNode(comptime T: type) type { return struct { @@ -104,7 +104,7 @@ fn GenNode(comptime T: type) type { } test "const decls in struct" { - assert(GenericDataThing(3).count_plus_one == 4); + assertOrPanic(GenericDataThing(3).count_plus_one == 4); } fn GenericDataThing(comptime count: isize) type { return struct { @@ -113,15 +113,15 @@ fn GenericDataThing(comptime count: isize) type { } test "use generic param in generic param" { - assert(aGenericFn(i32, 3, 4) == 7); + assertOrPanic(aGenericFn(i32, 3, 4) == 7); } fn aGenericFn(comptime T: type, comptime a: T, b: T) T { return a + b; } test "generic fn with implicit cast" { - assert(getFirstByte(u8, []u8{13}) == 13); - assert(getFirstByte(u16, []u16{ + assertOrPanic(getFirstByte(u8, []u8{13}) == 13); + assertOrPanic(getFirstByte(u16, []u16{ 0, 13, }) == 0); @@ -146,6 +146,6 @@ fn foo2(arg: var) bool { } test "array of generic fns" { - assert(foos[0](true)); - assert(!foos[1](true)); + assertOrPanic(foos[0](true)); + assertOrPanic(!foos[1](true)); } diff --git a/test/cases/if.zig b/test/stage1/behavior/if.zig similarity index 85% rename from test/cases/if.zig rename to test/stage1/behavior/if.zig index 808936bfa5fcf8dc01b1a5b326e3691ab59623a4..58d1b8fd734f5e5d2047430cb80fb425f6a7e3af 100644 --- a/test/cases/if.zig +++ b/test/stage1/behavior/if.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "if statements" { shouldBeEqual(1, 1); @@ -24,7 +24,7 @@ fn firstEqlThird(a: i32, b: i32, c: i32) void { } test "else if expression" { - assert(elseIfExpressionF(1) == 1); + assertOrPanic(elseIfExpressionF(1) == 1); } fn elseIfExpressionF(c: u8) u8 { if (c == 0) { diff --git a/test/stage1/behavior/import.zig b/test/stage1/behavior/import.zig new file mode 100644 index 0000000000000000000000000000000000000000..736e4c219ddc6f4e9d5c286fbd93c9c48df6d2ee --- /dev/null +++ b/test/stage1/behavior/import.zig @@ -0,0 +1,10 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; +const a_namespace = @import("import/a_namespace.zig"); + +test "call fn via namespace lookup" { + assertOrPanic(a_namespace.foo() == 1234); +} + +test "importing the same thing gives the same import" { + assertOrPanic(@import("std") == @import("std")); +} diff --git a/test/cases/import/a_namespace.zig b/test/stage1/behavior/import/a_namespace.zig similarity index 100% rename from test/cases/import/a_namespace.zig rename to test/stage1/behavior/import/a_namespace.zig diff --git a/test/cases/incomplete_struct_param_tld.zig b/test/stage1/behavior/incomplete_struct_param_tld.zig similarity index 78% rename from test/cases/incomplete_struct_param_tld.zig rename to test/stage1/behavior/incomplete_struct_param_tld.zig index f1ac03a29279abdfbd4a803642e6b69f908f2760..d062311b2e9fe2d44c9b243d00ad32ff6a0fb9ad 100644 --- a/test/cases/incomplete_struct_param_tld.zig +++ b/test/stage1/behavior/incomplete_struct_param_tld.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const A = struct { b: B, @@ -26,5 +26,5 @@ test "incomplete struct param top level declaration" { .c = C{ .x = 13 }, }, }; - assert(foo(a) == 13); + assertOrPanic(foo(a) == 13); } diff --git a/test/cases/inttoptr.zig b/test/stage1/behavior/inttoptr.zig similarity index 99% rename from test/cases/inttoptr.zig rename to test/stage1/behavior/inttoptr.zig index ba3cc52f090501e041f7e1a7bc8f6b339aab50b9..bf657fc86a9c720d1be79b352aee94dc8031e150 100644 --- a/test/cases/inttoptr.zig +++ b/test/stage1/behavior/inttoptr.zig @@ -24,4 +24,3 @@ fn forceCompilerAnalyzeBranchHardCodedPtrDereference(x: bool) void { return; } } - diff --git a/test/cases/ir_block_deps.zig b/test/stage1/behavior/ir_block_deps.zig similarity index 64% rename from test/cases/ir_block_deps.zig rename to test/stage1/behavior/ir_block_deps.zig index 5c1b18c00e45ae146208411eec6dee055fdcc10a..bc61e11df7a212a53315e05d910a879fb5553ff3 100644 --- a/test/cases/ir_block_deps.zig +++ b/test/stage1/behavior/ir_block_deps.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; fn foo(id: u64) !i32 { return switch (id) { @@ -16,6 +16,6 @@ fn getErrInt() anyerror!i32 { } test "ir block deps" { - assert((foo(1) catch unreachable) == 0); - assert((foo(2) catch unreachable) == 0); + assertOrPanic((foo(1) catch unreachable) == 0); + assertOrPanic((foo(2) catch unreachable) == 0); } diff --git a/test/cases/math.zig b/test/stage1/behavior/math.zig similarity index 56% rename from test/cases/math.zig rename to test/stage1/behavior/math.zig index 7d6b1bd9acbcb454cbfa0d04e14755920da567ee..9d6a5a499799273335bc8c5d1dd3142131d5b1ff 100644 --- a/test/cases/math.zig +++ b/test/stage1/behavior/math.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const maxInt = std.math.maxInt; const minInt = std.math.minInt; @@ -8,57 +8,57 @@ test "division" { comptime testDivision(); } fn testDivision() void { - assert(div(u32, 13, 3) == 4); - assert(div(f16, 1.0, 2.0) == 0.5); - assert(div(f32, 1.0, 2.0) == 0.5); + assertOrPanic(div(u32, 13, 3) == 4); + assertOrPanic(div(f16, 1.0, 2.0) == 0.5); + assertOrPanic(div(f32, 1.0, 2.0) == 0.5); - assert(divExact(u32, 55, 11) == 5); - assert(divExact(i32, -55, 11) == -5); - assert(divExact(f16, 55.0, 11.0) == 5.0); - assert(divExact(f16, -55.0, 11.0) == -5.0); - assert(divExact(f32, 55.0, 11.0) == 5.0); - assert(divExact(f32, -55.0, 11.0) == -5.0); + assertOrPanic(divExact(u32, 55, 11) == 5); + assertOrPanic(divExact(i32, -55, 11) == -5); + assertOrPanic(divExact(f16, 55.0, 11.0) == 5.0); + assertOrPanic(divExact(f16, -55.0, 11.0) == -5.0); + assertOrPanic(divExact(f32, 55.0, 11.0) == 5.0); + assertOrPanic(divExact(f32, -55.0, 11.0) == -5.0); - assert(divFloor(i32, 5, 3) == 1); - assert(divFloor(i32, -5, 3) == -2); - assert(divFloor(f16, 5.0, 3.0) == 1.0); - assert(divFloor(f16, -5.0, 3.0) == -2.0); - assert(divFloor(f32, 5.0, 3.0) == 1.0); - assert(divFloor(f32, -5.0, 3.0) == -2.0); - assert(divFloor(i32, -0x80000000, -2) == 0x40000000); - assert(divFloor(i32, 0, -0x80000000) == 0); - assert(divFloor(i32, -0x40000001, 0x40000000) == -2); - assert(divFloor(i32, -0x80000000, 1) == -0x80000000); + assertOrPanic(divFloor(i32, 5, 3) == 1); + assertOrPanic(divFloor(i32, -5, 3) == -2); + assertOrPanic(divFloor(f16, 5.0, 3.0) == 1.0); + assertOrPanic(divFloor(f16, -5.0, 3.0) == -2.0); + assertOrPanic(divFloor(f32, 5.0, 3.0) == 1.0); + assertOrPanic(divFloor(f32, -5.0, 3.0) == -2.0); + assertOrPanic(divFloor(i32, -0x80000000, -2) == 0x40000000); + assertOrPanic(divFloor(i32, 0, -0x80000000) == 0); + assertOrPanic(divFloor(i32, -0x40000001, 0x40000000) == -2); + assertOrPanic(divFloor(i32, -0x80000000, 1) == -0x80000000); - assert(divTrunc(i32, 5, 3) == 1); - assert(divTrunc(i32, -5, 3) == -1); - assert(divTrunc(f16, 5.0, 3.0) == 1.0); - assert(divTrunc(f16, -5.0, 3.0) == -1.0); - assert(divTrunc(f32, 5.0, 3.0) == 1.0); - assert(divTrunc(f32, -5.0, 3.0) == -1.0); - assert(divTrunc(f64, 5.0, 3.0) == 1.0); - assert(divTrunc(f64, -5.0, 3.0) == -1.0); + assertOrPanic(divTrunc(i32, 5, 3) == 1); + assertOrPanic(divTrunc(i32, -5, 3) == -1); + assertOrPanic(divTrunc(f16, 5.0, 3.0) == 1.0); + assertOrPanic(divTrunc(f16, -5.0, 3.0) == -1.0); + assertOrPanic(divTrunc(f32, 5.0, 3.0) == 1.0); + assertOrPanic(divTrunc(f32, -5.0, 3.0) == -1.0); + assertOrPanic(divTrunc(f64, 5.0, 3.0) == 1.0); + assertOrPanic(divTrunc(f64, -5.0, 3.0) == -1.0); comptime { - assert( + assertOrPanic( 1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600, ); - assert( + assertOrPanic( @rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600, ); - assert( + assertOrPanic( 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2, ); - assert( + assertOrPanic( @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2, ); - assert( + assertOrPanic( @divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2, ); - assert( + assertOrPanic( @divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2, ); - assert( + assertOrPanic( 4126227191251978491697987544882340798050766755606969681711 % 10 == 1, ); } @@ -78,9 +78,9 @@ fn divTrunc(comptime T: type, a: T, b: T) T { test "@addWithOverflow" { var result: u8 = undefined; - assert(@addWithOverflow(u8, 250, 100, &result)); - assert(!@addWithOverflow(u8, 100, 150, &result)); - assert(result == 250); + assertOrPanic(@addWithOverflow(u8, 250, 100, &result)); + assertOrPanic(!@addWithOverflow(u8, 100, 150, &result)); + assertOrPanic(result == 250); } // TODO test mulWithOverflow @@ -88,9 +88,9 @@ test "@addWithOverflow" { test "@shlWithOverflow" { var result: u16 = undefined; - assert(@shlWithOverflow(u16, 0b0010111111111111, 3, &result)); - assert(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result)); - assert(result == 0b1011111111111100); + assertOrPanic(@shlWithOverflow(u16, 0b0010111111111111, 3, &result)); + assertOrPanic(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result)); + assertOrPanic(result == 0b1011111111111100); } test "@clz" { @@ -99,11 +99,11 @@ test "@clz" { } fn testClz() void { - assert(clz(u8(0b00001010)) == 4); - assert(clz(u8(0b10001010)) == 0); - assert(clz(u8(0b00000000)) == 8); - assert(clz(u128(0xffffffffffffffff)) == 64); - assert(clz(u128(0x10000000000000000)) == 63); + assertOrPanic(clz(u8(0b00001010)) == 4); + assertOrPanic(clz(u8(0b10001010)) == 0); + assertOrPanic(clz(u8(0b00000000)) == 8); + assertOrPanic(clz(u128(0xffffffffffffffff)) == 64); + assertOrPanic(clz(u128(0x10000000000000000)) == 63); } fn clz(x: var) usize { @@ -116,9 +116,9 @@ test "@ctz" { } fn testCtz() void { - assert(ctz(u8(0b10100000)) == 5); - assert(ctz(u8(0b10001010)) == 1); - assert(ctz(u8(0b00000000)) == 8); + assertOrPanic(ctz(u8(0b10100000)) == 5); + assertOrPanic(ctz(u8(0b10001010)) == 1); + assertOrPanic(ctz(u8(0b00000000)) == 8); } fn ctz(x: var) usize { @@ -128,27 +128,27 @@ fn ctz(x: var) usize { test "assignment operators" { var i: u32 = 0; i += 5; - assert(i == 5); + assertOrPanic(i == 5); i -= 2; - assert(i == 3); + assertOrPanic(i == 3); i *= 20; - assert(i == 60); + assertOrPanic(i == 60); i /= 3; - assert(i == 20); + assertOrPanic(i == 20); i %= 11; - assert(i == 9); + assertOrPanic(i == 9); i <<= 1; - assert(i == 18); + assertOrPanic(i == 18); i >>= 2; - assert(i == 4); + assertOrPanic(i == 4); i = 6; i &= 5; - assert(i == 4); + assertOrPanic(i == 4); i ^= 6; - assert(i == 2); + assertOrPanic(i == 2); i = 6; i |= 3; - assert(i == 7); + assertOrPanic(i == 7); } test "three expr in a row" { @@ -170,14 +170,14 @@ fn testThreeExprInARow(f: bool, t: bool) void { assertFalse(i32(7) != --(i32(7))); } fn assertFalse(b: bool) void { - assert(!b); + assertOrPanic(!b); } test "const number literal" { const one = 1; const eleven = ten + one; - assert(eleven == 11); + assertOrPanic(eleven == 11); } const ten = 10; @@ -187,9 +187,9 @@ test "unsigned wrapping" { } fn testUnsignedWrappingEval(x: u32) void { const zero = x +% 1; - assert(zero == 0); + assertOrPanic(zero == 0); const orig = zero -% 1; - assert(orig == maxInt(u32)); + assertOrPanic(orig == maxInt(u32)); } test "signed wrapping" { @@ -198,9 +198,9 @@ test "signed wrapping" { } fn testSignedWrappingEval(x: i32) void { const min_val = x +% 1; - assert(min_val == minInt(i32)); + assertOrPanic(min_val == minInt(i32)); const max_val = min_val -% 1; - assert(max_val == maxInt(i32)); + assertOrPanic(max_val == maxInt(i32)); } test "negation wrapping" { @@ -208,9 +208,9 @@ test "negation wrapping" { comptime testNegationWrappingEval(minInt(i16)); } fn testNegationWrappingEval(x: i16) void { - assert(x == -32768); + assertOrPanic(x == -32768); const neg = -%x; - assert(neg == -32768); + assertOrPanic(neg == -32768); } test "unsigned 64-bit division" { @@ -219,8 +219,8 @@ test "unsigned 64-bit division" { } fn test_u64_div() void { const result = divWithResult(1152921504606846976, 34359738365); - assert(result.quotient == 33554432); - assert(result.remainder == 100663296); + assertOrPanic(result.quotient == 33554432); + assertOrPanic(result.remainder == 100663296); } fn divWithResult(a: u64, b: u64) DivResult { return DivResult{ @@ -234,36 +234,36 @@ const DivResult = struct { }; test "binary not" { - assert(comptime x: { + assertOrPanic(comptime x: { break :x ~u16(0b1010101010101010) == 0b0101010101010101; }); - assert(comptime x: { + assertOrPanic(comptime x: { break :x ~u64(2147483647) == 18446744071562067968; }); testBinaryNot(0b1010101010101010); } fn testBinaryNot(x: u16) void { - assert(~x == 0b0101010101010101); + assertOrPanic(~x == 0b0101010101010101); } test "small int addition" { var x: @IntType(false, 2) = 0; - assert(x == 0); + assertOrPanic(x == 0); x += 1; - assert(x == 1); + assertOrPanic(x == 1); x += 1; - assert(x == 2); + assertOrPanic(x == 2); x += 1; - assert(x == 3); + assertOrPanic(x == 3); var result: @typeOf(x) = 3; - assert(@addWithOverflow(@typeOf(x), x, 1, &result)); + assertOrPanic(@addWithOverflow(@typeOf(x), x, 1, &result)); - assert(result == 0); + assertOrPanic(result == 0); } test "float equality" { @@ -276,20 +276,20 @@ test "float equality" { fn testFloatEqualityImpl(x: f64, y: f64) void { const y2 = x + 1.0; - assert(y == y2); + assertOrPanic(y == y2); } test "allow signed integer division/remainder when values are comptime known and positive or exact" { - assert(5 / 3 == 1); - assert(-5 / -3 == 1); - assert(-6 / 3 == -2); + assertOrPanic(5 / 3 == 1); + assertOrPanic(-5 / -3 == 1); + assertOrPanic(-6 / 3 == -2); - assert(5 % 3 == 2); - assert(-6 % 3 == 0); + assertOrPanic(5 % 3 == 2); + assertOrPanic(-6 % 3 == 0); } test "hex float literal parsing" { - comptime assert(0x1.0 == 1.0); + comptime assertOrPanic(0x1.0 == 1.0); } test "quad hex float literal parsing in range" { @@ -304,7 +304,7 @@ test "quad hex float literal parsing accurate" { // implied 1 is dropped, with an exponent of 0 (0x3fff) after biasing. const expected: u128 = 0x3fff1111222233334444555566667777; - assert(@bitCast(u128, a) == expected); + assertOrPanic(@bitCast(u128, a) == expected); } test "hex float literal within range" { @@ -319,7 +319,7 @@ test "truncating shift left" { } fn testShlTrunc(x: u16) void { const shifted = x << 1; - assert(shifted == 65534); + assertOrPanic(shifted == 65534); } test "truncating shift right" { @@ -328,7 +328,7 @@ test "truncating shift right" { } fn testShrTrunc(x: u16) void { const shifted = x >> 1; - assert(shifted == 32767); + assertOrPanic(shifted == 32767); } test "exact shift left" { @@ -337,7 +337,7 @@ test "exact shift left" { } fn testShlExact(x: u8) void { const shifted = @shlExact(x, 2); - assert(shifted == 0b11010100); + assertOrPanic(shifted == 0b11010100); } test "exact shift right" { @@ -346,22 +346,22 @@ test "exact shift right" { } fn testShrExact(x: u8) void { const shifted = @shrExact(x, 2); - assert(shifted == 0b00101101); + assertOrPanic(shifted == 0b00101101); } test "comptime_int addition" { comptime { - assert(35361831660712422535336160538497375248 + 101752735581729509668353361206450473702 == 137114567242441932203689521744947848950); - assert(594491908217841670578297176641415611445982232488944558774612 + 390603545391089362063884922208143568023166603618446395589768 == 985095453608931032642182098849559179469148836107390954364380); + assertOrPanic(35361831660712422535336160538497375248 + 101752735581729509668353361206450473702 == 137114567242441932203689521744947848950); + assertOrPanic(594491908217841670578297176641415611445982232488944558774612 + 390603545391089362063884922208143568023166603618446395589768 == 985095453608931032642182098849559179469148836107390954364380); } } test "comptime_int multiplication" { comptime { - assert( + assertOrPanic( 45960427431263824329884196484953148229 * 128339149605334697009938835852565949723 == 5898522172026096622534201617172456926982464453350084962781392314016180490567, ); - assert( + assertOrPanic( 594491908217841670578297176641415611445982232488944558774612 * 390603545391089362063884922208143568023166603618446395589768 == 232210647056203049913662402532976186578842425262306016094292237500303028346593132411865381225871291702600263463125370016, ); } @@ -369,7 +369,7 @@ test "comptime_int multiplication" { test "comptime_int shifting" { comptime { - assert((u128(1) << 127) == 0x80000000000000000000000000000000); + assertOrPanic((u128(1) << 127) == 0x80000000000000000000000000000000); } } @@ -377,16 +377,16 @@ test "comptime_int multi-limb shift and mask" { comptime { var a = 0xefffffffa0000001eeeeeeefaaaaaaab; - assert(u32(a & 0xffffffff) == 0xaaaaaaab); + assertOrPanic(u32(a & 0xffffffff) == 0xaaaaaaab); a >>= 32; - assert(u32(a & 0xffffffff) == 0xeeeeeeef); + assertOrPanic(u32(a & 0xffffffff) == 0xeeeeeeef); a >>= 32; - assert(u32(a & 0xffffffff) == 0xa0000001); + assertOrPanic(u32(a & 0xffffffff) == 0xa0000001); a >>= 32; - assert(u32(a & 0xffffffff) == 0xefffffff); + assertOrPanic(u32(a & 0xffffffff) == 0xefffffff); a >>= 32; - assert(a == 0); + assertOrPanic(a == 0); } } @@ -394,7 +394,7 @@ test "comptime_int multi-limb partial shift right" { comptime { var a = 0x1ffffffffeeeeeeee; a >>= 16; - assert(a == 0x1ffffffffeeee); + assertOrPanic(a == 0x1ffffffffeeee); } } @@ -404,23 +404,23 @@ test "xor" { } fn test_xor() void { - assert(0xFF ^ 0x00 == 0xFF); - assert(0xF0 ^ 0x0F == 0xFF); - assert(0xFF ^ 0xF0 == 0x0F); - assert(0xFF ^ 0x0F == 0xF0); - assert(0xFF ^ 0xFF == 0x00); + assertOrPanic(0xFF ^ 0x00 == 0xFF); + assertOrPanic(0xF0 ^ 0x0F == 0xFF); + assertOrPanic(0xFF ^ 0xF0 == 0x0F); + assertOrPanic(0xFF ^ 0x0F == 0xF0); + assertOrPanic(0xFF ^ 0xFF == 0x00); } test "comptime_int xor" { comptime { - assert(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0x00000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - assert(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0x0000000000000000FFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - assert(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x0000000000000000FFFFFFFFFFFFFFFF); - assert(0x0000000000000000FFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFF0000000000000000); - assert(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000000000000000000000000000); - assert(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0x00000000FFFFFFFF00000000FFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); - assert(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000FFFFFFFF00000000FFFFFFFF); - assert(0x00000000FFFFFFFF00000000FFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFF00000000FFFFFFFF00000000); + assertOrPanic(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0x00000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + assertOrPanic(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0x0000000000000000FFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + assertOrPanic(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x0000000000000000FFFFFFFFFFFFFFFF); + assertOrPanic(0x0000000000000000FFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFF0000000000000000); + assertOrPanic(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000000000000000000000000000); + assertOrPanic(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0x00000000FFFFFFFF00000000FFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); + assertOrPanic(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000FFFFFFFF00000000FFFFFFFF); + assertOrPanic(0x00000000FFFFFFFF00000000FFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFF00000000FFFFFFFF00000000); } } @@ -434,23 +434,23 @@ fn make_f128(x: f128) f128 { } fn test_f128() void { - assert(@sizeOf(f128) == 16); - assert(make_f128(1.0) == 1.0); - assert(make_f128(1.0) != 1.1); - assert(make_f128(1.0) > 0.9); - assert(make_f128(1.0) >= 0.9); - assert(make_f128(1.0) >= 1.0); + assertOrPanic(@sizeOf(f128) == 16); + assertOrPanic(make_f128(1.0) == 1.0); + assertOrPanic(make_f128(1.0) != 1.1); + assertOrPanic(make_f128(1.0) > 0.9); + assertOrPanic(make_f128(1.0) >= 0.9); + assertOrPanic(make_f128(1.0) >= 1.0); should_not_be_zero(1.0); } fn should_not_be_zero(x: f128) void { - assert(x != 0.0); + assertOrPanic(x != 0.0); } test "comptime float rem int" { comptime { var x = f32(1) % 2; - assert(x == 1.0); + assertOrPanic(x == 1.0); } } @@ -465,8 +465,8 @@ test "remainder division" { } fn remdiv(comptime T: type) void { - assert(T(1) == T(1) % T(2)); - assert(T(1) == T(7) % T(3)); + assertOrPanic(T(1) == T(1) % T(2)); + assertOrPanic(T(1) == T(7) % T(3)); } test "@sqrt" { @@ -480,19 +480,19 @@ test "@sqrt" { const x = 14.0; const y = x * x; const z = @sqrt(@typeOf(y), y); - comptime assert(z == x); + comptime assertOrPanic(z == x); } fn testSqrt(comptime T: type, x: T) void { - assert(@sqrt(T, x * x) == x); + assertOrPanic(@sqrt(T, x * x) == x); } test "comptime_int param and return" { const a = comptimeAdd(35361831660712422535336160538497375248, 101752735581729509668353361206450473702); - assert(a == 137114567242441932203689521744947848950); + assertOrPanic(a == 137114567242441932203689521744947848950); const b = comptimeAdd(594491908217841670578297176641415611445982232488944558774612, 390603545391089362063884922208143568023166603618446395589768); - assert(b == 985095453608931032642182098849559179469148836107390954364380); + assertOrPanic(b == 985095453608931032642182098849559179469148836107390954364380); } fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int { diff --git a/test/cases/merge_error_sets.zig b/test/stage1/behavior/merge_error_sets.zig similarity index 100% rename from test/cases/merge_error_sets.zig rename to test/stage1/behavior/merge_error_sets.zig diff --git a/test/cases/misc.zig b/test/stage1/behavior/misc.zig similarity index 61% rename from test/cases/misc.zig rename to test/stage1/behavior/misc.zig index 1a34d54e9e15f974b645a4cbfdc6d0d61a63bd0b..8d2555ddddb390f928c6440024cfe008f58f6744 100644 --- a/test/cases/misc.zig +++ b/test/stage1/behavior/misc.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; const cstr = std.cstr; const builtin = @import("builtin"); @@ -26,38 +26,38 @@ test "call disabled extern fn" { } test "@IntType builtin" { - assert(@IntType(true, 8) == i8); - assert(@IntType(true, 16) == i16); - assert(@IntType(true, 32) == i32); - assert(@IntType(true, 64) == i64); + assertOrPanic(@IntType(true, 8) == i8); + assertOrPanic(@IntType(true, 16) == i16); + assertOrPanic(@IntType(true, 32) == i32); + assertOrPanic(@IntType(true, 64) == i64); - assert(@IntType(false, 8) == u8); - assert(@IntType(false, 16) == u16); - assert(@IntType(false, 32) == u32); - assert(@IntType(false, 64) == u64); + assertOrPanic(@IntType(false, 8) == u8); + assertOrPanic(@IntType(false, 16) == u16); + assertOrPanic(@IntType(false, 32) == u32); + assertOrPanic(@IntType(false, 64) == u64); - assert(i8.bit_count == 8); - assert(i16.bit_count == 16); - assert(i32.bit_count == 32); - assert(i64.bit_count == 64); + assertOrPanic(i8.bit_count == 8); + assertOrPanic(i16.bit_count == 16); + assertOrPanic(i32.bit_count == 32); + assertOrPanic(i64.bit_count == 64); - assert(i8.is_signed); - assert(i16.is_signed); - assert(i32.is_signed); - assert(i64.is_signed); - assert(isize.is_signed); + assertOrPanic(i8.is_signed); + assertOrPanic(i16.is_signed); + assertOrPanic(i32.is_signed); + assertOrPanic(i64.is_signed); + assertOrPanic(isize.is_signed); - assert(!u8.is_signed); - assert(!u16.is_signed); - assert(!u32.is_signed); - assert(!u64.is_signed); - assert(!usize.is_signed); + assertOrPanic(!u8.is_signed); + assertOrPanic(!u16.is_signed); + assertOrPanic(!u32.is_signed); + assertOrPanic(!u64.is_signed); + assertOrPanic(!usize.is_signed); } test "floating point primitive bit counts" { - assert(f16.bit_count == 16); - assert(f32.bit_count == 32); - assert(f64.bit_count == 64); + assertOrPanic(f16.bit_count == 16); + assertOrPanic(f32.bit_count == 32); + assertOrPanic(f64.bit_count == 64); } test "short circuit" { @@ -72,7 +72,7 @@ fn testShortCircuit(f: bool, t: bool) void { var hit_4 = f; if (t or x: { - assert(f); + assertOrPanic(f); break :x f; }) { hit_1 = t; @@ -81,31 +81,31 @@ fn testShortCircuit(f: bool, t: bool) void { hit_2 = t; break :x f; }) { - assert(f); + assertOrPanic(f); } if (t and x: { hit_3 = t; break :x f; }) { - assert(f); + assertOrPanic(f); } if (f and x: { - assert(f); + assertOrPanic(f); break :x f; }) { - assert(f); + assertOrPanic(f); } else { hit_4 = t; } - assert(hit_1); - assert(hit_2); - assert(hit_3); - assert(hit_4); + assertOrPanic(hit_1); + assertOrPanic(hit_2); + assertOrPanic(hit_3); + assertOrPanic(hit_4); } test "truncate" { - assert(testTruncate(0x10fd) == 0xfd); + assertOrPanic(testTruncate(0x10fd) == 0xfd); } fn testTruncate(x: u32) u8 { return @truncate(u8, x); @@ -116,16 +116,16 @@ fn first4KeysOfHomeRow() []const u8 { } test "return string from function" { - assert(mem.eql(u8, first4KeysOfHomeRow(), "aoeu")); + assertOrPanic(mem.eql(u8, first4KeysOfHomeRow(), "aoeu")); } const g1: i32 = 1233 + 1; var g2: i32 = 0; test "global variables" { - assert(g2 == 0); + assertOrPanic(g2 == 0); g2 = g1; - assert(g2 == 1234); + assertOrPanic(g2 == 1234); } test "memcpy and memset intrinsics" { @@ -142,7 +142,7 @@ test "builtin static eval" { const x: i32 = comptime x: { break :x 1 + 2 + 3; }; - assert(x == comptime 6); + assertOrPanic(x == comptime 6); } test "slicing" { @@ -163,7 +163,7 @@ test "slicing" { test "constant equal function pointers" { const alias = emptyFn; - assert(comptime x: { + assertOrPanic(comptime x: { break :x emptyFn == alias; }); } @@ -171,25 +171,25 @@ test "constant equal function pointers" { fn emptyFn() void {} test "hex escape" { - assert(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello")); + assertOrPanic(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello")); } test "string concatenation" { - assert(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED")); + assertOrPanic(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED")); } test "array mult operator" { - assert(mem.eql(u8, "ab" ** 5, "ababababab")); + assertOrPanic(mem.eql(u8, "ab" ** 5, "ababababab")); } test "string escapes" { - assert(mem.eql(u8, "\"", "\x22")); - assert(mem.eql(u8, "\'", "\x27")); - assert(mem.eql(u8, "\n", "\x0a")); - assert(mem.eql(u8, "\r", "\x0d")); - assert(mem.eql(u8, "\t", "\x09")); - assert(mem.eql(u8, "\\", "\x5c")); - assert(mem.eql(u8, "\u1234\u0069", "\xe1\x88\xb4\x69")); + assertOrPanic(mem.eql(u8, "\"", "\x22")); + assertOrPanic(mem.eql(u8, "\'", "\x27")); + assertOrPanic(mem.eql(u8, "\n", "\x0a")); + assertOrPanic(mem.eql(u8, "\r", "\x0d")); + assertOrPanic(mem.eql(u8, "\t", "\x09")); + assertOrPanic(mem.eql(u8, "\\", "\x5c")); + assertOrPanic(mem.eql(u8, "\u1234\u0069", "\xe1\x88\xb4\x69")); } test "multiline string" { @@ -199,7 +199,7 @@ test "multiline string" { \\three ; const s2 = "one\ntwo)\nthree"; - assert(mem.eql(u8, s1, s2)); + assertOrPanic(mem.eql(u8, s1, s2)); } test "multiline C string" { @@ -209,11 +209,11 @@ test "multiline C string" { c\\three ; const s2 = c"one\ntwo)\nthree"; - assert(cstr.cmp(s1, s2) == 0); + assertOrPanic(cstr.cmp(s1, s2) == 0); } test "type equality" { - assert(*const u8 != *u8); + assertOrPanic(*const u8 != *u8); } const global_a: i32 = 1234; @@ -221,7 +221,7 @@ const global_b: *const i32 = &global_a; const global_c: *const f32 = @ptrCast(*const f32, global_b); test "compile time global reinterpret" { const d = @ptrCast(*const i32, global_c); - assert(d.* == 1234); + assertOrPanic(d.* == 1234); } test "explicit cast maybe pointers" { @@ -247,8 +247,8 @@ test "cast undefined" { fn testCastUndefined(x: []const u8) void {} test "cast small unsigned to larger signed" { - assert(castSmallUnsignedToLargerSigned1(200) == i16(200)); - assert(castSmallUnsignedToLargerSigned2(9999) == i64(9999)); + assertOrPanic(castSmallUnsignedToLargerSigned1(200) == i16(200)); + assertOrPanic(castSmallUnsignedToLargerSigned2(9999) == i64(9999)); } fn castSmallUnsignedToLargerSigned1(x: u8) i16 { return x; @@ -258,7 +258,7 @@ fn castSmallUnsignedToLargerSigned2(x: u16) i64 { } test "implicit cast after unreachable" { - assert(outer() == 1234); + assertOrPanic(outer() == 1234); } fn inner() i32 { return 1234; @@ -273,13 +273,13 @@ test "pointer dereferencing" { y.* += 1; - assert(x == 4); - assert(y.* == 4); + assertOrPanic(x == 4); + assertOrPanic(y.* == 4); } test "call result of if else expression" { - assert(mem.eql(u8, f2(true), "a")); - assert(mem.eql(u8, f2(false), "b")); + assertOrPanic(mem.eql(u8, f2(true), "a")); + assertOrPanic(mem.eql(u8, f2(false), "b")); } fn f2(x: bool) []const u8 { return (if (x) fA else fB)(); @@ -321,8 +321,8 @@ const test3_bar = Test3Foo{ .Two = 13 }; fn test3_1(f: Test3Foo) void { switch (f) { Test3Foo.Three => |pt| { - assert(pt.x == 3); - assert(pt.y == 4); + assertOrPanic(pt.x == 3); + assertOrPanic(pt.y == 4); }, else => unreachable, } @@ -330,14 +330,14 @@ fn test3_1(f: Test3Foo) void { fn test3_2(f: Test3Foo) void { switch (f) { Test3Foo.Two => |x| { - assert(x == 13); + assertOrPanic(x == 13); }, else => unreachable, } } test "character literals" { - assert('\'' == single_quote); + assertOrPanic('\'' == single_quote); } const single_quote = '\''; @@ -346,13 +346,13 @@ test "take address of parameter" { } fn testTakeAddressOfParameter(f: f32) void { const f_ptr = &f; - assert(f_ptr.* == 12.34); + assertOrPanic(f_ptr.* == 12.34); } test "pointer comparison" { const a = ([]const u8)("a"); const b = &a; - assert(ptrEql(b, b)); + assertOrPanic(ptrEql(b, b)); } fn ptrEql(a: *const []const u8, b: *const []const u8) bool { return a == b; @@ -367,36 +367,31 @@ test "C string concatenation" { { var i: u32 = 0; while (i < len_with_null) : (i += 1) { - assert(a[i] == b[i]); + assertOrPanic(a[i] == b[i]); } } - assert(a[len] == 0); - assert(b[len] == 0); + assertOrPanic(a[len] == 0); + assertOrPanic(b[len] == 0); } test "cast slice to u8 slice" { - assert(@sizeOf(i32) == 4); - var big_thing_array = []i32{ - 1, - 2, - 3, - 4, - }; + assertOrPanic(@sizeOf(i32) == 4); + var big_thing_array = []i32{ 1, 2, 3, 4 }; const big_thing_slice: []i32 = big_thing_array[0..]; const bytes = @sliceToBytes(big_thing_slice); - assert(bytes.len == 4 * 4); + assertOrPanic(bytes.len == 4 * 4); bytes[4] = 0; bytes[5] = 0; bytes[6] = 0; bytes[7] = 0; - assert(big_thing_slice[1] == 0); + assertOrPanic(big_thing_slice[1] == 0); const big_thing_again = @bytesToSlice(i32, bytes); - assert(big_thing_again[2] == 3); + assertOrPanic(big_thing_again[2] == 3); big_thing_again[2] = -1; - assert(bytes[8] == maxInt(u8)); - assert(bytes[9] == maxInt(u8)); - assert(bytes[10] == maxInt(u8)); - assert(bytes[11] == maxInt(u8)); + assertOrPanic(bytes[8] == maxInt(u8)); + assertOrPanic(bytes[9] == maxInt(u8)); + assertOrPanic(bytes[10] == maxInt(u8)); + assertOrPanic(bytes[11] == maxInt(u8)); } test "pointer to void return type" { @@ -413,7 +408,7 @@ fn testPointerToVoidReturnType2() *const void { test "non const ptr to aliased type" { const int = i32; - assert(?*int == ?*i32); + assertOrPanic(?*int == ?*i32); } test "array 2D const double ptr" { @@ -426,8 +421,8 @@ test "array 2D const double ptr" { fn testArray2DConstDoublePtr(ptr: *const f32) void { const ptr2 = @ptrCast([*]const f32, ptr); - assert(ptr2[0] == 1.0); - assert(ptr2[1] == 2.0); + assertOrPanic(ptr2[0] == 1.0); + assertOrPanic(ptr2[1] == 2.0); } const Tid = builtin.TypeId; @@ -449,32 +444,32 @@ const AUnion = union { test "@typeId" { comptime { - assert(@typeId(type) == Tid.Type); - assert(@typeId(void) == Tid.Void); - assert(@typeId(bool) == Tid.Bool); - assert(@typeId(noreturn) == Tid.NoReturn); - assert(@typeId(i8) == Tid.Int); - assert(@typeId(u8) == Tid.Int); - assert(@typeId(i64) == Tid.Int); - assert(@typeId(u64) == Tid.Int); - assert(@typeId(f32) == Tid.Float); - assert(@typeId(f64) == Tid.Float); - assert(@typeId(*f32) == Tid.Pointer); - assert(@typeId([2]u8) == Tid.Array); - assert(@typeId(AStruct) == Tid.Struct); - assert(@typeId(@typeOf(1)) == Tid.ComptimeInt); - assert(@typeId(@typeOf(1.0)) == Tid.ComptimeFloat); - assert(@typeId(@typeOf(undefined)) == Tid.Undefined); - assert(@typeId(@typeOf(null)) == Tid.Null); - assert(@typeId(?i32) == Tid.Optional); - assert(@typeId(anyerror!i32) == Tid.ErrorUnion); - assert(@typeId(anyerror) == Tid.ErrorSet); - assert(@typeId(AnEnum) == Tid.Enum); - assert(@typeId(@typeOf(AUnionEnum.One)) == Tid.Enum); - assert(@typeId(AUnionEnum) == Tid.Union); - assert(@typeId(AUnion) == Tid.Union); - assert(@typeId(fn () void) == Tid.Fn); - assert(@typeId(@typeOf(builtin)) == Tid.Namespace); + assertOrPanic(@typeId(type) == Tid.Type); + assertOrPanic(@typeId(void) == Tid.Void); + assertOrPanic(@typeId(bool) == Tid.Bool); + assertOrPanic(@typeId(noreturn) == Tid.NoReturn); + assertOrPanic(@typeId(i8) == Tid.Int); + assertOrPanic(@typeId(u8) == Tid.Int); + assertOrPanic(@typeId(i64) == Tid.Int); + assertOrPanic(@typeId(u64) == Tid.Int); + assertOrPanic(@typeId(f32) == Tid.Float); + assertOrPanic(@typeId(f64) == Tid.Float); + assertOrPanic(@typeId(*f32) == Tid.Pointer); + assertOrPanic(@typeId([2]u8) == Tid.Array); + assertOrPanic(@typeId(AStruct) == Tid.Struct); + assertOrPanic(@typeId(@typeOf(1)) == Tid.ComptimeInt); + assertOrPanic(@typeId(@typeOf(1.0)) == Tid.ComptimeFloat); + assertOrPanic(@typeId(@typeOf(undefined)) == Tid.Undefined); + assertOrPanic(@typeId(@typeOf(null)) == Tid.Null); + assertOrPanic(@typeId(?i32) == Tid.Optional); + assertOrPanic(@typeId(anyerror!i32) == Tid.ErrorUnion); + assertOrPanic(@typeId(anyerror) == Tid.ErrorSet); + assertOrPanic(@typeId(AnEnum) == Tid.Enum); + assertOrPanic(@typeId(@typeOf(AUnionEnum.One)) == Tid.Enum); + assertOrPanic(@typeId(AUnionEnum) == Tid.Union); + assertOrPanic(@typeId(AUnion) == Tid.Union); + assertOrPanic(@typeId(fn () void) == Tid.Fn); + assertOrPanic(@typeId(@typeOf(builtin)) == Tid.Namespace); // TODO bound fn // TODO arg tuple // TODO opaque @@ -490,13 +485,13 @@ test "@typeName" { Unused, }; comptime { - assert(mem.eql(u8, @typeName(i64), "i64")); - assert(mem.eql(u8, @typeName(*usize), "*usize")); + assertOrPanic(mem.eql(u8, @typeName(i64), "i64")); + assertOrPanic(mem.eql(u8, @typeName(*usize), "*usize")); // https://github.com/ziglang/zig/issues/675 - assert(mem.eql(u8, @typeName(TypeFromFn(u8)), "TypeFromFn(u8)")); - assert(mem.eql(u8, @typeName(Struct), "Struct")); - assert(mem.eql(u8, @typeName(Union), "Union")); - assert(mem.eql(u8, @typeName(Enum), "Enum")); + assertOrPanic(mem.eql(u8, @typeName(TypeFromFn(u8)), "TypeFromFn(u8)")); + assertOrPanic(mem.eql(u8, @typeName(Struct), "Struct")); + assertOrPanic(mem.eql(u8, @typeName(Union), "Union")); + assertOrPanic(mem.eql(u8, @typeName(Enum), "Enum")); } } @@ -504,28 +499,16 @@ fn TypeFromFn(comptime T: type) type { return struct {}; } -test "volatile load and store" { - var number: i32 = 1234; - const ptr = (*volatile i32)(&number); - ptr.* += 1; - assert(ptr.* == 1235); +test "double implicit cast in same expression" { + var x = i32(u16(nine())); + assertOrPanic(x == 9); } - -test "slice string literal has type []const u8" { - comptime { - assert(@typeOf("aoeu"[0..]) == []const u8); - const array = []i32{ - 1, - 2, - 3, - 4, - }; - assert(@typeOf(array[0..]) == []const i32); - } +fn nine() u8 { + return 9; } test "global variable initialized to global variable array element" { - assert(global_ptr == &gdt[0]); + assertOrPanic(global_ptr == &gdt[0]); } const GDTEntry = struct { field: i32, @@ -543,16 +526,12 @@ export fn writeToVRam() void { vram[0] = 'X'; } -test "pointer child field" { - assert((*u32).Child == u32); -} - const OpaqueA = @OpaqueType(); const OpaqueB = @OpaqueType(); test "@OpaqueType" { - assert(*OpaqueA != *OpaqueB); - assert(mem.eql(u8, @typeName(OpaqueA), "OpaqueA")); - assert(mem.eql(u8, @typeName(OpaqueB), "OpaqueB")); + assertOrPanic(*OpaqueA != *OpaqueB); + assertOrPanic(mem.eql(u8, @typeName(OpaqueA), "OpaqueA")); + assertOrPanic(mem.eql(u8, @typeName(OpaqueB), "OpaqueB")); } test "variable is allowed to be a pointer to an opaque type" { @@ -581,25 +560,6 @@ test "implicit comptime while" { } } -test "struct inside function" { - testStructInFn(); - comptime testStructInFn(); -} - -fn testStructInFn() void { - const BlockKind = u32; - - const Block = struct { - kind: BlockKind, - }; - - var block = Block{ .kind = 1234 }; - - block.kind += 1; - - assert(block.kind == 1235); -} - fn fnThatClosesOverLocalConst() type { const c = 1; return struct { @@ -611,7 +571,7 @@ fn fnThatClosesOverLocalConst() type { test "function closes over local const" { const x = fnThatClosesOverLocalConst().g(); - assert(x == 1); + assertOrPanic(x == 1); } test "cold function" { @@ -648,21 +608,21 @@ export fn testPackedStuff(a: *const PackedStruct, b: *const PackedUnion, c: Pack test "slicing zero length array" { const s1 = ""[0..]; const s2 = ([]u32{})[0..]; - assert(s1.len == 0); - assert(s2.len == 0); - assert(mem.eql(u8, s1, "")); - assert(mem.eql(u32, s2, []u32{})); + assertOrPanic(s1.len == 0); + assertOrPanic(s2.len == 0); + assertOrPanic(mem.eql(u8, s1, "")); + assertOrPanic(mem.eql(u32, s2, []u32{})); } const addr1 = @ptrCast(*const u8, emptyFn); test "comptime cast fn to ptr" { const addr2 = @ptrCast(*const u8, emptyFn); - comptime assert(addr1 == addr2); + comptime assertOrPanic(addr1 == addr2); } test "equality compare fn ptrs" { var a = emptyFn; - assert(a == a); + assertOrPanic(a == a); } test "self reference through fn ptr field" { @@ -677,5 +637,51 @@ test "self reference through fn ptr field" { }; var a: S.A = undefined; a.f = S.foo; - assert(a.f(a) == 12); + assertOrPanic(a.f(a) == 12); +} + +test "volatile load and store" { + var number: i32 = 1234; + const ptr = (*volatile i32)(&number); + ptr.* += 1; + assertOrPanic(ptr.* == 1235); +} + +test "slice string literal has type []const u8" { + comptime { + assertOrPanic(@typeOf("aoeu"[0..]) == []const u8); + const array = []i32{ 1, 2, 3, 4 }; + assertOrPanic(@typeOf(array[0..]) == []const i32); + } +} + +test "pointer child field" { + assertOrPanic((*u32).Child == u32); +} + +test "struct inside function" { + testStructInFn(); + comptime testStructInFn(); +} + +fn testStructInFn() void { + const BlockKind = u32; + + const Block = struct { + kind: BlockKind, + }; + + var block = Block{ .kind = 1234 }; + + block.kind += 1; + + assertOrPanic(block.kind == 1235); +} + +test "fn call returning scalar optional in equality expression" { + assertOrPanic(getNull() == null); +} + +fn getNull() ?*i32 { + return null; } diff --git a/test/cases/namespace_depends_on_compile_var/a.zig b/test/stage1/behavior/namespace_depends_on_compile_var/a.zig similarity index 100% rename from test/cases/namespace_depends_on_compile_var/a.zig rename to test/stage1/behavior/namespace_depends_on_compile_var/a.zig diff --git a/test/cases/namespace_depends_on_compile_var/b.zig b/test/stage1/behavior/namespace_depends_on_compile_var/b.zig similarity index 100% rename from test/cases/namespace_depends_on_compile_var/b.zig rename to test/stage1/behavior/namespace_depends_on_compile_var/b.zig diff --git a/test/cases/namespace_depends_on_compile_var/index.zig b/test/stage1/behavior/namespace_depends_on_compile_var/index.zig similarity index 62% rename from test/cases/namespace_depends_on_compile_var/index.zig rename to test/stage1/behavior/namespace_depends_on_compile_var/index.zig index ccc49d9367d83ba4c2e491e5539d7bcd31228cc6..fe3e0cc02093840810993eaacaeb9a1b6d5414f4 100644 --- a/test/cases/namespace_depends_on_compile_var/index.zig +++ b/test/stage1/behavior/namespace_depends_on_compile_var/index.zig @@ -1,11 +1,11 @@ const builtin = @import("builtin"); -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "namespace depends on compile var" { if (some_namespace.a_bool) { - assert(some_namespace.a_bool); + assertOrPanic(some_namespace.a_bool); } else { - assert(!some_namespace.a_bool); + assertOrPanic(!some_namespace.a_bool); } } const some_namespace = switch (builtin.os) { diff --git a/test/cases/new_stack_call.zig b/test/stage1/behavior/new_stack_call.zig similarity index 72% rename from test/cases/new_stack_call.zig rename to test/stage1/behavior/new_stack_call.zig index 5912550d545aa6b8369afcabf8995574d8541616..b9ae2d27cdb773017cd221a5badf73aea1fb10d3 100644 --- a/test/cases/new_stack_call.zig +++ b/test/stage1/behavior/new_stack_call.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; var new_stack_bytes: [1024]u8 = undefined; @@ -10,17 +10,17 @@ test "calling a function with a new stack" { const b = @newStackCall(new_stack_bytes[512..], targetFunction, arg); _ = targetFunction(arg); - assert(arg == 1234); - assert(a < b); + assertOrPanic(arg == 1234); + assertOrPanic(a < b); } fn targetFunction(x: i32) usize { - assert(x == 1234); + assertOrPanic(x == 1234); var local_variable: i32 = 42; const ptr = &local_variable; ptr.* += 1; - assert(local_variable == 43); + assertOrPanic(local_variable == 43); return @ptrToInt(ptr); } diff --git a/test/cases/null.zig b/test/stage1/behavior/null.zig similarity index 80% rename from test/cases/null.zig rename to test/stage1/behavior/null.zig index 825db88b1e58cc1a2f7b9ee26b634d1e0da6798b..e2f86a05ba442a2bf0469e47e49fb616282d9d4a 100644 --- a/test/cases/null.zig +++ b/test/stage1/behavior/null.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "optional type" { const x: ?bool = true; @@ -17,13 +17,13 @@ test "optional type" { const z = next_x orelse 1234; - assert(z == 1234); + assertOrPanic(z == 1234); const final_x: ?i32 = 13; const num = final_x orelse unreachable; - assert(num == 13); + assertOrPanic(num == 13); } test "test maybe object and get a pointer to the inner value" { @@ -33,7 +33,7 @@ test "test maybe object and get a pointer to the inner value" { b.* = false; } - assert(maybe_bool.? == false); + assertOrPanic(maybe_bool.? == false); } test "rhs maybe unwrap return" { @@ -47,9 +47,9 @@ test "maybe return" { } fn maybeReturnImpl() void { - assert(foo(1235).?); + assertOrPanic(foo(1235).?); if (foo(null) != null) unreachable; - assert(!foo(1234).?); + assertOrPanic(!foo(1234).?); } fn foo(x: ?i32) ?bool { @@ -58,7 +58,7 @@ fn foo(x: ?i32) ?bool { } test "if var maybe pointer" { - assert(shouldBeAPlus1(Particle{ + assertOrPanic(shouldBeAPlus1(Particle{ .a = 14, .b = 1, .c = 1, @@ -84,10 +84,10 @@ const Particle = struct { test "null literal outside function" { const is_null = here_is_a_null_literal.context == null; - assert(is_null); + assertOrPanic(is_null); const is_non_null = here_is_a_null_literal.context != null; - assert(!is_non_null); + assertOrPanic(!is_non_null); } const SillyStruct = struct { context: ?i32, @@ -98,8 +98,8 @@ test "test null runtime" { testTestNullRuntime(null); } fn testTestNullRuntime(x: ?i32) void { - assert(x == null); - assert(!(x != null)); + assertOrPanic(x == null); + assertOrPanic(!(x != null)); } test "optional void" { @@ -108,8 +108,8 @@ test "optional void" { } fn optionalVoidImpl() void { - assert(bar(null) == null); - assert(bar({}) != null); + assertOrPanic(bar(null) == null); + assertOrPanic(bar({}) != null); } fn bar(x: ?void) ?void { @@ -133,7 +133,7 @@ test "unwrap optional which is field of global var" { } struct_with_optional.field = 1234; if (struct_with_optional.field) |payload| { - assert(payload == 1234); + assertOrPanic(payload == 1234); } else { unreachable; } @@ -141,13 +141,13 @@ test "unwrap optional which is field of global var" { test "null with default unwrap" { const x: i32 = null orelse 1; - assert(x == 1); + assertOrPanic(x == 1); } test "optional types" { comptime { const opt_type_struct = StructWithOptionalType{ .t = u8 }; - assert(opt_type_struct.t != null and opt_type_struct.t.? == u8); + assertOrPanic(opt_type_struct.t != null and opt_type_struct.t.? == u8); } } @@ -158,5 +158,5 @@ const StructWithOptionalType = struct { test "optional pointer to 0 bit type null value at runtime" { const EmptyStruct = struct {}; var x: ?*EmptyStruct = null; - assert(x == null); + assertOrPanic(x == null); } diff --git a/test/stage1/behavior/optional.zig b/test/stage1/behavior/optional.zig new file mode 100644 index 0000000000000000000000000000000000000000..14692cb1ea1b901922822b9c7f868c3ba2356345 --- /dev/null +++ b/test/stage1/behavior/optional.zig @@ -0,0 +1,81 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +pub const EmptyStruct = struct {}; + +test "optional pointer to size zero struct" { + var e = EmptyStruct{}; + var o: ?*EmptyStruct = &e; + assertOrPanic(o != null); +} + +test "equality compare nullable pointers" { + testNullPtrsEql(); + comptime testNullPtrsEql(); +} + +fn testNullPtrsEql() void { + var number: i32 = 1234; + + var x: ?*i32 = null; + var y: ?*i32 = null; + assertOrPanic(x == y); + y = &number; + assertOrPanic(x != y); + assertOrPanic(x != &number); + assertOrPanic(&number != x); + x = &number; + assertOrPanic(x == y); + assertOrPanic(x == &number); + assertOrPanic(&number == x); +} + +test "address of unwrap optional" { + const S = struct { + const Foo = struct { + a: i32, + }; + + var global: ?Foo = null; + + pub fn getFoo() anyerror!*Foo { + return &global.?; + } + }; + S.global = S.Foo{ .a = 1234 }; + const foo = S.getFoo() catch unreachable; + assertOrPanic(foo.a == 1234); +} + +test "passing an optional integer as a parameter" { + const S = struct { + fn entry() bool { + var x: i32 = 1234; + return foo(x); + } + + fn foo(x: ?i32) bool { + return x.? == 1234; + } + }; + assertOrPanic(S.entry()); + comptime assertOrPanic(S.entry()); +} + +test "unwrap function call with optional pointer return value" { + const S = struct { + fn entry() void { + assertOrPanic(foo().?.* == 1234); + assertOrPanic(bar() == null); + } + const global: i32 = 1234; + fn foo() ?*const i32 { + return &global; + } + fn bar() ?*i32 { + return null; + } + }; + S.entry(); + // TODO https://github.com/ziglang/zig/issues/1901 + //comptime S.entry(); +} diff --git a/test/cases/pointers.zig b/test/stage1/behavior/pointers.zig similarity index 50% rename from test/cases/pointers.zig rename to test/stage1/behavior/pointers.zig index 47afb60a2efd90df70caa936fc957979a12a0033..1142d89ab5576746ef57667357de59931fcb97f9 100644 --- a/test/cases/pointers.zig +++ b/test/stage1/behavior/pointers.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; test "dereference pointer" { comptime testDerefPtr(); @@ -10,33 +10,33 @@ fn testDerefPtr() void { var x: i32 = 1234; var y = &x; y.* += 1; - assert(x == 1235); + assertOrPanic(x == 1235); } test "pointer arithmetic" { var ptr = c"abcd"; - assert(ptr[0] == 'a'); + assertOrPanic(ptr[0] == 'a'); ptr += 1; - assert(ptr[0] == 'b'); + assertOrPanic(ptr[0] == 'b'); ptr += 1; - assert(ptr[0] == 'c'); + assertOrPanic(ptr[0] == 'c'); ptr += 1; - assert(ptr[0] == 'd'); + assertOrPanic(ptr[0] == 'd'); ptr += 1; - assert(ptr[0] == 0); + assertOrPanic(ptr[0] == 0); ptr -= 1; - assert(ptr[0] == 'd'); + assertOrPanic(ptr[0] == 'd'); ptr -= 1; - assert(ptr[0] == 'c'); + assertOrPanic(ptr[0] == 'c'); ptr -= 1; - assert(ptr[0] == 'b'); + assertOrPanic(ptr[0] == 'b'); ptr -= 1; - assert(ptr[0] == 'a'); + assertOrPanic(ptr[0] == 'a'); } test "double pointer parsing" { - comptime assert(PtrOf(PtrOf(i32)) == **i32); + comptime assertOrPanic(PtrOf(PtrOf(i32)) == **i32); } fn PtrOf(comptime T: type) type { diff --git a/test/stage1/behavior/popcount.zig b/test/stage1/behavior/popcount.zig new file mode 100644 index 0000000000000000000000000000000000000000..f7f8bb523bf4b218df8140b5220d443e97b91de1 --- /dev/null +++ b/test/stage1/behavior/popcount.zig @@ -0,0 +1,25 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; + +test "@popCount" { + comptime testPopCount(); + testPopCount(); +} + +fn testPopCount() void { + { + var x: u32 = 0xaa; + assertOrPanic(@popCount(x) == 4); + } + { + var x: u32 = 0xaaaaaaaa; + assertOrPanic(@popCount(x) == 16); + } + { + var x: i16 = -1; + assertOrPanic(@popCount(x) == 16); + } + comptime { + assertOrPanic(@popCount(0b11111111000110001100010000100001000011000011100101010001) == 24); + } +} + diff --git a/test/cases/ptrcast.zig b/test/stage1/behavior/ptrcast.zig similarity index 100% rename from test/cases/ptrcast.zig rename to test/stage1/behavior/ptrcast.zig diff --git a/test/cases/pub_enum/index.zig b/test/stage1/behavior/pub_enum/index.zig similarity index 54% rename from test/cases/pub_enum/index.zig rename to test/stage1/behavior/pub_enum/index.zig index 7fdd07b8a3846fb23141d1821acd8ea35e2c4969..181113f6bf1922cf635a675b1d660efabf4ab358 100644 --- a/test/cases/pub_enum/index.zig +++ b/test/stage1/behavior/pub_enum/index.zig @@ -1,13 +1,13 @@ const other = @import("other.zig"); -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "pub enum" { pubEnumTest(other.APubEnum.Two); } fn pubEnumTest(foo: other.APubEnum) void { - assert(foo == other.APubEnum.Two); + assertOrPanic(foo == other.APubEnum.Two); } test "cast with imported symbol" { - assert(other.size_t(42) == 42); + assertOrPanic(other.size_t(42) == 42); } diff --git a/test/cases/pub_enum/other.zig b/test/stage1/behavior/pub_enum/other.zig similarity index 100% rename from test/cases/pub_enum/other.zig rename to test/stage1/behavior/pub_enum/other.zig diff --git a/test/cases/ref_var_in_if_after_if_2nd_switch_prong.zig b/test/stage1/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig similarity index 77% rename from test/cases/ref_var_in_if_after_if_2nd_switch_prong.zig rename to test/stage1/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig index 3c94bb0d49bfa5c060eb6bafa152cf227f63eb7f..acbe6b245978a018a846f77b9fdcb976b77cfee7 100644 --- a/test/cases/ref_var_in_if_after_if_2nd_switch_prong.zig +++ b/test/stage1/behavior/ref_var_in_if_after_if_2nd_switch_prong.zig @@ -1,14 +1,14 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const mem = @import("std").mem; var ok: bool = false; test "reference a variable in an if after an if in the 2nd switch prong" { foo(true, Num.Two, false, "aoeu"); - assert(!ok); + assertOrPanic(!ok); foo(false, Num.One, false, "aoeu"); - assert(!ok); + assertOrPanic(!ok); foo(true, Num.One, false, "aoeu"); - assert(ok); + assertOrPanic(ok); } const Num = enum { @@ -32,6 +32,6 @@ fn foo(c: bool, k: Num, c2: bool, b: []const u8) void { } fn a(x: []const u8) void { - assert(mem.eql(u8, x, "aoeu")); + assertOrPanic(mem.eql(u8, x, "aoeu")); ok = true; } diff --git a/test/stage1/behavior/reflection.zig b/test/stage1/behavior/reflection.zig new file mode 100644 index 0000000000000000000000000000000000000000..f4c142e0f75576884c6e259bd67c9fc640b9edd2 --- /dev/null +++ b/test/stage1/behavior/reflection.zig @@ -0,0 +1,96 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; +const mem = @import("std").mem; +const reflection = @This(); + +test "reflection: array, pointer, optional, error union type child" { + comptime { + assertOrPanic(([10]u8).Child == u8); + assertOrPanic((*u8).Child == u8); + assertOrPanic((anyerror!u8).Payload == u8); + assertOrPanic((?u8).Child == u8); + } +} + +test "reflection: function return type, var args, and param types" { + comptime { + assertOrPanic(@typeOf(dummy).ReturnType == i32); + assertOrPanic(!@typeOf(dummy).is_var_args); + assertOrPanic(@typeOf(dummy_varargs).is_var_args); + assertOrPanic(@typeOf(dummy).arg_count == 3); + assertOrPanic(@ArgType(@typeOf(dummy), 0) == bool); + assertOrPanic(@ArgType(@typeOf(dummy), 1) == i32); + assertOrPanic(@ArgType(@typeOf(dummy), 2) == f32); + } +} + +fn dummy(a: bool, b: i32, c: f32) i32 { + return 1234; +} +fn dummy_varargs(args: ...) void {} + +test "reflection: struct member types and names" { + comptime { + assertOrPanic(@memberCount(Foo) == 3); + + assertOrPanic(@memberType(Foo, 0) == i32); + assertOrPanic(@memberType(Foo, 1) == bool); + assertOrPanic(@memberType(Foo, 2) == void); + + assertOrPanic(mem.eql(u8, @memberName(Foo, 0), "one")); + assertOrPanic(mem.eql(u8, @memberName(Foo, 1), "two")); + assertOrPanic(mem.eql(u8, @memberName(Foo, 2), "three")); + } +} + +test "reflection: enum member types and names" { + comptime { + assertOrPanic(@memberCount(Bar) == 4); + + assertOrPanic(@memberType(Bar, 0) == void); + assertOrPanic(@memberType(Bar, 1) == i32); + assertOrPanic(@memberType(Bar, 2) == bool); + assertOrPanic(@memberType(Bar, 3) == f64); + + assertOrPanic(mem.eql(u8, @memberName(Bar, 0), "One")); + assertOrPanic(mem.eql(u8, @memberName(Bar, 1), "Two")); + assertOrPanic(mem.eql(u8, @memberName(Bar, 2), "Three")); + assertOrPanic(mem.eql(u8, @memberName(Bar, 3), "Four")); + } +} + +test "reflection: @field" { + var f = Foo{ + .one = 42, + .two = true, + .three = void{}, + }; + + assertOrPanic(f.one == f.one); + assertOrPanic(@field(f, "o" ++ "ne") == f.one); + assertOrPanic(@field(f, "t" ++ "wo") == f.two); + assertOrPanic(@field(f, "th" ++ "ree") == f.three); + assertOrPanic(@field(Foo, "const" ++ "ant") == Foo.constant); + assertOrPanic(@field(Bar, "O" ++ "ne") == Bar.One); + assertOrPanic(@field(Bar, "T" ++ "wo") == Bar.Two); + assertOrPanic(@field(Bar, "Th" ++ "ree") == Bar.Three); + assertOrPanic(@field(Bar, "F" ++ "our") == Bar.Four); + assertOrPanic(@field(reflection, "dum" ++ "my")(true, 1, 2) == dummy(true, 1, 2)); + @field(f, "o" ++ "ne") = 4; + assertOrPanic(f.one == 4); +} + +const Foo = struct { + const constant = 52; + + one: i32, + two: bool, + three: void, +}; + +const Bar = union(enum) { + One: void, + Two: i32, + Three: bool, + Four: f64, +}; + diff --git a/test/stage1/behavior/sizeof_and_typeof.zig b/test/stage1/behavior/sizeof_and_typeof.zig new file mode 100644 index 0000000000000000000000000000000000000000..ddaea4c24299dd75828a27861205e0a5568d55cb --- /dev/null +++ b/test/stage1/behavior/sizeof_and_typeof.zig @@ -0,0 +1,69 @@ +const builtin = @import("builtin"); +const assertOrPanic = @import("std").debug.assertOrPanic; + +test "@sizeOf and @typeOf" { + const y: @typeOf(x) = 120; + assertOrPanic(@sizeOf(@typeOf(y)) == 2); +} +const x: u16 = 13; +const z: @typeOf(x) = 19; + +const A = struct { + a: u8, + b: u32, + c: u8, + d: u3, + e: u5, + f: u16, + g: u16, +}; + +const P = packed struct { + a: u8, + b: u32, + c: u8, + d: u3, + e: u5, + f: u16, + g: u16, +}; + +test "@byteOffsetOf" { + // Packed structs have fixed memory layout + assertOrPanic(@byteOffsetOf(P, "a") == 0); + assertOrPanic(@byteOffsetOf(P, "b") == 1); + assertOrPanic(@byteOffsetOf(P, "c") == 5); + assertOrPanic(@byteOffsetOf(P, "d") == 6); + assertOrPanic(@byteOffsetOf(P, "e") == 6); + assertOrPanic(@byteOffsetOf(P, "f") == 7); + assertOrPanic(@byteOffsetOf(P, "g") == 9); + + // Normal struct fields can be moved/padded + var a: A = undefined; + assertOrPanic(@ptrToInt(&a.a) - @ptrToInt(&a) == @byteOffsetOf(A, "a")); + assertOrPanic(@ptrToInt(&a.b) - @ptrToInt(&a) == @byteOffsetOf(A, "b")); + assertOrPanic(@ptrToInt(&a.c) - @ptrToInt(&a) == @byteOffsetOf(A, "c")); + assertOrPanic(@ptrToInt(&a.d) - @ptrToInt(&a) == @byteOffsetOf(A, "d")); + assertOrPanic(@ptrToInt(&a.e) - @ptrToInt(&a) == @byteOffsetOf(A, "e")); + assertOrPanic(@ptrToInt(&a.f) - @ptrToInt(&a) == @byteOffsetOf(A, "f")); + assertOrPanic(@ptrToInt(&a.g) - @ptrToInt(&a) == @byteOffsetOf(A, "g")); +} + +test "@bitOffsetOf" { + // Packed structs have fixed memory layout + assertOrPanic(@bitOffsetOf(P, "a") == 0); + assertOrPanic(@bitOffsetOf(P, "b") == 8); + assertOrPanic(@bitOffsetOf(P, "c") == 40); + assertOrPanic(@bitOffsetOf(P, "d") == 48); + assertOrPanic(@bitOffsetOf(P, "e") == 51); + assertOrPanic(@bitOffsetOf(P, "f") == 56); + assertOrPanic(@bitOffsetOf(P, "g") == 72); + + assertOrPanic(@byteOffsetOf(A, "a") * 8 == @bitOffsetOf(A, "a")); + assertOrPanic(@byteOffsetOf(A, "b") * 8 == @bitOffsetOf(A, "b")); + assertOrPanic(@byteOffsetOf(A, "c") * 8 == @bitOffsetOf(A, "c")); + assertOrPanic(@byteOffsetOf(A, "d") * 8 == @bitOffsetOf(A, "d")); + assertOrPanic(@byteOffsetOf(A, "e") * 8 == @bitOffsetOf(A, "e")); + assertOrPanic(@byteOffsetOf(A, "f") * 8 == @bitOffsetOf(A, "f")); + assertOrPanic(@byteOffsetOf(A, "g") * 8 == @bitOffsetOf(A, "g")); +} diff --git a/test/cases/slice.zig b/test/stage1/behavior/slice.zig similarity index 62% rename from test/cases/slice.zig rename to test/stage1/behavior/slice.zig index b4b43bdd1933b2053a0325225fca88324372e6a1..cc29e43485b2848c53e82d8963802b26aa04d719 100644 --- a/test/cases/slice.zig +++ b/test/stage1/behavior/slice.zig @@ -1,20 +1,20 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const mem = @import("std").mem; const x = @intToPtr([*]i32, 0x1000)[0..0x500]; const y = x[0x100..]; test "compile time slice of pointer to hard coded address" { - assert(@ptrToInt(x.ptr) == 0x1000); - assert(x.len == 0x500); + assertOrPanic(@ptrToInt(x.ptr) == 0x1000); + assertOrPanic(x.len == 0x500); - assert(@ptrToInt(y.ptr) == 0x1100); - assert(y.len == 0x400); + assertOrPanic(@ptrToInt(y.ptr) == 0x1100); + assertOrPanic(y.len == 0x400); } test "slice child property" { var array: [5]i32 = undefined; var slice = array[0..]; - assert(@typeOf(slice).Child == i32); + assertOrPanic(@typeOf(slice).Child == i32); } test "runtime safety lets us slice from len..len" { @@ -23,7 +23,7 @@ test "runtime safety lets us slice from len..len" { 2, 3, }; - assert(mem.eql(u8, sliceFromLenToLen(an_array[0..], 3, 3), "")); + assertOrPanic(mem.eql(u8, sliceFromLenToLen(an_array[0..], 3, 3), "")); } fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 { @@ -36,5 +36,5 @@ test "implicitly cast array of size 0 to slice" { } fn assertLenIsZero(msg: []const u8) void { - assert(msg.len == 0); + assertOrPanic(msg.len == 0); } diff --git a/test/cases/struct.zig b/test/stage1/behavior/struct.zig similarity index 64% rename from test/cases/struct.zig rename to test/stage1/behavior/struct.zig index bbbd21912c324a70dd25b34fd6f7dcb409599aa6..92ae2baa152d5f9f4bffbf67d8402a6ea58899cf 100644 --- a/test/cases/struct.zig +++ b/test/stage1/behavior/struct.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const builtin = @import("builtin"); const maxInt = std.math.maxInt; @@ -12,7 +12,7 @@ const empty_global_instance = StructWithNoFields{}; test "call struct static method" { const result = StructWithNoFields.add(3, 4); - assert(result == 7); + assertOrPanic(result == 7); } test "return empty struct instance" { @@ -24,8 +24,8 @@ fn returnEmptyStructInstance() StructWithNoFields { const should_be_11 = StructWithNoFields.add(5, 6); -test "invake static method in global scope" { - assert(should_be_11 == 11); +test "invoke static method in global scope" { + assertOrPanic(should_be_11 == 11); } test "void struct fields" { @@ -34,8 +34,8 @@ test "void struct fields" { .b = 1, .c = void{}, }; - assert(foo.b == 1); - assert(@sizeOf(VoidStructFieldsFoo) == 4); + assertOrPanic(foo.b == 1); + assertOrPanic(@sizeOf(VoidStructFieldsFoo) == 4); } const VoidStructFieldsFoo = struct { a: void, @@ -50,7 +50,7 @@ test "structs" { foo.b = foo.a == 1; testFoo(foo); testMutation(&foo); - assert(foo.c == 100); + assertOrPanic(foo.c == 100); } const StructFoo = struct { a: i32, @@ -58,7 +58,7 @@ const StructFoo = struct { c: f32, }; fn testFoo(foo: StructFoo) void { - assert(foo.b); + assertOrPanic(foo.b); } fn testMutation(foo: *StructFoo) void { foo.c = 100; @@ -83,7 +83,7 @@ test "struct point to self" { root.next = &node; - assert(node.next.next.next.val.x == 1); + assertOrPanic(node.next.next.next.val.x == 1); } test "struct byval assign" { @@ -92,18 +92,18 @@ test "struct byval assign" { foo1.a = 1234; foo2.a = 0; - assert(foo2.a == 0); + assertOrPanic(foo2.a == 0); foo2 = foo1; - assert(foo2.a == 1234); + assertOrPanic(foo2.a == 1234); } fn structInitializer() void { const val = Val{ .x = 42 }; - assert(val.x == 42); + assertOrPanic(val.x == 42); } test "fn call of struct field" { - assert(callStructField(Foo{ .ptr = aFunc }) == 13); + assertOrPanic(callStructField(Foo{ .ptr = aFunc }) == 13); } const Foo = struct { @@ -122,7 +122,7 @@ test "store member function in variable" { const instance = MemberFnTestFoo{ .x = 1234 }; const memberFn = MemberFnTestFoo.member; const result = memberFn(instance); - assert(result == 1234); + assertOrPanic(result == 1234); } const MemberFnTestFoo = struct { x: i32, @@ -134,12 +134,12 @@ const MemberFnTestFoo = struct { test "call member function directly" { const instance = MemberFnTestFoo{ .x = 1234 }; const result = MemberFnTestFoo.member(instance); - assert(result == 1234); + assertOrPanic(result == 1234); } test "member functions" { const r = MemberFnRand{ .seed = 1234 }; - assert(r.getSeed() == 1234); + assertOrPanic(r.getSeed() == 1234); } const MemberFnRand = struct { seed: u32, @@ -150,7 +150,7 @@ const MemberFnRand = struct { test "return struct byval from function" { const bar = makeBar(1234, 5678); - assert(bar.y == 5678); + assertOrPanic(bar.y == 5678); } const Bar = struct { x: i32, @@ -165,7 +165,7 @@ fn makeBar(x: i32, y: i32) Bar { test "empty struct method call" { const es = EmptyStruct{}; - assert(es.method() == 1234); + assertOrPanic(es.method() == 1234); } const EmptyStruct = struct { fn method(es: *const EmptyStruct) i32 { @@ -182,7 +182,7 @@ fn testReturnEmptyStructFromFn() EmptyStruct2 { } test "pass slice of empty struct to fn" { - assert(testPassSliceOfEmptyStructToFn([]EmptyStruct2{EmptyStruct2{}}) == 1); + assertOrPanic(testPassSliceOfEmptyStructToFn([]EmptyStruct2{EmptyStruct2{}}) == 1); } fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) usize { return slice.len; @@ -200,7 +200,7 @@ test "packed struct" { }; foo.y += 1; const four = foo.x + foo.y; - assert(four == 4); + assertOrPanic(four == 4); } const BitField1 = packed struct { @@ -217,17 +217,17 @@ const bit_field_1 = BitField1{ test "bit field access" { var data = bit_field_1; - assert(getA(&data) == 1); - assert(getB(&data) == 2); - assert(getC(&data) == 3); - comptime assert(@sizeOf(BitField1) == 1); + assertOrPanic(getA(&data) == 1); + assertOrPanic(getB(&data) == 2); + assertOrPanic(getC(&data) == 3); + comptime assertOrPanic(@sizeOf(BitField1) == 1); data.b += 1; - assert(data.b == 3); + assertOrPanic(data.b == 3); data.a += 1; - assert(data.a == 2); - assert(data.b == 3); + assertOrPanic(data.a == 2); + assertOrPanic(data.b == 3); } fn getA(data: *const BitField1) u3 { @@ -254,8 +254,8 @@ const Foo96Bits = packed struct { test "packed struct 24bits" { comptime { - assert(@sizeOf(Foo24Bits) == 3); - assert(@sizeOf(Foo96Bits) == 12); + assertOrPanic(@sizeOf(Foo24Bits) == 3); + assertOrPanic(@sizeOf(Foo96Bits) == 12); } var value = Foo96Bits{ @@ -265,28 +265,28 @@ test "packed struct 24bits" { .d = 0, }; value.a += 1; - assert(value.a == 1); - assert(value.b == 0); - assert(value.c == 0); - assert(value.d == 0); + assertOrPanic(value.a == 1); + assertOrPanic(value.b == 0); + assertOrPanic(value.c == 0); + assertOrPanic(value.d == 0); value.b += 1; - assert(value.a == 1); - assert(value.b == 1); - assert(value.c == 0); - assert(value.d == 0); + assertOrPanic(value.a == 1); + assertOrPanic(value.b == 1); + assertOrPanic(value.c == 0); + assertOrPanic(value.d == 0); value.c += 1; - assert(value.a == 1); - assert(value.b == 1); - assert(value.c == 1); - assert(value.d == 0); + assertOrPanic(value.a == 1); + assertOrPanic(value.b == 1); + assertOrPanic(value.c == 1); + assertOrPanic(value.d == 0); value.d += 1; - assert(value.a == 1); - assert(value.b == 1); - assert(value.c == 1); - assert(value.d == 1); + assertOrPanic(value.a == 1); + assertOrPanic(value.b == 1); + assertOrPanic(value.c == 1); + assertOrPanic(value.d == 1); } const FooArray24Bits = packed struct { @@ -297,43 +297,43 @@ const FooArray24Bits = packed struct { test "packed array 24bits" { comptime { - assert(@sizeOf([9]Foo24Bits) == 9 * 3); - assert(@sizeOf(FooArray24Bits) == 2 + 2 * 3 + 2); + assertOrPanic(@sizeOf([9]Foo24Bits) == 9 * 3); + assertOrPanic(@sizeOf(FooArray24Bits) == 2 + 2 * 3 + 2); } var bytes = []u8{0} ** (@sizeOf(FooArray24Bits) + 1); bytes[bytes.len - 1] = 0xaa; const ptr = &@bytesToSlice(FooArray24Bits, bytes[0 .. bytes.len - 1])[0]; - assert(ptr.a == 0); - assert(ptr.b[0].field == 0); - assert(ptr.b[1].field == 0); - assert(ptr.c == 0); + assertOrPanic(ptr.a == 0); + assertOrPanic(ptr.b[0].field == 0); + assertOrPanic(ptr.b[1].field == 0); + assertOrPanic(ptr.c == 0); ptr.a = maxInt(u16); - assert(ptr.a == maxInt(u16)); - assert(ptr.b[0].field == 0); - assert(ptr.b[1].field == 0); - assert(ptr.c == 0); + assertOrPanic(ptr.a == maxInt(u16)); + assertOrPanic(ptr.b[0].field == 0); + assertOrPanic(ptr.b[1].field == 0); + assertOrPanic(ptr.c == 0); ptr.b[0].field = maxInt(u24); - assert(ptr.a == maxInt(u16)); - assert(ptr.b[0].field == maxInt(u24)); - assert(ptr.b[1].field == 0); - assert(ptr.c == 0); + assertOrPanic(ptr.a == maxInt(u16)); + assertOrPanic(ptr.b[0].field == maxInt(u24)); + assertOrPanic(ptr.b[1].field == 0); + assertOrPanic(ptr.c == 0); ptr.b[1].field = maxInt(u24); - assert(ptr.a == maxInt(u16)); - assert(ptr.b[0].field == maxInt(u24)); - assert(ptr.b[1].field == maxInt(u24)); - assert(ptr.c == 0); + assertOrPanic(ptr.a == maxInt(u16)); + assertOrPanic(ptr.b[0].field == maxInt(u24)); + assertOrPanic(ptr.b[1].field == maxInt(u24)); + assertOrPanic(ptr.c == 0); ptr.c = maxInt(u16); - assert(ptr.a == maxInt(u16)); - assert(ptr.b[0].field == maxInt(u24)); - assert(ptr.b[1].field == maxInt(u24)); - assert(ptr.c == maxInt(u16)); + assertOrPanic(ptr.a == maxInt(u16)); + assertOrPanic(ptr.b[0].field == maxInt(u24)); + assertOrPanic(ptr.b[1].field == maxInt(u24)); + assertOrPanic(ptr.c == maxInt(u16)); - assert(bytes[bytes.len - 1] == 0xaa); + assertOrPanic(bytes[bytes.len - 1] == 0xaa); } const FooStructAligned = packed struct { @@ -347,17 +347,17 @@ const FooArrayOfAligned = packed struct { test "aligned array of packed struct" { comptime { - assert(@sizeOf(FooStructAligned) == 2); - assert(@sizeOf(FooArrayOfAligned) == 2 * 2); + assertOrPanic(@sizeOf(FooStructAligned) == 2); + assertOrPanic(@sizeOf(FooArrayOfAligned) == 2 * 2); } var bytes = []u8{0xbb} ** @sizeOf(FooArrayOfAligned); const ptr = &@bytesToSlice(FooArrayOfAligned, bytes[0..bytes.len])[0]; - assert(ptr.a[0].a == 0xbb); - assert(ptr.a[0].b == 0xbb); - assert(ptr.a[1].a == 0xbb); - assert(ptr.a[1].b == 0xbb); + assertOrPanic(ptr.a[0].a == 0xbb); + assertOrPanic(ptr.a[0].b == 0xbb); + assertOrPanic(ptr.a[1].a == 0xbb); + assertOrPanic(ptr.a[1].b == 0xbb); } test "runtime struct initialization of bitfield" { @@ -370,10 +370,10 @@ test "runtime struct initialization of bitfield" { .y = @intCast(u4, x2), }; - assert(s1.x == x1); - assert(s1.y == x1); - assert(s2.x == @intCast(u4, x2)); - assert(s2.y == @intCast(u4, x2)); + assertOrPanic(s1.x == x1); + assertOrPanic(s1.y == x1); + assertOrPanic(s2.x == @intCast(u4, x2)); + assertOrPanic(s2.y == @intCast(u4, x2)); } var x1 = u4(1); @@ -400,18 +400,18 @@ test "native bit field understands endianness" { @memcpy(bytes[0..].ptr, @ptrCast([*]u8, &all), 8); var bitfields = @ptrCast(*Bitfields, bytes[0..].ptr).*; - assert(bitfields.f1 == 0x1111); - assert(bitfields.f2 == 0x2222); - assert(bitfields.f3 == 0x33); - assert(bitfields.f4 == 0x44); - assert(bitfields.f5 == 0x5); - assert(bitfields.f6 == 0x6); - assert(bitfields.f7 == 0x77); + assertOrPanic(bitfields.f1 == 0x1111); + assertOrPanic(bitfields.f2 == 0x2222); + assertOrPanic(bitfields.f3 == 0x33); + assertOrPanic(bitfields.f4 == 0x44); + assertOrPanic(bitfields.f5 == 0x5); + assertOrPanic(bitfields.f6 == 0x6); + assertOrPanic(bitfields.f7 == 0x77); } test "align 1 field before self referential align 8 field as slice return type" { const result = alloc(Expr); - assert(result.len == 0); + assertOrPanic(result.len == 0); } const Expr = union(enum) { @@ -434,10 +434,10 @@ test "call method with mutable reference to struct with no fields" { }; var s = S{}; - assert(S.doC(&s)); - assert(s.doC()); - assert(S.do(&s)); - assert(s.do()); + assertOrPanic(S.doC(&s)); + assertOrPanic(s.doC()); + assertOrPanic(S.do(&s)); + assertOrPanic(s.do()); } test "implicit cast packed struct field to const ptr" { @@ -453,7 +453,7 @@ test "implicit cast packed struct field to const ptr" { var lup: LevelUpMove = undefined; lup.level = 12; const res = LevelUpMove.toInt(lup.level); - assert(res == 12); + assertOrPanic(res == 12); } test "pointer to packed struct member in a stack variable" { @@ -464,7 +464,7 @@ test "pointer to packed struct member in a stack variable" { var s = S{ .a = 2, .b = 0 }; var b_ptr = &s.b; - assert(s.b == 0); + assertOrPanic(s.b == 0); b_ptr.* = 2; - assert(s.b == 2); + assertOrPanic(s.b == 2); } diff --git a/test/cases/struct_contains_null_ptr_itself.zig b/test/stage1/behavior/struct_contains_null_ptr_itself.zig similarity index 81% rename from test/cases/struct_contains_null_ptr_itself.zig rename to test/stage1/behavior/struct_contains_null_ptr_itself.zig index 21175974b3d82e988f03d0d6d542cb968262531f..4cc479f31c4aee69e6f88757f65d78e1388ed94e 100644 --- a/test/cases/struct_contains_null_ptr_itself.zig +++ b/test/stage1/behavior/struct_contains_null_ptr_itself.zig @@ -1,9 +1,9 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; test "struct contains null pointer which contains original struct" { var x: ?*NodeLineComment = null; - assert(x == null); + assertOrPanic(x == null); } pub const Node = struct { diff --git a/test/cases/struct_contains_slice_of_itself.zig b/test/stage1/behavior/struct_contains_slice_of_itself.zig similarity index 68% rename from test/cases/struct_contains_slice_of_itself.zig rename to test/stage1/behavior/struct_contains_slice_of_itself.zig index aa3075312cc76c4f8b0f1be9d0891063c48a8258..15780a7c4456461fe28ff31bf3d098aef60f1dea 100644 --- a/test/cases/struct_contains_slice_of_itself.zig +++ b/test/stage1/behavior/struct_contains_slice_of_itself.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const Node = struct { payload: i32, @@ -39,12 +39,12 @@ test "struct contains slice of itself" { .payload = 1234, .children = nodes[0..], }; - assert(root.payload == 1234); - assert(root.children[0].payload == 1); - assert(root.children[1].payload == 2); - assert(root.children[2].payload == 3); - assert(root.children[2].children[0].payload == 31); - assert(root.children[2].children[1].payload == 32); + assertOrPanic(root.payload == 1234); + assertOrPanic(root.children[0].payload == 1); + assertOrPanic(root.children[1].payload == 2); + assertOrPanic(root.children[2].payload == 3); + assertOrPanic(root.children[2].children[0].payload == 31); + assertOrPanic(root.children[2].children[1].payload == 32); } test "struct contains aligned slice of itself" { @@ -76,10 +76,10 @@ test "struct contains aligned slice of itself" { .payload = 1234, .children = nodes[0..], }; - assert(root.payload == 1234); - assert(root.children[0].payload == 1); - assert(root.children[1].payload == 2); - assert(root.children[2].payload == 3); - assert(root.children[2].children[0].payload == 31); - assert(root.children[2].children[1].payload == 32); + assertOrPanic(root.payload == 1234); + assertOrPanic(root.children[0].payload == 1); + assertOrPanic(root.children[1].payload == 2); + assertOrPanic(root.children[2].payload == 3); + assertOrPanic(root.children[2].children[0].payload == 31); + assertOrPanic(root.children[2].children[1].payload == 32); } diff --git a/test/cases/switch.zig b/test/stage1/behavior/switch.zig similarity index 72% rename from test/cases/switch.zig rename to test/stage1/behavior/switch.zig index 1162fdd4b2eab7519bed9796d817389454af7291..4ac971397e0498888fd7c19aac2e773ed16d21f0 100644 --- a/test/cases/switch.zig +++ b/test/stage1/behavior/switch.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "switch with numbers" { testSwitchWithNumbers(13); @@ -10,14 +10,14 @@ fn testSwitchWithNumbers(x: u32) void { 13 => true, else => false, }; - assert(result); + assertOrPanic(result); } test "switch with all ranges" { - assert(testSwitchWithAllRanges(50, 3) == 1); - assert(testSwitchWithAllRanges(101, 0) == 2); - assert(testSwitchWithAllRanges(300, 5) == 3); - assert(testSwitchWithAllRanges(301, 6) == 6); + assertOrPanic(testSwitchWithAllRanges(50, 3) == 1); + assertOrPanic(testSwitchWithAllRanges(101, 0) == 2); + assertOrPanic(testSwitchWithAllRanges(300, 5) == 3); + assertOrPanic(testSwitchWithAllRanges(301, 6) == 6); } fn testSwitchWithAllRanges(x: u32, y: u32) u32 { @@ -40,7 +40,7 @@ test "implicit comptime switch" { }; comptime { - assert(result + 1 == 14); + assertOrPanic(result + 1 == 14); } } @@ -71,7 +71,7 @@ fn nonConstSwitch(foo: SwitchStatmentFoo) void { SwitchStatmentFoo.C => 3, SwitchStatmentFoo.D => 4, }; - assert(val == 3); + assertOrPanic(val == 3); } const SwitchStatmentFoo = enum { A, @@ -93,10 +93,10 @@ const SwitchProngWithVarEnum = union(enum) { fn switchProngWithVarFn(a: SwitchProngWithVarEnum) void { switch (a) { SwitchProngWithVarEnum.One => |x| { - assert(x == 13); + assertOrPanic(x == 13); }, SwitchProngWithVarEnum.Two => |x| { - assert(x == 13.0); + assertOrPanic(x == 13.0); }, SwitchProngWithVarEnum.Meh => |x| { const v: void = x; @@ -116,7 +116,7 @@ fn testSwitchEnumPtrCapture() void { else => unreachable, } switch (value) { - SwitchProngWithVarEnum.One => |x| assert(x == 1235), + SwitchProngWithVarEnum.One => |x| assertOrPanic(x == 1235), else => unreachable, } } @@ -127,7 +127,7 @@ test "switch with multiple expressions" { 4, 5, 6 => 2, else => i32(3), }; - assert(x == 2); + assertOrPanic(x == 2); } fn returnsFive() i32 { return 5; @@ -149,12 +149,12 @@ fn returnsFalse() bool { } } test "switch on const enum with var" { - assert(!returnsFalse()); + assertOrPanic(!returnsFalse()); } test "switch on type" { - assert(trueIfBoolFalseOtherwise(bool)); - assert(!trueIfBoolFalseOtherwise(i32)); + assertOrPanic(trueIfBoolFalseOtherwise(bool)); + assertOrPanic(!trueIfBoolFalseOtherwise(i32)); } fn trueIfBoolFalseOtherwise(comptime T: type) bool { @@ -170,16 +170,16 @@ test "switch handles all cases of number" { } fn testSwitchHandleAllCases() void { - assert(testSwitchHandleAllCasesExhaustive(0) == 3); - assert(testSwitchHandleAllCasesExhaustive(1) == 2); - assert(testSwitchHandleAllCasesExhaustive(2) == 1); - assert(testSwitchHandleAllCasesExhaustive(3) == 0); + assertOrPanic(testSwitchHandleAllCasesExhaustive(0) == 3); + assertOrPanic(testSwitchHandleAllCasesExhaustive(1) == 2); + assertOrPanic(testSwitchHandleAllCasesExhaustive(2) == 1); + assertOrPanic(testSwitchHandleAllCasesExhaustive(3) == 0); - assert(testSwitchHandleAllCasesRange(100) == 0); - assert(testSwitchHandleAllCasesRange(200) == 1); - assert(testSwitchHandleAllCasesRange(201) == 2); - assert(testSwitchHandleAllCasesRange(202) == 4); - assert(testSwitchHandleAllCasesRange(230) == 3); + assertOrPanic(testSwitchHandleAllCasesRange(100) == 0); + assertOrPanic(testSwitchHandleAllCasesRange(200) == 1); + assertOrPanic(testSwitchHandleAllCasesRange(201) == 2); + assertOrPanic(testSwitchHandleAllCasesRange(202) == 4); + assertOrPanic(testSwitchHandleAllCasesRange(230) == 3); } fn testSwitchHandleAllCasesExhaustive(x: u2) u2 { @@ -207,8 +207,8 @@ test "switch all prongs unreachable" { } fn testAllProngsUnreachable() void { - assert(switchWithUnreachable(1) == 2); - assert(switchWithUnreachable(2) == 10); + assertOrPanic(switchWithUnreachable(1) == 2); + assertOrPanic(switchWithUnreachable(2) == 10); } fn switchWithUnreachable(x: i32) i32 { @@ -230,7 +230,7 @@ test "capture value of switch with all unreachable prongs" { const x = return_a_number() catch |err| switch (err) { else => unreachable, }; - assert(x == 1); + assertOrPanic(x == 1); } test "switching on booleans" { @@ -239,14 +239,14 @@ test "switching on booleans" { } fn testSwitchOnBools() void { - assert(testSwitchOnBoolsTrueAndFalse(true) == false); - assert(testSwitchOnBoolsTrueAndFalse(false) == true); + assertOrPanic(testSwitchOnBoolsTrueAndFalse(true) == false); + assertOrPanic(testSwitchOnBoolsTrueAndFalse(false) == true); - assert(testSwitchOnBoolsTrueWithElse(true) == false); - assert(testSwitchOnBoolsTrueWithElse(false) == true); + assertOrPanic(testSwitchOnBoolsTrueWithElse(true) == false); + assertOrPanic(testSwitchOnBoolsTrueWithElse(false) == true); - assert(testSwitchOnBoolsFalseWithElse(true) == false); - assert(testSwitchOnBoolsFalseWithElse(false) == true); + assertOrPanic(testSwitchOnBoolsFalseWithElse(true) == false); + assertOrPanic(testSwitchOnBoolsFalseWithElse(false) == true); } fn testSwitchOnBoolsTrueAndFalse(x: bool) bool { diff --git a/test/cases/switch_prong_err_enum.zig b/test/stage1/behavior/switch_prong_err_enum.zig similarity index 79% rename from test/cases/switch_prong_err_enum.zig rename to test/stage1/behavior/switch_prong_err_enum.zig index 89060690fc0181e5ed640292b10177952f3f77a0..6ac1919f0d5a17100ba3409c8523b43eadb8590c 100644 --- a/test/cases/switch_prong_err_enum.zig +++ b/test/stage1/behavior/switch_prong_err_enum.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; var read_count: u64 = 0; @@ -22,9 +22,9 @@ fn doThing(form_id: u64) anyerror!FormValue { test "switch prong returns error enum" { switch (doThing(17) catch unreachable) { FormValue.Address => |payload| { - assert(payload == 1); + assertOrPanic(payload == 1); }, else => unreachable, } - assert(read_count == 1); + assertOrPanic(read_count == 1); } diff --git a/test/cases/switch_prong_implicit_cast.zig b/test/stage1/behavior/switch_prong_implicit_cast.zig similarity index 82% rename from test/cases/switch_prong_implicit_cast.zig rename to test/stage1/behavior/switch_prong_implicit_cast.zig index 56d37e290f1cdb7203e555a5854e0d0cb966294e..4ca031e2e11b9beeb27d3e2cb2171e0a744d989c 100644 --- a/test/cases/switch_prong_implicit_cast.zig +++ b/test/stage1/behavior/switch_prong_implicit_cast.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const FormValue = union(enum) { One: void, @@ -18,5 +18,5 @@ test "switch prong implicit cast" { FormValue.One => false, FormValue.Two => |x| x, }; - assert(result); + assertOrPanic(result); } diff --git a/test/cases/syntax.zig b/test/stage1/behavior/syntax.zig similarity index 99% rename from test/cases/syntax.zig rename to test/stage1/behavior/syntax.zig index 0c8c3c5ed314b414bbf06efe27ab64e949cb2536..451e396142922fd102fc6ab8745e22e30b21b0eb 100644 --- a/test/cases/syntax.zig +++ b/test/stage1/behavior/syntax.zig @@ -57,3 +57,4 @@ fn asm_lists() void { :::"a","b",); } } + diff --git a/test/cases/this.zig b/test/stage1/behavior/this.zig similarity index 74% rename from test/cases/this.zig rename to test/stage1/behavior/this.zig index c7be074f36c2e5ad0b2361a419a9356a2aa82233..0e3a7a03ae078bf43dc7f8c2cb66fbfda66860eb 100644 --- a/test/cases/this.zig +++ b/test/stage1/behavior/this.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const module = @This(); @@ -20,7 +20,7 @@ fn add(x: i32, y: i32) i32 { } test "this refer to module call private fn" { - assert(module.add(1, 2) == 3); + assertOrPanic(module.add(1, 2) == 3); } test "this refer to container" { @@ -29,6 +29,7 @@ test "this refer to container" { .y = 34, }; pt.addOne(); - assert(pt.x == 13); - assert(pt.y == 35); + assertOrPanic(pt.x == 13); + assertOrPanic(pt.y == 35); } + diff --git a/test/cases/truncate.zig b/test/stage1/behavior/truncate.zig similarity index 65% rename from test/cases/truncate.zig rename to test/stage1/behavior/truncate.zig index 02b5085ccd25cbb7136c2f352ba5d141d16ac84c..b7904bc7fbca8f21ad3ba7800a0f5235a84b3fa5 100644 --- a/test/cases/truncate.zig +++ b/test/stage1/behavior/truncate.zig @@ -1,8 +1,8 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; test "truncate u0 to larger integer allowed and has comptime known result" { var x: u0 = 0; const y = @truncate(u8, x); - comptime assert(y == 0); + comptime assertOrPanic(y == 0); } diff --git a/test/cases/try.zig b/test/stage1/behavior/try.zig similarity index 79% rename from test/cases/try.zig rename to test/stage1/behavior/try.zig index 450a9af6ac0bb06a530dad4bf7923d4778844dec..ed48875eb46629628b6201aa1a3f4df7d7ca677a 100644 --- a/test/cases/try.zig +++ b/test/stage1/behavior/try.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "try on error union" { tryOnErrorUnionImpl(); @@ -11,7 +11,7 @@ fn tryOnErrorUnionImpl() void { error.CrappedOut => i32(2), else => unreachable, }; - assert(x == 11); + assertOrPanic(x == 11); } fn returnsTen() anyerror!i32 { @@ -20,10 +20,10 @@ fn returnsTen() anyerror!i32 { test "try without vars" { const result1 = if (failIfTrue(true)) 1 else |_| i32(2); - assert(result1 == 2); + assertOrPanic(result1 == 2); const result2 = if (failIfTrue(false)) 1 else |_| i32(2); - assert(result2 == 1); + assertOrPanic(result2 == 1); } fn failIfTrue(ok: bool) anyerror!void { @@ -38,6 +38,6 @@ test "try then not executed with assignment" { if (failIfTrue(true)) { unreachable; } else |err| { - assert(err == error.ItBroke); + assertOrPanic(err == error.ItBroke); } } diff --git a/test/stage1/behavior/type_info.zig b/test/stage1/behavior/type_info.zig new file mode 100644 index 0000000000000000000000000000000000000000..5ad80e06e178f48e755f54c28f50389bd0d53c91 --- /dev/null +++ b/test/stage1/behavior/type_info.zig @@ -0,0 +1,264 @@ +const assertOrPanic = @import("std").debug.assertOrPanic; +const mem = @import("std").mem; +const TypeInfo = @import("builtin").TypeInfo; +const TypeId = @import("builtin").TypeId; + +test "type info: tag type, void info" { + testBasic(); + comptime testBasic(); +} + +fn testBasic() void { + assertOrPanic(@TagType(TypeInfo) == TypeId); + const void_info = @typeInfo(void); + assertOrPanic(TypeId(void_info) == TypeId.Void); + assertOrPanic(void_info.Void == {}); +} + +test "type info: integer, floating point type info" { + testIntFloat(); + comptime testIntFloat(); +} + +fn testIntFloat() void { + const u8_info = @typeInfo(u8); + assertOrPanic(TypeId(u8_info) == TypeId.Int); + assertOrPanic(!u8_info.Int.is_signed); + assertOrPanic(u8_info.Int.bits == 8); + + const f64_info = @typeInfo(f64); + assertOrPanic(TypeId(f64_info) == TypeId.Float); + assertOrPanic(f64_info.Float.bits == 64); +} + +test "type info: pointer type info" { + testPointer(); + comptime testPointer(); +} + +fn testPointer() void { + const u32_ptr_info = @typeInfo(*u32); + assertOrPanic(TypeId(u32_ptr_info) == TypeId.Pointer); + assertOrPanic(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.One); + assertOrPanic(u32_ptr_info.Pointer.is_const == false); + assertOrPanic(u32_ptr_info.Pointer.is_volatile == false); + assertOrPanic(u32_ptr_info.Pointer.alignment == @alignOf(u32)); + assertOrPanic(u32_ptr_info.Pointer.child == u32); +} + +test "type info: unknown length pointer type info" { + testUnknownLenPtr(); + comptime testUnknownLenPtr(); +} + +fn testUnknownLenPtr() void { + const u32_ptr_info = @typeInfo([*]const volatile f64); + assertOrPanic(TypeId(u32_ptr_info) == TypeId.Pointer); + assertOrPanic(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many); + assertOrPanic(u32_ptr_info.Pointer.is_const == true); + assertOrPanic(u32_ptr_info.Pointer.is_volatile == true); + assertOrPanic(u32_ptr_info.Pointer.alignment == @alignOf(f64)); + assertOrPanic(u32_ptr_info.Pointer.child == f64); +} + +test "type info: slice type info" { + testSlice(); + comptime testSlice(); +} + +fn testSlice() void { + const u32_slice_info = @typeInfo([]u32); + assertOrPanic(TypeId(u32_slice_info) == TypeId.Pointer); + assertOrPanic(u32_slice_info.Pointer.size == TypeInfo.Pointer.Size.Slice); + assertOrPanic(u32_slice_info.Pointer.is_const == false); + assertOrPanic(u32_slice_info.Pointer.is_volatile == false); + assertOrPanic(u32_slice_info.Pointer.alignment == 4); + assertOrPanic(u32_slice_info.Pointer.child == u32); +} + +test "type info: array type info" { + testArray(); + comptime testArray(); +} + +fn testArray() void { + const arr_info = @typeInfo([42]bool); + assertOrPanic(TypeId(arr_info) == TypeId.Array); + assertOrPanic(arr_info.Array.len == 42); + assertOrPanic(arr_info.Array.child == bool); +} + +test "type info: optional type info" { + testOptional(); + comptime testOptional(); +} + +fn testOptional() void { + const null_info = @typeInfo(?void); + assertOrPanic(TypeId(null_info) == TypeId.Optional); + assertOrPanic(null_info.Optional.child == void); +} + +test "type info: promise info" { + testPromise(); + comptime testPromise(); +} + +fn testPromise() void { + const null_promise_info = @typeInfo(promise); + assertOrPanic(TypeId(null_promise_info) == TypeId.Promise); + assertOrPanic(null_promise_info.Promise.child == null); + + const promise_info = @typeInfo(promise->usize); + assertOrPanic(TypeId(promise_info) == TypeId.Promise); + assertOrPanic(promise_info.Promise.child.? == usize); +} + +test "type info: error set, error union info" { + testErrorSet(); + comptime testErrorSet(); +} + +fn testErrorSet() void { + const TestErrorSet = error{ + First, + Second, + Third, + }; + + const error_set_info = @typeInfo(TestErrorSet); + assertOrPanic(TypeId(error_set_info) == TypeId.ErrorSet); + assertOrPanic(error_set_info.ErrorSet.errors.len == 3); + assertOrPanic(mem.eql(u8, error_set_info.ErrorSet.errors[0].name, "First")); + assertOrPanic(error_set_info.ErrorSet.errors[2].value == @errorToInt(TestErrorSet.Third)); + + const error_union_info = @typeInfo(TestErrorSet!usize); + assertOrPanic(TypeId(error_union_info) == TypeId.ErrorUnion); + assertOrPanic(error_union_info.ErrorUnion.error_set == TestErrorSet); + assertOrPanic(error_union_info.ErrorUnion.payload == usize); +} + +test "type info: enum info" { + testEnum(); + comptime testEnum(); +} + +fn testEnum() void { + const Os = enum { + Windows, + Macos, + Linux, + FreeBSD, + }; + + const os_info = @typeInfo(Os); + assertOrPanic(TypeId(os_info) == TypeId.Enum); + assertOrPanic(os_info.Enum.layout == TypeInfo.ContainerLayout.Auto); + assertOrPanic(os_info.Enum.fields.len == 4); + assertOrPanic(mem.eql(u8, os_info.Enum.fields[1].name, "Macos")); + assertOrPanic(os_info.Enum.fields[3].value == 3); + assertOrPanic(os_info.Enum.tag_type == u2); + assertOrPanic(os_info.Enum.defs.len == 0); +} + +test "type info: union info" { + testUnion(); + comptime testUnion(); +} + +fn testUnion() void { + const typeinfo_info = @typeInfo(TypeInfo); + assertOrPanic(TypeId(typeinfo_info) == TypeId.Union); + assertOrPanic(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto); + assertOrPanic(typeinfo_info.Union.tag_type.? == TypeId); + assertOrPanic(typeinfo_info.Union.fields.len == 24); + assertOrPanic(typeinfo_info.Union.fields[4].enum_field != null); + assertOrPanic(typeinfo_info.Union.fields[4].enum_field.?.value == 4); + assertOrPanic(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int)); + assertOrPanic(typeinfo_info.Union.defs.len == 20); + + const TestNoTagUnion = union { + Foo: void, + Bar: u32, + }; + + const notag_union_info = @typeInfo(TestNoTagUnion); + assertOrPanic(TypeId(notag_union_info) == TypeId.Union); + assertOrPanic(notag_union_info.Union.tag_type == null); + assertOrPanic(notag_union_info.Union.layout == TypeInfo.ContainerLayout.Auto); + assertOrPanic(notag_union_info.Union.fields.len == 2); + assertOrPanic(notag_union_info.Union.fields[0].enum_field == null); + assertOrPanic(notag_union_info.Union.fields[1].field_type == u32); + + const TestExternUnion = extern union { + foo: *c_void, + }; + + const extern_union_info = @typeInfo(TestExternUnion); + assertOrPanic(extern_union_info.Union.layout == TypeInfo.ContainerLayout.Extern); + assertOrPanic(extern_union_info.Union.tag_type == null); + assertOrPanic(extern_union_info.Union.fields[0].enum_field == null); + assertOrPanic(extern_union_info.Union.fields[0].field_type == *c_void); +} + +test "type info: struct info" { + testStruct(); + comptime testStruct(); +} + +fn testStruct() void { + const struct_info = @typeInfo(TestStruct); + assertOrPanic(TypeId(struct_info) == TypeId.Struct); + assertOrPanic(struct_info.Struct.layout == TypeInfo.ContainerLayout.Packed); + assertOrPanic(struct_info.Struct.fields.len == 3); + assertOrPanic(struct_info.Struct.fields[1].offset == null); + assertOrPanic(struct_info.Struct.fields[2].field_type == *TestStruct); + assertOrPanic(struct_info.Struct.defs.len == 2); + assertOrPanic(struct_info.Struct.defs[0].is_pub); + assertOrPanic(!struct_info.Struct.defs[0].data.Fn.is_extern); + assertOrPanic(struct_info.Struct.defs[0].data.Fn.lib_name == null); + assertOrPanic(struct_info.Struct.defs[0].data.Fn.return_type == void); + assertOrPanic(struct_info.Struct.defs[0].data.Fn.fn_type == fn (*const TestStruct) void); +} + +const TestStruct = packed struct { + const Self = @This(); + + fieldA: usize, + fieldB: void, + fieldC: *Self, + + pub fn foo(self: *const Self) void {} +}; + +test "type info: function type info" { + testFunction(); + comptime testFunction(); +} + +fn testFunction() void { + const fn_info = @typeInfo(@typeOf(foo)); + assertOrPanic(TypeId(fn_info) == TypeId.Fn); + assertOrPanic(fn_info.Fn.calling_convention == TypeInfo.CallingConvention.Unspecified); + assertOrPanic(fn_info.Fn.is_generic); + assertOrPanic(fn_info.Fn.args.len == 2); + assertOrPanic(fn_info.Fn.is_var_args); + assertOrPanic(fn_info.Fn.return_type == null); + assertOrPanic(fn_info.Fn.async_allocator_type == null); + + const test_instance: TestStruct = undefined; + const bound_fn_info = @typeInfo(@typeOf(test_instance.foo)); + assertOrPanic(TypeId(bound_fn_info) == TypeId.BoundFn); + assertOrPanic(bound_fn_info.BoundFn.args[0].arg_type.? == *const TestStruct); +} + +fn foo(comptime a: usize, b: bool, args: ...) usize { + return 0; +} + +test "typeInfo with comptime parameter in struct fn def" { + const S = struct { + pub fn func(comptime x: f32) void {} + }; + comptime var info = @typeInfo(S); +} diff --git a/test/cases/undefined.zig b/test/stage1/behavior/undefined.zig similarity index 59% rename from test/cases/undefined.zig rename to test/stage1/behavior/undefined.zig index 83c620d21159c99aee500ca1aa609c11da291d54..333e217d493bdc3d13800861bc13533f13be8773 100644 --- a/test/cases/undefined.zig +++ b/test/stage1/behavior/undefined.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const mem = @import("std").mem; fn initStaticArray() [10]i32 { @@ -11,16 +11,16 @@ fn initStaticArray() [10]i32 { } const static_array = initStaticArray(); test "init static array to undefined" { - assert(static_array[0] == 1); - assert(static_array[4] == 2); - assert(static_array[7] == 3); - assert(static_array[9] == 4); + assertOrPanic(static_array[0] == 1); + assertOrPanic(static_array[4] == 2); + assertOrPanic(static_array[7] == 3); + assertOrPanic(static_array[9] == 4); comptime { - assert(static_array[0] == 1); - assert(static_array[4] == 2); - assert(static_array[7] == 3); - assert(static_array[9] == 4); + assertOrPanic(static_array[0] == 1); + assertOrPanic(static_array[4] == 2); + assertOrPanic(static_array[7] == 3); + assertOrPanic(static_array[9] == 4); } } @@ -40,12 +40,12 @@ test "assign undefined to struct" { comptime { var foo: Foo = undefined; setFooX(&foo); - assert(foo.x == 2); + assertOrPanic(foo.x == 2); } { var foo: Foo = undefined; setFooX(&foo); - assert(foo.x == 2); + assertOrPanic(foo.x == 2); } } @@ -53,16 +53,17 @@ test "assign undefined to struct with method" { comptime { var foo: Foo = undefined; foo.setFooXMethod(); - assert(foo.x == 3); + assertOrPanic(foo.x == 3); } { var foo: Foo = undefined; foo.setFooXMethod(); - assert(foo.x == 3); + assertOrPanic(foo.x == 3); } } test "type name of undefined" { const x = undefined; - assert(mem.eql(u8, @typeName(@typeOf(x)), "(undefined)")); + assertOrPanic(mem.eql(u8, @typeName(@typeOf(x)), "(undefined)")); } + diff --git a/test/cases/underscore.zig b/test/stage1/behavior/underscore.zig similarity index 91% rename from test/cases/underscore.zig rename to test/stage1/behavior/underscore.zig index da1c97659cf33eb65eecb3e6682fa44ad8358d70..7443319336b0c66a63eb94bd4a287858f5e86118 100644 --- a/test/cases/underscore.zig +++ b/test/stage1/behavior/underscore.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; test "ignore lval with underscore" { _ = false; diff --git a/test/cases/union.zig b/test/stage1/behavior/union.zig similarity index 77% rename from test/cases/union.zig rename to test/stage1/behavior/union.zig index 019a7012daf0ddce0fb10ae70e522126a262cafe..c8e8feb11ea661cb5711ebccd19ed0a599ce495f 100644 --- a/test/cases/union.zig +++ b/test/stage1/behavior/union.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const Value = union(enum) { Int: u64, @@ -27,11 +27,11 @@ const array = []Value{ test "unions embedded in aggregate types" { switch (array[1]) { - Value.Array => |arr| assert(arr[4] == 3), + Value.Array => |arr| assertOrPanic(arr[4] == 3), else => unreachable, } switch ((err catch unreachable).val1) { - Value.Int => |x| assert(x == 1234), + Value.Int => |x| assertOrPanic(x == 1234), else => unreachable, } } @@ -43,18 +43,18 @@ const Foo = union { test "basic unions" { var foo = Foo{ .int = 1 }; - assert(foo.int == 1); + assertOrPanic(foo.int == 1); foo = Foo{ .float = 12.34 }; - assert(foo.float == 12.34); + assertOrPanic(foo.float == 12.34); } test "comptime union field access" { comptime { var foo = Foo{ .int = 0 }; - assert(foo.int == 0); + assertOrPanic(foo.int == 0); foo = Foo{ .float = 42.42 }; - assert(foo.float == 42.42); + assertOrPanic(foo.float == 42.42); } } @@ -62,10 +62,10 @@ test "init union with runtime value" { var foo: Foo = undefined; setFloat(&foo, 12.34); - assert(foo.float == 12.34); + assertOrPanic(foo.float == 12.34); setInt(&foo, 42); - assert(foo.int == 42); + assertOrPanic(foo.int == 42); } fn setFloat(foo: *Foo, x: f64) void { @@ -83,9 +83,9 @@ const FooExtern = extern union { test "basic extern unions" { var foo = FooExtern{ .int = 1 }; - assert(foo.int == 1); + assertOrPanic(foo.int == 1); foo.float = 12.34; - assert(foo.float == 12.34); + assertOrPanic(foo.float == 12.34); } const Letter = enum { @@ -105,11 +105,11 @@ test "union with specified enum tag" { } fn doTest() void { - assert(bar(Payload{ .A = 1234 }) == -10); + assertOrPanic(bar(Payload{ .A = 1234 }) == -10); } fn bar(value: Payload) i32 { - assert(Letter(value) == Letter.A); + assertOrPanic(Letter(value) == Letter.A); return switch (value) { Payload.A => |x| return x - 1244, Payload.B => |x| if (x == 12.34) i32(20) else 21, @@ -125,8 +125,8 @@ const MultipleChoice = union(enum(u32)) { }; test "simple union(enum(u32))" { var x = MultipleChoice.C; - assert(x == MultipleChoice.C); - assert(@enumToInt(@TagType(MultipleChoice)(x)) == 60); + assertOrPanic(x == MultipleChoice.C); + assertOrPanic(@enumToInt(@TagType(MultipleChoice)(x)) == 60); } const MultipleChoice2 = union(enum(u32)) { @@ -142,14 +142,14 @@ const MultipleChoice2 = union(enum(u32)) { }; test "union(enum(u32)) with specified and unspecified tag values" { - comptime assert(@TagType(@TagType(MultipleChoice2)) == u32); + comptime assertOrPanic(@TagType(@TagType(MultipleChoice2)) == u32); testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); } fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { - assert(@enumToInt(@TagType(MultipleChoice2)(x)) == 60); - assert(1123 == switch (x) { + assertOrPanic(@enumToInt(@TagType(MultipleChoice2)(x)) == 60); + assertOrPanic(1123 == switch (x) { MultipleChoice2.A => 1, MultipleChoice2.B => 2, MultipleChoice2.C => |v| i32(1000) + v, @@ -167,7 +167,7 @@ const ExternPtrOrInt = extern union { int: u64, }; test "extern union size" { - comptime assert(@sizeOf(ExternPtrOrInt) == 8); + comptime assertOrPanic(@sizeOf(ExternPtrOrInt) == 8); } const PackedPtrOrInt = packed union { @@ -175,14 +175,14 @@ const PackedPtrOrInt = packed union { int: u64, }; test "extern union size" { - comptime assert(@sizeOf(PackedPtrOrInt) == 8); + comptime assertOrPanic(@sizeOf(PackedPtrOrInt) == 8); } const ZeroBits = union { OnlyField: void, }; test "union with only 1 field which is void should be zero bits" { - comptime assert(@sizeOf(ZeroBits) == 0); + comptime assertOrPanic(@sizeOf(ZeroBits) == 0); } const TheTag = enum { @@ -196,9 +196,9 @@ const TheUnion = union(TheTag) { C: i32, }; test "union field access gives the enum values" { - assert(TheUnion.A == TheTag.A); - assert(TheUnion.B == TheTag.B); - assert(TheUnion.C == TheTag.C); + assertOrPanic(TheUnion.A == TheTag.A); + assertOrPanic(TheUnion.B == TheTag.B); + assertOrPanic(TheUnion.C == TheTag.C); } test "cast union to tag type of union" { @@ -207,12 +207,12 @@ test "cast union to tag type of union" { } fn testCastUnionToTagType(x: TheUnion) void { - assert(TheTag(x) == TheTag.B); + assertOrPanic(TheTag(x) == TheTag.B); } test "cast tag type of union to union" { var x: Value2 = Letter2.B; - assert(Letter2(x) == Letter2.B); + assertOrPanic(Letter2(x) == Letter2.B); } const Letter2 = enum { A, @@ -227,11 +227,11 @@ const Value2 = union(Letter2) { test "implicit cast union to its tag type" { var x: Value2 = Letter2.B; - assert(x == Letter2.B); + assertOrPanic(x == Letter2.B); giveMeLetterB(x); } fn giveMeLetterB(x: Letter2) void { - assert(x == Value2.B); + assertOrPanic(x == Value2.B); } pub const PackThis = union(enum) { @@ -244,7 +244,7 @@ test "constant packed union" { } fn testConstPackedUnion(expected_tokens: []const PackThis) void { - assert(expected_tokens[0].StringLiteral == 1); + assertOrPanic(expected_tokens[0].StringLiteral == 1); } test "switch on union with only 1 field" { @@ -256,7 +256,7 @@ test "switch on union with only 1 field" { z = PartialInstWithPayload{ .Compiled = 1234 }; switch (z) { PartialInstWithPayload.Compiled => |x| { - assert(x == 1234); + assertOrPanic(x == 1234); return; }, } @@ -282,11 +282,11 @@ test "access a member of tagged union with conflicting enum tag name" { const B = void; }; - comptime assert(Bar.A == u8); + comptime assertOrPanic(Bar.A == u8); } test "tagged union initialization with runtime void" { - assert(testTaggedUnionInit({})); + assertOrPanic(testTaggedUnionInit({})); } const TaggedUnionWithAVoid = union(enum) { @@ -324,9 +324,9 @@ test "union with only 1 field casted to its enum type" { var e = Expr{ .Literal = Literal{ .Bool = true } }; const Tag = @TagType(Expr); - comptime assert(@TagType(Tag) == comptime_int); + comptime assertOrPanic(@TagType(Tag) == comptime_int); var t = Tag(e); - assert(t == Expr.Literal); + assertOrPanic(t == Expr.Literal); } test "union with only 1 field casted to its enum type which has enum value specified" { @@ -344,9 +344,9 @@ test "union with only 1 field casted to its enum type which has enum value speci }; var e = Expr{ .Literal = Literal{ .Bool = true } }; - comptime assert(@TagType(Tag) == comptime_int); + comptime assertOrPanic(@TagType(Tag) == comptime_int); var t = Tag(e); - assert(t == Expr.Literal); - assert(@enumToInt(t) == 33); - comptime assert(@enumToInt(t) == 33); + assertOrPanic(t == Expr.Literal); + assertOrPanic(@enumToInt(t) == 33); + comptime assertOrPanic(@enumToInt(t) == 33); } diff --git a/test/cases/var_args.zig b/test/stage1/behavior/var_args.zig similarity index 56% rename from test/cases/var_args.zig rename to test/stage1/behavior/var_args.zig index 3eb6e304486b22492c7a3470f8cc4aa61dcd8fb1..1f782a3bb3a3c9ba1e5b43c7a3fce0556b6f05e6 100644 --- a/test/cases/var_args.zig +++ b/test/stage1/behavior/var_args.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; fn add(args: ...) i32 { var sum = i32(0); @@ -12,9 +12,9 @@ fn add(args: ...) i32 { } test "add arbitrary args" { - assert(add(i32(1), i32(2), i32(3), i32(4)) == 10); - assert(add(i32(1234)) == 1234); - assert(add() == 0); + assertOrPanic(add(i32(1), i32(2), i32(3), i32(4)) == 10); + assertOrPanic(add(i32(1234)) == 1234); + assertOrPanic(add() == 0); } fn readFirstVarArg(args: ...) void { @@ -26,9 +26,9 @@ test "send void arg to var args" { } test "pass args directly" { - assert(addSomeStuff(i32(1), i32(2), i32(3), i32(4)) == 10); - assert(addSomeStuff(i32(1234)) == 1234); - assert(addSomeStuff() == 0); + assertOrPanic(addSomeStuff(i32(1), i32(2), i32(3), i32(4)) == 10); + assertOrPanic(addSomeStuff(i32(1234)) == 1234); + assertOrPanic(addSomeStuff() == 0); } fn addSomeStuff(args: ...) i32 { @@ -36,24 +36,24 @@ fn addSomeStuff(args: ...) i32 { } test "runtime parameter before var args" { - assert(extraFn(10) == 0); - assert(extraFn(10, false) == 1); - assert(extraFn(10, false, true) == 2); + assertOrPanic(extraFn(10) == 0); + assertOrPanic(extraFn(10, false) == 1); + assertOrPanic(extraFn(10, false, true) == 2); // TODO issue #313 //comptime { - // assert(extraFn(10) == 0); - // assert(extraFn(10, false) == 1); - // assert(extraFn(10, false, true) == 2); + // assertOrPanic(extraFn(10) == 0); + // assertOrPanic(extraFn(10, false) == 1); + // assertOrPanic(extraFn(10, false, true) == 2); //} } fn extraFn(extra: u32, args: ...) usize { if (args.len >= 1) { - assert(args[0] == false); + assertOrPanic(args[0] == false); } if (args.len >= 2) { - assert(args[1] == true); + assertOrPanic(args[1] == true); } return args.len; } @@ -71,8 +71,8 @@ fn foo2(args: ...) bool { } test "array of var args functions" { - assert(foos[0]()); - assert(!foos[1]()); + assertOrPanic(foos[0]()); + assertOrPanic(!foos[1]()); } test "pass zero length array to var args param" { diff --git a/test/cases/void.zig b/test/stage1/behavior/void.zig similarity index 67% rename from test/cases/void.zig rename to test/stage1/behavior/void.zig index 7121ac664b947f738c3c55b7825accb7ddbc0e0d..431d3f4eb1033606614c9abe35898951ea8a5f69 100644 --- a/test/cases/void.zig +++ b/test/stage1/behavior/void.zig @@ -1,4 +1,4 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; const Foo = struct { a: void, @@ -13,14 +13,14 @@ test "compare void with void compile time known" { .b = 1, .c = {}, }; - assert(foo.a == {}); + assertOrPanic(foo.a == {}); } } test "iterate over a void slice" { var j: usize = 0; for (times(10)) |_, i| { - assert(i == j); + assertOrPanic(i == j); j += 1; } } @@ -28,3 +28,8 @@ test "iterate over a void slice" { fn times(n: usize) []const void { return ([*]void)(undefined)[0..n]; } + +test "void optional" { + var x: ?void = {}; + assertOrPanic(x != null); +} diff --git a/test/cases/while.zig b/test/stage1/behavior/while.zig similarity index 83% rename from test/cases/while.zig rename to test/stage1/behavior/while.zig index f774e0ec6b328fb83ff178f37b9196315f7de581..579b4e4db8529cdbf8819249228a7dfe876f9aec 100644 --- a/test/cases/while.zig +++ b/test/stage1/behavior/while.zig @@ -1,12 +1,12 @@ -const assert = @import("std").debug.assert; +const assertOrPanic = @import("std").debug.assertOrPanic; test "while loop" { var i: i32 = 0; while (i < 4) { i += 1; } - assert(i == 4); - assert(whileLoop1() == 1); + assertOrPanic(i == 4); + assertOrPanic(whileLoop1() == 1); } fn whileLoop1() i32 { return whileLoop2(); @@ -16,8 +16,9 @@ fn whileLoop2() i32 { return 1; } } + test "static eval while" { - assert(static_eval_while_number == 1); + assertOrPanic(static_eval_while_number == 1); } const static_eval_while_number = staticWhileLoop1(); fn staticWhileLoop1() i32 { @@ -31,7 +32,7 @@ fn staticWhileLoop2() i32 { test "continue and break" { runContinueAndBreakTest(); - assert(continue_and_break_counter == 8); + assertOrPanic(continue_and_break_counter == 8); } var continue_and_break_counter: i32 = 0; fn runContinueAndBreakTest() void { @@ -44,7 +45,7 @@ fn runContinueAndBreakTest() void { } break; } - assert(i == 4); + assertOrPanic(i == 4); } test "return with implicit cast from while loop" { @@ -65,7 +66,7 @@ test "while with continue expression" { sum += i; } } - assert(sum == 40); + assertOrPanic(sum == 40); } test "while with else" { @@ -77,8 +78,8 @@ test "while with else" { } else { got_else += 1; } - assert(sum == 10); - assert(got_else == 1); + assertOrPanic(sum == 10); + assertOrPanic(got_else == 1); } test "while with optional as condition" { @@ -87,7 +88,7 @@ test "while with optional as condition" { while (getNumberOrNull()) |value| { sum += value; } - assert(sum == 45); + assertOrPanic(sum == 45); } test "while with optional as condition with else" { @@ -96,12 +97,12 @@ test "while with optional as condition with else" { var got_else: i32 = 0; while (getNumberOrNull()) |value| { sum += value; - assert(got_else == 0); + assertOrPanic(got_else == 0); } else { got_else += 1; } - assert(sum == 45); - assert(got_else == 1); + assertOrPanic(sum == 45); + assertOrPanic(got_else == 1); } test "while with error union condition" { @@ -111,11 +112,11 @@ test "while with error union condition" { while (getNumberOrErr()) |value| { sum += value; } else |err| { - assert(err == error.OutOfNumbers); + assertOrPanic(err == error.OutOfNumbers); got_else += 1; } - assert(sum == 45); - assert(got_else == 1); + assertOrPanic(sum == 45); + assertOrPanic(got_else == 1); } var numbers_left: i32 = undefined; @@ -137,7 +138,7 @@ test "while on optional with else result follow else prong" { break value; } else i32(2); - assert(result == 2); + assertOrPanic(result == 2); } test "while on optional with else result follow break prong" { @@ -145,7 +146,7 @@ test "while on optional with else result follow break prong" { break value; } else i32(2); - assert(result == 10); + assertOrPanic(result == 10); } test "while on error union with else result follow else prong" { @@ -153,7 +154,7 @@ test "while on error union with else result follow else prong" { break value; } else |err| i32(2); - assert(result == 2); + assertOrPanic(result == 2); } test "while on error union with else result follow break prong" { @@ -161,7 +162,7 @@ test "while on error union with else result follow break prong" { break value; } else |err| i32(2); - assert(result == 10); + assertOrPanic(result == 10); } test "while on bool with else result follow else prong" { @@ -169,7 +170,7 @@ test "while on bool with else result follow else prong" { break i32(10); } else i32(2); - assert(result == 2); + assertOrPanic(result == 2); } test "while on bool with else result follow break prong" { @@ -177,7 +178,7 @@ test "while on bool with else result follow break prong" { break i32(10); } else i32(2); - assert(result == 10); + assertOrPanic(result == 10); } test "break from outer while loop" { diff --git a/test/cases/widening.zig b/test/stage1/behavior/widening.zig similarity index 75% rename from test/cases/widening.zig rename to test/stage1/behavior/widening.zig index cf6ab4ca0fb4eeacf06e8d334554b106ff231686..7577868aff5414a5d27e1983ff0137bb577dc038 100644 --- a/test/cases/widening.zig +++ b/test/stage1/behavior/widening.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const assert = std.debug.assert; +const assertOrPanic = std.debug.assertOrPanic; const mem = std.mem; test "integer widening" { @@ -9,13 +9,13 @@ test "integer widening" { var d: u64 = c; var e: u64 = d; var f: u128 = e; - assert(f == a); + assertOrPanic(f == a); } test "implicit unsigned integer to signed integer" { var a: u8 = 250; var b: i16 = a; - assert(b == 250); + assertOrPanic(b == 250); } test "float widening" { @@ -23,5 +23,6 @@ test "float widening" { var b: f32 = a; var c: f64 = b; var d: f128 = c; - assert(d == a); + assertOrPanic(d == a); } + -- 2.54.0 From 9ca94bbba55b523abe3544e87c1710a83064d29a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 30 Jan 2019 02:53:50 -0500 Subject: [PATCH 22/37] fix freebsd ci from previous commit --- .builds/freebsd.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index c7d92712b02655001c95e0f5bf393a2a3c566081..934b8a4f4a83007b3e27e51ced31175dc35b0c13 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -12,28 +12,28 @@ tasks: ninja install - test: | cd zig/build - bin/zig test ../test/behavior.zig + bin/zig test ../test/stage1/behavior.zig bin/zig test ../std/special/compiler_rt/index.zig - bin/zig test ../test/behavior.zig --library c + bin/zig test ../test/stage1/behavior.zig --library c bin/zig test ../std/special/compiler_rt/index.zig --library c - bin/zig test ../test/behavior.zig --release-fast + bin/zig test ../test/stage1/behavior.zig --release-fast bin/zig test ../std/special/compiler_rt/index.zig --release-fast - bin/zig test ../test/behavior.zig --release-fast --library c + bin/zig test ../test/stage1/behavior.zig --release-fast --library c bin/zig test ../std/special/compiler_rt/index.zig --release-fast --library c - bin/zig test ../test/behavior.zig --release-small --library c + bin/zig test ../test/stage1/behavior.zig --release-small --library c bin/zig test ../std/special/compiler_rt/index.zig --release-small --library c - bin/zig test ../test/behavior.zig --release-small + bin/zig test ../test/stage1/behavior.zig --release-small bin/zig test ../std/special/compiler_rt/index.zig --release-small - bin/zig test ../test/behavior.zig --release-safe + bin/zig test ../test/stage1/behavior.zig --release-safe bin/zig test ../std/special/compiler_rt/index.zig --release-safe - bin/zig test ../test/behavior.zig --release-safe --library c + bin/zig test ../test/stage1/behavior.zig --release-safe --library c bin/zig test ../std/special/compiler_rt/index.zig --release-safe --library c # TODO enable all tests #bin/zig build --build-file ../build.zig test -- 2.54.0 From ecb0cb661a75a130cf6978fa42d464e2d654d8df Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 30 Jan 2019 02:53:59 -0500 Subject: [PATCH 23/37] darwin time code: don't cast to int until the end See #1648 --- std/os/time.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/std/os/time.zig b/std/os/time.zig index 42f32f8fee230c35dc1d6eed40ed01c32640a6ae..304f65206a1cac055cb9330064ceb092a3827171 100644 --- a/std/os/time.zig +++ b/std/os/time.zig @@ -84,9 +84,9 @@ fn milliTimestampDarwin() u64 { var tv: darwin.timeval = undefined; var err = darwin.gettimeofday(&tv, null); debug.assert(err == 0); - const sec_ms = @intCast(u64, tv.tv_sec) * ms_per_s; - const usec_ms = @divFloor(@intCast(u64, tv.tv_usec), us_per_s / ms_per_s); - return u64(sec_ms) + u64(usec_ms); + const sec_ms = tv.tv_sec * ms_per_s; + const usec_ms = @divFloor(tv.tv_usec, us_per_s / ms_per_s); + return @intCast(u64, sec_ms + usec_ms); } fn milliTimestampPosix() u64 { -- 2.54.0 From ad8381e0d2936ffecaa0b54e51e26d6bdb98682e Mon Sep 17 00:00:00 2001 From: Matthew McAllister Date: Tue, 29 Jan 2019 16:20:38 -0800 Subject: [PATCH 24/37] Move tokenizer error location to offending char Previously, it pointed to the start of the current token, but this made it difficult to tell where the error occurred when it was, say, in the middle of a string. --- src/tokenizer.cpp | 9 ++------- test/compile_errors.zig | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 6215541876f23241ba7ea2f738e83ad61c233c42..d43bfabf6de6a11321ffc84f52e70f0a0439f860 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -248,13 +248,8 @@ ATTRIBUTE_PRINTF(2, 3) static void tokenize_error(Tokenize *t, const char *format, ...) { t->state = TokenizeStateError; - if (t->cur_tok) { - t->out->err_line = t->cur_tok->start_line; - t->out->err_column = t->cur_tok->start_column; - } else { - t->out->err_line = t->line; - t->out->err_column = t->column; - } + t->out->err_line = t->line; + t->out->err_column = t->column; va_list ap; va_start(ap, format); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index bc1ef660c3a6528a8749fc4362071ce684c07191..2aca7e9141355bd4d4f638367b67d3e2f6da3f57 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2645,7 +2645,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } , - ".tmp_source.zig:1:13: error: newline not allowed in string literal", + ".tmp_source.zig:1:15: error: newline not allowed in string literal", ); cases.add( -- 2.54.0 From 59c050e7ff83aeecd507cef24d7eb914ec59581d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 30 Jan 2019 16:06:18 -0500 Subject: [PATCH 25/37] collapse os_file_mtime into os_file_open_r and check for directory This is a manual merge of kristate's pull request #1754, due to conflicts + a couple fixups. closes #1754 --- src/cache_hash.cpp | 16 +++--------- src/os.cpp | 62 +++++++++++++++++++++++----------------------- src/os.hpp | 3 +-- 3 files changed, 35 insertions(+), 46 deletions(-) diff --git a/src/cache_hash.cpp b/src/cache_hash.cpp index 5e6c3b9a9d50848435a071c3412dbe9d237c4b0a..4526a83c27023955b9137ed921aad44e8a0fdd73 100644 --- a/src/cache_hash.cpp +++ b/src/cache_hash.cpp @@ -222,14 +222,9 @@ static Error populate_file_hash(CacheHash *ch, CacheHashFile *chf, Buf *contents assert(chf->path != nullptr); OsFile this_file; - if ((err = os_file_open_r(chf->path, &this_file))) + if ((err = os_file_open_r(chf->path, &this_file, &chf->mtime))) return err; - if ((err = os_file_mtime(this_file, &chf->mtime))) { - os_file_close(this_file); - return err; - } - if ((err = hash_file(chf->bin_digest, this_file, contents))) { os_file_close(this_file); return err; @@ -351,17 +346,12 @@ Error cache_hit(CacheHash *ch, Buf *out_digest) { // if the mtime matches we can trust the digest OsFile this_file; - if ((err = os_file_open_r(chf->path, &this_file))) { + OsTimeStamp actual_mtime; + if ((err = os_file_open_r(chf->path, &this_file, &actual_mtime))) { fprintf(stderr, "Unable to open %s\n: %s", buf_ptr(chf->path), err_str(err)); os_file_close(ch->manifest_file); return ErrorCacheUnavailable; } - OsTimeStamp actual_mtime; - if ((err = os_file_mtime(this_file, &actual_mtime))) { - os_file_close(this_file); - os_file_close(ch->manifest_file); - return err; - } if (chf->mtime.sec == actual_mtime.sec && chf->mtime.nsec == actual_mtime.nsec) { os_file_close(this_file); } else { diff --git a/src/os.cpp b/src/os.cpp index 2f0379c09b2ee6403bd173025c5c85928f208384..715786735888ad6d15506513bf431b7907cda818 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -1808,7 +1808,7 @@ Error os_self_exe_shared_libs(ZigList &paths) { #endif } -Error os_file_open_r(Buf *full_path, OsFile *out_file) { +Error os_file_open_r(Buf *full_path, OsFile *out_file, OsTimeStamp *mtime) { #if defined(ZIG_OS_WINDOWS) // TODO use CreateFileW HANDLE result = CreateFileA(buf_ptr(full_path), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); @@ -1834,8 +1834,18 @@ Error os_file_open_r(Buf *full_path, OsFile *out_file) { return ErrorUnexpected; } } - *out_file = result; + + if (mtime != nullptr) { + FILETIME last_write_time; + if (!GetFileTime(file, nullptr, nullptr, &last_write_time)) { + CloseHandle(result); + return ErrorUnexpected; + } + mtime->sec = (((ULONGLONG) last_write_time.dwHighDateTime) << 32) + last_write_time.dwLowDateTime; + mtime->nsec = 0; + } + return ErrorNone; #else for (;;) { @@ -1858,7 +1868,26 @@ Error os_file_open_r(Buf *full_path, OsFile *out_file) { return ErrorFileSystem; } } + struct stat statbuf; + if (fstat(fd, &statbuf) == -1) { + close(fd); + return ErrorFileSystem; + } + if (S_ISDIR(statbuf.st_mode)) { + close(fd); + return ErrorIsDir; + } *out_file = fd; + + if (mtime != nullptr) { +#if defined(ZIG_OS_DARWIN) + mtime->sec = statbuf.st_mtimespec.tv_sec; + mtime->nsec = statbuf.st_mtimespec.tv_nsec; +#else + mtime->sec = statbuf.st_mtim.tv_sec; + mtime->nsec = statbuf.st_mtim.tv_nsec; +#endif + } return ErrorNone; } #endif @@ -1948,35 +1977,6 @@ Error os_file_open_lock_rw(Buf *full_path, OsFile *out_file) { #endif } -Error os_file_mtime(OsFile file, OsTimeStamp *mtime) { -#if defined(ZIG_OS_WINDOWS) - FILETIME last_write_time; - if (!GetFileTime(file, nullptr, nullptr, &last_write_time)) - return ErrorUnexpected; - mtime->sec = (((ULONGLONG) last_write_time.dwHighDateTime) << 32) + last_write_time.dwLowDateTime; - mtime->nsec = 0; - return ErrorNone; -#elif defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) - struct stat statbuf; - if (fstat(file, &statbuf) == -1) - return ErrorFileSystem; - - mtime->sec = statbuf.st_mtim.tv_sec; - mtime->nsec = statbuf.st_mtim.tv_nsec; - return ErrorNone; -#elif defined(ZIG_OS_DARWIN) - struct stat statbuf; - if (fstat(file, &statbuf) == -1) - return ErrorFileSystem; - - mtime->sec = statbuf.st_mtimespec.tv_sec; - mtime->nsec = statbuf.st_mtimespec.tv_nsec; - return ErrorNone; -#else -#error unimplemented -#endif -} - Error os_file_read(OsFile file, void *ptr, size_t *len) { #if defined(ZIG_OS_WINDOWS) DWORD amt_read; diff --git a/src/os.hpp b/src/os.hpp index d81a2362a559a0671204d06cf808cd4828d5587d..68ac6e017163f4967037b852e9b3ac84da1d5f8c 100644 --- a/src/os.hpp +++ b/src/os.hpp @@ -101,9 +101,8 @@ bool os_path_is_absolute(Buf *path); Error ATTRIBUTE_MUST_USE os_make_path(Buf *path); Error ATTRIBUTE_MUST_USE os_make_dir(Buf *path); -Error ATTRIBUTE_MUST_USE os_file_open_r(Buf *full_path, OsFile *out_file); +Error ATTRIBUTE_MUST_USE os_file_open_r(Buf *full_path, OsFile *out_file, OsTimeStamp *mtime); Error ATTRIBUTE_MUST_USE os_file_open_lock_rw(Buf *full_path, OsFile *out_file); -Error ATTRIBUTE_MUST_USE os_file_mtime(OsFile file, OsTimeStamp *mtime); Error ATTRIBUTE_MUST_USE os_file_read(OsFile file, void *ptr, size_t *len); Error ATTRIBUTE_MUST_USE os_file_read_all(OsFile file, Buf *contents); Error ATTRIBUTE_MUST_USE os_file_overwrite(OsFile file, Buf *contents); -- 2.54.0 From 169a789b343e9070b4658de9c8bd96c6736c6edc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 30 Jan 2019 16:12:30 -0500 Subject: [PATCH 26/37] fix test after merging ad8381e0d2936f this test slipped through due to branching --- test/compile_errors.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 2aca7e9141355bd4d4f638367b67d3e2f6da3f57..f2a2ba26415a1f3b94b42456ae71e19c18533662 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -10,7 +10,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return 5678; \\} , - ".tmp_source.zig:2:11: error: `&&` is invalid. Note that `and` is boolean AND.", + ".tmp_source.zig:2:12: error: `&&` is invalid. Note that `and` is boolean AND.", ); cases.add( -- 2.54.0 From 545064c1d9137a603e3e28aa066ce9e65a1ed4b6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 30 Jan 2019 23:36:52 -0500 Subject: [PATCH 27/37] introduce vector type for SIMD See #903 * create with `@Vector(len, ElemType)` * only wrapping addition is implemented This feature is far from complete; this is only the beginning. --- doc/langref.html.in | 34 +++++ src-self-hosted/type.zig | 16 +++ src/all_types.hpp | 26 ++++ src/analyze.cpp | 91 +++++++++++- src/analyze.hpp | 1 + src/codegen.cpp | 47 ++++++- src/ir.cpp | 213 +++++++++++++++++++++++------ src/ir_print.cpp | 11 ++ src/zig_llvm.cpp | 13 ++ src/zig_llvm.h | 3 + std/hash_map.zig | 2 + test/stage1/behavior/type_info.zig | 16 ++- 12 files changed, 419 insertions(+), 54 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 909c0f5817dc059cc31a3fc92a12bd4778380505..e192dbbf76d018ea950992b252fed27a00a87e2f 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -1531,6 +1531,29 @@ test "array initialization with function calls" { {#code_end#} {#see_also|for|Slices#} {#header_close#} + + {#header_open|Vectors#} +

+ A vector is a group of {#link|Integers#}, {#link|Floats#}, or {#link|Pointers#} which are operated on + in parallel using a single instruction ({#link|SIMD#}). Vector types are created with the builtin + function {#link|@Vector#}. +

+

+ TODO talk about C ABI interop +

+ {#header_open|SIMD#} +

+ TODO Zig's SIMD abilities are just beginning to be fleshed out. Here are some talking points to update the + docs with: + * What kind of operations can you do? All the operations on integers and floats? What about mixing scalar and vector? + * How to convert to/from vectors/arrays + * How to access individual elements from vectors, how to loop over the elements + * "shuffle" + * Advice on writing high perf software, how to abstract the best way +

+ {#header_close#} + {#header_close#} + {#header_open|Pointers#}

Zig has two kinds of pointers: @@ -6607,6 +6630,17 @@ pub const TypeInfo = union(TypeId) { expression passed as an argument. The expression is evaluated.

+ {#header_close#} + + {#header_open|@Vector#} +
{#syntax#}@Vector(comptime len: u32, comptime ElemType: type) type{#endsyntax#}
+

+ This function returns a vector type for {#link|SIMD#}. +

+

+ {#syntax#}ElemType{#endsyntax#} must be an {#link|integer|Integers#}, a {#link|float|Floats#}, or a + {#link|pointer|Pointers#}. +

{#header_close#} {#header_close#} diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index aa00bb876d54b2d4d0e600434bb0565497dfd443..fa3134390252917c54b0eb6ca16ad24d33896e91 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -44,6 +44,7 @@ pub const Type = struct { Id.ArgTuple => @fieldParentPtr(ArgTuple, "base", base).destroy(comp), Id.Opaque => @fieldParentPtr(Opaque, "base", base).destroy(comp), Id.Promise => @fieldParentPtr(Promise, "base", base).destroy(comp), + Id.Vector => @fieldParentPtr(Vector, "base", base).destroy(comp), } } @@ -77,6 +78,7 @@ pub const Type = struct { Id.ArgTuple => unreachable, Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(allocator, llvm_context), Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(allocator, llvm_context), + Id.Vector => return @fieldParentPtr(Vector, "base", base).getLlvmType(allocator, llvm_context), } } @@ -103,6 +105,7 @@ pub const Type = struct { Id.Enum, Id.Fn, Id.Promise, + Id.Vector, => return false, Id.Struct => @panic("TODO"), @@ -135,6 +138,7 @@ pub const Type = struct { Id.Float, Id.Fn, Id.Promise, + Id.Vector, => return true, Id.Pointer => { @@ -902,6 +906,18 @@ pub const Type = struct { } }; + pub const Vector = struct { + base: Type, + + pub fn destroy(self: *Vector, comp: *Compilation) void { + comp.gpa().destroy(self); + } + + pub fn getLlvmType(self: *Vector, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef { + @panic("TODO"); + } + }; + pub const ComptimeFloat = struct { base: Type, diff --git a/src/all_types.hpp b/src/all_types.hpp index 4b134361a39a62115364f2befab2a29f23b5cc06..f6fe72891d6a221ae411cf5ab7216ceae29b79b1 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -252,6 +252,10 @@ struct ConstArgTuple { size_t end_index; }; +struct ConstVector { + ConstExprValue *elements; +}; + enum ConstValSpecial { ConstValSpecialRuntime, ConstValSpecialStatic, @@ -318,6 +322,7 @@ struct ConstExprValue { ConstPtrValue x_ptr; ImportTableEntry *x_import; ConstArgTuple x_arg_tuple; + ConstVector x_vector; // populated if special == ConstValSpecialRuntime RuntimeHintErrorUnion rh_error_union; @@ -1210,6 +1215,12 @@ struct ZigTypePromise { ZigType *result_type; }; +struct ZigTypeVector { + // The type must be a pointer, integer, or float + ZigType *elem_type; + uint32_t len; +}; + enum ZigTypeId { ZigTypeIdInvalid, ZigTypeIdMetaType, @@ -1236,6 +1247,7 @@ enum ZigTypeId { ZigTypeIdArgTuple, ZigTypeIdOpaque, ZigTypeIdPromise, + ZigTypeIdVector, }; struct ZigType { @@ -1262,6 +1274,7 @@ struct ZigType { ZigTypeFn fn; ZigTypeBoundFn bound_fn; ZigTypePromise promise; + ZigTypeVector vector; } data; // use these fields to make sure we don't duplicate type table entries for the same type @@ -1415,6 +1428,7 @@ enum BuiltinFnId { BuiltinFnIdEnumToInt, BuiltinFnIdIntToEnum, BuiltinFnIdIntType, + BuiltinFnIdVectorType, BuiltinFnIdSetCold, BuiltinFnIdSetRuntimeSafety, BuiltinFnIdSetFloatMode, @@ -1505,6 +1519,10 @@ struct TypeId { ZigType *err_set_type; ZigType *payload_type; } error_union; + struct { + ZigType *elem_type; + uint32_t len; + } vector; } data; }; @@ -2139,6 +2157,7 @@ enum IrInstructionId { IrInstructionIdFloatToInt, IrInstructionIdBoolToInt, IrInstructionIdIntType, + IrInstructionIdVectorType, IrInstructionIdBoolNot, IrInstructionIdMemset, IrInstructionIdMemcpy, @@ -2807,6 +2826,13 @@ struct IrInstructionIntType { IrInstruction *bit_count; }; +struct IrInstructionVectorType { + IrInstruction base; + + IrInstruction *len; + IrInstruction *elem_type; +}; + struct IrInstructionBoolNot { IrInstruction base; diff --git a/src/analyze.cpp b/src/analyze.cpp index 194888068cf42f8eae2238ef34b64a0b148faea1..99378eb7a85298401f4bbea50a9d1088fe502f0f 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -250,6 +250,7 @@ AstNode *type_decl_node(ZigType *type_entry) { case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdPromise: + case ZigTypeIdVector: return nullptr; } zig_unreachable(); @@ -311,6 +312,7 @@ bool type_is_resolved(ZigType *type_entry, ResolveStatus status) { case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdPromise: + case ZigTypeIdVector: return true; } zig_unreachable(); @@ -1055,11 +1057,7 @@ bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id) { } if (g->zig_target.arch.arch == ZigLLVM_x86_64) { X64CABIClass abi_class = type_c_abi_x86_64_class(g, fn_type_id->return_type); - if (abi_class == X64CABIClass_MEMORY) { - return true; - } - zig_panic("TODO implement C ABI for x86_64 return types. type '%s'\nSee https://github.com/ziglang/zig/issues/1481", - buf_ptr(&fn_type_id->return_type->name)); + return abi_class == X64CABIClass_MEMORY; } else if (target_is_arm(&g->zig_target)) { return type_size(g, fn_type_id->return_type) > 16; } @@ -1424,6 +1422,7 @@ static bool type_allowed_in_packed_struct(ZigType *type_entry) { case ZigTypeIdPointer: case ZigTypeIdArray: case ZigTypeIdFn: + case ZigTypeIdVector: return true; case ZigTypeIdStruct: return type_entry->data.structure.layout == ContainerLayoutPacked; @@ -1472,6 +1471,8 @@ static bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry) { default: return false; } + case ZigTypeIdVector: + return type_allowed_in_extern(g, type_entry->data.vector.elem_type); case ZigTypeIdFloat: return true; case ZigTypeIdArray: @@ -1625,6 +1626,7 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdPromise: + case ZigTypeIdVector: switch (type_requires_comptime(g, type_entry)) { case ReqCompTimeNo: break; @@ -1720,6 +1722,7 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdPromise: + case ZigTypeIdVector: switch (type_requires_comptime(g, fn_type_id.return_type)) { case ReqCompTimeInvalid: return g->builtin_types.entry_invalid; @@ -3577,6 +3580,7 @@ ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry case ZigTypeIdFn: case ZigTypeIdBoundFn: case ZigTypeIdPromise: + case ZigTypeIdVector: return type_entry; } zig_unreachable(); @@ -3943,6 +3947,7 @@ static bool is_container(ZigType *type_entry) { case ZigTypeIdArgTuple: case ZigTypeIdOpaque: case ZigTypeIdPromise: + case ZigTypeIdVector: return false; } zig_unreachable(); @@ -4002,6 +4007,7 @@ void resolve_container_type(CodeGen *g, ZigType *type_entry) { case ZigTypeIdArgTuple: case ZigTypeIdOpaque: case ZigTypeIdPromise: + case ZigTypeIdVector: zig_unreachable(); } } @@ -4451,6 +4457,34 @@ ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) { return new_entry; } +ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type) { + TypeId type_id = {}; + type_id.id = ZigTypeIdVector; + type_id.data.vector.len = len; + type_id.data.vector.elem_type = elem_type; + + { + auto entry = g->type_table.maybe_get(type_id); + if (entry) + return entry->value; + } + + ZigType *entry = new_type_table_entry(ZigTypeIdVector); + entry->zero_bits = (len == 0) || !type_has_bits(elem_type); + entry->type_ref = entry->zero_bits ? LLVMVoidType() : LLVMVectorType(elem_type->type_ref, len); + entry->data.vector.len = len; + entry->data.vector.elem_type = elem_type; + + buf_resize(&entry->name, 0); + buf_appendf(&entry->name, "@Vector(%u, %s)", len, buf_ptr(&elem_type->name)); + + entry->di_type = ZigLLVMDIBuilderCreateVectorType(g->dbuilder, len, + LLVMABIAlignmentOfType(g->target_data_ref, entry->type_ref), elem_type->di_type); + + g->type_table.put(type_id, entry); + return entry; +} + ZigType **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type) { return &g->builtin_types.entry_c_int[c_int_type]; } @@ -4482,6 +4516,7 @@ bool handle_is_ptr(ZigType *type_entry) { case ZigTypeIdFn: case ZigTypeIdEnum: case ZigTypeIdPromise: + case ZigTypeIdVector: return false; case ZigTypeIdArray: case ZigTypeIdStruct: @@ -4914,6 +4949,9 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { return hash_const_val_error_set(const_val); case ZigTypeIdNamespace: return hash_ptr(const_val->data.x_import); + case ZigTypeIdVector: + // TODO better hashing algorithm + return 3647867726; case ZigTypeIdBoundFn: case ZigTypeIdInvalid: case ZigTypeIdUnreachable: @@ -4966,6 +5004,7 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) { case ZigTypeIdBool: case ZigTypeIdUnreachable: case ZigTypeIdInt: + case ZigTypeIdVector: case ZigTypeIdFloat: case ZigTypeIdComptimeFloat: case ZigTypeIdComptimeInt: @@ -5049,6 +5088,7 @@ static bool return_type_is_cacheable(ZigType *return_type) { case ZigTypeIdErrorSet: case ZigTypeIdEnum: case ZigTypeIdPointer: + case ZigTypeIdVector: return true; case ZigTypeIdArray: @@ -5201,6 +5241,7 @@ OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) { case ZigTypeIdErrorSet: case ZigTypeIdEnum: case ZigTypeIdInt: + case ZigTypeIdVector: return type_has_bits(type_entry) ? OnePossibleValueNo : OnePossibleValueYes; case ZigTypeIdPointer: return type_has_one_possible_value(g, type_entry->data.pointer.child_type); @@ -5251,6 +5292,7 @@ ReqCompTime type_requires_comptime(CodeGen *g, ZigType *type_entry) { case ZigTypeIdErrorSet: case ZigTypeIdBool: case ZigTypeIdInt: + case ZigTypeIdVector: case ZigTypeIdFloat: case ZigTypeIdVoid: case ZigTypeIdUnreachable: @@ -5777,7 +5819,7 @@ bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) { ConstExprValue *a_elems = a->data.x_array.data.s_none.elements; ConstExprValue *b_elems = b->data.x_array.data.s_none.elements; - for (size_t i = 0; i < len; ++i) { + for (size_t i = 0; i < len; i += 1) { if (!const_values_equal(g, &a_elems[i], &b_elems[i])) return false; } @@ -5811,6 +5853,20 @@ bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) { case ZigTypeIdArgTuple: return a->data.x_arg_tuple.start_index == b->data.x_arg_tuple.start_index && a->data.x_arg_tuple.end_index == b->data.x_arg_tuple.end_index; + case ZigTypeIdVector: { + assert(a->type->data.vector.len == b->type->data.vector.len); + + size_t len = a->type->data.vector.len; + ConstExprValue *a_elems = a->data.x_vector.elements; + ConstExprValue *b_elems = b->data.x_vector.elements; + + for (size_t i = 0; i < len; i += 1) { + if (!const_values_equal(g, &a_elems[i], &b_elems[i])) + return false; + } + + return true; + } case ZigTypeIdBoundFn: case ZigTypeIdInvalid: case ZigTypeIdUnreachable: @@ -6042,6 +6098,18 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { } } zig_unreachable(); + case ZigTypeIdVector: { + buf_appendf(buf, "%s{", buf_ptr(&type_entry->name)); + uint64_t len = type_entry->data.vector.len; + for (uint32_t i = 0; i < len; i += 1) { + if (i != 0) + buf_appendf(buf, ","); + ConstExprValue *child_value = &const_val->data.x_vector.elements[i]; + render_const_value(g, buf, child_value); + } + buf_appendf(buf, "}"); + return; + } case ZigTypeIdNull: { buf_appendf(buf, "null"); @@ -6200,6 +6268,8 @@ uint32_t type_id_hash(TypeId x) { case ZigTypeIdInt: return (x.data.integer.is_signed ? (uint32_t)2652528194 : (uint32_t)163929201) + (((uint32_t)x.data.integer.bit_count) ^ (uint32_t)2998081557); + case ZigTypeIdVector: + return hash_ptr(x.data.vector.elem_type) * (x.data.vector.len * 526582681); } zig_unreachable(); } @@ -6248,6 +6318,9 @@ bool type_id_eql(TypeId a, TypeId b) { case ZigTypeIdInt: return a.data.integer.is_signed == b.data.integer.is_signed && a.data.integer.bit_count == b.data.integer.bit_count; + case ZigTypeIdVector: + return a.data.vector.elem_type == b.data.vector.elem_type && + a.data.vector.len == b.data.vector.len; } zig_unreachable(); } @@ -6382,6 +6455,7 @@ static const ZigTypeId all_type_ids[] = { ZigTypeIdArgTuple, ZigTypeIdOpaque, ZigTypeIdPromise, + ZigTypeIdVector, }; ZigTypeId type_id_at_index(size_t index) { @@ -6447,6 +6521,8 @@ size_t type_id_index(ZigType *entry) { return 22; case ZigTypeIdPromise: return 23; + case ZigTypeIdVector: + return 24; } zig_unreachable(); } @@ -6503,6 +6579,8 @@ const char *type_id_name(ZigTypeId id) { return "Opaque"; case ZigTypeIdPromise: return "Promise"; + case ZigTypeIdVector: + return "Vector"; } zig_unreachable(); } @@ -6658,6 +6736,7 @@ X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty) { case ZigTypeIdBool: return X64CABIClass_INTEGER; case ZigTypeIdFloat: + case ZigTypeIdVector: return X64CABIClass_SSE; case ZigTypeIdStruct: { // "If the size of an object is larger than four eightbytes, or it contains unaligned diff --git a/src/analyze.hpp b/src/analyze.hpp index 1bac15ebcce7f60cb360448adcf44e2f303e62f3..da5265a59416758ff5a6f2dc85b02465a86da1b2 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -20,6 +20,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons uint64_t type_size(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); ZigType **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type); ZigType *get_c_int_type(CodeGen *g, CIntType c_int_type); ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id); diff --git a/src/codegen.cpp b/src/codegen.cpp index 2f360735dd0630f1a542129193d52c28c909c2de..e2576f53359ed3066e7ff4f72cbd39ee9937fa7f 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1980,7 +1980,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ break; } - if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat || + if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat || ty->id == ZigTypeIdVector || ty->id == ZigTypeIdInt // TODO investigate if we need to change this ) { switch (fn_walk->id) { @@ -2660,6 +2660,27 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, } else { return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, ""); } + } else if (type_entry->id == ZigTypeIdVector) { + ZigType *elem_type = type_entry->data.vector.elem_type; + if (elem_type->id == ZigTypeIdFloat) { + ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); + return LLVMBuildFAdd(g->builder, op1_value, op2_value, ""); + } else if (elem_type->id == ZigTypeIdPointer) { + zig_panic("TODO codegen for pointers in vectors"); + } else if (elem_type->id == ZigTypeIdInt) { + bool is_wrapping = (op_id == IrBinOpAddWrap); + if (is_wrapping) { + return LLVMBuildAdd(g->builder, op1_value, op2_value, ""); + } else if (want_runtime_safety) { + zig_panic("TODO runtime safety for vector integer addition"); + } else if (elem_type->data.integral.is_signed) { + return LLVMBuildNSWAdd(g->builder, op1_value, op2_value, ""); + } else { + return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, ""); + } + } else { + zig_unreachable(); + } } else { zig_unreachable(); } @@ -5211,6 +5232,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdCUndef: case IrInstructionIdEmbedFile: case IrInstructionIdIntType: + case IrInstructionIdVectorType: case IrInstructionIdMemberCount: case IrInstructionIdMemberType: case IrInstructionIdMemberName: @@ -5620,6 +5642,8 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con } case ZigTypeIdArray: zig_panic("TODO bit pack an array"); + case ZigTypeIdVector: + zig_panic("TODO bit pack a vector"); case ZigTypeIdUnion: zig_panic("TODO bit pack a union"); case ZigTypeIdStruct: @@ -5992,6 +6016,14 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c } } } + case ZigTypeIdVector: { + uint32_t len = type_entry->data.vector.len; + LLVMValueRef *values = allocate(len); + for (uint32_t i = 0; i < len; i += 1) { + values[i] = gen_const_val(g, &const_val->data.x_vector.elements[i], ""); + } + return LLVMConstVector(values, len); + } case ZigTypeIdUnion: { LLVMTypeRef union_type_ref = type_entry->data.unionation.union_type_ref; @@ -6927,6 +6959,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1); create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); // TODO rename to Int + create_builtin_fn(g, BuiltinFnIdVectorType, "Vector", 2); create_builtin_fn(g, BuiltinFnIdSetCold, "setCold", 1); create_builtin_fn(g, BuiltinFnIdSetRuntimeSafety, "setRuntimeSafety", 1); create_builtin_fn(g, BuiltinFnIdSetFloatMode, "setFloatMode", 1); @@ -7152,6 +7185,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { " ArgTuple: void,\n" " Opaque: void,\n" " Promise: Promise,\n" + " Vector: Vector,\n" "\n\n" " pub const Int = struct {\n" " is_signed: bool,\n" @@ -7270,6 +7304,11 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { " child: ?type,\n" " };\n" "\n" + " pub const Vector = struct {\n" + " len: u32,\n" + " child: type,\n" + " };\n" + "\n" " pub const Definition = struct {\n" " name: []const u8,\n" " is_pub: bool,\n" @@ -7841,6 +7880,9 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, ZigType *type_e case ZigTypeIdArray: prepend_c_type_to_decl_list(g, gen_h, type_entry->data.array.child_type); return; + case ZigTypeIdVector: + prepend_c_type_to_decl_list(g, gen_h, type_entry->data.vector.elem_type); + return; case ZigTypeIdOptional: prepend_c_type_to_decl_list(g, gen_h, type_entry->data.maybe.child_type); return; @@ -7972,6 +8014,8 @@ static void get_c_type(CodeGen *g, GenH *gen_h, ZigType *type_entry, Buf *out_bu buf_appendf(out_buf, "%s", buf_ptr(child_buf)); return; } + case ZigTypeIdVector: + zig_panic("TODO implement get_c_type for vector types"); case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: case ZigTypeIdFn: @@ -8137,6 +8181,7 @@ static void gen_h_file(CodeGen *g) { case ZigTypeIdOptional: case ZigTypeIdFn: case ZigTypeIdPromise: + case ZigTypeIdVector: zig_unreachable(); case ZigTypeIdEnum: if (type_entry->data.enumeration.layout == ContainerLayoutExtern) { diff --git a/src/ir.cpp b/src/ir.cpp index 06eb4a47f1cfe113df5633869e32508a01131c01..d5152f3c85e9490ef847536b8006328d3a35e580 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -587,6 +587,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionIntType *) { return IrInstructionIdIntType; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionVectorType *) { + return IrInstructionIdVectorType; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionBoolNot *) { return IrInstructionIdBoolNot; } @@ -1953,6 +1957,19 @@ static IrInstruction *ir_build_int_type(IrBuilder *irb, Scope *scope, AstNode *s return &instruction->base; } +static IrInstruction *ir_build_vector_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *len, + IrInstruction *elem_type) +{ + IrInstructionVectorType *instruction = ir_build_instruction(irb, scope, source_node); + instruction->len = len; + instruction->elem_type = elem_type; + + ir_ref_instruction(len, irb->current_basic_block); + ir_ref_instruction(elem_type, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_bool_not(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionBoolNot *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; @@ -4230,6 +4247,21 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *int_type = ir_build_int_type(irb, scope, node, arg0_value, arg1_value); return ir_lval_wrap(irb, scope, int_type, lval); } + case BuiltinFnIdVectorType: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_instruction) + return arg1_value; + + IrInstruction *vector_type = ir_build_vector_type(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, vector_type, lval); + } case BuiltinFnIdMemcpy: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -11617,6 +11649,7 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp * case ZigTypeIdComptimeInt: case ZigTypeIdInt: case ZigTypeIdFloat: + case ZigTypeIdVector: operator_allowed = true; break; @@ -12032,6 +12065,48 @@ static IrInstruction *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *b return result; } +static bool ok_float_op(IrBinOp op) { + switch (op) { + case IrBinOpInvalid: + zig_unreachable(); + case IrBinOpAdd: + case IrBinOpSub: + case IrBinOpMult: + case IrBinOpDivUnspecified: + case IrBinOpDivTrunc: + case IrBinOpDivFloor: + case IrBinOpDivExact: + case IrBinOpRemRem: + case IrBinOpRemMod: + return true; + + case IrBinOpBoolOr: + case IrBinOpBoolAnd: + case IrBinOpCmpEq: + case IrBinOpCmpNotEq: + case IrBinOpCmpLessThan: + case IrBinOpCmpGreaterThan: + case IrBinOpCmpLessOrEq: + case IrBinOpCmpGreaterOrEq: + case IrBinOpBinOr: + case IrBinOpBinXor: + case IrBinOpBinAnd: + case IrBinOpBitShiftLeftLossy: + case IrBinOpBitShiftLeftExact: + case IrBinOpBitShiftRightLossy: + case IrBinOpBitShiftRightExact: + case IrBinOpAddWrap: + case IrBinOpSubWrap: + case IrBinOpMultWrap: + case IrBinOpRemUnspecified: + case IrBinOpArrayCat: + case IrBinOpArrayMult: + case IrBinOpMergeErrorSets: + return false; + } + zig_unreachable(); +} + static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp *instruction) { IrInstruction *op1 = instruction->op1->child; if (type_is_invalid(op1->value.type)) @@ -12169,21 +12244,20 @@ static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp op_id = IrBinOpRemRem; } + bool ok = false; if (is_int) { - // int - } else if (is_float && - (op_id == IrBinOpAdd || - op_id == IrBinOpSub || - op_id == IrBinOpMult || - op_id == IrBinOpDivUnspecified || - op_id == IrBinOpDivTrunc || - op_id == IrBinOpDivFloor || - op_id == IrBinOpDivExact || - op_id == IrBinOpRemRem || - op_id == IrBinOpRemMod)) - { - // float - } else { + ok = true; + } else if (is_float && ok_float_op(op_id)) { + ok = true; + } else if (resolved_type->id == ZigTypeIdVector) { + ZigType *elem_type = resolved_type->data.vector.elem_type; + if (elem_type->id == ZigTypeIdInt || elem_type->id == ZigTypeIdComptimeInt) { + ok = true; + } else if ((elem_type->id == ZigTypeIdFloat || elem_type->id == ZigTypeIdComptimeFloat) && ok_float_op(op_id)) { + ok = true; + } + } + if (!ok) { AstNode *source_node = instruction->base.source_node; ir_add_error_node(ira, source_node, buf_sprintf("invalid operands to binary expression: '%s' and '%s'", @@ -12817,6 +12891,7 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio case ZigTypeIdPointer: case ZigTypeIdArray: case ZigTypeIdBool: + case ZigTypeIdVector: break; case ZigTypeIdMetaType: case ZigTypeIdVoid: @@ -12851,6 +12926,7 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio case ZigTypeIdOptional: case ZigTypeIdErrorUnion: case ZigTypeIdErrorSet: + case ZigTypeIdVector: zig_panic("TODO export const value of type %s", buf_ptr(&target->value.type->name)); case ZigTypeIdNamespace: case ZigTypeIdBoundFn: @@ -14009,6 +14085,7 @@ static IrInstruction *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_ case ZigTypeIdVoid: case ZigTypeIdBool: case ZigTypeIdInt: + case ZigTypeIdVector: case ZigTypeIdFloat: case ZigTypeIdPointer: case ZigTypeIdArray: @@ -15383,37 +15460,7 @@ static IrInstruction *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructio ZigType *type_entry = expr_value->value.type; if (type_is_invalid(type_entry)) return ira->codegen->invalid_instruction; - switch (type_entry->id) { - case ZigTypeIdInvalid: - zig_unreachable(); // handled above - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - case ZigTypeIdNamespace: - case ZigTypeIdBoundFn: - case ZigTypeIdMetaType: - case ZigTypeIdVoid: - case ZigTypeIdBool: - case ZigTypeIdUnreachable: - case ZigTypeIdInt: - case ZigTypeIdFloat: - case ZigTypeIdPointer: - case ZigTypeIdArray: - case ZigTypeIdStruct: - case ZigTypeIdOptional: - case ZigTypeIdErrorUnion: - case ZigTypeIdErrorSet: - case ZigTypeIdEnum: - case ZigTypeIdUnion: - case ZigTypeIdFn: - case ZigTypeIdArgTuple: - case ZigTypeIdOpaque: - case ZigTypeIdPromise: - return ir_const_type(ira, &typeof_instruction->base, type_entry); - } - - zig_unreachable(); + return ir_const_type(ira, &typeof_instruction->base, type_entry); } static IrInstruction *ir_analyze_instruction_to_ptr_type(IrAnalyze *ira, @@ -15652,6 +15699,7 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira, case ZigTypeIdNamespace: case ZigTypeIdBoundFn: case ZigTypeIdPromise: + case ZigTypeIdVector: { if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; @@ -15772,6 +15820,7 @@ static IrInstruction *ir_analyze_instruction_array_type(IrAnalyze *ira, case ZigTypeIdNamespace: case ZigTypeIdBoundFn: case ZigTypeIdPromise: + case ZigTypeIdVector: { if ((err = ensure_complete_type(ira->codegen, child_type))) return ira->codegen->invalid_instruction; @@ -15838,6 +15887,7 @@ static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira, case ZigTypeIdUnion: case ZigTypeIdFn: case ZigTypeIdPromise: + case ZigTypeIdVector: { uint64_t size_in_bytes = type_size(ira->codegen, type_entry); return ir_const_unsigned(ira, &size_of_instruction->base, size_in_bytes); @@ -16307,6 +16357,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, case ZigTypeIdBoundFn: case ZigTypeIdArgTuple: case ZigTypeIdOpaque: + case ZigTypeIdVector: ir_add_error(ira, &switch_target_instruction->base, buf_sprintf("invalid switch target type '%s'", buf_ptr(&target_type->name))); return ira->codegen->invalid_instruction; @@ -17496,6 +17547,27 @@ static Error ir_make_type_info_value(IrAnalyze *ira, ZigType *type_entry, ConstE break; } + case ZigTypeIdVector: { + result = create_const_vals(1); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Vector", nullptr); + + ConstExprValue *fields = create_const_vals(2); + result->data.x_struct.fields = fields; + + // len: usize + ensure_field_index(result->type, "len", 0); + fields[0].special = ConstValSpecialStatic; + fields[0].type = ira->codegen->builtin_types.entry_u32; + bigint_init_unsigned(&fields[0].data.x_bigint, type_entry->data.vector.len); + // child: type + ensure_field_index(result->type, "child", 1); + fields[1].special = ConstValSpecialStatic; + fields[1].type = ira->codegen->builtin_types.entry_type; + fields[1].data.x_type = type_entry->data.vector.elem_type; + + break; + } case ZigTypeIdOptional: { result = create_const_vals(1); @@ -18671,6 +18743,30 @@ static IrInstruction *ir_analyze_instruction_int_type(IrAnalyze *ira, IrInstruct return ir_const_type(ira, &instruction->base, get_int_type(ira->codegen, is_signed, (uint32_t)bit_count)); } +static IrInstruction *ir_analyze_instruction_vector_type(IrAnalyze *ira, IrInstructionVectorType *instruction) { + uint64_t len; + if (!ir_resolve_unsigned(ira, instruction->len->child, ira->codegen->builtin_types.entry_u32, &len)) + return ira->codegen->invalid_instruction; + + ZigType *elem_type = ir_resolve_type(ira, instruction->elem_type->child); + if (type_is_invalid(elem_type)) + return ira->codegen->invalid_instruction; + + if (elem_type->id != ZigTypeIdInt && + elem_type->id != ZigTypeIdFloat && + get_codegen_ptr_type(elem_type) == nullptr) + { + ir_add_error(ira, instruction->elem_type, + buf_sprintf("vector element type must be integer, float, or pointer; '%s' is invalid", + buf_ptr(&elem_type->name))); + return ira->codegen->invalid_instruction; + } + + ZigType *vector_type = get_vector_type(ira->codegen, len, elem_type); + + return ir_const_type(ira, &instruction->base, vector_type); +} + static IrInstruction *ir_analyze_instruction_bool_not(IrAnalyze *ira, IrInstructionBoolNot *instruction) { IrInstruction *value = instruction->value->child; if (type_is_invalid(value->value.type)) @@ -19474,6 +19570,7 @@ static IrInstruction *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruct case ZigTypeIdEnum: case ZigTypeIdUnion: case ZigTypeIdFn: + case ZigTypeIdVector: { uint64_t align_in_bytes = get_abi_alignment(ira->codegen, type_entry); return ir_const_unsigned(ira, &instruction->base, align_in_bytes); @@ -20311,6 +20408,15 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue } } return; + case ZigTypeIdVector: { + size_t buf_i = 0; + for (uint32_t elem_i = 0; elem_i < val->type->data.vector.len; elem_i += 1) { + ConstExprValue *elem = &val->data.x_vector.elements[elem_i]; + buf_write_value_bytes(codegen, &buf[buf_i], elem); + buf_i += type_size(codegen, elem->type); + } + return; + } case ZigTypeIdStruct: zig_panic("TODO buf_write_value_bytes struct type"); case ZigTypeIdOptional: @@ -20387,6 +20493,20 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou } zig_unreachable(); } + case ZigTypeIdVector: { + uint64_t elem_size = type_size(codegen, val->type->data.vector.elem_type); + uint32_t len = val->type->data.vector.len; + + val->data.x_vector.elements = create_const_vals(len); + for (uint32_t i = 0; i < len; i += 1) { + ConstExprValue *elem = &val->data.x_vector.elements[i]; + elem->special = ConstValSpecialStatic; + elem->type = val->type->data.vector.elem_type; + if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem))) + return err; + } + return ErrorNone; + } case ZigTypeIdEnum: switch (val->type->data.enumeration.layout) { case ContainerLayoutAuto: @@ -21633,6 +21753,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio return ir_analyze_instruction_bool_to_int(ira, (IrInstructionBoolToInt *)instruction); case IrInstructionIdIntType: return ir_analyze_instruction_int_type(ira, (IrInstructionIntType *)instruction); + case IrInstructionIdVectorType: + return ir_analyze_instruction_vector_type(ira, (IrInstructionVectorType *)instruction); case IrInstructionIdBoolNot: return ir_analyze_instruction_bool_not(ira, (IrInstructionBoolNot *)instruction); case IrInstructionIdMemset: @@ -21943,6 +22065,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdEmbedFile: case IrInstructionIdTruncate: case IrInstructionIdIntType: + case IrInstructionIdVectorType: case IrInstructionIdBoolNot: case IrInstructionIdSlice: case IrInstructionIdMemberCount: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index a3ec8e9d3589d5098d603d351552dde5daa06071..a1fd450b65f6a07739a1b7d9fa48a8e91f52afc4 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -719,6 +719,14 @@ static void ir_print_int_type(IrPrint *irp, IrInstructionIntType *instruction) { fprintf(irp->f, ")"); } +static void ir_print_vector_type(IrPrint *irp, IrInstructionVectorType *instruction) { + fprintf(irp->f, "@Vector("); + ir_print_other_instruction(irp, instruction->len); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->elem_type); + fprintf(irp->f, ")"); +} + static void ir_print_bool_not(IrPrint *irp, IrInstructionBoolNot *instruction) { fprintf(irp->f, "! "); ir_print_other_instruction(irp, instruction->value); @@ -1577,6 +1585,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdIntType: ir_print_int_type(irp, (IrInstructionIntType *)instruction); break; + case IrInstructionIdVectorType: + ir_print_vector_type(irp, (IrInstructionVectorType *)instruction); + break; case IrInstructionIdBoolNot: ir_print_bool_not(irp, (IrInstructionBoolNot *)instruction); break; diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index 3c01a0954dec2e27148f6dc7055743a5c4d7ee92..65f6b5abb7e233c50efaa99a84b00e89a2086e4c 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -263,6 +263,19 @@ ZigLLVMDIType *ZigLLVMCreateDebugBasicType(ZigLLVMDIBuilder *dibuilder, const ch return reinterpret_cast(di_type); } +struct ZigLLVMDIType *ZigLLVMDIBuilderCreateVectorType(struct ZigLLVMDIBuilder *dibuilder, + uint64_t Size, uint32_t AlignInBits, struct ZigLLVMDIType *Ty) +{ + SmallVector subrange; + subrange.push_back(reinterpret_cast(dibuilder)->getOrCreateSubrange(0, Size)); + DIType *di_type = reinterpret_cast(dibuilder)->createVectorType( + Size, + AlignInBits, + reinterpret_cast(Ty), + reinterpret_cast(dibuilder)->getOrCreateArray(subrange)); + return reinterpret_cast(di_type); +} + ZigLLVMDIType *ZigLLVMCreateDebugArrayType(ZigLLVMDIBuilder *dibuilder, uint64_t size_in_bits, uint64_t align_in_bits, ZigLLVMDIType *elem_type, int elem_count) { diff --git a/src/zig_llvm.h b/src/zig_llvm.h index 551a4a744865207ccb4a5f4f1d727896637d1f2b..26dca1198bf73ec570e5039f93a78ad32ccb867f 100644 --- a/src/zig_llvm.h +++ b/src/zig_llvm.h @@ -191,6 +191,9 @@ ZIG_EXTERN_C struct ZigLLVMDISubprogram *ZigLLVMCreateFunction(struct ZigLLVMDIB unsigned lineno, struct ZigLLVMDIType *fn_di_type, bool is_local_to_unit, bool is_definition, unsigned scope_line, unsigned flags, bool is_optimized, struct ZigLLVMDISubprogram *decl_subprogram); +ZIG_EXTERN_C struct ZigLLVMDIType *ZigLLVMDIBuilderCreateVectorType(struct ZigLLVMDIBuilder *dibuilder, + uint64_t Size, uint32_t AlignInBits, struct ZigLLVMDIType *Ty); + ZIG_EXTERN_C void ZigLLVMFnSetSubprogram(LLVMValueRef fn, struct ZigLLVMDISubprogram *subprogram); ZIG_EXTERN_C void ZigLLVMDIBuilderFinalize(struct ZigLLVMDIBuilder *dibuilder); diff --git a/std/hash_map.zig b/std/hash_map.zig index 99237047e008b0a42464f1c295babe9e44a191f1..a63a549814fbc17262a5adfb596db1a7cf5d9b56 100644 --- a/std/hash_map.zig +++ b/std/hash_map.zig @@ -508,6 +508,7 @@ pub fn autoHash(key: var, comptime rng: *std.rand.Random, comptime HashInt: type builtin.TypeId.Optional => @compileError("TODO auto hash for optionals"), builtin.TypeId.Array => @compileError("TODO auto hash for arrays"), + builtin.TypeId.Vector => @compileError("TODO auto hash for vectors"), builtin.TypeId.Struct => @compileError("TODO auto hash for structs"), builtin.TypeId.Union => @compileError("TODO auto hash for unions"), builtin.TypeId.ErrorUnion => @compileError("TODO auto hash for unions"), @@ -555,5 +556,6 @@ pub fn autoEql(a: var, b: @typeOf(a)) bool { builtin.TypeId.Struct => @compileError("TODO auto eql for structs"), builtin.TypeId.Union => @compileError("TODO auto eql for unions"), builtin.TypeId.ErrorUnion => @compileError("TODO auto eql for unions"), + builtin.TypeId.Vector => @compileError("TODO auto eql for vectors"), } } diff --git a/test/stage1/behavior/type_info.zig b/test/stage1/behavior/type_info.zig index 5ad80e06e178f48e755f54c28f50389bd0d53c91..f3bb17b282a66e857379d4846c0f9098b39a9421 100644 --- a/test/stage1/behavior/type_info.zig +++ b/test/stage1/behavior/type_info.zig @@ -171,11 +171,11 @@ fn testUnion() void { assertOrPanic(TypeId(typeinfo_info) == TypeId.Union); assertOrPanic(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto); assertOrPanic(typeinfo_info.Union.tag_type.? == TypeId); - assertOrPanic(typeinfo_info.Union.fields.len == 24); + assertOrPanic(typeinfo_info.Union.fields.len == 25); assertOrPanic(typeinfo_info.Union.fields[4].enum_field != null); assertOrPanic(typeinfo_info.Union.fields[4].enum_field.?.value == 4); assertOrPanic(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int)); - assertOrPanic(typeinfo_info.Union.defs.len == 20); + assertOrPanic(typeinfo_info.Union.defs.len == 21); const TestNoTagUnion = union { Foo: void, @@ -262,3 +262,15 @@ test "typeInfo with comptime parameter in struct fn def" { }; comptime var info = @typeInfo(S); } + +test "type info: vectors" { + testVector(); + comptime testVector(); +} + +fn testVector() void { + const vec_info = @typeInfo(@Vector(4, i32)); + assertOrPanic(TypeId(vec_info) == TypeId.Vector); + assertOrPanic(vec_info.Vector.len == 4); + assertOrPanic(vec_info.Vector.child == i32); +} -- 2.54.0 From 28873e762295d0e03bf913d45d5317aae8d57bcd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 31 Jan 2019 16:52:59 -0500 Subject: [PATCH 28/37] os.cpp: fix regression on Windows from 59c050e7 --- src/os.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/os.cpp b/src/os.cpp index 715786735888ad6d15506513bf431b7907cda818..8eb6b346541c2fe2b8105418c994d055e40facd3 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -1838,7 +1838,7 @@ Error os_file_open_r(Buf *full_path, OsFile *out_file, OsTimeStamp *mtime) { if (mtime != nullptr) { FILETIME last_write_time; - if (!GetFileTime(file, nullptr, nullptr, &last_write_time)) { + if (!GetFileTime(result, nullptr, nullptr, &last_write_time)) { CloseHandle(result); return ErrorUnexpected; } -- 2.54.0 From 7a4ed10981cf7ac5bed93b2c9bad280ad31c50cf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 31 Jan 2019 18:47:45 -0500 Subject: [PATCH 29/37] darwin: fix incorrect timeval struct type closes #1648 I also added some more extern functions for darwin time functions, although nothing depends on them currently. --- std/c/darwin.zig | 22 ++++++++++++++++++++-- std/c/index.zig | 2 +- std/os/time.zig | 2 -- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/std/c/darwin.zig b/std/c/darwin.zig index 86c5e4828e5ad5d37c8574e06444899eb37ec39a..4f0ea4a02bd95c24c30e2cbf723418e4978446ea 100644 --- a/std/c/darwin.zig +++ b/std/c/darwin.zig @@ -73,8 +73,8 @@ pub const sockaddr_in6 = extern struct { }; pub const timeval = extern struct { - tv_sec: isize, - tv_usec: isize, + tv_sec: c_long, + tv_usec: i32, }; pub const timezone = extern struct { @@ -176,6 +176,24 @@ pub const kevent64_s = extern struct { ext: [2]u64, }; +pub const mach_port_t = c_uint; +pub const clock_serv_t = mach_port_t; +pub const clock_res_t = c_int; +pub const mach_port_name_t = natural_t; +pub const natural_t = c_uint; +pub const mach_timespec_t = extern struct { + tv_sec: c_uint, + tv_nsec: clock_res_t, +}; +pub const kern_return_t = c_int; +pub const host_t = mach_port_t; +pub const CALENDAR_CLOCK = 1; + +pub extern fn mach_host_self() mach_port_t; +pub extern fn clock_get_time(clock_serv: clock_serv_t, cur_time: *mach_timespec_t) kern_return_t; +pub extern fn host_get_clock_service(host: host_t, clock_id: clock_id_t, clock_serv: ?[*]clock_serv_t) kern_return_t; +pub extern fn mach_port_deallocate(task: ipc_space_t, name: mach_port_name_t) kern_return_t; + // sys/types.h on macos uses #pragma pack() so these checks are // to make sure the struct is laid out the same. These values were // produced from C code using the offsetof macro. diff --git a/std/c/index.zig b/std/c/index.zig index 4aab39d931d9a5548382df931146aff18a9cbca8..058d879c093e1a65ed40c8e2d76ae6868f692e27 100644 --- a/std/c/index.zig +++ b/std/c/index.zig @@ -44,7 +44,7 @@ pub extern "c" fn dup2(old_fd: c_int, new_fd: c_int) c_int; pub extern "c" fn readlink(noalias path: [*]const u8, noalias buf: [*]u8, bufsize: usize) isize; pub extern "c" fn realpath(noalias file_name: [*]const u8, noalias resolved_name: [*]u8) ?[*]u8; pub extern "c" fn sigprocmask(how: c_int, noalias set: *const sigset_t, noalias oset: ?*sigset_t) c_int; -pub extern "c" fn gettimeofday(tv: ?*timeval, tz: ?*timezone) c_int; +pub extern "c" fn gettimeofday(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int; pub extern "c" fn sigaction(sig: c_int, noalias act: *const Sigaction, noalias oact: ?*Sigaction) c_int; pub extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int; pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int; diff --git a/std/os/time.zig b/std/os/time.zig index 304f65206a1cac055cb9330064ceb092a3827171..4b11a69376ff1799ed7cfb58be566f6dcdd749d2 100644 --- a/std/os/time.zig +++ b/std/os/time.zig @@ -79,8 +79,6 @@ fn milliTimestampWindows() u64 { } fn milliTimestampDarwin() u64 { - //Sources suggest MacOS 10.12 has support for - // posix clock_gettime. var tv: darwin.timeval = undefined; var err = darwin.gettimeofday(&tv, null); debug.assert(err == 0); -- 2.54.0 From bfc1772d8ead9e8fcde0a315411ddcd040cc3d28 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 1 Feb 2019 12:22:21 -0500 Subject: [PATCH 30/37] fixups --- CMakeLists.txt | 31 ++++---- std/fmt/index.zig | 2 - std/index.zig | 2 + std/mutex.zig | 61 +++------------- std/os/windows/kernel32.zig | 14 ++-- std/statically_initialized_mutex.zig | 105 +++++++++++++++++++++++++++ 6 files changed, 142 insertions(+), 73 deletions(-) create mode 100644 std/statically_initialized_mutex.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 3841519f295f72ea624c92f59cbbc71b4a97c47b..4dd6a1dcfa620c3796801b895101402d2f5c66d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -454,10 +454,10 @@ set(ZIG_STD_FILES "crypto/hmac.zig" "crypto/index.zig" "crypto/md5.zig" + "crypto/poly1305.zig" "crypto/sha1.zig" "crypto/sha2.zig" "crypto/sha3.zig" - "crypto/poly1305.zig" "crypto/x25519.zig" "cstr.zig" "debug/failing_allocator.zig" @@ -566,9 +566,9 @@ set(ZIG_STD_FILES "math/tan.zig" "math/tanh.zig" "math/trunc.zig" + "mem.zig" "meta/index.zig" "meta/trait.zig" - "mem.zig" "mutex.zig" "net.zig" "os/child_process.zig" @@ -576,16 +576,16 @@ set(ZIG_STD_FILES "os/darwin/errno.zig" "os/epoch.zig" "os/file.zig" + "os/freebsd/errno.zig" + "os/freebsd/index.zig" "os/get_app_data_dir.zig" "os/get_user_id.zig" "os/index.zig" + "os/linux/arm64.zig" "os/linux/errno.zig" "os/linux/index.zig" "os/linux/vdso.zig" "os/linux/x86_64.zig" - "os/linux/arm64.zig" - "os/freebsd/errno.zig" - "os/freebsd/index.zig" "os/path.zig" "os/time.zig" "os/uefi.zig" @@ -612,6 +612,16 @@ set(ZIG_STD_FILES "special/compiler_rt/comparetf2.zig" "special/compiler_rt/divti3.zig" "special/compiler_rt/extendXfYf2.zig" + "special/compiler_rt/fixdfdi.zig" + "special/compiler_rt/fixdfsi.zig" + "special/compiler_rt/fixdfti.zig" + "special/compiler_rt/fixint.zig" + "special/compiler_rt/fixsfdi.zig" + "special/compiler_rt/fixsfsi.zig" + "special/compiler_rt/fixsfti.zig" + "special/compiler_rt/fixtfdi.zig" + "special/compiler_rt/fixtfsi.zig" + "special/compiler_rt/fixtfti.zig" "special/compiler_rt/fixuint.zig" "special/compiler_rt/fixunsdfdi.zig" "special/compiler_rt/fixunsdfsi.zig" @@ -622,16 +632,6 @@ set(ZIG_STD_FILES "special/compiler_rt/fixunstfdi.zig" "special/compiler_rt/fixunstfsi.zig" "special/compiler_rt/fixunstfti.zig" - "special/compiler_rt/fixint.zig" - "special/compiler_rt/fixdfdi.zig" - "special/compiler_rt/fixdfsi.zig" - "special/compiler_rt/fixdfti.zig" - "special/compiler_rt/fixsfdi.zig" - "special/compiler_rt/fixsfsi.zig" - "special/compiler_rt/fixsfti.zig" - "special/compiler_rt/fixtfdi.zig" - "special/compiler_rt/fixtfsi.zig" - "special/compiler_rt/fixtfti.zig" "special/compiler_rt/floattidf.zig" "special/compiler_rt/floattisf.zig" "special/compiler_rt/floattitf.zig" @@ -656,6 +656,7 @@ set(ZIG_STD_FILES "special/panic.zig" "special/test_runner.zig" "spinlock.zig" + "statically_initialized_mutex.zig" "unicode.zig" "zig/ast.zig" "zig/index.zig" diff --git a/std/fmt/index.zig b/std/fmt/index.zig index b010072273b5a359ed8bf5d59dcc59507dad935d..6097a12c23e8f5e99a2b8359ad78a02f33c72324 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -982,13 +982,11 @@ test "fmt.format" { context = BufPrintContext{ .remaining = buf1[0..] }; try formatType('a', "c", &context, error{BufferTooSmall}, bufPrintWrite); res = buf1[0 .. buf1.len - context.remaining.len]; - debug.warn("{}\n", res); assert(mem.eql(u8, res, "a")); context = BufPrintContext{ .remaining = buf1[0..] }; try formatType(0b1100, "b", &context, error{BufferTooSmall}, bufPrintWrite); res = buf1[0 .. buf1.len - context.remaining.len]; - debug.warn("{}\n", res); assert(mem.eql(u8, res, "1100")); } { diff --git a/std/index.zig b/std/index.zig index 33eec14b0e58935dffcd40b81e2c025456ac29c1..80d1e46bb6cb069874d79f251f726b15e7cfbc5d 100644 --- a/std/index.zig +++ b/std/index.zig @@ -9,6 +9,7 @@ pub const DynLib = @import("dynamic_library.zig").DynLib; pub const HashMap = @import("hash_map.zig").HashMap; pub const LinkedList = @import("linked_list.zig").LinkedList; pub const Mutex = @import("mutex.zig").Mutex; +pub const StaticallyInitializedMutex = @import("statically_initialized_mutex.zig").StaticallyInitializedMutex; pub const SegmentedList = @import("segmented_list.zig").SegmentedList; pub const SpinLock = @import("spinlock.zig").SpinLock; @@ -55,6 +56,7 @@ test "std" { _ = @import("hash_map.zig"); _ = @import("linked_list.zig"); _ = @import("mutex.zig"); + _ = @import("statically_initialized_mutex.zig"); _ = @import("segmented_list.zig"); _ = @import("spinlock.zig"); diff --git a/std/mutex.zig b/std/mutex.zig index 2885a99ed7a3d37d3c4d2c3373769fe2c10c2978..723581cbefab3399de5012ca33174fca060c453d 100644 --- a/std/mutex.zig +++ b/std/mutex.zig @@ -9,6 +9,9 @@ const windows = std.os.windows; /// Lock may be held only once. If the same thread /// tries to acquire the same mutex twice, it deadlocks. +/// This type must be initialized at runtime, and then deinitialized when no +/// longer needed, to free resources. +/// If you need static initialization, use std.StaticallyInitializedMutex. /// The Linux implementation is based on mutex3 from /// https://www.akkadia.org/drepper/futex.pdf pub const Mutex = switch(builtin.os) { @@ -60,57 +63,15 @@ pub const Mutex = switch(builtin.os) { return Held { .mutex = self }; } }, - builtin.Os.windows => struct { - - lock: windows.CRITICAL_SECTION, - init_once: windows.RTL_RUN_ONCE, - - pub const Held = struct { - mutex: *Mutex, - - pub fn release(self: Held) void { - windows.LeaveCriticalSection(&self.mutex.lock); - } - }; - - pub fn init() Mutex { - return Mutex { - .lock = undefined, - .init_once = windows.INIT_ONCE_STATIC_INIT, - }; - } - - extern fn initCriticalSection( - InitOnce: *windows.RTL_RUN_ONCE, - Parameter: ?windows.PVOID, - Context: ?windows.PVOID - ) windows.BOOL { - var lock = @ptrCast( - *windows.CRITICAL_SECTION, - @alignCast(@alignOf(*windows.CRITICAL_SECTION), Context.?) - ); - windows.InitializeCriticalSection(lock); - return windows.TRUE; - } - - pub fn deinit(self: *Mutex) void { - windows.DeleteCriticalSection(&self.lock); - } - - pub fn acquire(self: *Mutex) Held { - if (windows.InitOnceExecuteOnce( - &self.init_once, - initCriticalSection, - null, @ptrCast(?windows.PVOID, self) - ) == windows.FALSE) { - unreachable; - } - windows.EnterCriticalSection(&self.lock); - return Held { .mutex = self }; - } - }, + // TODO once https://github.com/ziglang/zig/issues/287 (copy elision) is solved, we can make a + // better implementation of this. The problem is we need the init() function to have access to + // the address of the CRITICAL_SECTION, and then have it not move. + builtin.Os.windows => std.StaticallyInitializedMutex, else => struct { - /// TODO better implementation than spin lock + /// TODO better implementation than spin lock. + /// When changing this, one must also change the corresponding + /// std.StaticallyInitializedMutex code, since it aliases this type, + /// under the assumption that it works both statically and at runtime. lock: SpinLock, pub const Held = struct { diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index 7a6126133a0a591d68c8f206cf7c5b72d31456c1..66b9552c5fff674ace0f921e153d65ab3fbba259 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -253,15 +253,17 @@ pub const RTL_CRITICAL_SECTION = extern struct { }; pub const CRITICAL_SECTION = RTL_CRITICAL_SECTION; +pub const INIT_ONCE = RTL_RUN_ONCE; +pub const INIT_ONCE_STATIC_INIT = RTL_RUN_ONCE_INIT; -pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *RTL_RUN_ONCE, InitFn: PINIT_ONCE_FN, Context: ?PVOID, Parameter: ?LPVOID) BOOL; +pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *INIT_ONCE, InitFn: INIT_ONCE_FN, Parameter: ?*c_void, Context: ?*c_void) BOOL; -pub const PINIT_ONCE_FN = ?extern fn(InitOnce: *RTL_RUN_ONCE, Parameter: ?PVOID, Context: ?PVOID) BOOL; +pub const INIT_ONCE_FN = extern fn(InitOnce: *INIT_ONCE, Parameter: ?*c_void, Context: ?*c_void) BOOL; pub const RTL_RUN_ONCE = extern struct { - Ptr: ?PVOID, + Ptr: ?*c_void, }; -pub const INIT_ONCE_STATIC_INIT = RTL_RUN_ONCE { - .Ptr = null, -}; \ No newline at end of file +pub const RTL_RUN_ONCE_INIT = RTL_RUN_ONCE { + .Ptr = null, +}; diff --git a/std/statically_initialized_mutex.zig b/std/statically_initialized_mutex.zig new file mode 100644 index 0000000000000000000000000000000000000000..dd875eeaf9d70140fdf693e938f26809bbfe9b84 --- /dev/null +++ b/std/statically_initialized_mutex.zig @@ -0,0 +1,105 @@ +const std = @import("index.zig"); +const builtin = @import("builtin"); +const AtomicOrder = builtin.AtomicOrder; +const AtomicRmwOp = builtin.AtomicRmwOp; +const assert = std.debug.assert; +const windows = std.os.windows; + +/// Lock may be held only once. If the same thread +/// tries to acquire the same mutex twice, it deadlocks. +/// This type is intended to be initialized statically. If you don't +/// require static initialization, use std.Mutex. +/// On Windows, this mutex allocates resources when it is +/// first used, and the resources cannot be freed. +/// On Linux, this is an alias of std.Mutex. +pub const StaticallyInitializedMutex = switch(builtin.os) { + builtin.Os.linux => std.Mutex, + builtin.Os.windows => struct { + lock: windows.CRITICAL_SECTION, + init_once: windows.RTL_RUN_ONCE, + + pub const Held = struct { + mutex: *StaticallyInitializedMutex, + + pub fn release(self: Held) void { + windows.LeaveCriticalSection(&self.mutex.lock); + } + }; + + pub fn init() StaticallyInitializedMutex { + return StaticallyInitializedMutex { + .lock = undefined, + .init_once = windows.INIT_ONCE_STATIC_INIT, + }; + } + + extern fn initCriticalSection( + InitOnce: *windows.RTL_RUN_ONCE, + Parameter: ?*c_void, + Context: ?*c_void, + ) windows.BOOL { + const lock = @ptrCast(*windows.CRITICAL_SECTION, @alignCast(@alignOf(windows.CRITICAL_SECTION), Parameter)); + windows.InitializeCriticalSection(lock); + return windows.TRUE; + } + + /// TODO: once https://github.com/ziglang/zig/issues/287 is solved and std.Mutex has a better + /// implementation of a runtime initialized mutex, remove this function. + pub fn deinit(self: *StaticallyInitializedMutex) void { + assert(windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, &self.lock, null) != 0); + windows.DeleteCriticalSection(&self.lock); + } + + pub fn acquire(self: *StaticallyInitializedMutex) Held { + assert(windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, &self.lock, null) != 0); + windows.EnterCriticalSection(&self.lock); + return Held { .mutex = self }; + } + }, + else => std.Mutex, +}; + +test "std.StaticallyInitializedMutex" { + const TestContext = struct { + data: i128, + + const TestContext = @This(); + const incr_count = 10000; + + var mutex = StaticallyInitializedMutex.init(); + + fn worker(ctx: *TestContext) void { + var i: usize = 0; + while (i != TestContext.incr_count) : (i += 1) { + const held = mutex.acquire(); + defer held.release(); + + ctx.data += 1; + } + } + }; + + var direct_allocator = std.heap.DirectAllocator.init(); + defer direct_allocator.deinit(); + + var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 300 * 1024); + defer direct_allocator.allocator.free(plenty_of_memory); + + var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory); + var a = &fixed_buffer_allocator.allocator; + + + var context = TestContext{ + .data = 0, + }; + + const thread_count = 10; + var threads: [thread_count]*std.os.Thread = undefined; + for (threads) |*t| { + t.* = try std.os.spawnThread(&context, TestContext.worker); + } + for (threads) |t| + t.wait(); + + std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count); +} -- 2.54.0 From ae1ebe09b7c1258bfa8de37244fd9b510b1447a4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 1 Feb 2019 14:06:51 -0500 Subject: [PATCH 31/37] add compile errror for @bitCast when bit counts mismatch fixes invalid LLVM IR from previous commit --- src/ir.cpp | 14 +++++++++++-- std/io.zig | 46 ++++++++++++++++++++++------------------- test/compile_errors.zig | 9 ++++++++ 3 files changed, 46 insertions(+), 23 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index d5152f3c85e9490ef847536b8006328d3a35e580..dc700c41783caf803ed6fafa1ddb1a6320383b39 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -20580,10 +20580,10 @@ static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruct if (type_is_invalid(src_type)) return ira->codegen->invalid_instruction; - if ((err = ensure_complete_type(ira->codegen, dest_type))) + if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_instruction; - if ((err = ensure_complete_type(ira->codegen, src_type))) + if ((err = type_resolve(ira->codegen, src_type, ResolveStatusSizeKnown))) return ira->codegen->invalid_instruction; if (get_codegen_ptr_type(src_type) != nullptr) { @@ -20646,6 +20646,16 @@ static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruct return ira->codegen->invalid_instruction; } + uint64_t dest_size_bits = type_size_bits(ira->codegen, dest_type); + uint64_t src_size_bits = type_size_bits(ira->codegen, src_type); + if (dest_size_bits != src_size_bits) { + ir_add_error(ira, &instruction->base, + buf_sprintf("destination type '%s' has %" ZIG_PRI_u64 " bits but source type '%s' has %" ZIG_PRI_u64 " bits", + buf_ptr(&dest_type->name), dest_size_bits, + buf_ptr(&src_type->name), src_size_bits)); + return ira->codegen->invalid_instruction; + } + if (instr_is_comptime(value)) { ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); if (!val) diff --git a/std/io.zig b/std/io.zig index 57f2ef3df59929033083a6a5869c19f0475cd692..46625cd34b78f83fe2c614b6af7ea4dd541e2eb2 100644 --- a/std/io.zig +++ b/std/io.zig @@ -503,13 +503,13 @@ pub fn BitInStream(endian: builtin.Endian, comptime Error: type) type { /// containing them in the least significant end. The number of bits successfully /// read is placed in `out_bits`, as reaching the end of the stream is not an error. pub fn readBits(self: *Self, comptime U: type, bits: usize, out_bits: *usize) Error!U { - debug.assert(trait.isUnsignedInt(U)); + comptime assert(trait.isUnsignedInt(U)); //by extending the buffer to a minimum of u8 we can cover a number of edge cases // related to shifting and casting. const u_bit_count = comptime meta.bitCount(U); const buf_bit_count = bc: { - debug.assert(u_bit_count >= bits); + assert(u_bit_count >= bits); break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count; }; const Buf = @IntType(false, buf_bit_count); @@ -664,7 +664,7 @@ test "io.SliceOutStream" { const stream = &slice_stream.stream; try stream.print("{}{}!", "Hello", "World"); - debug.assert(mem.eql(u8, "HelloWorld!", slice_stream.getWritten())); + debug.assertOrPanic(mem.eql(u8, "HelloWorld!", slice_stream.getWritten())); } var null_out_stream_state = NullOutStream.init(); @@ -726,7 +726,7 @@ test "io.CountingOutStream" { const bytes = "yay" ** 10000; stream.write(bytes) catch unreachable; - debug.assert(counting_stream.bytes_written == bytes.len); + debug.assertOrPanic(counting_stream.bytes_written == bytes.len); } pub fn BufferedOutStream(comptime Error: type) type { @@ -835,13 +835,13 @@ pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type { if (bits == 0) return; const U = @typeOf(value); - debug.assert(trait.isUnsignedInt(U)); + comptime assert(trait.isUnsignedInt(U)); //by extending the buffer to a minimum of u8 we can cover a number of edge cases // related to shifting and casting. const u_bit_count = comptime meta.bitCount(U); const buf_bit_count = bc: { - debug.assert(u_bit_count >= bits); + assert(u_bit_count >= bits); break :bc if (u_bit_count <= u8_bit_count) u8_bit_count else u_bit_count; }; const Buf = @IntType(false, buf_bit_count); @@ -1013,10 +1013,10 @@ test "io.readLineFrom" { ); const stream = &mem_stream.stream; - debug.assert(mem.eql(u8, "Line 1", try readLineFrom(stream, &buf))); - debug.assert(mem.eql(u8, "Line 22", try readLineFrom(stream, &buf))); + debug.assertOrPanic(mem.eql(u8, "Line 1", try readLineFrom(stream, &buf))); + debug.assertOrPanic(mem.eql(u8, "Line 22", try readLineFrom(stream, &buf))); debug.assertError(readLineFrom(stream, &buf), error.EndOfStream); - debug.assert(mem.eql(u8, buf.toSlice(), "Line 1Line 22Line 333")); + debug.assertOrPanic(mem.eql(u8, buf.toSlice(), "Line 1Line 22Line 333")); } pub fn readLineSlice(slice: []u8) ![]u8 { @@ -1044,7 +1044,7 @@ test "io.readLineSliceFrom" { ); const stream = &mem_stream.stream; - debug.assert(mem.eql(u8, "Line 1", try readLineSliceFrom(stream, buf[0..]))); + debug.assertOrPanic(mem.eql(u8, "Line 1", try readLineSliceFrom(stream, buf[0..]))); debug.assertError(readLineSliceFrom(stream, buf[0..]), error.OutOfMemory); } @@ -1057,7 +1057,7 @@ test "io.readLineSliceFrom" { /// which will be called when the deserializer is used to deserialize /// that type. It will pass a pointer to the type instance to deserialize /// into and a pointer to the deserializer struct. -pub fn Deserializer(endian: builtin.Endian, is_packed: bool, comptime Error: type) type { +pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime Error: type) type { return struct { const Self = @This(); @@ -1079,9 +1079,9 @@ pub fn Deserializer(endian: builtin.Endian, is_packed: bool, comptime Error: typ //@BUG: inferred error issue. See: #1386 fn deserializeInt(self: *Self, comptime T: type) (Stream.Error || error{EndOfStream})!T { - debug.assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T)); + comptime assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T)); - const u8_bit_count = comptime meta.bitCount(u8); + const u8_bit_count = 8; const t_bit_count = comptime meta.bitCount(T); const U = @IntType(false, t_bit_count); @@ -1097,13 +1097,17 @@ pub fn Deserializer(endian: builtin.Endian, is_packed: bool, comptime Error: typ const read_size = try self.in_stream.read(buffer[0..]); if (read_size < int_size) return error.EndOfStream; - if (int_size == 1) return @bitCast(T, buffer[0]); + if (int_size == 1) { + if (t_bit_count == 8) return @bitCast(T, buffer[0]); + const PossiblySignedByte = @IntType(T.is_signed, 8); + return @truncate(T, @bitCast(PossiblySignedByte, buffer[0])); + } var result = U(0); for (buffer) |byte, i| { switch (endian) { builtin.Endian.Big => { - result = (result << @intCast(u4, u8_bit_count)) | byte; + result = (result << u8_bit_count) | byte; }, builtin.Endian.Little => { result |= U(byte) << @intCast(Log2U, u8_bit_count * i); @@ -1118,12 +1122,12 @@ pub fn Deserializer(endian: builtin.Endian, is_packed: bool, comptime Error: typ // see: #1315 fn setTag(ptr: var, tag: var) void { const T = @typeOf(ptr); - comptime debug.assert(trait.isPtrTo(builtin.TypeId.Union)(T)); + comptime assert(trait.isPtrTo(builtin.TypeId.Union)(T)); const U = meta.Child(T); const info = @typeInfo(U).Union; if (info.tag_type) |TagType| { - debug.assert(TagType == @typeOf(tag)); + comptime assert(TagType == @typeOf(tag)); var ptr_tag = ptr: { if (@alignOf(TagType) >= @alignOf(U)) break :ptr @ptrCast(*TagType, ptr); @@ -1151,7 +1155,7 @@ pub fn Deserializer(endian: builtin.Endian, is_packed: bool, comptime Error: typ /// Deserializes data into the type pointed to by `ptr` pub fn deserializeInto(self: *Self, ptr: var) !void { const T = @typeOf(ptr); - debug.assert(trait.is(builtin.TypeId.Pointer)(T)); + comptime assert(trait.is(builtin.TypeId.Pointer)(T)); if (comptime trait.isSlice(T) or comptime trait.isPtrTo(builtin.TypeId.Array)(T)) { for (ptr) |*v| @@ -1159,7 +1163,7 @@ pub fn Deserializer(endian: builtin.Endian, is_packed: bool, comptime Error: typ return; } - comptime debug.assert(trait.isSingleItemPtr(T)); + comptime assert(trait.isSingleItemPtr(T)); const C = comptime meta.Child(T); const child_type_id = @typeId(C); @@ -1266,7 +1270,7 @@ pub fn Deserializer(endian: builtin.Endian, is_packed: bool, comptime Error: typ /// which will be called when the serializer is used to serialize that type. It will /// pass a const pointer to the type instance to be serialized and a pointer /// to the serializer struct. -pub fn Serializer(endian: builtin.Endian, is_packed: bool, comptime Error: type) type { +pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, comptime Error: type) type { return struct { const Self = @This(); @@ -1288,7 +1292,7 @@ pub fn Serializer(endian: builtin.Endian, is_packed: bool, comptime Error: type) fn serializeInt(self: *Self, value: var) !void { const T = @typeOf(value); - debug.assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T)); + comptime assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T)); const t_bit_count = comptime meta.bitCount(T); const u8_bit_count = comptime meta.bitCount(u8); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index f2a2ba26415a1f3b94b42456ae71e19c18533662..74602b77ff15a2846b37fdec9a711bf5ba4b40bd 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,15 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "@bitCast same size but bit count mismatch", + \\export fn entry(byte: u8) void { + \\ var oops = @bitCast(u7, byte); + \\} + , + ".tmp_source.zig:2:16: error: destination type 'u7' has 7 bits but source type 'u8' has 8 bits", + ); + cases.add( "attempted `&&`", \\export fn entry(a: bool, b: bool) i32 { -- 2.54.0 From 8bedb10939b511e39787b2693249d2d3e8102854 Mon Sep 17 00:00:00 2001 From: Matthew McAllister Date: Fri, 1 Feb 2019 03:12:56 -0800 Subject: [PATCH 32/37] Fix runtime assignment to comptime aggregate field This was causing a segfault --- src/ir.cpp | 6 ++++-- test/compile_errors.zig | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index dc700c41783caf803ed6fafa1ddb1a6320383b39..d676e7a6c5280465f1644a76421a40b631144cd6 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16533,7 +16533,8 @@ static IrInstruction *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrI if ((err = type_resolve(ira->codegen, casted_field_value->value.type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_instruction; - bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope); + bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope) + || type_requires_comptime(ira->codegen, container_type) == ReqCompTimeYes; if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime || !type_has_bits(casted_field_value->value.type)) { @@ -16584,7 +16585,8 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc IrInstructionStructInitField *new_fields = allocate(actual_field_count); - bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope); + bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope) + || type_requires_comptime(ira->codegen, container_type) == ReqCompTimeYes; ConstExprValue const_val = {}; const_val.special = ConstValSpecialStatic; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 74602b77ff15a2846b37fdec9a711bf5ba4b40bd..30d9ca5d702d5959011c34d94f51807c6717ffd6 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -5367,4 +5367,32 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , ".tmp_source.zig:2:35: error: expected sized integer or sized float, found comptime_float", ); + + cases.add( + "runtime assignment to comptime struct type", + \\const Foo = struct { + \\ Bar: u8, + \\ Baz: type, + \\}; + \\export fn f() void { + \\ var x: u8 = 0; + \\ const foo = Foo { .Bar = x, .Baz = u8 }; + \\} + , + ".tmp_source.zig:7:30: error: unable to evaluate constant expression", + ); + + cases.add( + "runtime assignment to comptime union type", + \\const Foo = union { + \\ Bar: u8, + \\ Baz: type, + \\}; + \\export fn f() void { + \\ var x: u8 = 0; + \\ const foo = Foo { .Bar = x }; + \\} + , + ".tmp_source.zig:7:30: error: unable to evaluate constant expression", + ); } -- 2.54.0 From 9b8e23934bc87f1fd6a42cdfdd551212994b6e58 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 1 Feb 2019 17:49:29 -0500 Subject: [PATCH 33/37] introduce --single-threaded build option closes #1764 This adds another boolean to the test matrix; hopefully it does not inflate the time too much. std.event.Loop does not work with this option yet. See #1908 --- doc/langref.html.in | 19 +++++++ src/all_types.hpp | 1 + src/codegen.cpp | 4 ++ src/main.cpp | 6 +++ std/atomic/queue.zig | 42 ++++++++++----- std/atomic/stack.zig | 78 +++++++++++++++++++--------- std/event/channel.zig | 3 ++ std/event/future.zig | 3 ++ std/event/group.zig | 3 ++ std/event/lock.zig | 3 ++ std/event/loop.zig | 22 ++++++++ std/event/net.zig | 3 ++ std/event/rwlock.zig | 3 ++ std/mutex.zig | 66 ++++++++++++++++------- std/os/index.zig | 1 + std/os/test.zig | 4 ++ std/statically_initialized_mutex.zig | 21 +++++--- test/tests.zig | 43 ++++++++------- 18 files changed, 244 insertions(+), 81 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index e192dbbf76d018ea950992b252fed27a00a87e2f..144c8571c430ab0ac51dff2ff25424632f0906c7 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -6714,6 +6714,25 @@ pub fn build(b: *Builder) void { {#header_close#} {#see_also|Compile Variables|Zig Build System|Undefined Behavior#} {#header_close#} + + {#header_open|Single Threaded Builds#} +

Zig has a compile option --single-threaded which has the following effects: +

    +
  • {#link|@atomicLoad#} is emitted as a normal load.
  • +
  • {#link|@atomicRmw#} is emitted as a normal memory load, modify, store.
  • +
  • {#link|@fence#} becomes a no-op.
  • +
  • Variables which have Thread Local Storage instead become globals. TODO thread local variables + are not implemented yet.
  • +
  • The overhead of {#link|Coroutines#} becomes equivalent to function call overhead. + TODO: please note this will not be implemented until the upcoming Coroutine Rewrite
  • +
  • The {#syntax#}@import("builtin").single_threaded{#endsyntax#} becomes {#syntax#}true{#endsyntax#} + and therefore various userland APIs which read this variable become more efficient. + For example {#syntax#}std.Mutex{#endsyntax#} becomes + an empty data structure and all of its functions become no-ops.
  • +
+

+ {#header_close#} + {#header_open|Undefined Behavior#}

Zig has many instances of undefined behavior. If undefined behavior is diff --git a/src/all_types.hpp b/src/all_types.hpp index f6fe72891d6a221ae411cf5ab7216ceae29b79b1..3fc6772b31d70355bf128f2b04ecfcd10a2ec202 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1808,6 +1808,7 @@ struct CodeGen { bool is_static; bool strip_debug_symbols; bool is_test_build; + bool is_single_threaded; bool is_native_target; bool linker_rdynamic; bool no_rosegment_workaround; diff --git a/src/codegen.cpp b/src/codegen.cpp index e2576f53359ed3066e7ff4f72cbd39ee9937fa7f..b73fda59d1352325b4d7eecf7bf4fdb888d90837 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -118,6 +118,7 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out g->string_literals_table.init(16); g->type_info_cache.init(32); g->is_test_build = false; + g->is_single_threaded = false; buf_resize(&g->global_asm, 0); for (size_t i = 0; i < array_length(symbols_that_llvm_depends_on); i += 1) { @@ -7377,6 +7378,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { buf_appendf(contents, "pub const endian = %s;\n", endian_str); } buf_appendf(contents, "pub const is_test = %s;\n", bool_to_str(g->is_test_build)); + buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded)); buf_appendf(contents, "pub const os = Os.%s;\n", cur_os); buf_appendf(contents, "pub const arch = Arch.%s;\n", cur_arch); buf_appendf(contents, "pub const environ = Environ.%s;\n", cur_environ); @@ -7411,6 +7413,7 @@ static Error define_builtin_compile_vars(CodeGen *g) { cache_buf(&cache_hash, compiler_id); cache_int(&cache_hash, g->build_mode); cache_bool(&cache_hash, g->is_test_build); + cache_bool(&cache_hash, g->is_single_threaded); cache_int(&cache_hash, g->zig_target.arch.arch); cache_int(&cache_hash, g->zig_target.arch.sub_arch); cache_int(&cache_hash, g->zig_target.vendor); @@ -8329,6 +8332,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_bool(ch, g->is_static); cache_bool(ch, g->strip_debug_symbols); cache_bool(ch, g->is_test_build); + cache_bool(ch, g->is_single_threaded); cache_bool(ch, g->is_native_target); cache_bool(ch, g->linker_rdynamic); cache_bool(ch, g->no_rosegment_workaround); diff --git a/src/main.cpp b/src/main.cpp index fd8e3db2fae23fae491d0dd8179786e8d0c29f01..81f49089be13055598b6d58259c7a57762b79ac6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -59,6 +59,7 @@ static int print_full_usage(const char *arg0) { " --release-fast build with optimizations on and safety off\n" " --release-safe build with optimizations on and safety on\n" " --release-small build with size optimizations on and safety off\n" + " --single-threaded source may assume it is only used single-threaded\n" " --static output will be statically linked\n" " --strip exclude debug symbols\n" " --target-arch [name] specify target architecture\n" @@ -393,6 +394,7 @@ int main(int argc, char **argv) { bool no_rosegment_workaround = false; bool system_linker_hack = false; TargetSubsystem subsystem = TargetSubsystemAuto; + bool is_single_threaded = false; if (argc >= 2 && strcmp(argv[1], "build") == 0) { Buf zig_exe_path_buf = BUF_INIT; @@ -550,6 +552,8 @@ int main(int argc, char **argv) { disable_pic = true; } else if (strcmp(arg, "--system-linker-hack") == 0) { system_linker_hack = true; + } else if (strcmp(arg, "--single-threaded") == 0) { + is_single_threaded = true; } else if (strcmp(arg, "--test-cmd-bin") == 0) { test_exec_args.append(nullptr); } else if (arg[1] == 'L' && arg[2] != 0) { @@ -816,6 +820,7 @@ int main(int argc, char **argv) { switch (cmd) { case CmdBuiltin: { CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir()); + g->is_single_threaded = is_single_threaded; Buf *builtin_source = codegen_generate_builtin_source(g); if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) { fprintf(stderr, "unable to write to stdout: %s\n", strerror(ferror(stdout))); @@ -889,6 +894,7 @@ int main(int argc, char **argv) { codegen_set_out_name(g, buf_out_name); codegen_set_lib_version(g, ver_major, ver_minor, ver_patch); codegen_set_is_test(g, cmd == CmdTest); + g->is_single_threaded = is_single_threaded; codegen_set_linker_script(g, linker_script); if (each_lib_rpath) codegen_set_each_lib_rpath(g, each_lib_rpath); diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig index 1aab4c32def93d41b8b7284e2867fa68c10763d1..6c61bcc0484189377321ce92ecd305078345cbba 100644 --- a/std/atomic/queue.zig +++ b/std/atomic/queue.zig @@ -170,20 +170,36 @@ test "std.atomic.Queue" { .get_count = 0, }; - var putters: [put_thread_count]*std.os.Thread = undefined; - for (putters) |*t| { - t.* = try std.os.spawnThread(&context, startPuts); - } - var getters: [put_thread_count]*std.os.Thread = undefined; - for (getters) |*t| { - t.* = try std.os.spawnThread(&context, startGets); - } + if (builtin.single_threaded) { + { + var i: usize = 0; + while (i < put_thread_count) : (i += 1) { + std.debug.assertOrPanic(startPuts(&context) == 0); + } + } + context.puts_done = 1; + { + var i: usize = 0; + while (i < put_thread_count) : (i += 1) { + std.debug.assertOrPanic(startGets(&context) == 0); + } + } + } else { + var putters: [put_thread_count]*std.os.Thread = undefined; + for (putters) |*t| { + t.* = try std.os.spawnThread(&context, startPuts); + } + var getters: [put_thread_count]*std.os.Thread = undefined; + for (getters) |*t| { + t.* = try std.os.spawnThread(&context, startGets); + } - for (putters) |t| - t.wait(); - _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); - for (getters) |t| - t.wait(); + for (putters) |t| + t.wait(); + _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + for (getters) |t| + t.wait(); + } if (context.put_sum != context.get_sum) { std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum); diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig index b69a93733cbe88f1738d3785e75844db43142c59..1e4981353bc0d47e21c7c5027cd62038eb835de9 100644 --- a/std/atomic/stack.zig +++ b/std/atomic/stack.zig @@ -4,10 +4,13 @@ const AtomicOrder = builtin.AtomicOrder; /// Many reader, many writer, non-allocating, thread-safe /// Uses a spinlock to protect push() and pop() +/// When building in single threaded mode, this is a simple linked list. pub fn Stack(comptime T: type) type { return struct { root: ?*Node, - lock: u8, + lock: @typeOf(lock_init), + + const lock_init = if (builtin.single_threaded) {} else u8(0); pub const Self = @This(); @@ -19,7 +22,7 @@ pub fn Stack(comptime T: type) type { pub fn init() Self { return Self{ .root = null, - .lock = 0, + .lock = lock_init, }; } @@ -31,20 +34,31 @@ pub fn Stack(comptime T: type) type { } pub fn push(self: *Self, node: *Node) void { - while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {} - defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1); + if (builtin.single_threaded) { + node.next = self.root; + self.root = node; + } else { + while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {} + defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1); - node.next = self.root; - self.root = node; + node.next = self.root; + self.root = node; + } } pub fn pop(self: *Self) ?*Node { - while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {} - defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1); + if (builtin.single_threaded) { + const root = self.root orelse return null; + self.root = root.next; + return root; + } else { + while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {} + defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1); - const root = self.root orelse return null; - self.root = root.next; - return root; + const root = self.root orelse return null; + self.root = root.next; + return root; + } } pub fn isEmpty(self: *Self) bool { @@ -90,20 +104,36 @@ test "std.atomic.stack" { .get_count = 0, }; - var putters: [put_thread_count]*std.os.Thread = undefined; - for (putters) |*t| { - t.* = try std.os.spawnThread(&context, startPuts); - } - var getters: [put_thread_count]*std.os.Thread = undefined; - for (getters) |*t| { - t.* = try std.os.spawnThread(&context, startGets); - } + if (builtin.single_threaded) { + { + var i: usize = 0; + while (i < put_thread_count) : (i += 1) { + std.debug.assertOrPanic(startPuts(&context) == 0); + } + } + context.puts_done = 1; + { + var i: usize = 0; + while (i < put_thread_count) : (i += 1) { + std.debug.assertOrPanic(startGets(&context) == 0); + } + } + } else { + var putters: [put_thread_count]*std.os.Thread = undefined; + for (putters) |*t| { + t.* = try std.os.spawnThread(&context, startPuts); + } + var getters: [put_thread_count]*std.os.Thread = undefined; + for (getters) |*t| { + t.* = try std.os.spawnThread(&context, startGets); + } - for (putters) |t| - t.wait(); - _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); - for (getters) |t| - t.wait(); + for (putters) |t| + t.wait(); + _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + for (getters) |t| + t.wait(); + } if (context.put_sum != context.get_sum) { std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum); diff --git a/std/event/channel.zig b/std/event/channel.zig index 133ce1c69c483fbeb6d148e87bcc2d68c5f64d44..f04ad1604e3d46f82f73f655de960b1fbebb3448 100644 --- a/std/event/channel.zig +++ b/std/event/channel.zig @@ -319,6 +319,9 @@ pub fn Channel(comptime T: type) type { } test "std.event.Channel" { + // https://github.com/ziglang/zig/issues/1908 + if (builtin.single_threaded) return error.SkipZigTest; + var da = std.heap.DirectAllocator.init(); defer da.deinit(); diff --git a/std/event/future.zig b/std/event/future.zig index d61768b1983164ca64873859186d8db3dc678060..55ed01046d030a859e3a0c5cbe16e32369590ea5 100644 --- a/std/event/future.zig +++ b/std/event/future.zig @@ -84,6 +84,9 @@ pub fn Future(comptime T: type) type { } test "std.event.Future" { + // https://github.com/ziglang/zig/issues/1908 + if (builtin.single_threaded) return error.SkipZigTest; + var da = std.heap.DirectAllocator.init(); defer da.deinit(); diff --git a/std/event/group.zig b/std/event/group.zig index 9f2687a5b371a5ee43c07d6b5a90062783cf8360..0e9c2d96557c1eac8f770fbb54861eedd21e26c2 100644 --- a/std/event/group.zig +++ b/std/event/group.zig @@ -121,6 +121,9 @@ pub fn Group(comptime ReturnType: type) type { } test "std.event.Group" { + // https://github.com/ziglang/zig/issues/1908 + if (builtin.single_threaded) return error.SkipZigTest; + var da = std.heap.DirectAllocator.init(); defer da.deinit(); diff --git a/std/event/lock.zig b/std/event/lock.zig index 46e0d134680112e4b925823595ee3d66628fa9f5..01978e1909dde0aa4d8df71c59cfcfb666da945b 100644 --- a/std/event/lock.zig +++ b/std/event/lock.zig @@ -122,6 +122,9 @@ pub const Lock = struct { }; test "std.event.Lock" { + // https://github.com/ziglang/zig/issues/1908 + if (builtin.single_threaded) return error.SkipZigTest; + var da = std.heap.DirectAllocator.init(); defer da.deinit(); diff --git a/std/event/loop.zig b/std/event/loop.zig index 43965e8293acd09964a18b8849df757a518d2bf6..d5228db6047851418ee71c349aa8960d964d87e0 100644 --- a/std/event/loop.zig +++ b/std/event/loop.zig @@ -97,6 +97,7 @@ pub const Loop = struct { /// TODO copy elision / named return values so that the threads referencing *Loop /// have the correct pointer value. pub fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void { + if (builtin.single_threaded) @compileError("initMultiThreaded unavailable when building in single-threaded mode"); const core_count = try os.cpuCount(allocator); return self.initInternal(allocator, core_count); } @@ -201,6 +202,11 @@ pub const Loop = struct { self.os_data.fs_thread.wait(); } + if (builtin.single_threaded) { + assert(extra_thread_count == 0); + return; + } + var extra_thread_index: usize = 0; errdefer { // writing 8 bytes to an eventfd cannot fail @@ -301,6 +307,11 @@ pub const Loop = struct { self.os_data.fs_thread.wait(); } + if (builtin.single_threaded) { + assert(extra_thread_count == 0); + return; + } + var extra_thread_index: usize = 0; errdefer { _ = os.bsdKEvent(self.os_data.kqfd, final_kev_arr, empty_kevs, null) catch unreachable; @@ -338,6 +349,11 @@ pub const Loop = struct { self.available_eventfd_resume_nodes.push(eventfd_node); } + if (builtin.single_threaded) { + assert(extra_thread_count == 0); + return; + } + var extra_thread_index: usize = 0; errdefer { var i: usize = 0; @@ -845,6 +861,9 @@ pub const Loop = struct { }; test "std.event.Loop - basic" { + // https://github.com/ziglang/zig/issues/1908 + if (builtin.single_threaded) return error.SkipZigTest; + var da = std.heap.DirectAllocator.init(); defer da.deinit(); @@ -858,6 +877,9 @@ test "std.event.Loop - basic" { } test "std.event.Loop - call" { + // https://github.com/ziglang/zig/issues/1908 + if (builtin.single_threaded) return error.SkipZigTest; + var da = std.heap.DirectAllocator.init(); defer da.deinit(); diff --git a/std/event/net.zig b/std/event/net.zig index 6838704084cfa9296260cfea20cbc1d40aec4d48..9dac6aa5661f3a164d978cbcb9e281b9b4043cd6 100644 --- a/std/event/net.zig +++ b/std/event/net.zig @@ -269,6 +269,9 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !os.File { } test "listen on a port, send bytes, receive bytes" { + // https://github.com/ziglang/zig/issues/1908 + if (builtin.single_threaded) return error.SkipZigTest; + if (builtin.os != builtin.Os.linux) { // TODO build abstractions for other operating systems return error.SkipZigTest; diff --git a/std/event/rwlock.zig b/std/event/rwlock.zig index 5d48ea893ebfe35b2648dc8caac11529cb33aa0e..f272ac71eaff04e6841b098820f452df9e5eadea 100644 --- a/std/event/rwlock.zig +++ b/std/event/rwlock.zig @@ -211,6 +211,9 @@ pub const RwLock = struct { }; test "std.event.RwLock" { + // https://github.com/ziglang/zig/issues/1908 + if (builtin.single_threaded) return error.SkipZigTest; + var da = std.heap.DirectAllocator.init(); defer da.deinit(); diff --git a/std/mutex.zig b/std/mutex.zig index 723581cbefab3399de5012ca33174fca060c453d..54173fa38ad167114693d6a595eac7f92e250a4c 100644 --- a/std/mutex.zig +++ b/std/mutex.zig @@ -14,7 +14,36 @@ const windows = std.os.windows; /// If you need static initialization, use std.StaticallyInitializedMutex. /// The Linux implementation is based on mutex3 from /// https://www.akkadia.org/drepper/futex.pdf -pub const Mutex = switch(builtin.os) { +/// When an application is built in single threaded release mode, all the functions are +/// no-ops. In single threaded debug mode, there is deadlock detection. +pub const Mutex = if (builtin.single_threaded) + struct { + lock: @typeOf(lock_init), + + const lock_init = if (std.debug.runtime_safety) false else {}; + + pub const Held = struct { + mutex: *Mutex, + + pub fn release(self: Held) void { + if (std.debug.runtime_safety) { + self.mutex.lock = false; + } + } + }; + pub fn init() Mutex { + return Mutex{ .lock = lock_init }; + } + pub fn deinit(self: *Mutex) void {} + + pub fn acquire(self: *Mutex) Held { + if (std.debug.runtime_safety and self.lock) { + @panic("deadlock detected"); + } + return Held{ .mutex = self }; + } + } +else switch (builtin.os) { builtin.Os.linux => struct { /// 0: unlocked /// 1: locked, no waiters @@ -39,9 +68,7 @@ pub const Mutex = switch(builtin.os) { }; pub fn init() Mutex { - return Mutex { - .lock = 0, - }; + return Mutex{ .lock = 0 }; } pub fn deinit(self: *Mutex) void {} @@ -60,7 +87,7 @@ pub const Mutex = switch(builtin.os) { } c = @atomicRmw(i32, &self.lock, AtomicRmwOp.Xchg, 2, AtomicOrder.Acquire); } - return Held { .mutex = self }; + return Held{ .mutex = self }; } }, // TODO once https://github.com/ziglang/zig/issues/287 (copy elision) is solved, we can make a @@ -78,21 +105,19 @@ pub const Mutex = switch(builtin.os) { mutex: *Mutex, pub fn release(self: Held) void { - SpinLock.Held.release(SpinLock.Held { .spinlock = &self.mutex.lock }); + SpinLock.Held.release(SpinLock.Held{ .spinlock = &self.mutex.lock }); } }; pub fn init() Mutex { - return Mutex { - .lock = SpinLock.init(), - }; + return Mutex{ .lock = SpinLock.init() }; } pub fn deinit(self: *Mutex) void {} pub fn acquire(self: *Mutex) Held { _ = self.lock.acquire(); - return Held { .mutex = self }; + return Held{ .mutex = self }; } }, }; @@ -122,15 +147,20 @@ test "std.Mutex" { .data = 0, }; - const thread_count = 10; - var threads: [thread_count]*std.os.Thread = undefined; - for (threads) |*t| { - t.* = try std.os.spawnThread(&context, worker); - } - for (threads) |t| - t.wait(); + if (builtin.single_threaded) { + worker(&context); + std.debug.assertOrPanic(context.data == TestContext.incr_count); + } else { + const thread_count = 10; + var threads: [thread_count]*std.os.Thread = undefined; + for (threads) |*t| { + t.* = try std.os.spawnThread(&context, worker); + } + for (threads) |t| + t.wait(); - std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count); + std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count); + } } fn worker(ctx: *TestContext) void { diff --git a/std/os/index.zig b/std/os/index.zig index 78d543583d8160d9e271334c3f4dc5da54681af6..75abe3bbdec268660d4d95484a1c2eccd4c2ce5c 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -3013,6 +3013,7 @@ pub const SpawnThreadError = error{ /// where T is u8, noreturn, void, or !void /// caller must call wait on the returned thread pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread { + if (builtin.single_threaded) @compileError("cannot spawn thread when building in single-threaded mode"); // TODO compile-time call graph analysis to determine stack upper bound // https://github.com/ziglang/zig/issues/157 const default_stack_size = 8 * 1024 * 1024; diff --git a/std/os/test.zig b/std/os/test.zig index 5142920687105dcd534c109aaa163a39995b1813..f14cf477862b60ffaa4294fd53fb600f375084c6 100644 --- a/std/os/test.zig +++ b/std/os/test.zig @@ -40,6 +40,8 @@ fn testThreadIdFn(thread_id: *os.Thread.Id) void { } test "std.os.Thread.getCurrentId" { + if (builtin.single_threaded) return error.SkipZigTest; + var thread_current_id: os.Thread.Id = undefined; const thread = try os.spawnThread(&thread_current_id, testThreadIdFn); const thread_id = thread.handle(); @@ -53,6 +55,8 @@ test "std.os.Thread.getCurrentId" { } test "spawn threads" { + if (builtin.single_threaded) return error.SkipZigTest; + var shared_ctx: i32 = 1; const thread1 = try std.os.spawnThread({}, start1); diff --git a/std/statically_initialized_mutex.zig b/std/statically_initialized_mutex.zig index dd875eeaf9d70140fdf693e938f26809bbfe9b84..37582d49c1b35132138493192dc151504039143b 100644 --- a/std/statically_initialized_mutex.zig +++ b/std/statically_initialized_mutex.zig @@ -93,13 +93,18 @@ test "std.StaticallyInitializedMutex" { .data = 0, }; - const thread_count = 10; - var threads: [thread_count]*std.os.Thread = undefined; - for (threads) |*t| { - t.* = try std.os.spawnThread(&context, TestContext.worker); + if (builtin.single_threaded) { + TestContext.worker(&context); + std.debug.assertOrPanic(context.data == TestContext.incr_count); + } else { + const thread_count = 10; + var threads: [thread_count]*std.os.Thread = undefined; + for (threads) |*t| { + t.* = try std.os.spawnThread(&context, TestContext.worker); + } + for (threads) |t| + t.wait(); + + std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count); } - for (threads) |t| - t.wait(); - - std.debug.assertOrPanic(context.data == thread_count * TestContext.incr_count); } diff --git a/test/tests.zig b/test/tests.zig index 1ca06b4b3452f73f173ea05c999ee130785c5e1b..73d4644d18395cbf79bc3b8ea9ebc5ce975a6225 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -163,25 +163,32 @@ pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []cons for (test_targets) |test_target| { const is_native = (test_target.os == builtin.os and test_target.arch == builtin.arch); for (modes) |mode| { - for ([]bool{ - false, - true, - }) |link_libc| { - if (link_libc and !is_native) { - // don't assume we have a cross-compiling libc set up - continue; + for ([]bool{ false, true }) |link_libc| { + for ([]bool{ false, true }) |single_threaded| { + if (link_libc and !is_native) { + // don't assume we have a cross-compiling libc set up + continue; + } + const these_tests = b.addTest(root_src); + these_tests.setNamePrefix(b.fmt( + "{}-{}-{}-{}-{}-{} ", + name, + @tagName(test_target.os), + @tagName(test_target.arch), + @tagName(mode), + if (link_libc) "c" else "bare", + if (single_threaded) "single" else "multi", + )); + these_tests.setFilter(test_filter); + these_tests.setBuildMode(mode); + if (!is_native) { + these_tests.setTarget(test_target.arch, test_target.os, test_target.environ); + } + if (link_libc) { + these_tests.linkSystemLibrary("c"); + } + step.dependOn(&these_tests.step); } - const these_tests = b.addTest(root_src); - these_tests.setNamePrefix(b.fmt("{}-{}-{}-{}-{} ", name, @tagName(test_target.os), @tagName(test_target.arch), @tagName(mode), if (link_libc) "c" else "bare")); - these_tests.setFilter(test_filter); - these_tests.setBuildMode(mode); - if (!is_native) { - these_tests.setTarget(test_target.arch, test_target.os, test_target.environ); - } - if (link_libc) { - these_tests.linkSystemLibrary("c"); - } - step.dependOn(&these_tests.step); } } } -- 2.54.0 From c90c256868a80cd35e9ba679ba082330592620c9 Mon Sep 17 00:00:00 2001 From: Matthew McAllister Date: Sat, 2 Feb 2019 13:57:53 -0800 Subject: [PATCH 34/37] Fix slice concatenation This was causing an underflow error --- src/ir.cpp | 33 ++++++++++++--------------------- test/stage1/behavior/eval.zig | 4 ++-- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index d676e7a6c5280465f1644a76421a40b631144cd6..3d3c501df35788d0c6b2ed2f926b08940851cace 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -12369,7 +12369,7 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i op1_array_val = ptr_val->data.x_ptr.data.base_array.array_val; op1_array_index = ptr_val->data.x_ptr.data.base_array.elem_index; ConstExprValue *len_val = &op1_val->data.x_struct.fields[slice_len_index]; - op1_array_end = bigint_as_unsigned(&len_val->data.x_bigint); + op1_array_end = op1_array_index + bigint_as_unsigned(&len_val->data.x_bigint); } else { ir_add_error(ira, op1, buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op1->value.type->name))); @@ -12379,13 +12379,9 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i ConstExprValue *op2_array_val; size_t op2_array_index; size_t op2_array_end; + bool op2_type_valid; if (op2_type->id == ZigTypeIdArray) { - if (op2_type->data.array.child_type != child_type) { - ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'", - buf_ptr(&child_type->name), - buf_ptr(&op2->value.type->name))); - return ira->codegen->invalid_instruction; - } + op2_type_valid = op2_type->data.array.child_type == child_type; op2_array_val = op2_val; op2_array_index = 0; op2_array_end = op2_array_val->type->data.array.len; @@ -12394,35 +12390,30 @@ static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *i op2_val->data.x_ptr.special == ConstPtrSpecialBaseArray && op2_val->data.x_ptr.data.base_array.is_cstr) { - if (child_type != ira->codegen->builtin_types.entry_u8) { - ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'", - buf_ptr(&child_type->name), - buf_ptr(&op2->value.type->name))); - return ira->codegen->invalid_instruction; - } + op2_type_valid = child_type == ira->codegen->builtin_types.entry_u8; op2_array_val = op2_val->data.x_ptr.data.base_array.array_val; op2_array_index = op2_val->data.x_ptr.data.base_array.elem_index; op2_array_end = op2_array_val->type->data.array.len - 1; } else if (is_slice(op2_type)) { ZigType *ptr_type = op2_type->data.structure.fields[slice_ptr_index].type_entry; - if (ptr_type->data.pointer.child_type != child_type) { - ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'", - buf_ptr(&child_type->name), - buf_ptr(&op2->value.type->name))); - return ira->codegen->invalid_instruction; - } + op2_type_valid = ptr_type->data.pointer.child_type == child_type; ConstExprValue *ptr_val = &op2_val->data.x_struct.fields[slice_ptr_index]; assert(ptr_val->data.x_ptr.special == ConstPtrSpecialBaseArray); op2_array_val = ptr_val->data.x_ptr.data.base_array.array_val; op2_array_index = ptr_val->data.x_ptr.data.base_array.elem_index; - op2_array_end = op2_array_val->type->data.array.len; ConstExprValue *len_val = &op2_val->data.x_struct.fields[slice_len_index]; - op2_array_end = bigint_as_unsigned(&len_val->data.x_bigint); + op2_array_end = op2_array_index + bigint_as_unsigned(&len_val->data.x_bigint); } else { ir_add_error(ira, op2, buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op2->value.type->name))); return ira->codegen->invalid_instruction; } + if (!op2_type_valid) { + ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'", + buf_ptr(&child_type->name), + buf_ptr(&op2->value.type->name))); + return ira->codegen->invalid_instruction; + } // The type of result is populated in the following if blocks IrInstruction *result = ir_const(ira, &instruction->base, nullptr); diff --git a/test/stage1/behavior/eval.zig b/test/stage1/behavior/eval.zig index 2d8494eb0b6db4fe27a6622a008d27d2774701c0..3e8af0524fb24fef4f578203893fee18682430f0 100644 --- a/test/stage1/behavior/eval.zig +++ b/test/stage1/behavior/eval.zig @@ -728,8 +728,8 @@ test "comptime pointer cast array and then slice" { test "slice bounds in comptime concatenation" { const bs = comptime blk: { - const b = c"11"; - break :blk b[0..1]; + const b = c"........1........"; + break :blk b[8..9]; }; const str = "" ++ bs; assertOrPanic(str.len == 1); -- 2.54.0 From dfbc063f79dc1358208216b466c1bf8c44baa430 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 3 Feb 2019 16:13:28 -0500 Subject: [PATCH 35/37] `std.mem.Allocator.create` replaced with better API `std.mem.Allocator.createOne` is renamed to `std.mem.Allocator.create`. The problem with the previous API is that even after copy elision, the initalization value passed as a parameter would always be a copy. With the new API, once copy elision is done, initialization functions can directly initialize allocated memory in place. Related: * #1872 * #1873 --- src-self-hosted/compilation.zig | 72 +++--- src-self-hosted/errmsg.zig | 20 +- src-self-hosted/ir.zig | 15 +- src-self-hosted/main.zig | 5 +- src-self-hosted/package.zig | 6 +- src-self-hosted/scope.zig | 18 +- src-self-hosted/type.zig | 17 +- src-self-hosted/value.zig | 35 +-- std/atomic/queue.zig | 5 +- std/atomic/stack.zig | 5 +- std/build.zig | 56 +++-- std/debug/index.zig | 7 +- std/event/channel.zig | 5 +- std/event/fs.zig | 8 +- std/event/group.zig | 5 +- std/heap.zig | 3 +- std/io.zig | 5 +- std/linked_list.zig | 2 +- std/mem.zig | 13 +- std/os/child_process.zig | 5 +- std/os/index.zig | 5 +- std/zig/parse.zig | 376 +++++++++++++++++++------------- test/tests.zig | 75 ++++--- 23 files changed, 451 insertions(+), 312 deletions(-) diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index c00c7c1d412e31ed49571a87d4214e046b8601b9..d60892432ee3322a5bc8d9ef6c24601d961a6d6f 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -83,10 +83,11 @@ pub const ZigCompiler = struct { const context_ref = c.LLVMContextCreate() orelse return error.OutOfMemory; errdefer c.LLVMContextDispose(context_ref); - const node = try self.loop.allocator.create(std.atomic.Stack(llvm.ContextRef).Node{ + const node = try self.loop.allocator.create(std.atomic.Stack(llvm.ContextRef).Node); + node.* = std.atomic.Stack(llvm.ContextRef).Node{ .next = undefined, .data = context_ref, - }); + }; errdefer self.loop.allocator.destroy(node); return LlvmHandle{ .node = node }; @@ -596,7 +597,8 @@ pub const Compilation = struct { } fn initTypes(comp: *Compilation) !void { - comp.meta_type = try comp.arena().create(Type.MetaType{ + comp.meta_type = try comp.arena().create(Type.MetaType); + comp.meta_type.* = Type.MetaType{ .base = Type{ .name = "type", .base = Value{ @@ -608,12 +610,13 @@ pub const Compilation = struct { .abi_alignment = Type.AbiAlignment.init(comp.loop), }, .value = undefined, - }); + }; comp.meta_type.value = &comp.meta_type.base; comp.meta_type.base.base.typ = &comp.meta_type.base; assert((try comp.primitive_type_table.put(comp.meta_type.base.name, &comp.meta_type.base)) == null); - comp.void_type = try comp.arena().create(Type.Void{ + comp.void_type = try comp.arena().create(Type.Void); + comp.void_type.* = Type.Void{ .base = Type{ .name = "void", .base = Value{ @@ -624,10 +627,11 @@ pub const Compilation = struct { .id = builtin.TypeId.Void, .abi_alignment = Type.AbiAlignment.init(comp.loop), }, - }); + }; assert((try comp.primitive_type_table.put(comp.void_type.base.name, &comp.void_type.base)) == null); - comp.noreturn_type = try comp.arena().create(Type.NoReturn{ + comp.noreturn_type = try comp.arena().create(Type.NoReturn); + comp.noreturn_type.* = Type.NoReturn{ .base = Type{ .name = "noreturn", .base = Value{ @@ -638,10 +642,11 @@ pub const Compilation = struct { .id = builtin.TypeId.NoReturn, .abi_alignment = Type.AbiAlignment.init(comp.loop), }, - }); + }; assert((try comp.primitive_type_table.put(comp.noreturn_type.base.name, &comp.noreturn_type.base)) == null); - comp.comptime_int_type = try comp.arena().create(Type.ComptimeInt{ + comp.comptime_int_type = try comp.arena().create(Type.ComptimeInt); + comp.comptime_int_type.* = Type.ComptimeInt{ .base = Type{ .name = "comptime_int", .base = Value{ @@ -652,10 +657,11 @@ pub const Compilation = struct { .id = builtin.TypeId.ComptimeInt, .abi_alignment = Type.AbiAlignment.init(comp.loop), }, - }); + }; assert((try comp.primitive_type_table.put(comp.comptime_int_type.base.name, &comp.comptime_int_type.base)) == null); - comp.bool_type = try comp.arena().create(Type.Bool{ + comp.bool_type = try comp.arena().create(Type.Bool); + comp.bool_type.* = Type.Bool{ .base = Type{ .name = "bool", .base = Value{ @@ -666,45 +672,50 @@ pub const Compilation = struct { .id = builtin.TypeId.Bool, .abi_alignment = Type.AbiAlignment.init(comp.loop), }, - }); + }; assert((try comp.primitive_type_table.put(comp.bool_type.base.name, &comp.bool_type.base)) == null); - comp.void_value = try comp.arena().create(Value.Void{ + comp.void_value = try comp.arena().create(Value.Void); + comp.void_value.* = Value.Void{ .base = Value{ .id = Value.Id.Void, .typ = &Type.Void.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, - }); + }; - comp.true_value = try comp.arena().create(Value.Bool{ + comp.true_value = try comp.arena().create(Value.Bool); + comp.true_value.* = Value.Bool{ .base = Value{ .id = Value.Id.Bool, .typ = &Type.Bool.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, .x = true, - }); + }; - comp.false_value = try comp.arena().create(Value.Bool{ + comp.false_value = try comp.arena().create(Value.Bool); + comp.false_value.* = Value.Bool{ .base = Value{ .id = Value.Id.Bool, .typ = &Type.Bool.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, .x = false, - }); + }; - comp.noreturn_value = try comp.arena().create(Value.NoReturn{ + comp.noreturn_value = try comp.arena().create(Value.NoReturn); + comp.noreturn_value.* = Value.NoReturn{ .base = Value{ .id = Value.Id.NoReturn, .typ = &Type.NoReturn.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, - }); + }; for (CInt.list) |cint, i| { - const c_int_type = try comp.arena().create(Type.Int{ + const c_int_type = try comp.arena().create(Type.Int); + c_int_type.* = Type.Int{ .base = Type{ .name = cint.zig_name, .base = Value{ @@ -720,11 +731,12 @@ pub const Compilation = struct { .bit_count = comp.target.cIntTypeSizeInBits(cint.id), }, .garbage_node = undefined, - }); + }; comp.c_int_types[i] = c_int_type; assert((try comp.primitive_type_table.put(cint.zig_name, &c_int_type.base)) == null); } - comp.u8_type = try comp.arena().create(Type.Int{ + comp.u8_type = try comp.arena().create(Type.Int); + comp.u8_type.* = Type.Int{ .base = Type{ .name = "u8", .base = Value{ @@ -740,7 +752,7 @@ pub const Compilation = struct { .bit_count = 8, }, .garbage_node = undefined, - }); + }; assert((try comp.primitive_type_table.put(comp.u8_type.base.name, &comp.u8_type.base)) == null); } @@ -829,7 +841,7 @@ pub const Compilation = struct { }; errdefer self.gpa().free(source_code); - const tree = try self.gpa().createOne(ast.Tree); + const tree = try self.gpa().create(ast.Tree); tree.* = try std.zig.parse(self.gpa(), source_code); errdefer { tree.deinit(); @@ -925,7 +937,8 @@ pub const Compilation = struct { } } else { // add new decl - const fn_decl = try self.gpa().create(Decl.Fn{ + const fn_decl = try self.gpa().create(Decl.Fn); + fn_decl.* = Decl.Fn{ .base = Decl{ .id = Decl.Id.Fn, .name = name, @@ -936,7 +949,7 @@ pub const Compilation = struct { }, .value = Decl.Fn.Val{ .Unresolved = {} }, .fn_proto = fn_proto, - }); + }; tree_scope.base.ref(); errdefer self.gpa().destroy(fn_decl); @@ -1140,12 +1153,13 @@ pub const Compilation = struct { } } - const link_lib = try self.gpa().create(LinkLib{ + const link_lib = try self.gpa().create(LinkLib); + link_lib.* = LinkLib{ .name = name, .path = null, .provided_explicitly = provided_explicitly, .symbols = ArrayList([]u8).init(self.gpa()), - }); + }; try self.link_libs_list.append(link_lib); if (is_libc) { self.libc_link_lib = link_lib; diff --git a/src-self-hosted/errmsg.zig b/src-self-hosted/errmsg.zig index 0e552fde7ef5fd047aa1c3c806362336a7952d11..fc49fad410b6423f3fa481d534f5b400c1c90ef5 100644 --- a/src-self-hosted/errmsg.zig +++ b/src-self-hosted/errmsg.zig @@ -118,7 +118,8 @@ pub const Msg = struct { const realpath = try mem.dupe(comp.gpa(), u8, tree_scope.root().realpath); errdefer comp.gpa().free(realpath); - const msg = try comp.gpa().create(Msg{ + const msg = try comp.gpa().create(Msg); + msg.* = Msg{ .text = text, .realpath = realpath, .data = Data{ @@ -128,7 +129,7 @@ pub const Msg = struct { .span = span, }, }, - }); + }; tree_scope.base.ref(); return msg; } @@ -139,13 +140,14 @@ pub const Msg = struct { const realpath_copy = try mem.dupe(comp.gpa(), u8, realpath); errdefer comp.gpa().free(realpath_copy); - const msg = try comp.gpa().create(Msg{ + const msg = try comp.gpa().create(Msg); + msg.* = Msg{ .text = text, .realpath = realpath_copy, .data = Data{ .Cli = Cli{ .allocator = comp.gpa() }, }, - }); + }; return msg; } @@ -164,7 +166,8 @@ pub const Msg = struct { var out_stream = &std.io.BufferOutStream.init(&text_buf).stream; try parse_error.render(&tree_scope.tree.tokens, out_stream); - const msg = try comp.gpa().create(Msg{ + const msg = try comp.gpa().create(Msg); + msg.* = Msg{ .text = undefined, .realpath = realpath_copy, .data = Data{ @@ -177,7 +180,7 @@ pub const Msg = struct { }, }, }, - }); + }; tree_scope.base.ref(); msg.text = text_buf.toOwnedSlice(); return msg; @@ -203,7 +206,8 @@ pub const Msg = struct { var out_stream = &std.io.BufferOutStream.init(&text_buf).stream; try parse_error.render(&tree.tokens, out_stream); - const msg = try allocator.create(Msg{ + const msg = try allocator.create(Msg); + msg.* = Msg{ .text = undefined, .realpath = realpath_copy, .data = Data{ @@ -216,7 +220,7 @@ pub const Msg = struct { }, }, }, - }); + }; msg.text = text_buf.toOwnedSlice(); errdefer allocator.destroy(msg); diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 562765b354d5a68b23fa696d67a68ab63b32763e..0362bb4ef83f604764f16b97ba5198804788542c 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -1021,12 +1021,13 @@ pub const Builder = struct { pub const Error = Analyze.Error; pub fn init(comp: *Compilation, tree_scope: *Scope.AstTree, begin_scope: ?*Scope) !Builder { - const code = try comp.gpa().create(Code{ + const code = try comp.gpa().create(Code); + code.* = Code{ .basic_block_list = undefined, .arena = std.heap.ArenaAllocator.init(comp.gpa()), .return_type = null, .tree_scope = tree_scope, - }); + }; code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator); errdefer code.destroy(comp.gpa()); @@ -1052,7 +1053,8 @@ pub const Builder = struct { /// No need to clean up resources thanks to the arena allocator. pub fn createBasicBlock(self: *Builder, scope: *Scope, name_hint: [*]const u8) !*BasicBlock { - const basic_block = try self.arena().create(BasicBlock{ + const basic_block = try self.arena().create(BasicBlock); + basic_block.* = BasicBlock{ .ref_count = 0, .name_hint = name_hint, .debug_id = self.next_debug_id, @@ -1063,7 +1065,7 @@ pub const Builder = struct { .ref_instruction = null, .llvm_block = undefined, .llvm_exit_block = undefined, - }); + }; self.next_debug_id += 1; return basic_block; } @@ -1774,7 +1776,8 @@ pub const Builder = struct { params: I.Params, is_generated: bool, ) !*Inst { - const inst = try self.arena().create(I{ + const inst = try self.arena().create(I); + inst.* = I{ .base = Inst{ .id = Inst.typeToId(I), .is_generated = is_generated, @@ -1793,7 +1796,7 @@ pub const Builder = struct { .owner_bb = self.current_basic_block, }, .params = params, - }); + }; // Look at the params and ref() other instructions comptime var i = 0; diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 0742cbfe6507f7deada6b82ce15aac847ab2d84e..1403ab860dd9deb0bf5049136a2f70a239f34669 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -944,12 +944,13 @@ const CliPkg = struct { parent: ?*CliPkg, pub fn init(allocator: *mem.Allocator, name: []const u8, path: []const u8, parent: ?*CliPkg) !*CliPkg { - var pkg = try allocator.create(CliPkg{ + var pkg = try allocator.create(CliPkg); + pkg.* = CliPkg{ .name = name, .path = path, .children = ArrayList(*CliPkg).init(allocator), .parent = parent, - }); + }; return pkg; } diff --git a/src-self-hosted/package.zig b/src-self-hosted/package.zig index 720b2796510518b7895cce7eca7e5c5c13707529..0d31731b551e6dd730aaf379c989dc3a8f098244 100644 --- a/src-self-hosted/package.zig +++ b/src-self-hosted/package.zig @@ -15,11 +15,13 @@ pub const Package = struct { /// makes internal copies of root_src_dir and root_src_path /// allocator should be an arena allocator because Package never frees anything pub fn create(allocator: *mem.Allocator, root_src_dir: []const u8, root_src_path: []const u8) !*Package { - return allocator.create(Package{ + const ptr = try allocator.create(Package); + ptr.* = Package{ .root_src_dir = try Buffer.init(allocator, root_src_dir), .root_src_path = try Buffer.init(allocator, root_src_path), .table = Table.init(allocator), - }); + }; + return ptr; } pub fn add(self: *Package, name: []const u8, package: *Package) !void { diff --git a/src-self-hosted/scope.zig b/src-self-hosted/scope.zig index 43d3b5a784e0d3ee4bf32a79036247bf2e9c5f94..b14c073a9e4c8fccd35a897b15f9e41a59e6f60c 100644 --- a/src-self-hosted/scope.zig +++ b/src-self-hosted/scope.zig @@ -120,7 +120,7 @@ pub const Scope = struct { /// Creates a Root scope with 1 reference /// Takes ownership of realpath pub fn create(comp: *Compilation, realpath: []u8) !*Root { - const self = try comp.gpa().createOne(Root); + const self = try comp.gpa().create(Root); self.* = Root{ .base = Scope{ .id = Id.Root, @@ -150,7 +150,7 @@ pub const Scope = struct { /// Creates a scope with 1 reference /// Takes ownership of tree, will deinit and destroy when done. pub fn create(comp: *Compilation, tree: *ast.Tree, root_scope: *Root) !*AstTree { - const self = try comp.gpa().createOne(AstTree); + const self = try comp.gpa().create(AstTree); self.* = AstTree{ .base = undefined, .tree = tree, @@ -182,7 +182,7 @@ pub const Scope = struct { /// Creates a Decls scope with 1 reference pub fn create(comp: *Compilation, parent: *Scope) !*Decls { - const self = try comp.gpa().createOne(Decls); + const self = try comp.gpa().create(Decls); self.* = Decls{ .base = undefined, .table = event.RwLocked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())), @@ -235,7 +235,7 @@ pub const Scope = struct { /// Creates a Block scope with 1 reference pub fn create(comp: *Compilation, parent: *Scope) !*Block { - const self = try comp.gpa().createOne(Block); + const self = try comp.gpa().create(Block); self.* = Block{ .base = undefined, .incoming_values = undefined, @@ -262,7 +262,7 @@ pub const Scope = struct { /// Creates a FnDef scope with 1 reference /// Must set the fn_val later pub fn create(comp: *Compilation, parent: *Scope) !*FnDef { - const self = try comp.gpa().createOne(FnDef); + const self = try comp.gpa().create(FnDef); self.* = FnDef{ .base = undefined, .fn_val = null, @@ -281,7 +281,7 @@ pub const Scope = struct { /// Creates a CompTime scope with 1 reference pub fn create(comp: *Compilation, parent: *Scope) !*CompTime { - const self = try comp.gpa().createOne(CompTime); + const self = try comp.gpa().create(CompTime); self.* = CompTime{ .base = undefined }; self.base.init(Id.CompTime, parent); return self; @@ -309,7 +309,7 @@ pub const Scope = struct { kind: Kind, defer_expr_scope: *DeferExpr, ) !*Defer { - const self = try comp.gpa().createOne(Defer); + const self = try comp.gpa().create(Defer); self.* = Defer{ .base = undefined, .defer_expr_scope = defer_expr_scope, @@ -333,7 +333,7 @@ pub const Scope = struct { /// Creates a DeferExpr scope with 1 reference pub fn create(comp: *Compilation, parent: *Scope, expr_node: *ast.Node) !*DeferExpr { - const self = try comp.gpa().createOne(DeferExpr); + const self = try comp.gpa().create(DeferExpr); self.* = DeferExpr{ .base = undefined, .expr_node = expr_node, @@ -398,7 +398,7 @@ pub const Scope = struct { } fn create(comp: *Compilation, parent: *Scope, name: []const u8, src_node: *ast.Node) !*Var { - const self = try comp.gpa().createOne(Var); + const self = try comp.gpa().create(Var); self.* = Var{ .base = undefined, .name = name, diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index fa3134390252917c54b0eb6ca16ad24d33896e91..8a05594b301bfcbe80f7400053bb5a950d0666e5 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -413,7 +413,7 @@ pub const Type = struct { key.ref(); errdefer key.deref(comp); - const self = try comp.gpa().createOne(Fn); + const self = try comp.gpa().create(Fn); self.* = Fn{ .base = undefined, .key = key, @@ -615,11 +615,12 @@ pub const Type = struct { } } - const self = try comp.gpa().create(Int{ + const self = try comp.gpa().create(Int); + self.* = Int{ .base = undefined, .key = key, .garbage_node = undefined, - }); + }; errdefer comp.gpa().destroy(self); const u_or_i = "ui"[@boolToInt(key.is_signed)]; @@ -781,11 +782,12 @@ pub const Type = struct { } } - const self = try comp.gpa().create(Pointer{ + const self = try comp.gpa().create(Pointer); + self.* = Pointer{ .base = undefined, .key = normal_key, .garbage_node = undefined, - }); + }; errdefer comp.gpa().destroy(self); const size_str = switch (self.key.size) { @@ -879,11 +881,12 @@ pub const Type = struct { } } - const self = try comp.gpa().create(Array{ + const self = try comp.gpa().create(Array); + self.* = Array{ .base = undefined, .key = key, .garbage_node = undefined, - }); + }; errdefer comp.gpa().destroy(self); const name = try std.fmt.allocPrint(comp.gpa(), "[{}]{}", key.len, key.elem_type.name); diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index e6dca4eff77e1b8c634b117635f9551316193b91..9431c614b92eeed292e15af6fa0039d30475d757 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -135,14 +135,15 @@ pub const Value = struct { symbol_name: Buffer, pub fn create(comp: *Compilation, fn_type: *Type.Fn, symbol_name: Buffer) !*FnProto { - const self = try comp.gpa().create(FnProto{ + const self = try comp.gpa().create(FnProto); + self.* = FnProto{ .base = Value{ .id = Value.Id.FnProto, .typ = &fn_type.base, .ref_count = std.atomic.Int(usize).init(1), }, .symbol_name = symbol_name, - }); + }; fn_type.base.base.ref(); return self; } @@ -190,14 +191,16 @@ pub const Value = struct { /// Creates a Fn value with 1 ref /// Takes ownership of symbol_name pub fn create(comp: *Compilation, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: Buffer) !*Fn { - const link_set_node = try comp.gpa().create(Compilation.FnLinkSet.Node{ + const link_set_node = try comp.gpa().create(Compilation.FnLinkSet.Node); + link_set_node.* = Compilation.FnLinkSet.Node{ .data = null, .next = undefined, .prev = undefined, - }); + }; errdefer comp.gpa().destroy(link_set_node); - const self = try comp.gpa().create(Fn{ + const self = try comp.gpa().create(Fn); + self.* = Fn{ .base = Value{ .id = Value.Id.Fn, .typ = &fn_type.base, @@ -209,7 +212,7 @@ pub const Value = struct { .symbol_name = symbol_name, .containing_object = Buffer.initNull(comp.gpa()), .link_set_node = link_set_node, - }); + }; fn_type.base.base.ref(); fndef_scope.fn_val = self; fndef_scope.base.ref(); @@ -353,7 +356,8 @@ pub const Value = struct { var ptr_type_consumed = false; errdefer if (!ptr_type_consumed) ptr_type.base.base.deref(comp); - const self = try comp.gpa().create(Value.Ptr{ + const self = try comp.gpa().create(Value.Ptr); + self.* = Value.Ptr{ .base = Value{ .id = Value.Id.Ptr, .typ = &ptr_type.base, @@ -366,7 +370,7 @@ pub const Value = struct { }, }, .mut = Mut.CompTimeConst, - }); + }; ptr_type_consumed = true; errdefer comp.gpa().destroy(self); @@ -430,14 +434,15 @@ pub const Value = struct { }) catch unreachable); errdefer array_type.base.base.deref(comp); - const self = try comp.gpa().create(Value.Array{ + const self = try comp.gpa().create(Value.Array); + self.* = Value.Array{ .base = Value{ .id = Value.Id.Array, .typ = &array_type.base, .ref_count = std.atomic.Int(usize).init(1), }, .special = Special{ .OwnedBuffer = buffer }, - }); + }; errdefer comp.gpa().destroy(self); return self; @@ -509,14 +514,15 @@ pub const Value = struct { big_int: std.math.big.Int, pub fn createFromString(comp: *Compilation, typ: *Type, base: u8, value: []const u8) !*Int { - const self = try comp.gpa().create(Value.Int{ + const self = try comp.gpa().create(Value.Int); + self.* = Value.Int{ .base = Value{ .id = Value.Id.Int, .typ = typ, .ref_count = std.atomic.Int(usize).init(1), }, .big_int = undefined, - }); + }; typ.base.ref(); errdefer comp.gpa().destroy(self); @@ -557,14 +563,15 @@ pub const Value = struct { old.base.typ.base.ref(); errdefer old.base.typ.base.deref(comp); - const new = try comp.gpa().create(Value.Int{ + const new = try comp.gpa().create(Value.Int); + new.* = Value.Int{ .base = Value{ .id = Value.Id.Int, .typ = old.base.typ, .ref_count = std.atomic.Int(usize).init(1), }, .big_int = undefined, - }); + }; errdefer comp.gpa().destroy(new); new.big_int = try old.big_int.clone(); diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig index 6c61bcc0484189377321ce92ecd305078345cbba..183c434dc574fdf1a78c6910b5e52c2145054346 100644 --- a/std/atomic/queue.zig +++ b/std/atomic/queue.zig @@ -221,11 +221,12 @@ fn startPuts(ctx: *Context) u8 { while (put_count != 0) : (put_count -= 1) { std.os.time.sleep(1); // let the os scheduler be our fuzz const x = @bitCast(i32, r.random.scalar(u32)); - const node = ctx.allocator.create(Queue(i32).Node{ + const node = ctx.allocator.create(Queue(i32).Node) catch unreachable; + node.* = Queue(i32).Node{ .prev = undefined, .next = undefined, .data = x, - }) catch unreachable; + }; ctx.queue.put(node); _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst); } diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig index 1e4981353bc0d47e21c7c5027cd62038eb835de9..503fa0c0ceb4fc4dbceadb86cde1e4705c0ad2d4 100644 --- a/std/atomic/stack.zig +++ b/std/atomic/stack.zig @@ -155,10 +155,11 @@ fn startPuts(ctx: *Context) u8 { while (put_count != 0) : (put_count -= 1) { std.os.time.sleep(1); // let the os scheduler be our fuzz const x = @bitCast(i32, r.random.scalar(u32)); - const node = ctx.allocator.create(Stack(i32).Node{ + const node = ctx.allocator.create(Stack(i32).Node) catch unreachable; + node.* = Stack(i32).Node{ .next = undefined, .data = x, - }) catch unreachable; + }; ctx.stack.push(node); _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst); } diff --git a/std/build.zig b/std/build.zig index 90f5bec656314860926fa62c811fc9ff5c2700c2..6f58594190126c5d8da9d9a9a5b5f8fe3bd8e961 100644 --- a/std/build.zig +++ b/std/build.zig @@ -89,7 +89,7 @@ pub const Builder = struct { }; pub fn init(allocator: *Allocator, zig_exe: []const u8, build_root: []const u8, cache_root: []const u8) Builder { - const env_map = allocator.createOne(BufMap) catch unreachable; + const env_map = allocator.create(BufMap) catch unreachable; env_map.* = os.getEnvMap(allocator) catch unreachable; var self = Builder{ .zig_exe = zig_exe, @@ -170,7 +170,8 @@ pub const Builder = struct { } pub fn addTest(self: *Builder, root_src: []const u8) *TestStep { - const test_step = self.allocator.create(TestStep.init(self, root_src)) catch unreachable; + const test_step = self.allocator.create(TestStep) catch unreachable; + test_step.* = TestStep.init(self, root_src); return test_step; } @@ -202,18 +203,21 @@ pub const Builder = struct { } pub fn addWriteFile(self: *Builder, file_path: []const u8, data: []const u8) *WriteFileStep { - const write_file_step = self.allocator.create(WriteFileStep.init(self, file_path, data)) catch unreachable; + const write_file_step = self.allocator.create(WriteFileStep) catch unreachable; + write_file_step.* = WriteFileStep.init(self, file_path, data); return write_file_step; } pub fn addLog(self: *Builder, comptime format: []const u8, args: ...) *LogStep { const data = self.fmt(format, args); - const log_step = self.allocator.create(LogStep.init(self, data)) catch unreachable; + const log_step = self.allocator.create(LogStep) catch unreachable; + log_step.* = LogStep.init(self, data); return log_step; } pub fn addRemoveDirTree(self: *Builder, dir_path: []const u8) *RemoveDirStep { - const remove_dir_step = self.allocator.create(RemoveDirStep.init(self, dir_path)) catch unreachable; + const remove_dir_step = self.allocator.create(RemoveDirStep) catch unreachable; + remove_dir_step.* = RemoveDirStep.init(self, dir_path); return remove_dir_step; } @@ -414,10 +418,11 @@ pub const Builder = struct { } pub fn step(self: *Builder, name: []const u8, description: []const u8) *Step { - const step_info = self.allocator.create(TopLevelStep{ + const step_info = self.allocator.create(TopLevelStep) catch unreachable; + step_info.* = TopLevelStep{ .step = Step.initNoOp(name, self.allocator), .description = description, - }) catch unreachable; + }; self.top_level_steps.append(step_info) catch unreachable; return &step_info.step; } @@ -616,7 +621,8 @@ pub const Builder = struct { const full_dest_path = os.path.resolve(self.allocator, self.prefix, dest_rel_path) catch unreachable; self.pushInstalledFile(full_dest_path); - const install_step = self.allocator.create(InstallFileStep.init(self, src_path, full_dest_path)) catch unreachable; + const install_step = self.allocator.create(InstallFileStep) catch unreachable; + install_step.* = InstallFileStep.init(self, src_path, full_dest_path); return install_step; } @@ -865,43 +871,51 @@ pub const LibExeObjStep = struct { }; pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?[]const u8, ver: Version) *LibExeObjStep { - const self = builder.allocator.create(initExtraArgs(builder, name, root_src, Kind.Lib, false, ver)) catch unreachable; + const self = builder.allocator.create(LibExeObjStep) catch unreachable; + self.* = initExtraArgs(builder, name, root_src, Kind.Lib, false, ver); return self; } pub fn createCSharedLibrary(builder: *Builder, name: []const u8, version: Version) *LibExeObjStep { - const self = builder.allocator.create(initC(builder, name, Kind.Lib, version, false)) catch unreachable; + const self = builder.allocator.create(LibExeObjStep) catch unreachable; + self.* = initC(builder, name, Kind.Lib, version, false); return self; } pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { - const self = builder.allocator.create(initExtraArgs(builder, name, root_src, Kind.Lib, true, builder.version(0, 0, 0))) catch unreachable; + const self = builder.allocator.create(LibExeObjStep) catch unreachable; + self.* = initExtraArgs(builder, name, root_src, Kind.Lib, true, builder.version(0, 0, 0)); return self; } pub fn createCStaticLibrary(builder: *Builder, name: []const u8) *LibExeObjStep { - const self = builder.allocator.create(initC(builder, name, Kind.Lib, builder.version(0, 0, 0), true)) catch unreachable; + const self = builder.allocator.create(LibExeObjStep) catch unreachable; + self.* = initC(builder, name, Kind.Lib, builder.version(0, 0, 0), true); return self; } pub fn createObject(builder: *Builder, name: []const u8, root_src: []const u8) *LibExeObjStep { - const self = builder.allocator.create(initExtraArgs(builder, name, root_src, Kind.Obj, false, builder.version(0, 0, 0))) catch unreachable; + const self = builder.allocator.create(LibExeObjStep) catch unreachable; + self.* = initExtraArgs(builder, name, root_src, Kind.Obj, false, builder.version(0, 0, 0)); return self; } pub fn createCObject(builder: *Builder, name: []const u8, src: []const u8) *LibExeObjStep { - const self = builder.allocator.create(initC(builder, name, Kind.Obj, builder.version(0, 0, 0), false)) catch unreachable; + const self = builder.allocator.create(LibExeObjStep) catch unreachable; + self.* = initC(builder, name, Kind.Obj, builder.version(0, 0, 0), false); self.object_src = src; return self; } pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?[]const u8, static: bool) *LibExeObjStep { - const self = builder.allocator.create(initExtraArgs(builder, name, root_src, Kind.Exe, static, builder.version(0, 0, 0))) catch unreachable; + const self = builder.allocator.create(LibExeObjStep) catch unreachable; + self.* = initExtraArgs(builder, name, root_src, Kind.Exe, static, builder.version(0, 0, 0)); return self; } pub fn createCExecutable(builder: *Builder, name: []const u8) *LibExeObjStep { - const self = builder.allocator.create(initC(builder, name, Kind.Exe, builder.version(0, 0, 0), false)) catch unreachable; + const self = builder.allocator.create(LibExeObjStep) catch unreachable; + self.* = initC(builder, name, Kind.Exe, builder.version(0, 0, 0), false); return self; } @@ -1914,13 +1928,14 @@ pub const CommandStep = struct { /// ::argv is copied. pub fn create(builder: *Builder, cwd: ?[]const u8, env_map: *const BufMap, argv: []const []const u8) *CommandStep { - const self = builder.allocator.create(CommandStep{ + const self = builder.allocator.create(CommandStep) catch unreachable; + self.* = CommandStep{ .builder = builder, .step = Step.init(argv[0], builder.allocator, make), .argv = builder.allocator.alloc([]u8, argv.len) catch unreachable, .cwd = cwd, .env_map = env_map, - }) catch unreachable; + }; mem.copy([]const u8, self.argv, argv); self.step.name = self.argv[0]; @@ -1949,12 +1964,13 @@ const InstallArtifactStep = struct { LibExeObjStep.Kind.Exe => builder.exe_dir, LibExeObjStep.Kind.Lib => builder.lib_dir, }; - const self = builder.allocator.create(Self{ + const self = builder.allocator.create(Self) catch unreachable; + self.* = Self{ .builder = builder, .step = Step.init(builder.fmt("install {}", artifact.step.name), builder.allocator, make), .artifact = artifact, .dest_file = os.path.join(builder.allocator, dest_dir, artifact.out_filename) catch unreachable, - }) catch unreachable; + }; self.step.dependOn(&artifact.step); builder.pushInstalledFile(self.dest_file); if (self.artifact.kind == LibExeObjStep.Kind.Lib and !self.artifact.static) { diff --git a/std/debug/index.zig b/std/debug/index.zig index 445f943594cd24f9ba73df3e053ea91b41a7ff3e..838bd0c166fdfb58866b3dd28e85552d91517904 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -751,7 +751,7 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { const self_file = try os.openSelfExe(); defer self_file.close(); - const coff_obj = try allocator.createOne(coff.Coff); + const coff_obj = try allocator.create(coff.Coff); coff_obj.* = coff.Coff{ .in_file = self_file, .allocator = allocator, @@ -1036,7 +1036,7 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { } } } - const sentinel = try allocator.createOne(macho.nlist_64); + const sentinel = try allocator.create(macho.nlist_64); sentinel.* = macho.nlist_64{ .n_strx = 0, .n_type = 36, @@ -1949,7 +1949,8 @@ fn scanAllCompileUnits(di: *DwarfInfo) !void { try di.dwarf_seekable_stream.seekTo(compile_unit_pos); - const compile_unit_die = try di.allocator().create(try parseDie(di, abbrev_table, is_64)); + const compile_unit_die = try di.allocator().create(Die); + compile_unit_die.* = try parseDie(di, abbrev_table, is_64); if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo; diff --git a/std/event/channel.zig b/std/event/channel.zig index f04ad1604e3d46f82f73f655de960b1fbebb3448..f8cdae6208906a949ff3fcce6660cba2bbe9fc2c 100644 --- a/std/event/channel.zig +++ b/std/event/channel.zig @@ -54,7 +54,8 @@ pub fn Channel(comptime T: type) type { const buffer_nodes = try loop.allocator.alloc(T, capacity); errdefer loop.allocator.free(buffer_nodes); - const self = try loop.allocator.create(SelfChannel{ + const self = try loop.allocator.create(SelfChannel); + self.* = SelfChannel{ .loop = loop, .buffer_len = 0, .buffer_nodes = buffer_nodes, @@ -66,7 +67,7 @@ pub fn Channel(comptime T: type) type { .or_null_queue = std.atomic.Queue(*std.atomic.Queue(GetNode).Node).init(), .get_count = 0, .put_count = 0, - }); + }; errdefer loop.allocator.destroy(self); return self; diff --git a/std/event/fs.zig b/std/event/fs.zig index 7e77b3e6e2de0fda045efc267e58910b22ef6be0..097f2beddcd3901aa4785f1956930400cec45452 100644 --- a/std/event/fs.zig +++ b/std/event/fs.zig @@ -495,7 +495,7 @@ pub const CloseOperation = struct { }; pub fn start(loop: *Loop) (error{OutOfMemory}!*CloseOperation) { - const self = try loop.allocator.createOne(CloseOperation); + const self = try loop.allocator.create(CloseOperation); self.* = CloseOperation{ .loop = loop, .os_data = switch (builtin.os) { @@ -787,7 +787,7 @@ pub fn Watch(comptime V: type) type { }, builtin.Os.windows => { - const self = try loop.allocator.createOne(Self); + const self = try loop.allocator.create(Self); errdefer loop.allocator.destroy(self); self.* = Self{ .channel = channel, @@ -802,7 +802,7 @@ pub fn Watch(comptime V: type) type { }, builtin.Os.macosx, builtin.Os.freebsd => { - const self = try loop.allocator.createOne(Self); + const self = try loop.allocator.create(Self); errdefer loop.allocator.destroy(self); self.* = Self{ @@ -1068,7 +1068,7 @@ pub fn Watch(comptime V: type) type { } } else { errdefer _ = self.os_data.dir_table.remove(dirname); - const dir = try self.channel.loop.allocator.createOne(OsData.Dir); + const dir = try self.channel.loop.allocator.create(OsData.Dir); errdefer self.channel.loop.allocator.destroy(dir); dir.* = OsData.Dir{ diff --git a/std/event/group.zig b/std/event/group.zig index 0e9c2d96557c1eac8f770fbb54861eedd21e26c2..7f6b5d953ba375c92667cd8e95288c2d544728ef 100644 --- a/std/event/group.zig +++ b/std/event/group.zig @@ -42,10 +42,11 @@ pub fn Group(comptime ReturnType: type) type { /// Add a promise to the group. Thread-safe. pub fn add(self: *Self, handle: promise->ReturnType) (error{OutOfMemory}!void) { - const node = try self.lock.loop.allocator.create(Stack.Node{ + const node = try self.lock.loop.allocator.create(Stack.Node); + node.* = Stack.Node{ .next = undefined, .data = handle, - }); + }; self.alloc_stack.push(node); } diff --git a/std/heap.zig b/std/heap.zig index 46b247fa7eadf63e725260de9f911cc055085017..fd2ce1e965fa44382a4f4ac83ddfc34d6401ec50 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -518,7 +518,8 @@ fn testAllocator(allocator: *mem.Allocator) !void { var slice = try allocator.alloc(*i32, 100); assert(slice.len == 100); for (slice) |*item, i| { - item.* = try allocator.create(@intCast(i32, i)); + item.* = try allocator.create(i32); + item.*.* = @intCast(i32, i); } slice = try allocator.realloc(*i32, slice, 20000); diff --git a/std/io.zig b/std/io.zig index 46625cd34b78f83fe2c614b6af7ea4dd541e2eb2..c8701aeda60d2d69c80bcfc4aa4e4ca1753c2349 100644 --- a/std/io.zig +++ b/std/io.zig @@ -944,12 +944,13 @@ pub const BufferedAtomicFile = struct { pub fn create(allocator: *mem.Allocator, dest_path: []const u8) !*BufferedAtomicFile { // TODO with well defined copy elision we don't need this allocation - var self = try allocator.create(BufferedAtomicFile{ + var self = try allocator.create(BufferedAtomicFile); + self.* = BufferedAtomicFile{ .atomic_file = undefined, .file_stream = undefined, .buffered_stream = undefined, .allocator = allocator, - }); + }; errdefer allocator.destroy(self); self.atomic_file = try os.AtomicFile.init(dest_path, os.File.default_mode); diff --git a/std/linked_list.zig b/std/linked_list.zig index c3db55b5a67cebf94d7bd3238c859bc6852538bf..7021cac70752ed4c2c96e695c90d2069021efc5d 100644 --- a/std/linked_list.zig +++ b/std/linked_list.zig @@ -190,7 +190,7 @@ pub fn LinkedList(comptime T: type) type { /// Returns: /// A pointer to the new node. pub fn allocateNode(list: *Self, allocator: *Allocator) !*Node { - return allocator.create(Node(undefined)); + return allocator.create(Node); } /// Deallocate a node. diff --git a/std/mem.zig b/std/mem.zig index fb5f6fd5da62cd402f856c1c87c08aebd4ab0c14..a403d80453ab14ac498d21cbe0405b2b91cc334b 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -36,20 +36,9 @@ pub const Allocator = struct { /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn` freeFn: fn (self: *Allocator, old_mem: []u8) void, - /// Call `destroy` with the result - /// TODO this is deprecated. use createOne instead - pub fn create(self: *Allocator, init: var) Error!*@typeOf(init) { - const T = @typeOf(init); - if (@sizeOf(T) == 0) return &(T{}); - const slice = try self.alloc(T, 1); - const ptr = &slice[0]; - ptr.* = init; - return ptr; - } - /// Call `destroy` with the result. /// Returns undefined memory. - pub fn createOne(self: *Allocator, comptime T: type) Error!*T { + pub fn create(self: *Allocator, comptime T: type) Error!*T { if (@sizeOf(T) == 0) return &(T{}); const slice = try self.alloc(T, 1); return &slice[0]; diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 0aa896ff1be209a37c522af72f08b1031a64502d..9f33bee9057541534a2ca721cdd194091afca884 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -88,7 +88,8 @@ pub const ChildProcess = struct { /// First argument in argv is the executable. /// On success must call deinit. pub fn init(argv: []const []const u8, allocator: *mem.Allocator) !*ChildProcess { - const child = try allocator.create(ChildProcess{ + const child = try allocator.create(ChildProcess); + child.* = ChildProcess{ .allocator = allocator, .argv = argv, .pid = undefined, @@ -109,7 +110,7 @@ pub const ChildProcess = struct { .stdin_behavior = StdIo.Inherit, .stdout_behavior = StdIo.Inherit, .stderr_behavior = StdIo.Inherit, - }); + }; errdefer allocator.destroy(child); return child; } diff --git a/std/os/index.zig b/std/os/index.zig index 75abe3bbdec268660d4d95484a1c2eccd4c2ce5c..0d0e07bfa353ca1c6203b90e74ff8aa41e015260 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -3047,7 +3047,8 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) orelse return SpawnThreadError.OutOfMemory; errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0); const bytes = @ptrCast([*]u8, bytes_ptr)[0..byte_count]; - const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext{ + const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable; + outer_context.* = WinThread.OuterContext{ .thread = Thread{ .data = Thread.Data{ .heap_handle = heap_handle, @@ -3056,7 +3057,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread }, }, .inner = context, - }) catch unreachable; + }; const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(*c_void, &outer_context.inner); outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) orelse { diff --git a/std/zig/parse.zig b/std/zig/parse.zig index a216484d7d669ba61a7f8ce0f012960866f01209..783464c620709cd4bd0470bbf9b284e213fd653d 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -17,14 +17,15 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { defer stack.deinit(); const arena = &tree_arena.allocator; - const root_node = try arena.create(ast.Node.Root{ + const root_node = try arena.create(ast.Node.Root); + root_node.* = ast.Node.Root{ .base = ast.Node{ .id = ast.Node.Id.Root }, .decls = ast.Node.Root.DeclList.init(arena), .doc_comments = null, .shebang = null, // initialized when we get the eof token .eof_token = undefined, - }); + }; var tree = ast.Tree{ .source = source, @@ -75,20 +76,22 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { Token.Id.Keyword_test => { stack.append(State.TopLevel) catch unreachable; - const block = try arena.create(ast.Node.Block{ + const block = try arena.create(ast.Node.Block); + block.* = ast.Node.Block{ .base = ast.Node{ .id = ast.Node.Id.Block }, .label = null, .lbrace = undefined, .statements = ast.Node.Block.StatementList.init(arena), .rbrace = undefined, - }); - const test_node = try arena.create(ast.Node.TestDecl{ + }; + const test_node = try arena.create(ast.Node.TestDecl); + test_node.* = ast.Node.TestDecl{ .base = ast.Node{ .id = ast.Node.Id.TestDecl }, .doc_comments = comments, .test_token = token_index, .name = undefined, .body_node = &block.base, - }); + }; try root_node.decls.push(&test_node.base); try stack.append(State{ .Block = block }); try stack.append(State{ @@ -119,19 +122,21 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_comptime => { - const block = try arena.create(ast.Node.Block{ + const block = try arena.create(ast.Node.Block); + block.* = ast.Node.Block{ .base = ast.Node{ .id = ast.Node.Id.Block }, .label = null, .lbrace = undefined, .statements = ast.Node.Block.StatementList.init(arena), .rbrace = undefined, - }); - const node = try arena.create(ast.Node.Comptime{ + }; + const node = try arena.create(ast.Node.Comptime); + node.* = ast.Node.Comptime{ .base = ast.Node{ .id = ast.Node.Id.Comptime }, .comptime_token = token_index, .expr = &block.base, .doc_comments = comments, - }); + }; try root_node.decls.push(&node.base); stack.append(State.TopLevel) catch unreachable; @@ -235,14 +240,15 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { return tree; } - const node = try arena.create(ast.Node.Use{ + const node = try arena.create(ast.Node.Use); + node.* = ast.Node.Use{ .base = ast.Node{ .id = ast.Node.Id.Use }, .use_token = token_index, .visib_token = ctx.visib_token, .expr = undefined, .semicolon_token = undefined, .doc_comments = ctx.comments, - }); + }; try ctx.decls.push(&node.base); stack.append(State{ @@ -276,7 +282,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc, Token.Id.Keyword_async => { - const fn_proto = try arena.create(ast.Node.FnProto{ + const fn_proto = try arena.create(ast.Node.FnProto); + fn_proto.* = ast.Node.FnProto{ .base = ast.Node{ .id = ast.Node.Id.FnProto }, .doc_comments = ctx.comments, .visib_token = ctx.visib_token, @@ -292,7 +299,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .lib_name = ctx.lib_name, .align_expr = null, .section_expr = null, - }); + }; try ctx.decls.push(&fn_proto.base); stack.append(State{ .FnDef = fn_proto }) catch unreachable; try stack.append(State{ .FnProto = fn_proto }); @@ -309,12 +316,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_async => { - const async_node = try arena.create(ast.Node.AsyncAttribute{ + const async_node = try arena.create(ast.Node.AsyncAttribute); + async_node.* = ast.Node.AsyncAttribute{ .base = ast.Node{ .id = ast.Node.Id.AsyncAttribute }, .async_token = token_index, .allocator_type = null, .rangle_bracket = null, - }); + }; fn_proto.async_attr = async_node; try stack.append(State{ @@ -341,13 +349,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.TopLevelExternOrField => |ctx| { if (eatToken(&tok_it, &tree, Token.Id.Identifier)) |identifier| { - const node = try arena.create(ast.Node.StructField{ + const node = try arena.create(ast.Node.StructField); + node.* = ast.Node.StructField{ .base = ast.Node{ .id = ast.Node.Id.StructField }, .doc_comments = ctx.comments, .visib_token = ctx.visib_token, .name_token = identifier, .type_expr = undefined, - }); + }; const node_ptr = try ctx.container_decl.fields_and_decls.addOne(); node_ptr.* = &node.base; @@ -391,7 +400,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token = nextToken(&tok_it, &tree); const token_index = token.index; const token_ptr = token.ptr; - const node = try arena.create(ast.Node.ContainerDecl{ + const node = try arena.create(ast.Node.ContainerDecl); + node.* = ast.Node.ContainerDecl{ .base = ast.Node{ .id = ast.Node.Id.ContainerDecl }, .layout_token = ctx.layout_token, .kind_token = switch (token_ptr.id) { @@ -405,7 +415,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .fields_and_decls = ast.Node.ContainerDecl.DeclList.init(arena), .lbrace_token = undefined, .rbrace_token = undefined, - }); + }; ctx.opt_ctx.store(&node.base); stack.append(State{ .ContainerDecl = node }) catch unreachable; @@ -464,13 +474,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { Token.Id.Identifier => { switch (tree.tokens.at(container_decl.kind_token).id) { Token.Id.Keyword_struct => { - const node = try arena.create(ast.Node.StructField{ + const node = try arena.create(ast.Node.StructField); + node.* = ast.Node.StructField{ .base = ast.Node{ .id = ast.Node.Id.StructField }, .doc_comments = comments, .visib_token = null, .name_token = token_index, .type_expr = undefined, - }); + }; const node_ptr = try container_decl.fields_and_decls.addOne(); node_ptr.* = &node.base; @@ -485,13 +496,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_union => { - const node = try arena.create(ast.Node.UnionTag{ + const node = try arena.create(ast.Node.UnionTag); + node.* = ast.Node.UnionTag{ .base = ast.Node{ .id = ast.Node.Id.UnionTag }, .name_token = token_index, .type_expr = null, .value_expr = null, .doc_comments = comments, - }); + }; try container_decl.fields_and_decls.push(&node.base); try stack.append(State{ @@ -506,12 +518,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_enum => { - const node = try arena.create(ast.Node.EnumTag{ + const node = try arena.create(ast.Node.EnumTag); + node.* = ast.Node.EnumTag{ .base = ast.Node{ .id = ast.Node.Id.EnumTag }, .name_token = token_index, .value = null, .doc_comments = comments, - }); + }; try container_decl.fields_and_decls.push(&node.base); try stack.append(State{ @@ -593,7 +606,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.VarDecl => |ctx| { - const var_decl = try arena.create(ast.Node.VarDecl{ + const var_decl = try arena.create(ast.Node.VarDecl); + var_decl.* = ast.Node.VarDecl{ .base = ast.Node{ .id = ast.Node.Id.VarDecl }, .doc_comments = ctx.comments, .visib_token = ctx.visib_token, @@ -609,7 +623,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .name_token = undefined, .eq_token = undefined, .semicolon_token = undefined, - }); + }; try ctx.list.push(&var_decl.base); try stack.append(State{ .VarDeclAlign = var_decl }); @@ -708,13 +722,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.LBrace => { - const block = try arena.create(ast.Node.Block{ + const block = try arena.create(ast.Node.Block); + block.* = ast.Node.Block{ .base = ast.Node{ .id = ast.Node.Id.Block }, .label = null, .lbrace = token_index, .statements = ast.Node.Block.StatementList.init(arena), .rbrace = undefined, - }); + }; fn_proto.body_node = &block.base; stack.append(State{ .Block = block }) catch unreachable; continue; @@ -770,10 +785,11 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { // TODO: this is a special case. Remove this when #760 is fixed if (token_ptr.id == Token.Id.Keyword_anyerror) { if (tok_it.peek().?.id == Token.Id.LBrace) { - const error_type_node = try arena.create(ast.Node.ErrorType{ + const error_type_node = try arena.create(ast.Node.ErrorType); + error_type_node.* = ast.Node.ErrorType{ .base = ast.Node{ .id = ast.Node.Id.ErrorType }, .token = token_index, - }); + }; fn_proto.return_type = ast.Node.FnProto.ReturnType{ .Explicit = &error_type_node.base }; continue; } @@ -791,14 +807,15 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { if (eatToken(&tok_it, &tree, Token.Id.RParen)) |_| { continue; } - const param_decl = try arena.create(ast.Node.ParamDecl{ + const param_decl = try arena.create(ast.Node.ParamDecl); + param_decl.* = ast.Node.ParamDecl{ .base = ast.Node{ .id = ast.Node.Id.ParamDecl }, .comptime_token = null, .noalias_token = null, .name_token = null, .type_node = undefined, .var_args_token = null, - }); + }; try fn_proto.params.push(¶m_decl.base); stack.append(State{ @@ -877,13 +894,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.LBrace => { - const block = try arena.create(ast.Node.Block{ + const block = try arena.create(ast.Node.Block); + block.* = ast.Node.Block{ .base = ast.Node{ .id = ast.Node.Id.Block }, .label = ctx.label, .lbrace = token_index, .statements = ast.Node.Block.StatementList.init(arena), .rbrace = undefined, - }); + }; ctx.opt_ctx.store(&block.base); stack.append(State{ .Block = block }) catch unreachable; continue; @@ -970,7 +988,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { } }, State.While => |ctx| { - const node = try arena.create(ast.Node.While{ + const node = try arena.create(ast.Node.While); + node.* = ast.Node.While{ .base = ast.Node{ .id = ast.Node.Id.While }, .label = ctx.label, .inline_token = ctx.inline_token, @@ -980,7 +999,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .continue_expr = null, .body = undefined, .@"else" = null, - }); + }; ctx.opt_ctx.store(&node.base); stack.append(State{ .Else = &node.@"else" }) catch unreachable; try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.body } }); @@ -999,7 +1018,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.For => |ctx| { - const node = try arena.create(ast.Node.For{ + const node = try arena.create(ast.Node.For); + node.* = ast.Node.For{ .base = ast.Node{ .id = ast.Node.Id.For }, .label = ctx.label, .inline_token = ctx.inline_token, @@ -1008,7 +1028,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .payload = null, .body = undefined, .@"else" = null, - }); + }; ctx.opt_ctx.store(&node.base); stack.append(State{ .Else = &node.@"else" }) catch unreachable; try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.body } }); @@ -1020,12 +1040,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.Else => |dest| { if (eatToken(&tok_it, &tree, Token.Id.Keyword_else)) |else_token| { - const node = try arena.create(ast.Node.Else{ + const node = try arena.create(ast.Node.Else); + node.* = ast.Node.Else{ .base = ast.Node{ .id = ast.Node.Id.Else }, .else_token = else_token, .payload = null, .body = undefined, - }); + }; dest.* = node; stack.append(State{ .Expression = OptionalCtx{ .Required = &node.body } }) catch unreachable; @@ -1083,11 +1104,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => { - const node = try arena.create(ast.Node.Defer{ + const node = try arena.create(ast.Node.Defer); + node.* = ast.Node.Defer{ .base = ast.Node{ .id = ast.Node.Id.Defer }, .defer_token = token_index, .expr = undefined, - }); + }; const node_ptr = try block.statements.addOne(); node_ptr.* = &node.base; @@ -1096,13 +1118,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.LBrace => { - const inner_block = try arena.create(ast.Node.Block{ + const inner_block = try arena.create(ast.Node.Block); + inner_block.* = ast.Node.Block{ .base = ast.Node{ .id = ast.Node.Id.Block }, .label = null, .lbrace = token_index, .statements = ast.Node.Block.StatementList.init(arena), .rbrace = undefined, - }); + }; try block.statements.push(&inner_block.base); stack.append(State{ .Block = inner_block }) catch unreachable; @@ -1164,14 +1187,15 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.create(ast.Node.AsmOutput{ + const node = try arena.create(ast.Node.AsmOutput); + node.* = ast.Node.AsmOutput{ .base = ast.Node{ .id = ast.Node.Id.AsmOutput }, .lbracket = lbracket_index, .symbolic_name = undefined, .constraint = undefined, .kind = undefined, .rparen = undefined, - }); + }; try items.push(node); stack.append(State{ .AsmOutputItems = items }) catch unreachable; @@ -1218,14 +1242,15 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.create(ast.Node.AsmInput{ + const node = try arena.create(ast.Node.AsmInput); + node.* = ast.Node.AsmInput{ .base = ast.Node{ .id = ast.Node.Id.AsmInput }, .lbracket = lbracket_index, .symbolic_name = undefined, .constraint = undefined, .expr = undefined, .rparen = undefined, - }); + }; try items.push(node); stack.append(State{ .AsmInputItems = items }) catch unreachable; @@ -1283,12 +1308,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.create(ast.Node.FieldInitializer{ + const node = try arena.create(ast.Node.FieldInitializer); + node.* = ast.Node.FieldInitializer{ .base = ast.Node{ .id = ast.Node.Id.FieldInitializer }, .period_token = undefined, .name_token = undefined, .expr = undefined, - }); + }; try list_state.list.push(&node.base); stack.append(State{ .FieldInitListCommaOrEnd = list_state }) catch unreachable; @@ -1390,13 +1416,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { } const comments = try eatDocComments(arena, &tok_it, &tree); - const node = try arena.create(ast.Node.SwitchCase{ + const node = try arena.create(ast.Node.SwitchCase); + node.* = ast.Node.SwitchCase{ .base = ast.Node{ .id = ast.Node.Id.SwitchCase }, .items = ast.Node.SwitchCase.ItemList.init(arena), .payload = null, .expr = undefined, .arrow_token = undefined, - }); + }; try list_state.list.push(&node.base); try stack.append(State{ .SwitchCaseCommaOrEnd = list_state }); try stack.append(State{ .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } }); @@ -1427,10 +1454,11 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (token_ptr.id == Token.Id.Keyword_else) { - const else_node = try arena.create(ast.Node.SwitchElse{ + const else_node = try arena.create(ast.Node.SwitchElse); + else_node.* = ast.Node.SwitchElse{ .base = ast.Node{ .id = ast.Node.Id.SwitchElse }, .token = token_index, - }); + }; try switch_case.items.push(&else_node.base); try stack.append(State{ @@ -1537,7 +1565,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { State.ExternType => |ctx| { if (eatToken(&tok_it, &tree, Token.Id.Keyword_fn)) |fn_token| { - const fn_proto = try arena.create(ast.Node.FnProto{ + const fn_proto = try arena.create(ast.Node.FnProto); + fn_proto.* = ast.Node.FnProto{ .base = ast.Node{ .id = ast.Node.Id.FnProto }, .doc_comments = ctx.comments, .visib_token = null, @@ -1553,7 +1582,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .lib_name = null, .align_expr = null, .section_expr = null, - }); + }; ctx.opt_ctx.store(&fn_proto.base); stack.append(State{ .FnProto = fn_proto }) catch unreachable; continue; @@ -1711,12 +1740,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.create(ast.Node.Payload{ + const node = try arena.create(ast.Node.Payload); + node.* = ast.Node.Payload{ .base = ast.Node{ .id = ast.Node.Id.Payload }, .lpipe = token_index, .error_symbol = undefined, .rpipe = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ @@ -1747,13 +1777,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.create(ast.Node.PointerPayload{ + const node = try arena.create(ast.Node.PointerPayload); + node.* = ast.Node.PointerPayload{ .base = ast.Node{ .id = ast.Node.Id.PointerPayload }, .lpipe = token_index, .ptr_token = null, .value_symbol = undefined, .rpipe = undefined, - }); + }; opt_ctx.store(&node.base); try stack.append(State{ @@ -1790,14 +1821,15 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.create(ast.Node.PointerIndexPayload{ + const node = try arena.create(ast.Node.PointerIndexPayload); + node.* = ast.Node.PointerIndexPayload{ .base = ast.Node{ .id = ast.Node.Id.PointerIndexPayload }, .lpipe = token_index, .ptr_token = null, .value_symbol = undefined, .index_symbol = null, .rpipe = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ @@ -1824,12 +1856,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.Keyword_return, Token.Id.Keyword_break, Token.Id.Keyword_continue => { - const node = try arena.create(ast.Node.ControlFlowExpression{ + const node = try arena.create(ast.Node.ControlFlowExpression); + node.* = ast.Node.ControlFlowExpression{ .base = ast.Node{ .id = ast.Node.Id.ControlFlowExpression }, .ltoken = token_index, .kind = undefined, .rhs = null, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .Expression = OptionalCtx{ .Optional = &node.rhs } }) catch unreachable; @@ -1853,7 +1886,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => { - const node = try arena.create(ast.Node.PrefixOp{ + const node = try arena.create(ast.Node.PrefixOp); + node.* = ast.Node.PrefixOp{ .base = ast.Node{ .id = ast.Node.Id.PrefixOp }, .op_token = token_index, .op = switch (token_ptr.id) { @@ -1863,7 +1897,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { else => unreachable, }, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .Expression = OptionalCtx{ .Required = &node.rhs } }) catch unreachable; @@ -1887,13 +1921,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Ellipsis3)) |ellipsis3| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = ellipsis3, .op = ast.Node.InfixOp.Op.Range, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .Expression = OptionalCtx{ .Required = &node.rhs } }) catch unreachable; continue; @@ -1912,13 +1947,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToAssignment(token_ptr.id)) |ass_id| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = ass_id, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .AssignmentExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.rhs } }); @@ -1942,13 +1978,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToUnwrapExpr(token_ptr.id)) |unwrap_id| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = unwrap_id, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .UnwrapExpressionEnd = opt_ctx.toRequired() }) catch unreachable; @@ -1974,13 +2011,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Keyword_or)) |or_token| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = or_token, .op = ast.Node.InfixOp.Op.BoolOr, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .BoolOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .BoolAndExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); @@ -1998,13 +2036,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Keyword_and)) |and_token| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = and_token, .op = ast.Node.InfixOp.Op.BoolAnd, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .BoolAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .ComparisonExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); @@ -2025,13 +2064,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToComparison(token_ptr.id)) |comp_id| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = comp_id, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .ComparisonExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .BinaryOrExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); @@ -2052,13 +2092,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Pipe)) |pipe| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = pipe, .op = ast.Node.InfixOp.Op.BitOr, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .BinaryOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .BinaryXorExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); @@ -2076,13 +2117,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Caret)) |caret| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = caret, .op = ast.Node.InfixOp.Op.BitXor, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .BinaryXorExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .BinaryAndExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); @@ -2100,13 +2142,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Ampersand)) |ampersand| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = ampersand, .op = ast.Node.InfixOp.Op.BitAnd, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .BinaryAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .BitShiftExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); @@ -2127,13 +2170,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToBitShift(token_ptr.id)) |bitshift_id| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = bitshift_id, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .BitShiftExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .AdditionExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); @@ -2157,13 +2201,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToAddition(token_ptr.id)) |add_id| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = add_id, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .AdditionExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .MultiplyExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); @@ -2187,13 +2232,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToMultiply(token_ptr.id)) |mult_id| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = mult_id, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .MultiplyExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .CurlySuffixExpressionBegin = OptionalCtx{ .Required = &node.rhs } }); @@ -2215,12 +2261,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (tok_it.peek().?.id == Token.Id.Period) { - const node = try arena.create(ast.Node.SuffixOp{ + const node = try arena.create(ast.Node.SuffixOp); + node.* = ast.Node.SuffixOp{ .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, .op = ast.Node.SuffixOp.Op{ .StructInitializer = ast.Node.SuffixOp.Op.InitList.init(arena) }, .rtoken = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; @@ -2234,12 +2281,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.create(ast.Node.SuffixOp{ + const node = try arena.create(ast.Node.SuffixOp); + node.* = ast.Node.SuffixOp{ .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, .op = ast.Node.SuffixOp.Op{ .ArrayInitializer = ast.Node.SuffixOp.Op.InitList.init(arena) }, .rtoken = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .IfToken = Token.Id.LBrace }); @@ -2263,13 +2311,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const lhs = opt_ctx.get() orelse continue; if (eatToken(&tok_it, &tree, Token.Id.Bang)) |bang| { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = bang, .op = ast.Node.InfixOp.Op.ErrorUnion, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .TypeExprEnd = opt_ctx.toRequired() }) catch unreachable; try stack.append(State{ .PrefixOpExpression = OptionalCtx{ .Required = &node.rhs } }); @@ -2282,22 +2331,24 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_index = token.index; const token_ptr = token.ptr; if (tokenIdToPrefixOp(token_ptr.id)) |prefix_id| { - var node = try arena.create(ast.Node.PrefixOp{ + var node = try arena.create(ast.Node.PrefixOp); + node.* = ast.Node.PrefixOp{ .base = ast.Node{ .id = ast.Node.Id.PrefixOp }, .op_token = token_index, .op = prefix_id, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); // Treat '**' token as two pointer types if (token_ptr.id == Token.Id.AsteriskAsterisk) { - const child = try arena.create(ast.Node.PrefixOp{ + const child = try arena.create(ast.Node.PrefixOp); + child.* = ast.Node.PrefixOp{ .base = ast.Node{ .id = ast.Node.Id.PrefixOp }, .op_token = token_index, .op = prefix_id, .rhs = undefined, - }); + }; node.rhs = &child.base; node = child; } @@ -2316,12 +2367,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { State.SuffixOpExpressionBegin => |opt_ctx| { if (eatToken(&tok_it, &tree, Token.Id.Keyword_async)) |async_token| { - const async_node = try arena.create(ast.Node.AsyncAttribute{ + const async_node = try arena.create(ast.Node.AsyncAttribute); + async_node.* = ast.Node.AsyncAttribute{ .base = ast.Node{ .id = ast.Node.Id.AsyncAttribute }, .async_token = async_token, .allocator_type = null, .rangle_bracket = null, - }); + }; stack.append(State{ .AsyncEnd = AsyncEndCtx{ .ctx = opt_ctx, @@ -2347,7 +2399,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { const token_ptr = token.ptr; switch (token_ptr.id) { Token.Id.LParen => { - const node = try arena.create(ast.Node.SuffixOp{ + const node = try arena.create(ast.Node.SuffixOp); + node.* = ast.Node.SuffixOp{ .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, .op = ast.Node.SuffixOp.Op{ @@ -2357,7 +2410,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, }, .rtoken = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; @@ -2371,12 +2424,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.LBracket => { - const node = try arena.create(ast.Node.SuffixOp{ + const node = try arena.create(ast.Node.SuffixOp); + node.* = ast.Node.SuffixOp{ .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, .op = ast.Node.SuffixOp.Op{ .ArrayAccess = undefined }, .rtoken = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; @@ -2386,34 +2440,37 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, Token.Id.Period => { if (eatToken(&tok_it, &tree, Token.Id.Asterisk)) |asterisk_token| { - const node = try arena.create(ast.Node.SuffixOp{ + const node = try arena.create(ast.Node.SuffixOp); + node.* = ast.Node.SuffixOp{ .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, .op = ast.Node.SuffixOp.Op.Deref, .rtoken = asterisk_token, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; continue; } if (eatToken(&tok_it, &tree, Token.Id.QuestionMark)) |question_token| { - const node = try arena.create(ast.Node.SuffixOp{ + const node = try arena.create(ast.Node.SuffixOp); + node.* = ast.Node.SuffixOp{ .base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .lhs = lhs, .op = ast.Node.SuffixOp.Op.UnwrapOptional, .rtoken = question_token, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; continue; } - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = lhs, .op_token = token_index, .op = ast.Node.InfixOp.Op.Period, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; @@ -2467,11 +2524,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_promise => { - const node = try arena.create(ast.Node.PromiseType{ + const node = try arena.create(ast.Node.PromiseType); + node.* = ast.Node.PromiseType{ .base = ast.Node{ .id = ast.Node.Id.PromiseType }, .promise_token = token.index, .result = null, - }); + }; opt_ctx.store(&node.base); const next_token = nextToken(&tok_it, &tree); const next_token_index = next_token.index; @@ -2493,12 +2551,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.LParen => { - const node = try arena.create(ast.Node.GroupedExpression{ + const node = try arena.create(ast.Node.GroupedExpression); + node.* = ast.Node.GroupedExpression{ .base = ast.Node{ .id = ast.Node.Id.GroupedExpression }, .lparen = token.index, .expr = undefined, .rparen = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ @@ -2511,12 +2570,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Builtin => { - const node = try arena.create(ast.Node.BuiltinCall{ + const node = try arena.create(ast.Node.BuiltinCall); + node.* = ast.Node.BuiltinCall{ .base = ast.Node{ .id = ast.Node.Id.BuiltinCall }, .builtin_token = token.index, .params = ast.Node.BuiltinCall.ParamList.init(arena), .rparen_token = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ @@ -2530,12 +2590,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.LBracket => { - const node = try arena.create(ast.Node.PrefixOp{ + const node = try arena.create(ast.Node.PrefixOp); + node.* = ast.Node.PrefixOp{ .base = ast.Node{ .id = ast.Node.Id.PrefixOp }, .op_token = token.index, .op = undefined, .rhs = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ .SliceOrArrayType = node }) catch unreachable; @@ -2593,7 +2654,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_fn => { - const fn_proto = try arena.create(ast.Node.FnProto{ + const fn_proto = try arena.create(ast.Node.FnProto); + fn_proto.* = ast.Node.FnProto{ .base = ast.Node{ .id = ast.Node.Id.FnProto }, .doc_comments = null, .visib_token = null, @@ -2609,13 +2671,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .lib_name = null, .align_expr = null, .section_expr = null, - }); + }; opt_ctx.store(&fn_proto.base); stack.append(State{ .FnProto = fn_proto }) catch unreachable; continue; }, Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { - const fn_proto = try arena.create(ast.Node.FnProto{ + const fn_proto = try arena.create(ast.Node.FnProto); + fn_proto.* = ast.Node.FnProto{ .base = ast.Node{ .id = ast.Node.Id.FnProto }, .doc_comments = null, .visib_token = null, @@ -2631,7 +2694,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .lib_name = null, .align_expr = null, .section_expr = null, - }); + }; opt_ctx.store(&fn_proto.base); stack.append(State{ .FnProto = fn_proto }) catch unreachable; try stack.append(State{ @@ -2643,7 +2706,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, Token.Id.Keyword_asm => { - const node = try arena.create(ast.Node.Asm{ + const node = try arena.create(ast.Node.Asm); + node.* = ast.Node.Asm{ .base = ast.Node{ .id = ast.Node.Id.Asm }, .asm_token = token.index, .volatile_token = null, @@ -2652,7 +2716,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .inputs = ast.Node.Asm.InputList.init(arena), .clobbers = ast.Node.Asm.ClobberList.init(arena), .rparen = undefined, - }); + }; opt_ctx.store(&node.base); stack.append(State{ @@ -2701,13 +2765,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { State.ErrorTypeOrSetDecl => |ctx| { if (eatToken(&tok_it, &tree, Token.Id.LBrace) == null) { - const node = try arena.create(ast.Node.InfixOp{ + const node = try arena.create(ast.Node.InfixOp); + node.* = ast.Node.InfixOp{ .base = ast.Node{ .id = ast.Node.Id.InfixOp }, .lhs = &(try createLiteral(arena, ast.Node.ErrorType, ctx.error_token)).base, .op_token = undefined, .op = ast.Node.InfixOp.Op.Period, .rhs = undefined, - }); + }; ctx.opt_ctx.store(&node.base); stack.append(State{ .Identifier = OptionalCtx{ .Required = &node.rhs } }) catch unreachable; try stack.append(State{ @@ -2719,12 +2784,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; } - const node = try arena.create(ast.Node.ErrorSetDecl{ + const node = try arena.create(ast.Node.ErrorSetDecl); + node.* = ast.Node.ErrorSetDecl{ .base = ast.Node{ .id = ast.Node.Id.ErrorSetDecl }, .error_token = ctx.error_token, .decls = ast.Node.ErrorSetDecl.DeclList.init(arena), .rbrace_token = undefined, - }); + }; ctx.opt_ctx.store(&node.base); stack.append(State{ @@ -2785,11 +2851,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { return tree; } - const node = try arena.create(ast.Node.ErrorTag{ + const node = try arena.create(ast.Node.ErrorTag); + node.* = ast.Node.ErrorTag{ .base = ast.Node{ .id = ast.Node.Id.ErrorTag }, .doc_comments = comments, .name_token = ident_token_index, - }); + }; node_ptr.* = &node.base; continue; }, @@ -3129,10 +3196,11 @@ fn pushDocComment(arena: *mem.Allocator, line_comment: TokenIndex, result: *?*as if (result.*) |comment_node| { break :blk comment_node; } else { - const comment_node = try arena.create(ast.Node.DocComment{ + const comment_node = try arena.create(ast.Node.DocComment); + comment_node.* = ast.Node.DocComment{ .base = ast.Node{ .id = ast.Node.Id.DocComment }, .lines = ast.Node.DocComment.LineList.init(arena), - }); + }; result.* = comment_node; break :blk comment_node; } @@ -3158,10 +3226,11 @@ fn parseStringLiteral(arena: *mem.Allocator, tok_it: *ast.Tree.TokenList.Iterato return &(try createLiteral(arena, ast.Node.StringLiteral, token_index)).base; }, Token.Id.MultilineStringLiteralLine => { - const node = try arena.create(ast.Node.MultilineStringLiteral{ + const node = try arena.create(ast.Node.MultilineStringLiteral); + node.* = ast.Node.MultilineStringLiteral{ .base = ast.Node{ .id = ast.Node.Id.MultilineStringLiteral }, .lines = ast.Node.MultilineStringLiteral.LineList.init(arena), - }); + }; try node.lines.push(token_index); while (true) { const multiline_str = nextToken(tok_it, tree); @@ -3186,25 +3255,27 @@ fn parseStringLiteral(arena: *mem.Allocator, tok_it: *ast.Tree.TokenList.Iterato fn parseBlockExpr(stack: *std.ArrayList(State), arena: *mem.Allocator, ctx: OptionalCtx, token_ptr: Token, token_index: TokenIndex) !bool { switch (token_ptr.id) { Token.Id.Keyword_suspend => { - const node = try arena.create(ast.Node.Suspend{ + const node = try arena.create(ast.Node.Suspend); + node.* = ast.Node.Suspend{ .base = ast.Node{ .id = ast.Node.Id.Suspend }, .suspend_token = token_index, .body = null, - }); + }; ctx.store(&node.base); stack.append(State{ .SuspendBody = node }) catch unreachable; return true; }, Token.Id.Keyword_if => { - const node = try arena.create(ast.Node.If{ + const node = try arena.create(ast.Node.If); + node.* = ast.Node.If{ .base = ast.Node{ .id = ast.Node.Id.If }, .if_token = token_index, .condition = undefined, .payload = null, .body = undefined, .@"else" = null, - }); + }; ctx.store(&node.base); stack.append(State{ .Else = &node.@"else" }) catch unreachable; @@ -3238,13 +3309,14 @@ fn parseBlockExpr(stack: *std.ArrayList(State), arena: *mem.Allocator, ctx: Opti return true; }, Token.Id.Keyword_switch => { - const node = try arena.create(ast.Node.Switch{ + const node = try arena.create(ast.Node.Switch); + node.* = ast.Node.Switch{ .base = ast.Node{ .id = ast.Node.Id.Switch }, .switch_token = token_index, .expr = undefined, .cases = ast.Node.Switch.CaseList.init(arena), .rbrace = undefined, - }); + }; ctx.store(&node.base); stack.append(State{ @@ -3260,25 +3332,27 @@ fn parseBlockExpr(stack: *std.ArrayList(State), arena: *mem.Allocator, ctx: Opti return true; }, Token.Id.Keyword_comptime => { - const node = try arena.create(ast.Node.Comptime{ + const node = try arena.create(ast.Node.Comptime); + node.* = ast.Node.Comptime{ .base = ast.Node{ .id = ast.Node.Id.Comptime }, .comptime_token = token_index, .expr = undefined, .doc_comments = null, - }); + }; ctx.store(&node.base); try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.expr } }); return true; }, Token.Id.LBrace => { - const block = try arena.create(ast.Node.Block{ + const block = try arena.create(ast.Node.Block); + block.* = ast.Node.Block{ .base = ast.Node{ .id = ast.Node.Id.Block }, .label = null, .lbrace = token_index, .statements = ast.Node.Block.StatementList.init(arena), .rbrace = undefined, - }); + }; ctx.store(&block.base); stack.append(State{ .Block = block }) catch unreachable; return true; @@ -3412,10 +3486,12 @@ fn tokenIdToPrefixOp(id: Token.Id) ?ast.Node.PrefixOp.Op { } fn createLiteral(arena: *mem.Allocator, comptime T: type, token_index: TokenIndex) !*T { - return arena.create(T{ + const result = try arena.create(T); + result.* = T{ .base = ast.Node{ .id = ast.Node.typeToId(T) }, .token = token_index, - }); + }; + return result; } fn createToCtxLiteral(arena: *mem.Allocator, opt_ctx: OptionalCtx, comptime T: type, token_index: TokenIndex) !*T { diff --git a/test/tests.zig b/test/tests.zig index 73d4644d18395cbf79bc3b8ea9ebc5ce975a6225..548496fa2f6dac4151b53d563ad1f2c3cd9436b8 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -48,13 +48,14 @@ const test_targets = []TestTarget{ const max_stdout_size = 1 * 1024 * 1024; // 1 MB pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { - const cases = b.allocator.create(CompareOutputContext{ + const cases = b.allocator.create(CompareOutputContext) catch unreachable; + cases.* = CompareOutputContext{ .b = b, .step = b.step("test-compare-output", "Run the compare output tests"), .test_index = 0, .test_filter = test_filter, .modes = modes, - }) catch unreachable; + }; compare_output.addCases(cases); @@ -62,13 +63,14 @@ pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8, modes: } pub fn addRuntimeSafetyTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { - const cases = b.allocator.create(CompareOutputContext{ + const cases = b.allocator.create(CompareOutputContext) catch unreachable; + cases.* = CompareOutputContext{ .b = b, .step = b.step("test-runtime-safety", "Run the runtime safety tests"), .test_index = 0, .test_filter = test_filter, .modes = modes, - }) catch unreachable; + }; runtime_safety.addCases(cases); @@ -76,13 +78,14 @@ pub fn addRuntimeSafetyTests(b: *build.Builder, test_filter: ?[]const u8, modes: } pub fn addCompileErrorTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { - const cases = b.allocator.create(CompileErrorContext{ + const cases = b.allocator.create(CompileErrorContext) catch unreachable; + cases.* = CompileErrorContext{ .b = b, .step = b.step("test-compile-errors", "Run the compile error tests"), .test_index = 0, .test_filter = test_filter, .modes = modes, - }) catch unreachable; + }; compile_errors.addCases(cases); @@ -90,13 +93,14 @@ pub fn addCompileErrorTests(b: *build.Builder, test_filter: ?[]const u8, modes: } pub fn addBuildExampleTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { - const cases = b.allocator.create(BuildExamplesContext{ + const cases = b.allocator.create(BuildExamplesContext) catch unreachable; + cases.* = BuildExamplesContext{ .b = b, .step = b.step("test-build-examples", "Build the examples"), .test_index = 0, .test_filter = test_filter, .modes = modes, - }) catch unreachable; + }; build_examples.addCases(cases); @@ -119,13 +123,14 @@ pub fn addCliTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const M } pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { - const cases = b.allocator.create(CompareOutputContext{ + const cases = b.allocator.create(CompareOutputContext) catch unreachable; + cases.* = CompareOutputContext{ .b = b, .step = b.step("test-asm-link", "Run the assemble and link tests"), .test_index = 0, .test_filter = test_filter, .modes = modes, - }) catch unreachable; + }; assemble_and_link.addCases(cases); @@ -133,12 +138,13 @@ pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8, mode } pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { - const cases = b.allocator.create(TranslateCContext{ + const cases = b.allocator.create(TranslateCContext) catch unreachable; + cases.* = TranslateCContext{ .b = b, .step = b.step("test-translate-c", "Run the C transation tests"), .test_index = 0, .test_filter = test_filter, - }) catch unreachable; + }; translate_c.addCases(cases); @@ -146,12 +152,13 @@ pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.St } pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { - const cases = b.allocator.create(GenHContext{ + const cases = b.allocator.create(GenHContext) catch unreachable; + cases.* = GenHContext{ .b = b, .step = b.step("test-gen-h", "Run the C header file generation tests"), .test_index = 0, .test_filter = test_filter, - }) catch unreachable; + }; gen_h.addCases(cases); @@ -244,7 +251,8 @@ pub const CompareOutputContext = struct { pub fn create(context: *CompareOutputContext, exe_path: []const u8, name: []const u8, expected_output: []const u8, cli_args: []const []const u8) *RunCompareOutputStep { const allocator = context.b.allocator; - const ptr = allocator.create(RunCompareOutputStep{ + const ptr = allocator.create(RunCompareOutputStep) catch unreachable; + ptr.* = RunCompareOutputStep{ .context = context, .exe_path = exe_path, .name = name, @@ -252,7 +260,7 @@ pub const CompareOutputContext = struct { .test_index = context.test_index, .step = build.Step.init("RunCompareOutput", allocator, make), .cli_args = cli_args, - }) catch unreachable; + }; context.test_index += 1; return ptr; } @@ -331,13 +339,14 @@ pub const CompareOutputContext = struct { pub fn create(context: *CompareOutputContext, exe_path: []const u8, name: []const u8) *RuntimeSafetyRunStep { const allocator = context.b.allocator; - const ptr = allocator.create(RuntimeSafetyRunStep{ + const ptr = allocator.create(RuntimeSafetyRunStep) catch unreachable; + ptr.* = RuntimeSafetyRunStep{ .context = context, .exe_path = exe_path, .name = name, .test_index = context.test_index, .step = build.Step.init("RuntimeSafetyRun", allocator, make), - }) catch unreachable; + }; context.test_index += 1; return ptr; @@ -542,14 +551,15 @@ pub const CompileErrorContext = struct { pub fn create(context: *CompileErrorContext, name: []const u8, case: *const TestCase, build_mode: Mode) *CompileCmpOutputStep { const allocator = context.b.allocator; - const ptr = allocator.create(CompileCmpOutputStep{ + const ptr = allocator.create(CompileCmpOutputStep) catch unreachable; + ptr.* = CompileCmpOutputStep{ .step = build.Step.init("CompileCmpOutput", allocator, make), .context = context, .name = name, .test_index = context.test_index, .case = case, .build_mode = build_mode, - }) catch unreachable; + }; context.test_index += 1; return ptr; @@ -661,13 +671,14 @@ pub const CompileErrorContext = struct { } pub fn create(self: *CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) *TestCase { - const tc = self.b.allocator.create(TestCase{ + const tc = self.b.allocator.create(TestCase) catch unreachable; + tc.* = TestCase{ .name = name, .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), .expected_errors = ArrayList([]const u8).init(self.b.allocator), .link_libc = false, .is_exe = false, - }) catch unreachable; + }; tc.addSourceFile(".tmp_source.zig", source); comptime var arg_i = 0; @@ -821,13 +832,14 @@ pub const TranslateCContext = struct { pub fn create(context: *TranslateCContext, name: []const u8, case: *const TestCase) *TranslateCCmpOutputStep { const allocator = context.b.allocator; - const ptr = allocator.create(TranslateCCmpOutputStep{ + const ptr = allocator.create(TranslateCCmpOutputStep) catch unreachable; + ptr.* = TranslateCCmpOutputStep{ .step = build.Step.init("ParseCCmpOutput", allocator, make), .context = context, .name = name, .test_index = context.test_index, .case = case, - }) catch unreachable; + }; context.test_index += 1; return ptr; @@ -928,12 +940,13 @@ pub const TranslateCContext = struct { } pub fn create(self: *TranslateCContext, allow_warnings: bool, filename: []const u8, name: []const u8, source: []const u8, expected_lines: ...) *TestCase { - const tc = self.b.allocator.create(TestCase{ + const tc = self.b.allocator.create(TestCase) catch unreachable; + tc.* = TestCase{ .name = name, .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), .expected_lines = ArrayList([]const u8).init(self.b.allocator), .allow_warnings = allow_warnings, - }) catch unreachable; + }; tc.addSourceFile(filename, source); comptime var arg_i = 0; @@ -1015,14 +1028,15 @@ pub const GenHContext = struct { pub fn create(context: *GenHContext, h_path: []const u8, name: []const u8, case: *const TestCase) *GenHCmpOutputStep { const allocator = context.b.allocator; - const ptr = allocator.create(GenHCmpOutputStep{ + const ptr = allocator.create(GenHCmpOutputStep) catch unreachable; + ptr.* = GenHCmpOutputStep{ .step = build.Step.init("ParseCCmpOutput", allocator, make), .context = context, .h_path = h_path, .name = name, .test_index = context.test_index, .case = case, - }) catch unreachable; + }; context.test_index += 1; return ptr; @@ -1062,11 +1076,12 @@ pub const GenHContext = struct { } pub fn create(self: *GenHContext, filename: []const u8, name: []const u8, source: []const u8, expected_lines: ...) *TestCase { - const tc = self.b.allocator.create(TestCase{ + const tc = self.b.allocator.create(TestCase) catch unreachable; + tc.* = TestCase{ .name = name, .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), .expected_lines = ArrayList([]const u8).init(self.b.allocator), - }) catch unreachable; + }; tc.addSourceFile(filename, source); comptime var arg_i = 0; -- 2.54.0 From 67bd45f0cf1b452cf8de5a016bc6ff2f85393d70 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 4 Feb 2019 15:24:06 -0500 Subject: [PATCH 36/37] adjustments to std.mem split / separate * rename std.mem.split to std.mem.tokenize * add future deprecation notice to docs * (unrelated) add note to std.os.path.resolve docs * std.mem.separate - assert delimiter.len not zero * fix implementation of std.mem.separate to respect the delimiter * separate the two iterators to different structs --- build.zig | 16 ++-- src-self-hosted/libc_installation.zig | 8 +- src-self-hosted/main.zig | 2 +- std/build.zig | 6 +- std/mem.zig | 129 ++++++++++++++------------ std/os/child_process.zig | 2 +- std/os/index.zig | 2 +- std/os/path.zig | 44 ++++----- 8 files changed, 112 insertions(+), 97 deletions(-) diff --git a/build.zig b/build.zig index d99165a6de51df7388639a8c60dc2738907f13a5..a41a5f808ba3a08c4606bfee5ab2255aff36669d 100644 --- a/build.zig +++ b/build.zig @@ -189,14 +189,14 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep { const prefix_output = try b.exec([][]const u8{ llvm_config_exe, "--prefix" }); var result = LibraryDep{ - .prefix = mem.split(prefix_output, " \r\n").next().?, + .prefix = mem.tokenize(prefix_output, " \r\n").next().?, .libs = ArrayList([]const u8).init(b.allocator), .system_libs = ArrayList([]const u8).init(b.allocator), .includes = ArrayList([]const u8).init(b.allocator), .libdirs = ArrayList([]const u8).init(b.allocator), }; { - var it = mem.split(libs_output, " \r\n"); + var it = mem.tokenize(libs_output, " \r\n"); while (it.next()) |lib_arg| { if (mem.startsWith(u8, lib_arg, "-l")) { try result.system_libs.append(lib_arg[2..]); @@ -210,7 +210,7 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep { } } { - var it = mem.split(includes_output, " \r\n"); + var it = mem.tokenize(includes_output, " \r\n"); while (it.next()) |include_arg| { if (mem.startsWith(u8, include_arg, "-I")) { try result.includes.append(include_arg[2..]); @@ -220,7 +220,7 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep { } } { - var it = mem.split(libdir_output, " \r\n"); + var it = mem.tokenize(libdir_output, " \r\n"); while (it.next()) |libdir| { if (mem.startsWith(u8, libdir, "-L")) { try result.libdirs.append(libdir[2..]); @@ -233,7 +233,7 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep { } pub fn installStdLib(b: *Builder, stdlib_files: []const u8) void { - var it = mem.split(stdlib_files, ";"); + var it = mem.tokenize(stdlib_files, ";"); while (it.next()) |stdlib_file| { const src_path = os.path.join(b.allocator, "std", stdlib_file) catch unreachable; const dest_path = os.path.join(b.allocator, "lib", "zig", "std", stdlib_file) catch unreachable; @@ -242,7 +242,7 @@ pub fn installStdLib(b: *Builder, stdlib_files: []const u8) void { } pub fn installCHeaders(b: *Builder, c_header_files: []const u8) void { - var it = mem.split(c_header_files, ";"); + var it = mem.tokenize(c_header_files, ";"); while (it.next()) |c_header_file| { const src_path = os.path.join(b.allocator, "c_headers", c_header_file) catch unreachable; const dest_path = os.path.join(b.allocator, "lib", "zig", "include", c_header_file) catch unreachable; @@ -277,7 +277,7 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void { addCppLib(b, exe, ctx.cmake_binary_dir, "zig_cpp"); if (ctx.lld_include_dir.len != 0) { exe.addIncludeDir(ctx.lld_include_dir); - var it = mem.split(ctx.lld_libraries, ";"); + var it = mem.tokenize(ctx.lld_libraries, ";"); while (it.next()) |lib| { exe.addObjectFile(lib); } @@ -334,7 +334,7 @@ fn addCxxKnownPath( ctx.cxx_compiler, b.fmt("-print-file-name={}", objname), }); - const path_unpadded = mem.split(path_padded, "\r\n").next().?; + const path_unpadded = mem.tokenize(path_padded, "\r\n").next().?; if (mem.eql(u8, path_unpadded, objname)) { if (errtxt) |msg| { warn("{}", msg); diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index 1c5d111c5ae69d919d4ba90cdcd986ee53007cf1..18d2daf0c2e894ebfb51fb691c272ea16b4d9508 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src-self-hosted/libc_installation.zig @@ -57,10 +57,10 @@ pub const LibCInstallation = struct { const contents = try std.io.readFileAlloc(allocator, libc_file); defer allocator.free(contents); - var it = std.mem.split(contents, "\n"); + var it = std.mem.tokenize(contents, "\n"); while (it.next()) |line| { if (line.len == 0 or line[0] == '#') continue; - var line_it = std.mem.split(line, "="); + var line_it = std.mem.separate(line, "="); const name = line_it.next() orelse { try stderr.print("missing equal sign after field name\n"); return error.ParseError; @@ -213,7 +213,7 @@ pub const LibCInstallation = struct { }, } - var it = std.mem.split(exec_result.stderr, "\n\r"); + var it = std.mem.tokenize(exec_result.stderr, "\n\r"); var search_paths = std.ArrayList([]const u8).init(loop.allocator); defer search_paths.deinit(); while (it.next()) |line| { @@ -410,7 +410,7 @@ async fn ccPrintFileName(loop: *event.Loop, o_file: []const u8, want_dirname: bo return error.CCompilerCrashed; }, } - var it = std.mem.split(exec_result.stdout, "\n\r"); + var it = std.mem.tokenize(exec_result.stdout, "\n\r"); const line = it.next() orelse return error.LibCRuntimeNotFound; const dirname = std.os.path.dirname(line) orelse return error.LibCRuntimeNotFound; diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 1403ab860dd9deb0bf5049136a2f70a239f34669..f6ee9a0513d029474685786181d6103ff232b93e 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -351,7 +351,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co const root_name = if (provided_name) |n| n else blk: { if (root_source_file) |file| { const basename = os.path.basename(file); - var it = mem.split(basename, "."); + var it = mem.separate(basename, "."); break :blk it.next() orelse basename; } else { try stderr.write("--name [name] not provided and unable to infer\n"); diff --git a/std/build.zig b/std/build.zig index 6f58594190126c5d8da9d9a9a5b5f8fe3bd8e961..5246d973398fd1f4bf2b1e308ebe0b47f38c8432 100644 --- a/std/build.zig +++ b/std/build.zig @@ -324,7 +324,7 @@ pub const Builder = struct { fn processNixOSEnvVars(self: *Builder) void { if (os.getEnvVarOwned(self.allocator, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| { - var it = mem.split(nix_cflags_compile, " "); + var it = mem.tokenize(nix_cflags_compile, " "); while (true) { const word = it.next() orelse break; if (mem.eql(u8, word, "-isystem")) { @@ -342,7 +342,7 @@ pub const Builder = struct { assert(err == error.EnvironmentVariableNotFound); } if (os.getEnvVarOwned(self.allocator, "NIX_LDFLAGS")) |nix_ldflags| { - var it = mem.split(nix_ldflags, " "); + var it = mem.tokenize(nix_ldflags, " "); while (true) { const word = it.next() orelse break; if (mem.eql(u8, word, "-rpath")) { @@ -689,7 +689,7 @@ pub const Builder = struct { if (os.path.isAbsolute(name)) { return name; } - var it = mem.split(PATH, []u8{os.path.delimiter}); + var it = mem.tokenize(PATH, []u8{os.path.delimiter}); while (it.next()) |path| { const full_path = try os.path.join(self.allocator, path, self.fmt("{}{}", name, exe_extension)); if (os.path.real(self.allocator, full_path)) |real_path| { diff --git a/std/mem.zig b/std/mem.zig index bec3816d881afb0dfdba4cded2d086c5a5669e83..26ae4ef089626223bdabebfa20a7005f4eb81d09 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -689,58 +689,57 @@ pub fn eql_slice_u8(a: []const u8, b: []const u8) bool { } /// Returns an iterator that iterates over the slices of `buffer` that are not -/// any of the bytes in `split_bytes`. -/// split(" abc def ghi ", " ") +/// any of the bytes in `delimiter_bytes`. +/// tokenize(" abc def ghi ", " ") /// Will return slices for "abc", "def", "ghi", null, in that order. -/// If `split_bytes` does not exist in buffer, +/// If `buffer` is empty, the iterator will return null. +/// If `delimiter_bytes` does not exist in buffer, /// the iterator will return `buffer`, null, in that order. -pub fn split(buffer: []const u8, split_bytes: []const u8) SplitIterator { - return SplitIterator{ +/// See also the related function `separate`. +pub fn tokenize(buffer: []const u8, delimiter_bytes: []const u8) TokenIterator { + return TokenIterator{ .index = 0, .buffer = buffer, - .split_bytes = split_bytes, - .glob = true, - .spun = false, + .delimiter_bytes = delimiter_bytes, }; } -test "mem.split" { - var it = split(" abc def ghi ", " "); +test "mem.tokenize" { + var it = tokenize(" abc def ghi ", " "); assert(eql(u8, it.next().?, "abc")); assert(eql(u8, it.next().?, "def")); assert(eql(u8, it.next().?, "ghi")); assert(it.next() == null); - it = split("..\\bob", "\\"); + it = tokenize("..\\bob", "\\"); assert(eql(u8, it.next().?, "..")); assert(eql(u8, "..", "..\\bob"[0..it.index])); assert(eql(u8, it.next().?, "bob")); assert(it.next() == null); - it = split("//a/b", "/"); + it = tokenize("//a/b", "/"); assert(eql(u8, it.next().?, "a")); assert(eql(u8, it.next().?, "b")); assert(eql(u8, "//a/b", "//a/b"[0..it.index])); assert(it.next() == null); - it = split("|", "|"); + it = tokenize("|", "|"); assert(it.next() == null); - it = split("", "|"); - assert(eql(u8, it.next().?, "")); + it = tokenize("", "|"); assert(it.next() == null); - it = split("hello", ""); + it = tokenize("hello", ""); assert(eql(u8, it.next().?, "hello")); assert(it.next() == null); - it = split("hello", " "); + it = tokenize("hello", " "); assert(eql(u8, it.next().?, "hello")); assert(it.next() == null); } -test "mem.split (multibyte)" { - var it = split("a|b,c/d e", " /,|"); +test "mem.tokenize (multibyte)" { + var it = tokenize("a|b,c/d e", " /,|"); assert(eql(u8, it.next().?, "a")); assert(eql(u8, it.next().?, "b")); assert(eql(u8, it.next().?, "c")); @@ -750,18 +749,21 @@ test "mem.split (multibyte)" { } /// Returns an iterator that iterates over the slices of `buffer` that -/// seperates by bytes in `delimiter`. +/// are separated by bytes in `delimiter`. /// separate("abc|def||ghi", "|") -/// Will return slices for "abc", "def", "", "ghi", null, in that order. +/// will return slices for "abc", "def", "", "ghi", null, in that order. /// If `delimiter` does not exist in buffer, /// the iterator will return `buffer`, null, in that order. +/// The delimiter length must not be zero. +/// See also the related function `tokenize`. +/// It is planned to rename this function to `split` before 1.0.0, like this: +/// pub fn split(buffer: []const u8, delimiter: []const u8) SplitIterator { pub fn separate(buffer: []const u8, delimiter: []const u8) SplitIterator { + assert(delimiter.len != 0); return SplitIterator{ .index = 0, .buffer = buffer, - .split_bytes = delimiter, - .glob = false, - .spun = false, + .delimiter = delimiter, }; } @@ -782,19 +784,15 @@ test "mem.separate" { assert(eql(u8, it.next().?, "")); assert(it.next() == null); - it = separate("hello", ""); - assert(eql(u8, it.next().?, "hello")); - assert(it.next() == null); - it = separate("hello", " "); assert(eql(u8, it.next().?, "hello")); assert(it.next() == null); } test "mem.separate (multibyte)" { - var it = separate("a|b,c/d e", " /,|"); + var it = separate("a, b ,, c, d, e", ", "); assert(eql(u8, it.next().?, "a")); - assert(eql(u8, it.next().?, "b")); + assert(eql(u8, it.next().?, "b ,")); assert(eql(u8, it.next().?, "c")); assert(eql(u8, it.next().?, "d")); assert(eql(u8, it.next().?, "e")); @@ -819,49 +817,38 @@ test "mem.endsWith" { assert(!endsWith(u8, "Bob", "Bo")); } -pub const SplitIterator = struct { +pub const TokenIterator = struct { buffer: []const u8, - split_bytes: []const u8, + delimiter_bytes: []const u8, index: usize, - glob: bool, - spun: bool, - /// Iterates and returns null or optionally a slice the next split segment - pub fn next(self: *SplitIterator) ?[]const u8 { - if (self.spun) { - if (self.index + 1 > self.buffer.len) return null; - self.index += 1; + /// Returns a slice of the next token, or null if tokenization is complete. + pub fn next(self: *TokenIterator) ?[]const u8 { + // move to beginning of token + while (self.index < self.buffer.len and self.isSplitByte(self.buffer[self.index])) : (self.index += 1) {} + const start = self.index; + if (start == self.buffer.len) { + return null; } - self.spun = true; + // move to end of token + while (self.index < self.buffer.len and !self.isSplitByte(self.buffer[self.index])) : (self.index += 1) {} + const end = self.index; - if (self.glob) { - while (self.index < self.buffer.len and self.isSplitByte(self.buffer[self.index])) : (self.index += 1) {} - } - - var cursor = self.index; - while (cursor < self.buffer.len and !self.isSplitByte(self.buffer[cursor])) : (cursor += 1) {} - - defer self.index = cursor; - - if (cursor == self.buffer.len) { - return if (self.glob and self.index == cursor and self.index > 0) null else self.buffer[self.index..]; - } - - return self.buffer[self.index..cursor]; + return self.buffer[start..end]; } /// Returns a slice of the remaining bytes. Does not affect iterator state. - pub fn rest(self: *const SplitIterator) []const u8 { + pub fn rest(self: TokenIterator) []const u8 { // move to beginning of token var index: usize = self.index; while (index < self.buffer.len and self.isSplitByte(self.buffer[index])) : (index += 1) {} return self.buffer[index..]; } - fn isSplitByte(self: *const SplitIterator, byte: u8) bool { - for (self.split_bytes) |split_byte| { - if (byte == split_byte) { + fn isSplitByte(self: TokenIterator, byte: u8) bool { + for (self.delimiter_bytes) |delimiter_byte| { + if (byte == delimiter_byte) { return true; } } @@ -869,6 +856,32 @@ pub const SplitIterator = struct { } }; +pub const SplitIterator = struct { + buffer: []const u8, + index: ?usize, + delimiter: []const u8, + + /// Returns a slice of the next field, or null if splitting is complete. + pub fn next(self: *SplitIterator) ?[]const u8 { + const start = self.index orelse return null; + const end = if (indexOfPos(u8, self.buffer, start, self.delimiter)) |delim_start| blk: { + self.index = delim_start + self.delimiter.len; + break :blk delim_start; + } else blk: { + self.index = null; + break :blk self.buffer.len; + }; + return self.buffer[start..end]; + } + + /// Returns a slice of the remaining bytes. Does not affect iterator state. + pub fn rest(self: SplitIterator) []const u8 { + const end = self.buffer.len; + const start = self.index orelse end; + return self.buffer[start..end]; + } +}; + /// Naively combines a series of strings with a separator. /// Allocates memory for the result, which must be freed by the caller. pub fn join(allocator: *Allocator, sep: u8, strings: ...) ![]u8 { diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 9f33bee9057541534a2ca721cdd194091afca884..7aa85823692825ff14e8fdb9c910f88ea71c9850 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -595,7 +595,7 @@ pub const ChildProcess = struct { const PATH = try os.getEnvVarOwned(self.allocator, "PATH"); defer self.allocator.free(PATH); - var it = mem.split(PATH, ";"); + var it = mem.tokenize(PATH, ";"); while (it.next()) |search_path| { const joined_path = try os.path.join(self.allocator, search_path, app_name); defer self.allocator.free(joined_path); diff --git a/std/os/index.zig b/std/os/index.zig index 0d0e07bfa353ca1c6203b90e74ff8aa41e015260..451c0a3436644feffdd72b97a6b775d394edb14d 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -608,7 +608,7 @@ pub fn posixExecve(argv: []const []const u8, env_map: *const BufMap, allocator: // +1 for the null terminating byte const path_buf = try allocator.alloc(u8, PATH.len + exe_path.len + 2); defer allocator.free(path_buf); - var it = mem.split(PATH, ":"); + var it = mem.tokenize(PATH, ":"); var seen_eacces = false; var err: usize = undefined; while (it.next()) |search_path| { diff --git a/std/os/path.zig b/std/os/path.zig index 4d3d3d6a8b53b6107d92e8ddc654be4ceaaef67c..0b960fa2da73db41b4401eaf33c63653007bf727 100644 --- a/std/os/path.zig +++ b/std/os/path.zig @@ -184,7 +184,7 @@ pub fn windowsParsePath(path: []const u8) WindowsPath { return relative_path; } - var it = mem.split(path, []u8{this_sep}); + var it = mem.tokenize(path, []u8{this_sep}); _ = (it.next() orelse return relative_path); _ = (it.next() orelse return relative_path); return WindowsPath{ @@ -202,7 +202,7 @@ pub fn windowsParsePath(path: []const u8) WindowsPath { return relative_path; } - var it = mem.split(path, []u8{this_sep}); + var it = mem.tokenize(path, []u8{this_sep}); _ = (it.next() orelse return relative_path); _ = (it.next() orelse return relative_path); return WindowsPath{ @@ -264,8 +264,8 @@ fn networkShareServersEql(ns1: []const u8, ns2: []const u8) bool { const sep1 = ns1[0]; const sep2 = ns2[0]; - var it1 = mem.split(ns1, []u8{sep1}); - var it2 = mem.split(ns2, []u8{sep2}); + var it1 = mem.tokenize(ns1, []u8{sep1}); + var it2 = mem.tokenize(ns2, []u8{sep2}); // TODO ASCII is wrong, we actually need full unicode support to compare paths. return asciiEqlIgnoreCase(it1.next().?, it2.next().?); @@ -285,8 +285,8 @@ fn compareDiskDesignators(kind: WindowsPath.Kind, p1: []const u8, p2: []const u8 const sep1 = p1[0]; const sep2 = p2[0]; - var it1 = mem.split(p1, []u8{sep1}); - var it2 = mem.split(p2, []u8{sep2}); + var it1 = mem.tokenize(p1, []u8{sep1}); + var it2 = mem.tokenize(p2, []u8{sep2}); // TODO ASCII is wrong, we actually need full unicode support to compare paths. return asciiEqlIgnoreCase(it1.next().?, it2.next().?) and asciiEqlIgnoreCase(it1.next().?, it2.next().?); @@ -337,6 +337,8 @@ pub fn resolveSlice(allocator: *Allocator, paths: []const []const u8) ![]u8 { /// If all paths are relative it uses the current working directory as a starting point. /// Each drive has its own current working directory. /// Path separators are canonicalized to '\\' and drives are canonicalized to capital letters. +/// Note: all usage of this function should be audited due to the existence of symlinks. +/// Without performing actual syscalls, resolving `..` could be incorrect. pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { if (paths.len == 0) { assert(is_windows); // resolveWindows called on non windows can't use getCwd @@ -416,7 +418,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { }, WindowsPath.Kind.NetworkShare => { result = try allocator.alloc(u8, max_size); - var it = mem.split(paths[first_index], "/\\"); + var it = mem.tokenize(paths[first_index], "/\\"); const server_name = it.next().?; const other_name = it.next().?; @@ -483,7 +485,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { if (!correct_disk_designator) { continue; } - var it = mem.split(p[parsed.disk_designator.len..], "/\\"); + var it = mem.tokenize(p[parsed.disk_designator.len..], "/\\"); while (it.next()) |component| { if (mem.eql(u8, component, ".")) { continue; @@ -516,6 +518,8 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 { /// It resolves "." and "..". /// The result does not have a trailing path separator. /// If all paths are relative it uses the current working directory as a starting point. +/// Note: all usage of this function should be audited due to the existence of symlinks. +/// Without performing actual syscalls, resolving `..` could be incorrect. pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 { if (paths.len == 0) { assert(!is_windows); // resolvePosix called on windows can't use getCwd @@ -550,7 +554,7 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 { errdefer allocator.free(result); for (paths[first_index..]) |p, i| { - var it = mem.split(p, "/"); + var it = mem.tokenize(p, "/"); while (it.next()) |component| { if (mem.eql(u8, component, ".")) { continue; @@ -937,8 +941,8 @@ pub fn relativeWindows(allocator: *Allocator, from: []const u8, to: []const u8) return resolved_to; } - var from_it = mem.split(resolved_from, "/\\"); - var to_it = mem.split(resolved_to, "/\\"); + var from_it = mem.tokenize(resolved_from, "/\\"); + var to_it = mem.tokenize(resolved_to, "/\\"); while (true) { const from_component = from_it.next() orelse return mem.dupe(allocator, u8, to_it.rest()); const to_rest = to_it.rest(); @@ -967,14 +971,12 @@ pub fn relativeWindows(allocator: *Allocator, from: []const u8, to: []const u8) // shave off the trailing slash result_index -= 1; - if (to_rest.len > 0) { - var rest_it = mem.split(to_rest, "/\\"); - while (rest_it.next()) |to_component| { - result[result_index] = '\\'; - result_index += 1; - mem.copy(u8, result[result_index..], to_component); - result_index += to_component.len; - } + var rest_it = mem.tokenize(to_rest, "/\\"); + while (rest_it.next()) |to_component| { + result[result_index] = '\\'; + result_index += 1; + mem.copy(u8, result[result_index..], to_component); + result_index += to_component.len; } return result[0..result_index]; @@ -990,8 +992,8 @@ pub fn relativePosix(allocator: *Allocator, from: []const u8, to: []const u8) ![ const resolved_to = try resolvePosix(allocator, [][]const u8{to}); defer allocator.free(resolved_to); - var from_it = mem.split(resolved_from, "/"); - var to_it = mem.split(resolved_to, "/"); + var from_it = mem.tokenize(resolved_from, "/"); + var to_it = mem.tokenize(resolved_to, "/"); while (true) { const from_component = from_it.next() orelse return mem.dupe(allocator, u8, to_it.rest()); const to_rest = to_it.rest(); -- 2.54.0 From 8c6fa982cd0a02775264b616c37da9907cc603bb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 4 Feb 2019 20:30:00 -0500 Subject: [PATCH 37/37] SIMD: array to vector, vector to array, wrapping int add also vectors and arrays now use the same ConstExprVal representation See #903 --- src/all_types.hpp | 20 ++- src/analyze.cpp | 178 +++++++++---------- src/analyze.hpp | 1 + src/codegen.cpp | 98 ++++++++--- src/ir.cpp | 291 ++++++++++++++++++++------------ src/ir_print.cpp | 18 ++ test/stage1/behavior.zig | 1 + test/stage1/behavior/vector.zig | 20 +++ 8 files changed, 403 insertions(+), 224 deletions(-) create mode 100644 test/stage1/behavior/vector.zig diff --git a/src/all_types.hpp b/src/all_types.hpp index 3fc6772b31d70355bf128f2b04ecfcd10a2ec202..c4c9e13cfb4c2427c9261d76d2c33ac588b6408e 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -252,10 +252,6 @@ struct ConstArgTuple { size_t end_index; }; -struct ConstVector { - ConstExprValue *elements; -}; - enum ConstValSpecial { ConstValSpecialRuntime, ConstValSpecialStatic, @@ -322,7 +318,6 @@ struct ConstExprValue { ConstPtrValue x_ptr; ImportTableEntry *x_import; ConstArgTuple x_arg_tuple; - ConstVector x_vector; // populated if special == ConstValSpecialRuntime RuntimeHintErrorUnion rh_error_union; @@ -2239,6 +2234,8 @@ enum IrInstructionId { IrInstructionIdToBytes, IrInstructionIdFromBytes, IrInstructionIdCheckRuntimeScope, + IrInstructionIdVectorToArray, + IrInstructionIdArrayToVector, }; struct IrInstruction { @@ -3368,6 +3365,19 @@ struct IrInstructionBitReverse { IrInstruction *op; }; +struct IrInstructionArrayToVector { + IrInstruction base; + + IrInstruction *array; +}; + +struct IrInstructionVectorToArray { + IrInstruction base; + + IrInstruction *vector; + LLVMValueRef tmp_ptr; +}; + static const size_t slice_ptr_index = 0; static const size_t slice_len_index = 1; diff --git a/src/analyze.cpp b/src/analyze.cpp index 99378eb7a85298401f4bbea50a9d1088fe502f0f..ff961a704448aa8b4bac7e13574b2a9fd47c14bd 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4457,7 +4457,15 @@ ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) { return new_entry; } +bool is_valid_vector_elem_type(ZigType *elem_type) { + return elem_type->id == ZigTypeIdInt || + elem_type->id == ZigTypeIdFloat || + get_codegen_ptr_type(elem_type) != nullptr; +} + ZigType *get_vector_type(CodeGen *g, uint32_t len, ZigType *elem_type) { + assert(is_valid_vector_elem_type(elem_type)); + TypeId type_id = {}; type_id.id = ZigTypeIdVector; type_id.data.vector.len = len; @@ -5749,6 +5757,28 @@ bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) { zig_unreachable(); } +static bool const_values_equal_array(CodeGen *g, ConstExprValue *a, ConstExprValue *b, size_t len) { + assert(a->data.x_array.special != ConstArraySpecialUndef); + assert(b->data.x_array.special != ConstArraySpecialUndef); + if (a->data.x_array.special == ConstArraySpecialBuf && + b->data.x_array.special == ConstArraySpecialBuf) + { + return buf_eql_buf(a->data.x_array.data.s_buf, b->data.x_array.data.s_buf); + } + expand_undef_array(g, a); + expand_undef_array(g, b); + + ConstExprValue *a_elems = a->data.x_array.data.s_none.elements; + ConstExprValue *b_elems = b->data.x_array.data.s_none.elements; + + for (size_t i = 0; i < len; i += 1) { + if (!const_values_equal(g, &a_elems[i], &b_elems[i])) + return false; + } + + return true; +} + bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) { assert(a->type->id == b->type->id); assert(a->special == ConstValSpecialStatic); @@ -5803,28 +5833,12 @@ bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) { case ZigTypeIdPointer: case ZigTypeIdFn: return const_values_equal_ptr(a, b); + case ZigTypeIdVector: + assert(a->type->data.vector.len == b->type->data.vector.len); + return const_values_equal_array(g, a, b, a->type->data.vector.len); case ZigTypeIdArray: { assert(a->type->data.array.len == b->type->data.array.len); - assert(a->data.x_array.special != ConstArraySpecialUndef); - assert(b->data.x_array.special != ConstArraySpecialUndef); - if (a->data.x_array.special == ConstArraySpecialBuf && - b->data.x_array.special == ConstArraySpecialBuf) - { - return buf_eql_buf(a->data.x_array.data.s_buf, b->data.x_array.data.s_buf); - } - expand_undef_array(g, a); - expand_undef_array(g, b); - - size_t len = a->type->data.array.len; - ConstExprValue *a_elems = a->data.x_array.data.s_none.elements; - ConstExprValue *b_elems = b->data.x_array.data.s_none.elements; - - for (size_t i = 0; i < len; i += 1) { - if (!const_values_equal(g, &a_elems[i], &b_elems[i])) - return false; - } - - return true; + return const_values_equal_array(g, a, b, a->type->data.array.len); } case ZigTypeIdStruct: for (size_t i = 0; i < a->type->data.structure.src_field_count; i += 1) { @@ -5853,20 +5867,6 @@ bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) { case ZigTypeIdArgTuple: return a->data.x_arg_tuple.start_index == b->data.x_arg_tuple.start_index && a->data.x_arg_tuple.end_index == b->data.x_arg_tuple.end_index; - case ZigTypeIdVector: { - assert(a->type->data.vector.len == b->type->data.vector.len); - - size_t len = a->type->data.vector.len; - ConstExprValue *a_elems = a->data.x_vector.elements; - ConstExprValue *b_elems = b->data.x_vector.elements; - - for (size_t i = 0; i < len; i += 1) { - if (!const_values_equal(g, &a_elems[i], &b_elems[i])) - return false; - } - - return true; - } case ZigTypeIdBoundFn: case ZigTypeIdInvalid: case ZigTypeIdUnreachable: @@ -5985,6 +5985,40 @@ static void render_const_val_err_set(CodeGen *g, Buf *buf, ConstExprValue *const } } +static void render_const_val_array(CodeGen *g, Buf *buf, ConstExprValue *const_val, size_t len) { + switch (const_val->data.x_array.special) { + case ConstArraySpecialUndef: + buf_append_str(buf, "undefined"); + return; + case ConstArraySpecialBuf: { + Buf *array_buf = const_val->data.x_array.data.s_buf; + buf_append_char(buf, '"'); + for (size_t i = 0; i < buf_len(array_buf); i += 1) { + uint8_t c = buf_ptr(array_buf)[i]; + if (c == '"') { + buf_append_str(buf, "\\\""); + } else { + buf_append_char(buf, c); + } + } + buf_append_char(buf, '"'); + return; + } + case ConstArraySpecialNone: { + buf_appendf(buf, "%s{", buf_ptr(&const_val->type->name)); + for (uint64_t i = 0; i < len; i += 1) { + if (i != 0) + buf_appendf(buf, ","); + ConstExprValue *child_value = &const_val->data.x_array.data.s_none.elements[i]; + render_const_value(g, buf, child_value); + } + buf_appendf(buf, "}"); + return; + } + } + zig_unreachable(); +} + void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { switch (const_val->special) { case ConstValSpecialRuntime: @@ -6065,51 +6099,10 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { } case ZigTypeIdPointer: return render_const_val_ptr(g, buf, const_val, type_entry); + case ZigTypeIdVector: + return render_const_val_array(g, buf, const_val, type_entry->data.vector.len); case ZigTypeIdArray: - switch (const_val->data.x_array.special) { - case ConstArraySpecialUndef: - buf_append_str(buf, "undefined"); - return; - case ConstArraySpecialBuf: { - Buf *array_buf = const_val->data.x_array.data.s_buf; - buf_append_char(buf, '"'); - for (size_t i = 0; i < buf_len(array_buf); i += 1) { - uint8_t c = buf_ptr(array_buf)[i]; - if (c == '"') { - buf_append_str(buf, "\\\""); - } else { - buf_append_char(buf, c); - } - } - buf_append_char(buf, '"'); - return; - } - case ConstArraySpecialNone: { - buf_appendf(buf, "%s{", buf_ptr(&type_entry->name)); - uint64_t len = type_entry->data.array.len; - for (uint64_t i = 0; i < len; i += 1) { - if (i != 0) - buf_appendf(buf, ","); - ConstExprValue *child_value = &const_val->data.x_array.data.s_none.elements[i]; - render_const_value(g, buf, child_value); - } - buf_appendf(buf, "}"); - return; - } - } - zig_unreachable(); - case ZigTypeIdVector: { - buf_appendf(buf, "%s{", buf_ptr(&type_entry->name)); - uint64_t len = type_entry->data.vector.len; - for (uint32_t i = 0; i < len; i += 1) { - if (i != 0) - buf_appendf(buf, ","); - ConstExprValue *child_value = &const_val->data.x_vector.elements[i]; - render_const_value(g, buf, child_value); - } - buf_appendf(buf, "}"); - return; - } + return render_const_val_array(g, buf, const_val, type_entry->data.array.len); case ZigTypeIdNull: { buf_appendf(buf, "null"); @@ -6379,7 +6372,17 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) { // Canonicalize the array value as ConstArraySpecialNone void expand_undef_array(CodeGen *g, ConstExprValue *const_val) { - assert(const_val->type->id == ZigTypeIdArray); + size_t elem_count; + ZigType *elem_type; + if (const_val->type->id == ZigTypeIdArray) { + elem_count = const_val->type->data.array.len; + elem_type = const_val->type->data.array.child_type; + } else if (const_val->type->id == ZigTypeIdVector) { + elem_count = const_val->type->data.vector.len; + elem_type = const_val->type->data.vector.elem_type; + } else { + zig_unreachable(); + } if (const_val->special == ConstValSpecialUndef) { const_val->special = ConstValSpecialStatic; const_val->data.x_array.special = ConstArraySpecialUndef; @@ -6389,18 +6392,14 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) { return; case ConstArraySpecialUndef: { const_val->data.x_array.special = ConstArraySpecialNone; - size_t elem_count = const_val->type->data.array.len; const_val->data.x_array.data.s_none.elements = create_const_vals(elem_count); for (size_t i = 0; i < elem_count; i += 1) { ConstExprValue *element_val = &const_val->data.x_array.data.s_none.elements[i]; - element_val->type = const_val->type->data.array.child_type; + element_val->type = elem_type; init_const_undefined(g, element_val); - ConstParent *parent = get_const_val_parent(g, element_val); - if (parent != nullptr) { - parent->id = ConstParentIdArray; - parent->data.p_array.array_val = const_val; - parent->data.p_array.elem_index = i; - } + element_val->parent.id = ConstParentIdArray; + element_val->parent.data.p_array.array_val = const_val; + element_val->parent.data.p_array.elem_index = i; } return; } @@ -6411,7 +6410,6 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) { g->string_literals_table.maybe_remove(buf); const_val->data.x_array.special = ConstArraySpecialNone; - size_t elem_count = const_val->type->data.array.len; assert(elem_count == buf_len(buf)); const_val->data.x_array.data.s_none.elements = create_const_vals(elem_count); for (size_t i = 0; i < elem_count; i += 1) { @@ -6419,6 +6417,9 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) { this_char->special = ConstValSpecialStatic; this_char->type = g->builtin_types.entry_u8; bigint_init_unsigned(&this_char->data.x_bigint, (uint8_t)buf_ptr(buf)[i]); + this_char->parent.id = ConstParentIdArray; + this_char->parent.data.p_array.array_val = const_val; + this_char->parent.data.p_array.elem_index = i; } return; } @@ -6426,6 +6427,7 @@ void expand_undef_array(CodeGen *g, ConstExprValue *const_val) { zig_unreachable(); } +// Deprecated. Reference the parent field directly. ConstParent *get_const_val_parent(CodeGen *g, ConstExprValue *value) { return &value->parent; } diff --git a/src/analyze.hpp b/src/analyze.hpp index da5265a59416758ff5a6f2dc85b02465a86da1b2..f558fa44b0647266cdce174f95658b3199117bf1 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -74,6 +74,7 @@ TypeUnionField *find_union_field_by_tag(ZigType *type_entry, const BigInt *tag); bool is_ref(ZigType *type_entry); bool is_array_ref(ZigType *type_entry); bool is_container_ref(ZigType *type_entry); +bool is_valid_vector_elem_type(ZigType *elem_type); void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node); void scan_import(CodeGen *g, ImportTableEntry *import); void preview_use_decl(CodeGen *g, AstNode *node); diff --git a/src/codegen.cpp b/src/codegen.cpp index b73fda59d1352325b4d7eecf7bf4fdb888d90837..de2222afb741ea18c9425991d45dd8d28db18ebc 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1921,9 +1921,8 @@ static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) { } static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment) { - assert(alignment > 0); LLVMValueRef result = LLVMBuildAlloca(g->builder, type_entry->type_ref, name); - LLVMSetAlignment(result, alignment); + LLVMSetAlignment(result, (alignment == 0) ? get_abi_alignment(g, type_entry) : alignment); return result; } @@ -3246,6 +3245,22 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI return LLVMBuildTrunc(g->builder, shifted_value, child_type->type_ref, ""); } +static bool value_is_all_undef_array(ConstExprValue *const_val, size_t len) { + switch (const_val->data.x_array.special) { + case ConstArraySpecialUndef: + return true; + case ConstArraySpecialBuf: + return false; + case ConstArraySpecialNone: + for (size_t i = 0; i < len; i += 1) { + if (!value_is_all_undef(&const_val->data.x_array.data.s_none.elements[i])) + return false; + } + return true; + } + zig_unreachable(); +} + static bool value_is_all_undef(ConstExprValue *const_val) { switch (const_val->special) { case ConstValSpecialRuntime: @@ -3260,19 +3275,9 @@ static bool value_is_all_undef(ConstExprValue *const_val) { } return true; } else if (const_val->type->id == ZigTypeIdArray) { - switch (const_val->data.x_array.special) { - case ConstArraySpecialUndef: - return true; - case ConstArraySpecialBuf: - return false; - case ConstArraySpecialNone: - for (size_t i = 0; i < const_val->type->data.array.len; i += 1) { - if (!value_is_all_undef(&const_val->data.x_array.data.s_none.elements[i])) - return false; - } - return true; - } - zig_unreachable(); + return value_is_all_undef_array(const_val, const_val->type->data.array.len); + } else if (const_val->type->id == ZigTypeIdVector) { + return value_is_all_undef_array(const_val, const_val->type->data.vector.len); } else { return false; } @@ -5194,6 +5199,32 @@ static LLVMValueRef ir_render_bit_reverse(CodeGen *g, IrExecutable *executable, return LLVMBuildCall(g->builder, fn_val, &op, 1, ""); } +static LLVMValueRef ir_render_vector_to_array(CodeGen *g, IrExecutable *executable, + IrInstructionVectorToArray *instruction) +{ + ZigType *array_type = instruction->base.value.type; + assert(array_type->id == ZigTypeIdArray); + assert(handle_is_ptr(array_type)); + assert(instruction->tmp_ptr); + LLVMValueRef vector = ir_llvm_value(g, instruction->vector); + LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, instruction->tmp_ptr, + LLVMPointerType(instruction->vector->value.type->type_ref, 0), ""); + gen_store_untyped(g, vector, casted_ptr, 0, false); + return instruction->tmp_ptr; +} + +static LLVMValueRef ir_render_array_to_vector(CodeGen *g, IrExecutable *executable, + IrInstructionArrayToVector *instruction) +{ + ZigType *vector_type = instruction->base.value.type; + assert(vector_type->id == ZigTypeIdVector); + assert(!handle_is_ptr(vector_type)); + LLVMValueRef array_ptr = ir_llvm_value(g, instruction->array); + LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, array_ptr, + LLVMPointerType(vector_type->type_ref, 0), ""); + return gen_load_untyped(g, casted_ptr, 0, false, ""); +} + static void set_debug_location(CodeGen *g, IrInstruction *instruction) { AstNode *source_node = instruction->source_node; Scope *scope = instruction->scope; @@ -5439,6 +5470,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_bswap(g, executable, (IrInstructionBswap *)instruction); case IrInstructionIdBitReverse: return ir_render_bit_reverse(g, executable, (IrInstructionBitReverse *)instruction); + case IrInstructionIdArrayToVector: + return ir_render_array_to_vector(g, executable, (IrInstructionArrayToVector *)instruction); + case IrInstructionIdVectorToArray: + return ir_render_vector_to_array(g, executable, (IrInstructionVectorToArray *)instruction); } zig_unreachable(); } @@ -6016,14 +6051,32 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c return LLVMConstString(buf_ptr(buf), (unsigned)buf_len(buf), true); } } + zig_unreachable(); } case ZigTypeIdVector: { uint32_t len = type_entry->data.vector.len; - LLVMValueRef *values = allocate(len); - for (uint32_t i = 0; i < len; i += 1) { - values[i] = gen_const_val(g, &const_val->data.x_vector.elements[i], ""); + switch (const_val->data.x_array.special) { + case ConstArraySpecialUndef: + return LLVMGetUndef(type_entry->type_ref); + case ConstArraySpecialNone: { + LLVMValueRef *values = allocate(len); + for (uint64_t i = 0; i < len; i += 1) { + ConstExprValue *elem_value = &const_val->data.x_array.data.s_none.elements[i]; + values[i] = gen_const_val(g, elem_value, ""); + } + return LLVMConstVector(values, len); + } + case ConstArraySpecialBuf: { + Buf *buf = const_val->data.x_array.data.s_buf; + assert(buf_len(buf) == len); + LLVMValueRef *values = allocate(len); + for (uint64_t i = 0; i < len; i += 1) { + values[i] = LLVMConstInt(g->builtin_types.entry_u8->type_ref, buf_ptr(buf)[i], false); + } + return LLVMConstVector(values, len); + } } - return LLVMConstVector(values, len); + zig_unreachable(); } case ZigTypeIdUnion: { @@ -6467,6 +6520,7 @@ static void do_code_gen(CodeGen *g) { IrInstruction *instruction = fn_table_entry->alloca_list.at(alloca_i); LLVMValueRef *slot; ZigType *slot_type = instruction->value.type; + uint32_t alignment_bytes = 0; if (instruction->id == IrInstructionIdCast) { IrInstructionCast *cast_instruction = (IrInstructionCast *)instruction; slot = &cast_instruction->tmp_ptr; @@ -6502,10 +6556,14 @@ static void do_code_gen(CodeGen *g) { } else if (instruction->id == IrInstructionIdCmpxchgGen) { IrInstructionCmpxchgGen *cmpxchg_instruction = (IrInstructionCmpxchgGen *)instruction; slot = &cmpxchg_instruction->tmp_ptr; + } else if (instruction->id == IrInstructionIdVectorToArray) { + IrInstructionVectorToArray *vector_to_array_instruction = (IrInstructionVectorToArray *)instruction; + alignment_bytes = get_abi_alignment(g, vector_to_array_instruction->vector->value.type); + slot = &vector_to_array_instruction->tmp_ptr; } else { zig_unreachable(); } - *slot = build_alloca(g, slot_type, "", get_abi_alignment(g, slot_type)); + *slot = build_alloca(g, slot_type, "", alignment_bytes); } ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base); diff --git a/src/ir.cpp b/src/ir.cpp index 3d3c501df35788d0c6b2ed2f926b08940851cace..3cbbdc810307581a0ca990285fee5b8da1e49885 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -168,6 +168,7 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_ static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed); static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs); static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align); +static void ir_add_alloca(IrAnalyze *ira, IrInstruction *instruction, ZigType *type_entry); static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) { assert(get_src_ptr_type(const_val->type) != nullptr); @@ -899,6 +900,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckRuntimeScop return IrInstructionIdCheckRuntimeScope; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionVectorToArray *) { + return IrInstructionIdVectorToArray; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayToVector *) { + return IrInstructionIdArrayToVector; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -2821,6 +2830,34 @@ static IrInstruction *ir_build_check_runtime_scope(IrBuilder *irb, Scope *scope, return &instruction->base; } +static IrInstruction *ir_build_vector_to_array(IrAnalyze *ira, IrInstruction *source_instruction, + IrInstruction *vector, ZigType *result_type) +{ + IrInstructionVectorToArray *instruction = ir_build_instruction(&ira->new_irb, + source_instruction->scope, source_instruction->source_node); + instruction->base.value.type = result_type; + instruction->vector = vector; + + ir_ref_instruction(vector, ira->new_irb.current_basic_block); + + ir_add_alloca(ira, &instruction->base, result_type); + + return &instruction->base; +} + +static IrInstruction *ir_build_array_to_vector(IrAnalyze *ira, IrInstruction *source_instruction, + IrInstruction *array, ZigType *result_type) +{ + IrInstructionArrayToVector *instruction = ir_build_instruction(&ira->new_irb, + source_instruction->scope, source_instruction->source_node); + instruction->base.value.type = result_type; + instruction->array = array; + + ir_ref_instruction(array, ira->new_irb.current_basic_block); + + return &instruction->base; +} + static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; @@ -8270,6 +8307,7 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc bool const_val_is_int = (const_val->type->id == ZigTypeIdInt || const_val->type->id == ZigTypeIdComptimeInt); bool const_val_is_float = (const_val->type->id == ZigTypeIdFloat || const_val->type->id == ZigTypeIdComptimeFloat); + assert(const_val_is_int || const_val_is_float); if (other_type->id == ZigTypeIdFloat) { if (const_val->type->id == ZigTypeIdComptimeInt || const_val->type->id == ZigTypeIdComptimeFloat) { @@ -10714,6 +10752,32 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa } } +static IrInstruction *ir_analyze_array_to_vector(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *array, ZigType *vector_type) +{ + if (instr_is_comptime(array)) { + // arrays and vectors have the same ConstExprValue representation + IrInstruction *result = ir_const(ira, source_instr, vector_type); + copy_const_val(&result->value, &array->value, false); + result->value.type = vector_type; + return result; + } + return ir_build_array_to_vector(ira, source_instr, array, vector_type); +} + +static IrInstruction *ir_analyze_vector_to_array(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *vector, ZigType *array_type) +{ + if (instr_is_comptime(vector)) { + // arrays and vectors have the same ConstExprValue representation + IrInstruction *result = ir_const(ira, source_instr, array_type); + copy_const_val(&result->value, &vector->value, false); + result->value.type = array_type; + return result; + } + return ir_build_vector_to_array(ira, source_instr, vector, array_type); +} + static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr, ZigType *wanted_type, IrInstruction *value) { @@ -11102,6 +11166,23 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } + // cast from @Vector(N, T) to [N]T + if (wanted_type->id == ZigTypeIdArray && actual_type->id == ZigTypeIdVector && + wanted_type->data.array.len == actual_type->data.vector.len && + types_match_const_cast_only(ira, wanted_type->data.array.child_type, + actual_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk) + { + return ir_analyze_vector_to_array(ira, source_instr, value, wanted_type); + } + + // cast from [N]T to @Vector(N, T) + if (actual_type->id == ZigTypeIdArray && wanted_type->id == ZigTypeIdVector && + actual_type->data.array.len == wanted_type->data.vector.len && + types_match_const_cast_only(ira, actual_type->data.array.child_type, + wanted_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk) + { + return ir_analyze_array_to_vector(ira, source_instr, value, wanted_type); + } // cast from undefined to anything if (actual_type->id == ZigTypeIdUndefined) { @@ -11780,8 +11861,8 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp * return result; } -static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val, - IrBinOp op_id, ConstExprValue *op2_val, ConstExprValue *out_val) +static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, IrInstruction *source_instr, ZigType *type_entry, + ConstExprValue *op1_val, IrBinOp op_id, ConstExprValue *op2_val, ConstExprValue *out_val) { bool is_int; bool is_float; @@ -11803,10 +11884,10 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val, if ((op_id == IrBinOpDivUnspecified || op_id == IrBinOpRemRem || op_id == IrBinOpRemMod || op_id == IrBinOpDivTrunc || op_id == IrBinOpDivFloor) && op2_zcmp == CmpEQ) { - return ErrorDivByZero; + return ir_add_error(ira, source_instr, buf_sprintf("division by zero")); } if ((op_id == IrBinOpRemRem || op_id == IrBinOpRemMod) && op2_zcmp == CmpLT) { - return ErrorNegativeDenominator; + return ir_add_error(ira, source_instr, buf_sprintf("negative denominator")); } switch (op_id) { @@ -11852,7 +11933,7 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val, BigInt orig_bigint; bigint_shl(&orig_bigint, &out_val->data.x_bigint, &op2_val->data.x_bigint); if (bigint_cmp(&op1_val->data.x_bigint, &orig_bigint) != CmpEQ) { - return ErrorShiftedOutOneBits; + return ir_add_error(ira, source_instr, buf_sprintf("exact shift shifted out 1 bits")); } break; } @@ -11920,14 +12001,14 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val, BigInt remainder; bigint_rem(&remainder, &op1_val->data.x_bigint, &op2_val->data.x_bigint); if (bigint_cmp_zero(&remainder) != CmpEQ) { - return ErrorExactDivRemainder; + return ir_add_error(ira, source_instr, buf_sprintf("exact division had a remainder")); } } else { float_div_trunc(out_val, op1_val, op2_val); ConstExprValue remainder; float_rem(&remainder, op1_val, op2_val); if (float_cmp_zero(&remainder) != CmpEQ) { - return ErrorExactDivRemainder; + return ir_add_error(ira, source_instr, buf_sprintf("exact division had a remainder")); } } break; @@ -11951,13 +12032,51 @@ static int ir_eval_math_op(ZigType *type_entry, ConstExprValue *op1_val, if (!bigint_fits_in_bits(&out_val->data.x_bigint, type_entry->data.integral.bit_count, type_entry->data.integral.is_signed)) { - return ErrorOverflow; + return ir_add_error(ira, source_instr, buf_sprintf("operation caused overflow")); } } out_val->type = type_entry; out_val->special = ConstValSpecialStatic; - return 0; + return nullptr; +} + +// This works on operands that have already been checked to be comptime known. +static IrInstruction *ir_analyze_math_op(IrAnalyze *ira, IrInstruction *source_instr, + ZigType *type_entry, ConstExprValue *op1_val, IrBinOp op_id, ConstExprValue *op2_val) +{ + IrInstruction *result_instruction = ir_const(ira, source_instr, type_entry); + ConstExprValue *out_val = &result_instruction->value; + if (type_entry->id == ZigTypeIdVector) { + expand_undef_array(ira->codegen, op1_val); + expand_undef_array(ira->codegen, op2_val); + out_val->special = ConstValSpecialUndef; + expand_undef_array(ira->codegen, out_val); + size_t len = type_entry->data.vector.len; + ZigType *scalar_type = type_entry->data.vector.elem_type; + for (size_t i = 0; i < len; i += 1) { + ConstExprValue *scalar_op1_val = &op1_val->data.x_array.data.s_none.elements[i]; + ConstExprValue *scalar_op2_val = &op2_val->data.x_array.data.s_none.elements[i]; + ConstExprValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i]; + assert(scalar_op1_val->type == scalar_type); + assert(scalar_op2_val->type == scalar_type); + assert(scalar_out_val->type == scalar_type); + ErrorMsg *msg = ir_eval_math_op_scalar(ira, source_instr, scalar_type, + scalar_op1_val, op_id, scalar_op2_val, scalar_out_val); + if (msg != nullptr) { + add_error_note(ira->codegen, msg, source_instr->source_node, + buf_sprintf("when computing vector element at index %" ZIG_PRI_usize, i)); + return ira->codegen->invalid_instruction; + } + } + out_val->type = type_entry; + out_val->special = ConstValSpecialStatic; + } else { + if (ir_eval_math_op_scalar(ira, source_instr, type_entry, op1_val, op_id, op2_val, out_val) != nullptr) { + return ira->codegen->invalid_instruction; + } + } + return ir_implicit_cast(ira, result_instruction, type_entry); } static IrInstruction *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { @@ -12029,24 +12148,7 @@ static IrInstruction *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *b if (op2_val == nullptr) return ira->codegen->invalid_instruction; - IrInstruction *result_instruction = ir_const(ira, &bin_op_instruction->base, op1->value.type); - - int err; - if ((err = ir_eval_math_op(op1->value.type, op1_val, op_id, op2_val, &result_instruction->value))) { - if (err == ErrorOverflow) { - ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("operation caused overflow")); - return ira->codegen->invalid_instruction; - } else if (err == ErrorShiftedOutOneBits) { - ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("exact shift shifted out 1 bits")); - return ira->codegen->invalid_instruction; - } else { - zig_unreachable(); - } - return ira->codegen->invalid_instruction; - } - - ir_num_lit_fits_in_other_type(ira, result_instruction, op1->value.type, false); - return result_instruction; + return ir_analyze_math_op(ira, &bin_op_instruction->base, op1->value.type, op1_val, op_id, op2_val); } else if (op1->value.type->id == ZigTypeIdComptimeInt) { ir_add_error(ira, &bin_op_instruction->base, buf_sprintf("LHS of shift must be an integer type, or RHS must be compile-time known")); @@ -12292,30 +12394,7 @@ static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp if (op2_val == nullptr) return ira->codegen->invalid_instruction; - IrInstruction *result_instruction = ir_const(ira, &instruction->base, resolved_type); - - int err; - if ((err = ir_eval_math_op(resolved_type, op1_val, op_id, op2_val, &result_instruction->value))) { - if (err == ErrorDivByZero) { - ir_add_error(ira, &instruction->base, buf_sprintf("division by zero")); - return ira->codegen->invalid_instruction; - } else if (err == ErrorOverflow) { - ir_add_error(ira, &instruction->base, buf_sprintf("operation caused overflow")); - return ira->codegen->invalid_instruction; - } else if (err == ErrorExactDivRemainder) { - ir_add_error(ira, &instruction->base, buf_sprintf("exact division had a remainder")); - return ira->codegen->invalid_instruction; - } else if (err == ErrorNegativeDenominator) { - ir_add_error(ira, &instruction->base, buf_sprintf("negative denominator")); - return ira->codegen->invalid_instruction; - } else { - zig_unreachable(); - } - return ira->codegen->invalid_instruction; - } - - ir_num_lit_fits_in_other_type(ira, result_instruction, resolved_type, false); - return result_instruction; + return ir_analyze_math_op(ira, &instruction->base, resolved_type, op1_val, op_id, op2_val); } IrInstruction *result = ir_build_bin_op(&ira->new_irb, instruction->base.scope, @@ -18745,10 +18824,7 @@ static IrInstruction *ir_analyze_instruction_vector_type(IrAnalyze *ira, IrInstr if (type_is_invalid(elem_type)) return ira->codegen->invalid_instruction; - if (elem_type->id != ZigTypeIdInt && - elem_type->id != ZigTypeIdFloat && - get_codegen_ptr_type(elem_type) == nullptr) - { + if (!is_valid_vector_elem_type(elem_type)) { ir_add_error(ira, instruction->elem_type, buf_sprintf("vector element type must be integer, float, or pointer; '%s' is invalid", buf_ptr(&elem_type->name))); @@ -20345,6 +20421,17 @@ static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstruct return ir_analyze_ptr_cast(ira, &instruction->base, ptr, dest_type, dest_type_value); } +static void buf_write_value_bytes_array(CodeGen *codegen, uint8_t *buf, ConstExprValue *val, size_t len) { + size_t buf_i = 0; + // TODO optimize the buf case + expand_undef_array(codegen, val); + for (size_t elem_i = 0; elem_i < val->type->data.array.len; elem_i += 1) { + ConstExprValue *elem = &val->data.x_array.data.s_none.elements[elem_i]; + buf_write_value_bytes(codegen, &buf[buf_i], elem); + buf_i += type_size(codegen, elem->type); + } +} + static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue *val) { if (val->special == ConstValSpecialUndef) val->special = ConstValSpecialStatic; @@ -20390,26 +20477,9 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue zig_unreachable(); } case ZigTypeIdArray: - { - size_t buf_i = 0; - // TODO optimize the buf case - expand_undef_array(codegen, val); - for (size_t elem_i = 0; elem_i < val->type->data.array.len; elem_i += 1) { - ConstExprValue *elem = &val->data.x_array.data.s_none.elements[elem_i]; - buf_write_value_bytes(codegen, &buf[buf_i], elem); - buf_i += type_size(codegen, elem->type); - } - } - return; - case ZigTypeIdVector: { - size_t buf_i = 0; - for (uint32_t elem_i = 0; elem_i < val->type->data.vector.len; elem_i += 1) { - ConstExprValue *elem = &val->data.x_vector.elements[elem_i]; - buf_write_value_bytes(codegen, &buf[buf_i], elem); - buf_i += type_size(codegen, elem->type); - } - return; - } + return buf_write_value_bytes_array(codegen, buf, val, val->type->data.array.len); + 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"); case ZigTypeIdOptional: @@ -20426,6 +20496,31 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue zig_unreachable(); } +static Error buf_read_value_bytes_array(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, + ConstExprValue *val, ZigType *elem_type, size_t len) +{ + Error err; + uint64_t elem_size = type_size(codegen, elem_type); + + switch (val->data.x_array.special) { + case ConstArraySpecialNone: + val->data.x_array.data.s_none.elements = create_const_vals(len); + for (size_t i = 0; i < len; i++) { + ConstExprValue *elem = &val->data.x_array.data.s_none.elements[i]; + elem->special = ConstValSpecialStatic; + elem->type = elem_type; + if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem))) + return err; + } + return ErrorNone; + case ConstArraySpecialUndef: + zig_panic("TODO buf_read_value_bytes ConstArraySpecialUndef array type"); + case ConstArraySpecialBuf: + zig_panic("TODO buf_read_value_bytes ConstArraySpecialBuf array type"); + } + zig_unreachable(); +} + static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, ConstExprValue *val) { Error err; assert(val->special == ConstValSpecialStatic); @@ -20464,42 +20559,12 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou val->data.x_ptr.data.hard_coded_addr.addr = bigint_as_unsigned(&bn); return ErrorNone; } - case ZigTypeIdArray: { - uint64_t elem_size = type_size(codegen, val->type->data.array.child_type); - size_t len = val->type->data.array.len; - - switch (val->data.x_array.special) { - case ConstArraySpecialNone: - val->data.x_array.data.s_none.elements = create_const_vals(len); - for (size_t i = 0; i < len; i++) { - ConstExprValue *elem = &val->data.x_array.data.s_none.elements[i]; - elem->special = ConstValSpecialStatic; - elem->type = val->type->data.array.child_type; - if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem))) - return err; - } - return ErrorNone; - case ConstArraySpecialUndef: - zig_panic("TODO buf_read_value_bytes ConstArraySpecialUndef array type"); - case ConstArraySpecialBuf: - zig_panic("TODO buf_read_value_bytes ConstArraySpecialBuf array type"); - } - zig_unreachable(); - } - case ZigTypeIdVector: { - uint64_t elem_size = type_size(codegen, val->type->data.vector.elem_type); - uint32_t len = val->type->data.vector.len; - - val->data.x_vector.elements = create_const_vals(len); - for (uint32_t i = 0; i < len; i += 1) { - ConstExprValue *elem = &val->data.x_vector.elements[i]; - elem->special = ConstValSpecialStatic; - elem->type = val->type->data.vector.elem_type; - if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem))) - return err; - } - return ErrorNone; - } + case ZigTypeIdArray: + return buf_read_value_bytes_array(ira, codegen, source_node, buf, val, val->type->data.array.child_type, + val->type->data.array.len); + case ZigTypeIdVector: + return buf_read_value_bytes_array(ira, codegen, source_node, buf, val, val->type->data.vector.elem_type, + val->type->data.vector.len); case ZigTypeIdEnum: switch (val->type->data.enumeration.layout) { case ContainerLayoutAuto: @@ -21634,6 +21699,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio case IrInstructionIdDeclVarGen: case IrInstructionIdPtrCastGen: case IrInstructionIdCmpxchgGen: + case IrInstructionIdArrayToVector: + case IrInstructionIdVectorToArray: zig_unreachable(); case IrInstructionIdReturn: @@ -22129,6 +22196,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdFromBytes: case IrInstructionIdToBytes: case IrInstructionIdEnumToInt: + case IrInstructionIdVectorToArray: + case IrInstructionIdArrayToVector: return false; case IrInstructionIdAsm: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index a1fd450b65f6a07739a1b7d9fa48a8e91f52afc4..e19aa6dda832b21d2a5a4cd1588ee36a4f72dce5 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -972,6 +972,18 @@ static void ir_print_check_runtime_scope(IrPrint *irp, IrInstructionCheckRuntime fprintf(irp->f, ")"); } +static void ir_print_array_to_vector(IrPrint *irp, IrInstructionArrayToVector *instruction) { + fprintf(irp->f, "ArrayToVector("); + ir_print_other_instruction(irp, instruction->array); + fprintf(irp->f, ")"); +} + +static void ir_print_vector_to_array(IrPrint *irp, IrInstructionVectorToArray *instruction) { + fprintf(irp->f, "VectorToArray("); + ir_print_other_instruction(irp, instruction->vector); + fprintf(irp->f, ")"); +} + static void ir_print_int_to_err(IrPrint *irp, IrInstructionIntToErr *instruction) { fprintf(irp->f, "inttoerr "); ir_print_other_instruction(irp, instruction->target); @@ -1825,6 +1837,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdDeclVarGen: ir_print_decl_var_gen(irp, (IrInstructionDeclVarGen *)instruction); break; + case IrInstructionIdArrayToVector: + ir_print_array_to_vector(irp, (IrInstructionArrayToVector *)instruction); + break; + case IrInstructionIdVectorToArray: + ir_print_vector_to_array(irp, (IrInstructionVectorToArray *)instruction); + break; } fprintf(irp->f, "\n"); } diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index e545a4c4184d3f6587b5ead0c453fe3c54e8ef11..1fa00b34fd67197765d831982b974ac3e489f6d2 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -74,6 +74,7 @@ comptime { _ = @import("behavior/underscore.zig"); _ = @import("behavior/union.zig"); _ = @import("behavior/var_args.zig"); + _ = @import("behavior/vector.zig"); _ = @import("behavior/void.zig"); _ = @import("behavior/while.zig"); _ = @import("behavior/widening.zig"); diff --git a/test/stage1/behavior/vector.zig b/test/stage1/behavior/vector.zig new file mode 100644 index 0000000000000000000000000000000000000000..53c5d0138179bfb715034a90050c298e6a42d433 --- /dev/null +++ b/test/stage1/behavior/vector.zig @@ -0,0 +1,20 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; + +test "implicit array to vector and vector to array" { + const S = struct { + fn doTheTest() void { + var v: @Vector(4, i32) = [4]i32{10, 20, 30, 40}; + const x: @Vector(4, i32) = [4]i32{1, 2, 3, 4}; + v +%= x; + const result: [4]i32 = v; + assertOrPanic(result[0] == 11); + assertOrPanic(result[1] == 22); + assertOrPanic(result[2] == 33); + assertOrPanic(result[3] == 44); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +} + -- 2.54.0