2025-10-16 22:11:51 +02:00
|
|
|
const builtin = @import("builtin");
|
2019-03-02 16:46:04 -05:00
|
|
|
const std = @import("std.zig");
|
2018-10-19 16:19:22 -05:00
|
|
|
const debug = std.debug;
|
|
|
|
|
const mem = std.mem;
|
|
|
|
|
const math = std.math;
|
2019-02-08 18:18:47 -05:00
|
|
|
const testing = std.testing;
|
2020-12-18 15:38:38 -07:00
|
|
|
const root = @import("root");
|
2018-10-19 16:19:22 -05:00
|
|
|
|
2020-07-14 17:17:37 -07:00
|
|
|
pub const TrailerFlags = @import("meta/trailer_flags.zig").TrailerFlags;
|
2018-10-19 16:19:22 -05:00
|
|
|
|
2022-02-01 11:42:41 -07:00
|
|
|
const Type = std.builtin.Type;
|
2018-10-19 16:19:22 -05:00
|
|
|
|
2023-04-21 20:46:05 -04:00
|
|
|
test {
|
2022-05-04 20:01:48 +02:00
|
|
|
_ = TrailerFlags;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-29 15:50:42 +00:00
|
|
|
/// Returns the variant of an enum type, `T`, which is named `str`, or `null` if no such variant exists.
|
2018-11-26 02:08:12 +11:00
|
|
|
pub fn stringToEnum(comptime T: type, str: []const u8) ?T {
|
2024-04-20 23:14:39 -07:00
|
|
|
// Using StaticStringMap here is more performant, but it will start to take too
|
2020-05-26 22:31:36 -07:00
|
|
|
// long to compile if the enum is large enough, due to the current limits of comptime
|
2020-05-26 23:26:19 -07:00
|
|
|
// performance when doing things like constructing lookup maps at comptime.
|
2020-05-26 22:31:36 -07:00
|
|
|
// TODO The '100' here is arbitrary and should be increased when possible:
|
|
|
|
|
// - https://github.com/ziglang/zig/issues/4055
|
|
|
|
|
// - https://github.com/ziglang/zig/issues/3863
|
2024-08-28 02:35:53 +01:00
|
|
|
if (@typeInfo(T).@"enum".fields.len <= 100) {
|
2020-05-26 22:31:36 -07:00
|
|
|
const kvs = comptime build_kvs: {
|
2022-12-08 22:05:30 +02:00
|
|
|
const EnumKV = struct { []const u8, T };
|
2024-08-28 02:35:53 +01:00
|
|
|
var kvs_array: [@typeInfo(T).@"enum".fields.len]EnumKV = undefined;
|
|
|
|
|
for (@typeInfo(T).@"enum".fields, 0..) |enumField, i| {
|
2022-12-08 22:05:30 +02:00
|
|
|
kvs_array[i] = .{ enumField.name, @field(T, enumField.name) };
|
2020-05-26 22:31:36 -07:00
|
|
|
}
|
|
|
|
|
break :build_kvs kvs_array[0..];
|
|
|
|
|
};
|
2024-04-20 23:14:39 -07:00
|
|
|
const map = std.StaticStringMap(T).initComptime(kvs);
|
2020-05-26 22:31:36 -07:00
|
|
|
return map.get(str);
|
|
|
|
|
} else {
|
2024-08-28 02:35:53 +01:00
|
|
|
inline for (@typeInfo(T).@"enum".fields) |enumField| {
|
2020-05-26 22:31:36 -07:00
|
|
|
if (mem.eql(u8, str, enumField.name)) {
|
|
|
|
|
return @field(T, enumField.name);
|
|
|
|
|
}
|
2018-11-26 02:08:12 +11:00
|
|
|
}
|
2020-05-26 22:31:36 -07:00
|
|
|
return null;
|
2018-11-26 02:08:12 +11:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-26 15:14:43 -08:00
|
|
|
test stringToEnum {
|
2018-11-26 02:08:12 +11:00
|
|
|
const E1 = enum {
|
|
|
|
|
A,
|
|
|
|
|
B,
|
|
|
|
|
};
|
2021-05-04 20:47:26 +03:00
|
|
|
try testing.expect(E1.A == stringToEnum(E1, "A").?);
|
|
|
|
|
try testing.expect(E1.B == stringToEnum(E1, "B").?);
|
|
|
|
|
try testing.expect(null == stringToEnum(E1, "C"));
|
2018-11-26 02:08:12 +11:00
|
|
|
}
|
|
|
|
|
|
2021-04-25 16:49:21 +02:00
|
|
|
/// Returns the alignment of type T.
|
2024-03-16 16:46:45 +01:00
|
|
|
/// Note that if T is a pointer type the result is different than the one
|
|
|
|
|
/// returned by @alignOf(T).
|
2021-04-25 16:49:21 +02:00
|
|
|
/// If T is a pointer type the alignment of the type it points to is returned.
|
2018-11-23 10:02:14 -06:00
|
|
|
pub fn alignment(comptime T: type) comptime_int {
|
2021-04-25 16:49:21 +02:00
|
|
|
return switch (@typeInfo(T)) {
|
2024-08-28 02:35:53 +01:00
|
|
|
.optional => |info| switch (@typeInfo(info.child)) {
|
|
|
|
|
.pointer, .@"fn" => alignment(info.child),
|
2021-04-25 16:49:21 +02:00
|
|
|
else => @alignOf(T),
|
|
|
|
|
},
|
2024-08-28 02:35:53 +01:00
|
|
|
.pointer => |info| info.alignment,
|
2021-04-25 16:49:21 +02:00
|
|
|
else => @alignOf(T),
|
|
|
|
|
};
|
2018-10-19 16:19:22 -05:00
|
|
|
}
|
|
|
|
|
|
2024-02-26 15:14:43 -08:00
|
|
|
test alignment {
|
2021-05-04 20:47:26 +03:00
|
|
|
try testing.expect(alignment(u8) == 1);
|
|
|
|
|
try testing.expect(alignment(*align(1) u8) == 1);
|
|
|
|
|
try testing.expect(alignment(*align(2) u8) == 2);
|
|
|
|
|
try testing.expect(alignment([]align(1) u8) == 1);
|
|
|
|
|
try testing.expect(alignment([]align(2) u8) == 2);
|
|
|
|
|
try testing.expect(alignment(fn () void) > 0);
|
2024-03-16 16:46:45 +01:00
|
|
|
try testing.expect(alignment(*const fn () void) > 0);
|
|
|
|
|
try testing.expect(alignment(*align(128) const fn () void) == 128);
|
2018-10-19 16:19:22 -05:00
|
|
|
}
|
|
|
|
|
|
2022-08-29 15:50:42 +00:00
|
|
|
/// Given a parameterized type (array, vector, pointer, optional), returns the "child type".
|
2018-10-19 16:19:22 -05:00
|
|
|
pub fn Child(comptime T: type) type {
|
|
|
|
|
return switch (@typeInfo(T)) {
|
2024-08-28 02:35:53 +01:00
|
|
|
.array => |info| info.child,
|
|
|
|
|
.vector => |info| info.child,
|
|
|
|
|
.pointer => |info| info.child,
|
|
|
|
|
.optional => |info| info.child,
|
2020-05-26 15:52:37 +05:00
|
|
|
else => @compileError("Expected pointer, optional, array or vector type, found '" ++ @typeName(T) ++ "'"),
|
2018-10-19 16:19:22 -05:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-26 15:14:43 -08:00
|
|
|
test Child {
|
2021-05-04 20:47:26 +03:00
|
|
|
try testing.expect(Child([1]u8) == u8);
|
|
|
|
|
try testing.expect(Child(*u8) == u8);
|
|
|
|
|
try testing.expect(Child([]u8) == u8);
|
|
|
|
|
try testing.expect(Child(?u8) == u8);
|
2023-06-04 22:57:35 +06:00
|
|
|
try testing.expect(Child(@Vector(2, u8)) == u8);
|
2018-10-19 16:19:22 -05:00
|
|
|
}
|
|
|
|
|
|
2022-08-29 15:50:42 +00:00
|
|
|
/// Given a "memory span" type (array, slice, vector, or pointer to such), returns the "element type".
|
2020-03-18 19:29:24 -04:00
|
|
|
pub fn Elem(comptime T: type) type {
|
|
|
|
|
switch (@typeInfo(T)) {
|
2024-08-28 02:35:53 +01:00
|
|
|
.array => |info| return info.child,
|
|
|
|
|
.vector => |info| return info.child,
|
|
|
|
|
.pointer => |info| switch (info.size) {
|
2025-01-15 17:53:05 +00:00
|
|
|
.one => switch (@typeInfo(info.child)) {
|
2024-08-28 02:35:53 +01:00
|
|
|
.array => |array_info| return array_info.child,
|
|
|
|
|
.vector => |vector_info| return vector_info.child,
|
2020-03-18 19:29:24 -04:00
|
|
|
else => {},
|
|
|
|
|
},
|
2025-01-15 17:53:05 +00:00
|
|
|
.many, .c, .slice => return info.child,
|
2020-03-18 19:29:24 -04:00
|
|
|
},
|
2024-08-28 02:35:53 +01:00
|
|
|
.optional => |info| return Elem(info.child),
|
2020-03-18 19:29:24 -04:00
|
|
|
else => {},
|
|
|
|
|
}
|
2020-05-26 15:52:37 +05:00
|
|
|
@compileError("Expected pointer, slice, array or vector type, found '" ++ @typeName(T) ++ "'");
|
2020-03-18 19:29:24 -04:00
|
|
|
}
|
|
|
|
|
|
2024-02-26 15:14:43 -08:00
|
|
|
test Elem {
|
2021-05-04 20:47:26 +03:00
|
|
|
try testing.expect(Elem([1]u8) == u8);
|
|
|
|
|
try testing.expect(Elem([*]u8) == u8);
|
|
|
|
|
try testing.expect(Elem([]u8) == u8);
|
|
|
|
|
try testing.expect(Elem(*[10]u8) == u8);
|
2023-06-04 22:57:35 +06:00
|
|
|
try testing.expect(Elem(@Vector(2, u8)) == u8);
|
|
|
|
|
try testing.expect(Elem(*@Vector(2, u8)) == u8);
|
2021-05-04 20:47:26 +03:00
|
|
|
try testing.expect(Elem(?[*]u8) == u8);
|
2020-03-18 19:29:24 -04:00
|
|
|
}
|
|
|
|
|
|
2020-03-17 18:54:09 -04:00
|
|
|
/// Given a type which can have a sentinel e.g. `[:0]u8`, returns the sentinel value,
|
|
|
|
|
/// or `null` if there is not one.
|
|
|
|
|
/// Types which cannot possibly have a sentinel will be a compile error.
|
2023-11-21 15:23:44 -07:00
|
|
|
/// Result is always comptime-known.
|
|
|
|
|
pub inline fn sentinel(comptime T: type) ?Elem(T) {
|
2019-12-27 14:31:20 +11:00
|
|
|
switch (@typeInfo(T)) {
|
2025-01-15 17:34:12 +00:00
|
|
|
.array => |info| return info.sentinel(),
|
2024-08-28 02:35:53 +01:00
|
|
|
.pointer => |info| {
|
2020-03-17 18:54:09 -04:00
|
|
|
switch (info.size) {
|
2025-01-15 17:34:12 +00:00
|
|
|
.many, .slice => return info.sentinel(),
|
2025-01-15 17:53:05 +00:00
|
|
|
.one => switch (@typeInfo(info.child)) {
|
2025-01-15 17:34:12 +00:00
|
|
|
.array => |array_info| return array_info.sentinel(),
|
2020-03-17 18:54:09 -04:00
|
|
|
else => {},
|
2019-12-27 14:31:20 +11:00
|
|
|
},
|
|
|
|
|
else => {},
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
else => {},
|
|
|
|
|
}
|
2020-03-17 18:54:09 -04:00
|
|
|
@compileError("type '" ++ @typeName(T) ++ "' cannot possibly have a sentinel");
|
2019-12-27 14:31:20 +11:00
|
|
|
}
|
|
|
|
|
|
2023-11-21 15:23:44 -07:00
|
|
|
test sentinel {
|
2021-05-04 20:47:26 +03:00
|
|
|
try testSentinel();
|
2023-06-05 14:29:28 +06:00
|
|
|
try comptime testSentinel();
|
2020-03-18 19:29:24 -04:00
|
|
|
}
|
|
|
|
|
|
2021-05-04 20:47:26 +03:00
|
|
|
fn testSentinel() !void {
|
|
|
|
|
try testing.expectEqual(@as(u8, 0), sentinel([:0]u8).?);
|
|
|
|
|
try testing.expectEqual(@as(u8, 0), sentinel([*:0]u8).?);
|
|
|
|
|
try testing.expectEqual(@as(u8, 0), sentinel([5:0]u8).?);
|
|
|
|
|
try testing.expectEqual(@as(u8, 0), sentinel(*const [5:0]u8).?);
|
2020-03-17 18:54:09 -04:00
|
|
|
|
2021-05-04 20:47:26 +03:00
|
|
|
try testing.expect(sentinel([]u8) == null);
|
|
|
|
|
try testing.expect(sentinel([*]u8) == null);
|
|
|
|
|
try testing.expect(sentinel([5]u8) == null);
|
|
|
|
|
try testing.expect(sentinel(*const [5]u8) == null);
|
2019-12-27 14:31:20 +11:00
|
|
|
}
|
|
|
|
|
|
2020-11-25 13:23:43 -07:00
|
|
|
/// Given a "memory span" type, returns the same type except with the given sentinel value.
|
|
|
|
|
pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type {
|
|
|
|
|
switch (@typeInfo(T)) {
|
2024-08-28 02:35:53 +01:00
|
|
|
.pointer => |info| switch (info.size) {
|
2025-01-15 17:53:05 +00:00
|
|
|
.one => switch (@typeInfo(info.child)) {
|
2025-04-30 18:42:23 +03:30
|
|
|
.array => |array_info| return @Pointer(.one, .{
|
|
|
|
|
.@"const" = info.is_const,
|
|
|
|
|
.@"volatile" = info.is_volatile,
|
|
|
|
|
.@"allowzero" = info.is_allowzero,
|
|
|
|
|
.@"align" = info.alignment,
|
|
|
|
|
.@"addrspace" = info.address_space,
|
|
|
|
|
}, [array_info.len:sentinel_val]array_info.child, null),
|
2020-12-09 13:54:26 +02:00
|
|
|
else => {},
|
|
|
|
|
},
|
2025-04-30 18:42:23 +03:30
|
|
|
.many, .slice => |size| return @Pointer(size, .{
|
|
|
|
|
.@"const" = info.is_const,
|
|
|
|
|
.@"volatile" = info.is_volatile,
|
|
|
|
|
.@"allowzero" = info.is_allowzero,
|
|
|
|
|
.@"align" = info.alignment,
|
|
|
|
|
.@"addrspace" = info.address_space,
|
|
|
|
|
}, info.child, sentinel_val),
|
2020-11-25 13:23:43 -07:00
|
|
|
else => {},
|
|
|
|
|
},
|
2024-08-28 02:35:53 +01:00
|
|
|
.optional => |info| switch (@typeInfo(info.child)) {
|
|
|
|
|
.pointer => |ptr_info| switch (ptr_info.size) {
|
2025-04-30 18:42:23 +03:30
|
|
|
.many => return ?@Pointer(.many, .{
|
|
|
|
|
.@"const" = ptr_info.is_const,
|
|
|
|
|
.@"volatile" = ptr_info.is_volatile,
|
|
|
|
|
.@"allowzero" = ptr_info.is_allowzero,
|
|
|
|
|
.@"align" = ptr_info.alignment,
|
|
|
|
|
.@"addrspace" = ptr_info.address_space,
|
|
|
|
|
.child = ptr_info.child,
|
|
|
|
|
}, ptr_info.child, sentinel_val),
|
2020-11-25 13:23:43 -07:00
|
|
|
else => {},
|
|
|
|
|
},
|
|
|
|
|
else => {},
|
|
|
|
|
},
|
|
|
|
|
else => {},
|
|
|
|
|
}
|
|
|
|
|
@compileError("Unable to derive a sentinel pointer type from " ++ @typeName(T));
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-01 11:42:41 -07:00
|
|
|
pub fn containerLayout(comptime T: type) Type.ContainerLayout {
|
2018-10-19 16:19:22 -05:00
|
|
|
return switch (@typeInfo(T)) {
|
2024-08-28 02:35:53 +01:00
|
|
|
.@"struct" => |info| info.layout,
|
|
|
|
|
.@"union" => |info| info.layout,
|
2022-12-16 18:05:21 +01:00
|
|
|
else => @compileError("expected struct or union type, found '" ++ @typeName(T) ++ "'"),
|
2018-10-19 16:19:22 -05:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-26 15:14:43 -08:00
|
|
|
test containerLayout {
|
2018-11-13 05:08:37 -08:00
|
|
|
const S1 = struct {};
|
|
|
|
|
const S2 = packed struct {};
|
|
|
|
|
const S3 = extern struct {};
|
|
|
|
|
const U1 = union {
|
2018-10-19 16:19:22 -05:00
|
|
|
a: u8,
|
|
|
|
|
};
|
2018-11-13 05:08:37 -08:00
|
|
|
const U2 = packed union {
|
2018-10-19 16:19:22 -05:00
|
|
|
a: u8,
|
|
|
|
|
};
|
2018-11-13 05:08:37 -08:00
|
|
|
const U3 = extern union {
|
2018-10-19 16:19:22 -05:00
|
|
|
a: u8,
|
|
|
|
|
};
|
|
|
|
|
|
2024-02-18 18:28:39 -08:00
|
|
|
try testing.expect(containerLayout(S1) == .auto);
|
|
|
|
|
try testing.expect(containerLayout(S2) == .@"packed");
|
|
|
|
|
try testing.expect(containerLayout(S3) == .@"extern");
|
|
|
|
|
try testing.expect(containerLayout(U1) == .auto);
|
|
|
|
|
try testing.expect(containerLayout(U2) == .@"packed");
|
|
|
|
|
try testing.expect(containerLayout(U3) == .@"extern");
|
2018-10-19 16:19:22 -05:00
|
|
|
}
|
|
|
|
|
|
2025-02-18 14:10:47 -03:00
|
|
|
/// Instead of this function, prefer to use e.g. `@typeInfo(foo).@"struct".decls`
|
2022-01-31 22:25:49 -07:00
|
|
|
/// directly when you know what kind of type it is.
|
2022-02-01 11:42:41 -07:00
|
|
|
pub fn declarations(comptime T: type) []const Type.Declaration {
|
2018-10-19 16:19:22 -05:00
|
|
|
return switch (@typeInfo(T)) {
|
2024-08-28 02:35:53 +01:00
|
|
|
.@"struct" => |info| info.decls,
|
|
|
|
|
.@"enum" => |info| info.decls,
|
|
|
|
|
.@"union" => |info| info.decls,
|
|
|
|
|
.@"opaque" => |info| info.decls,
|
2020-11-19 23:32:22 +01:00
|
|
|
else => @compileError("Expected struct, enum, union, or opaque type, found '" ++ @typeName(T) ++ "'"),
|
2018-10-19 16:19:22 -05:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-26 15:14:43 -08:00
|
|
|
test declarations {
|
2018-11-13 05:08:37 -08:00
|
|
|
const E1 = enum {
|
2018-10-19 16:19:22 -05:00
|
|
|
A,
|
|
|
|
|
|
2023-07-25 11:15:59 -04:00
|
|
|
pub fn a() void {}
|
2018-10-19 16:19:22 -05:00
|
|
|
};
|
2018-11-13 05:08:37 -08:00
|
|
|
const S1 = struct {
|
2023-07-25 11:15:59 -04:00
|
|
|
pub fn a() void {}
|
2018-10-19 16:19:22 -05:00
|
|
|
};
|
2018-11-13 05:08:37 -08:00
|
|
|
const U1 = union {
|
2024-08-28 17:56:53 +01:00
|
|
|
b: u8,
|
2018-10-19 16:19:22 -05:00
|
|
|
|
2023-07-25 11:15:59 -04:00
|
|
|
pub fn a() void {}
|
2018-10-19 16:19:22 -05:00
|
|
|
};
|
2020-11-21 12:38:35 +01:00
|
|
|
const O1 = opaque {
|
2023-07-25 11:15:59 -04:00
|
|
|
pub fn a() void {}
|
2020-11-21 12:38:35 +01:00
|
|
|
};
|
2018-10-19 16:19:22 -05:00
|
|
|
|
2022-02-01 11:42:41 -07:00
|
|
|
const decls = comptime [_][]const Type.Declaration{
|
2019-05-26 21:22:45 +00:00
|
|
|
declarations(E1),
|
|
|
|
|
declarations(S1),
|
|
|
|
|
declarations(U1),
|
2020-11-21 12:38:35 +01:00
|
|
|
declarations(O1),
|
2018-10-19 16:19:22 -05:00
|
|
|
};
|
|
|
|
|
|
2019-05-26 21:22:45 +00:00
|
|
|
inline for (decls) |decl| {
|
2021-05-04 20:47:26 +03:00
|
|
|
try testing.expect(decl.len == 1);
|
|
|
|
|
try testing.expect(comptime mem.eql(u8, decl[0].name, "a"));
|
2018-10-19 16:19:22 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-01 11:42:41 -07:00
|
|
|
pub fn declarationInfo(comptime T: type, comptime decl_name: []const u8) Type.Declaration {
|
2019-05-26 21:22:45 +00:00
|
|
|
inline for (comptime declarations(T)) |decl| {
|
|
|
|
|
if (comptime mem.eql(u8, decl.name, decl_name))
|
|
|
|
|
return decl;
|
2018-10-19 16:19:22 -05:00
|
|
|
}
|
|
|
|
|
|
2019-05-26 21:22:45 +00:00
|
|
|
@compileError("'" ++ @typeName(T) ++ "' has no declaration '" ++ decl_name ++ "'");
|
2018-10-19 16:19:22 -05:00
|
|
|
}
|
|
|
|
|
|
2024-02-26 15:14:43 -08:00
|
|
|
test declarationInfo {
|
2018-11-13 05:08:37 -08:00
|
|
|
const E1 = enum {
|
2018-10-19 16:19:22 -05:00
|
|
|
A,
|
|
|
|
|
|
2023-07-25 11:15:59 -04:00
|
|
|
pub fn a() void {}
|
2018-10-19 16:19:22 -05:00
|
|
|
};
|
2018-11-13 05:08:37 -08:00
|
|
|
const S1 = struct {
|
2023-07-25 11:15:59 -04:00
|
|
|
pub fn a() void {}
|
2018-10-19 16:19:22 -05:00
|
|
|
};
|
2018-11-13 05:08:37 -08:00
|
|
|
const U1 = union {
|
2024-08-28 17:56:53 +01:00
|
|
|
b: u8,
|
2018-10-19 16:19:22 -05:00
|
|
|
|
2023-07-25 11:15:59 -04:00
|
|
|
pub fn a() void {}
|
2018-10-19 16:19:22 -05:00
|
|
|
};
|
|
|
|
|
|
2022-02-01 11:42:41 -07:00
|
|
|
const infos = comptime [_]Type.Declaration{
|
2019-05-26 21:22:45 +00:00
|
|
|
declarationInfo(E1, "a"),
|
|
|
|
|
declarationInfo(S1, "a"),
|
|
|
|
|
declarationInfo(U1, "a"),
|
2018-10-19 16:19:22 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
inline for (infos) |info| {
|
2021-05-04 20:47:26 +03:00
|
|
|
try testing.expect(comptime mem.eql(u8, info.name, "a"));
|
2018-10-19 16:19:22 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pub fn fields(comptime T: type) switch (@typeInfo(T)) {
|
2024-08-28 02:35:53 +01:00
|
|
|
.@"struct" => []const Type.StructField,
|
|
|
|
|
.@"union" => []const Type.UnionField,
|
|
|
|
|
.@"enum" => []const Type.EnumField,
|
|
|
|
|
.error_set => []const Type.Error,
|
2018-10-19 23:27:16 +02:00
|
|
|
else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"),
|
2018-10-19 16:19:22 -05:00
|
|
|
} {
|
|
|
|
|
return switch (@typeInfo(T)) {
|
2024-08-28 02:35:53 +01:00
|
|
|
.@"struct" => |info| info.fields,
|
|
|
|
|
.@"union" => |info| info.fields,
|
|
|
|
|
.@"enum" => |info| info.fields,
|
|
|
|
|
.error_set => |errors| errors.?, // must be non global error set
|
2018-10-19 23:27:16 +02:00
|
|
|
else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"),
|
2018-10-19 16:19:22 -05:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-26 15:14:43 -08:00
|
|
|
test fields {
|
2018-11-13 05:08:37 -08:00
|
|
|
const E1 = enum {
|
2018-10-19 16:19:22 -05:00
|
|
|
A,
|
|
|
|
|
};
|
2018-11-13 05:08:37 -08:00
|
|
|
const E2 = error{A};
|
|
|
|
|
const S1 = struct {
|
2018-10-19 16:19:22 -05:00
|
|
|
a: u8,
|
|
|
|
|
};
|
2018-11-13 05:08:37 -08:00
|
|
|
const U1 = union {
|
2018-10-19 16:19:22 -05:00
|
|
|
a: u8,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const e1f = comptime fields(E1);
|
|
|
|
|
const e2f = comptime fields(E2);
|
|
|
|
|
const sf = comptime fields(S1);
|
|
|
|
|
const uf = comptime fields(U1);
|
|
|
|
|
|
2021-05-04 20:47:26 +03:00
|
|
|
try testing.expect(e1f.len == 1);
|
|
|
|
|
try testing.expect(e2f.len == 1);
|
|
|
|
|
try testing.expect(sf.len == 1);
|
|
|
|
|
try testing.expect(uf.len == 1);
|
|
|
|
|
try testing.expect(mem.eql(u8, e1f[0].name, "A"));
|
|
|
|
|
try testing.expect(mem.eql(u8, e2f[0].name, "A"));
|
|
|
|
|
try testing.expect(mem.eql(u8, sf[0].name, "a"));
|
|
|
|
|
try testing.expect(mem.eql(u8, uf[0].name, "a"));
|
2022-12-13 22:30:06 +01:00
|
|
|
try testing.expect(comptime sf[0].type == u8);
|
|
|
|
|
try testing.expect(comptime uf[0].type == u8);
|
2018-10-19 16:19:22 -05:00
|
|
|
}
|
|
|
|
|
|
2020-12-13 22:12:28 +11:00
|
|
|
pub fn fieldInfo(comptime T: type, comptime field: FieldEnum(T)) switch (@typeInfo(T)) {
|
2024-08-28 02:35:53 +01:00
|
|
|
.@"struct" => Type.StructField,
|
|
|
|
|
.@"union" => Type.UnionField,
|
|
|
|
|
.@"enum" => Type.EnumField,
|
|
|
|
|
.error_set => Type.Error,
|
2018-10-19 23:27:16 +02:00
|
|
|
else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"),
|
2018-10-19 16:19:22 -05:00
|
|
|
} {
|
2023-06-15 13:14:16 +06:00
|
|
|
return fields(T)[@intFromEnum(field)];
|
2018-10-19 16:19:22 -05:00
|
|
|
}
|
|
|
|
|
|
2024-02-26 15:14:43 -08:00
|
|
|
test fieldInfo {
|
2018-11-13 05:08:37 -08:00
|
|
|
const E1 = enum {
|
2018-10-19 16:19:22 -05:00
|
|
|
A,
|
|
|
|
|
};
|
2018-11-13 05:08:37 -08:00
|
|
|
const E2 = error{A};
|
|
|
|
|
const S1 = struct {
|
2018-10-19 16:19:22 -05:00
|
|
|
a: u8,
|
|
|
|
|
};
|
2018-11-13 05:08:37 -08:00
|
|
|
const U1 = union {
|
2018-10-19 16:19:22 -05:00
|
|
|
a: u8,
|
|
|
|
|
};
|
|
|
|
|
|
2020-12-13 22:12:28 +11:00
|
|
|
const e1f = fieldInfo(E1, .A);
|
|
|
|
|
const e2f = fieldInfo(E2, .A);
|
|
|
|
|
const sf = fieldInfo(S1, .a);
|
|
|
|
|
const uf = fieldInfo(U1, .a);
|
2018-10-19 16:19:22 -05:00
|
|
|
|
2021-05-04 20:47:26 +03:00
|
|
|
try testing.expect(mem.eql(u8, e1f.name, "A"));
|
|
|
|
|
try testing.expect(mem.eql(u8, e2f.name, "A"));
|
|
|
|
|
try testing.expect(mem.eql(u8, sf.name, "a"));
|
|
|
|
|
try testing.expect(mem.eql(u8, uf.name, "a"));
|
2022-12-13 22:30:06 +01:00
|
|
|
try testing.expect(comptime sf.type == u8);
|
|
|
|
|
try testing.expect(comptime uf.type == u8);
|
2018-10-19 16:19:22 -05:00
|
|
|
}
|
|
|
|
|
|
2024-02-22 08:54:10 +01:00
|
|
|
pub fn fieldNames(comptime T: type) *const [fields(T).len][:0]const u8 {
|
2023-03-05 12:39:32 +00:00
|
|
|
return comptime blk: {
|
2020-11-06 10:45:49 +02:00
|
|
|
const fieldInfos = fields(T);
|
2024-02-22 08:54:10 +01:00
|
|
|
var names: [fieldInfos.len][:0]const u8 = undefined;
|
2025-01-24 02:19:28 +00:00
|
|
|
for (&names, fieldInfos) |*name, field| name.* = field.name;
|
compiler: implement analysis-local comptime-mutable memory
This commit changes how we represent comptime-mutable memory
(`comptime var`) in the compiler in order to implement the intended
behavior that references to such memory can only exist at comptime.
It does *not* clean up the representation of mutable values, improve the
representation of comptime-known pointers, or fix the many bugs in the
comptime pointer access code. These will be future enhancements.
Comptime memory lives for the duration of a single Sema, and is not
permitted to escape that one analysis, either by becoming runtime-known
or by becoming comptime-known to other analyses. These restrictions mean
that we can represent comptime allocations not via Decl, but with state
local to Sema - specifically, the new `Sema.comptime_allocs` field. All
comptime-mutable allocations, as well as any comptime-known const allocs
containing references to such memory, live in here. This allows for
relatively fast checking of whether a value references any
comptime-mtuable memory, since we need only traverse values up to
pointers: pointers to Decls can never reference comptime-mutable memory,
and pointers into `Sema.comptime_allocs` always do.
This change exposed some faulty pointer access logic in `Value.zig`.
I've fixed the important cases, but there are some TODOs I've put in
which are definitely possible to hit with sufficiently esoteric code. I
plan to resolve these by auditing all direct accesses to pointers (most
of them ought to use Sema to perform the pointer access!), but for now
this is sufficient for all realistic code and to get tests passing.
This change eliminates `Zcu.tmp_hack_arena`, instead using the Sema
arena for comptime memory mutations, which is possible since comptime
memory is now local to the current Sema.
This change should allow `Decl` to store only an `InternPool.Index`
rather than a full-blown `ty: Type, val: Value`. This commit does not
perform this refactor.
2024-03-22 23:39:44 +00:00
|
|
|
const final = names;
|
|
|
|
|
break :blk &final;
|
2023-03-05 12:39:32 +00:00
|
|
|
};
|
2020-11-06 10:45:49 +02:00
|
|
|
}
|
|
|
|
|
|
2024-02-26 15:14:43 -08:00
|
|
|
test fieldNames {
|
2021-02-24 21:29:01 -07:00
|
|
|
const E1 = enum { A, B };
|
2020-11-06 10:45:49 +02:00
|
|
|
const E2 = error{A};
|
|
|
|
|
const S1 = struct {
|
|
|
|
|
a: u8,
|
|
|
|
|
};
|
|
|
|
|
const U1 = union {
|
|
|
|
|
a: u8,
|
|
|
|
|
b: void,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const e1names = fieldNames(E1);
|
|
|
|
|
const e2names = fieldNames(E2);
|
|
|
|
|
const s1names = fieldNames(S1);
|
|
|
|
|
const u1names = fieldNames(U1);
|
|
|
|
|
|
2021-05-04 20:47:26 +03:00
|
|
|
try testing.expect(e1names.len == 2);
|
|
|
|
|
try testing.expectEqualSlices(u8, e1names[0], "A");
|
|
|
|
|
try testing.expectEqualSlices(u8, e1names[1], "B");
|
|
|
|
|
try testing.expect(e2names.len == 1);
|
|
|
|
|
try testing.expectEqualSlices(u8, e2names[0], "A");
|
|
|
|
|
try testing.expect(s1names.len == 1);
|
|
|
|
|
try testing.expectEqualSlices(u8, s1names[0], "a");
|
|
|
|
|
try testing.expect(u1names.len == 2);
|
|
|
|
|
try testing.expectEqualSlices(u8, u1names[0], "a");
|
|
|
|
|
try testing.expectEqualSlices(u8, u1names[1], "b");
|
2020-11-06 10:45:49 +02:00
|
|
|
}
|
|
|
|
|
|
2022-04-25 19:08:39 +02:00
|
|
|
/// Given an enum or error set type, returns a pointer to an array containing all tags for that
|
|
|
|
|
/// enum or error set.
|
|
|
|
|
pub fn tags(comptime T: type) *const [fields(T).len]T {
|
2023-03-05 12:39:32 +00:00
|
|
|
return comptime blk: {
|
2022-04-25 19:08:39 +02:00
|
|
|
const fieldInfos = fields(T);
|
|
|
|
|
var res: [fieldInfos.len]T = undefined;
|
2023-02-18 09:02:57 -07:00
|
|
|
for (fieldInfos, 0..) |field, i| {
|
2022-04-25 19:08:39 +02:00
|
|
|
res[i] = @field(T, field.name);
|
|
|
|
|
}
|
compiler: implement analysis-local comptime-mutable memory
This commit changes how we represent comptime-mutable memory
(`comptime var`) in the compiler in order to implement the intended
behavior that references to such memory can only exist at comptime.
It does *not* clean up the representation of mutable values, improve the
representation of comptime-known pointers, or fix the many bugs in the
comptime pointer access code. These will be future enhancements.
Comptime memory lives for the duration of a single Sema, and is not
permitted to escape that one analysis, either by becoming runtime-known
or by becoming comptime-known to other analyses. These restrictions mean
that we can represent comptime allocations not via Decl, but with state
local to Sema - specifically, the new `Sema.comptime_allocs` field. All
comptime-mutable allocations, as well as any comptime-known const allocs
containing references to such memory, live in here. This allows for
relatively fast checking of whether a value references any
comptime-mtuable memory, since we need only traverse values up to
pointers: pointers to Decls can never reference comptime-mutable memory,
and pointers into `Sema.comptime_allocs` always do.
This change exposed some faulty pointer access logic in `Value.zig`.
I've fixed the important cases, but there are some TODOs I've put in
which are definitely possible to hit with sufficiently esoteric code. I
plan to resolve these by auditing all direct accesses to pointers (most
of them ought to use Sema to perform the pointer access!), but for now
this is sufficient for all realistic code and to get tests passing.
This change eliminates `Zcu.tmp_hack_arena`, instead using the Sema
arena for comptime memory mutations, which is possible since comptime
memory is now local to the current Sema.
This change should allow `Decl` to store only an `InternPool.Index`
rather than a full-blown `ty: Type, val: Value`. This commit does not
perform this refactor.
2024-03-22 23:39:44 +00:00
|
|
|
const final = res;
|
|
|
|
|
break :blk &final;
|
2023-03-05 12:39:32 +00:00
|
|
|
};
|
2022-04-25 19:08:39 +02:00
|
|
|
}
|
|
|
|
|
|
2024-02-26 15:14:43 -08:00
|
|
|
test tags {
|
2022-04-25 19:08:39 +02:00
|
|
|
const E1 = enum { A, B };
|
|
|
|
|
const E2 = error{A};
|
|
|
|
|
|
|
|
|
|
const e1_tags = tags(E1);
|
|
|
|
|
const e2_tags = tags(E2);
|
|
|
|
|
|
|
|
|
|
try testing.expect(e1_tags.len == 2);
|
|
|
|
|
try testing.expectEqual(E1.A, e1_tags[0]);
|
|
|
|
|
try testing.expectEqual(E1.B, e1_tags[1]);
|
|
|
|
|
try testing.expect(e2_tags.len == 1);
|
|
|
|
|
try testing.expectEqual(E2.A, e2_tags[0]);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-29 15:50:42 +00:00
|
|
|
/// Returns an enum with a variant named after each field of `T`.
|
2020-12-13 22:03:59 +11:00
|
|
|
pub fn FieldEnum(comptime T: type) type {
|
2025-04-30 18:42:23 +03:30
|
|
|
const field_names = fieldNames(T);
|
2022-11-19 23:37:29 -06:00
|
|
|
|
2025-04-30 18:42:23 +03:30
|
|
|
switch (@typeInfo(T)) {
|
|
|
|
|
.@"union" => |@"union"| if (@"union".tag_type) |EnumTag| {
|
|
|
|
|
for (std.enums.values(EnumTag), 0..) |v, i| {
|
2023-06-15 13:14:16 +06:00
|
|
|
if (@intFromEnum(v) != i) break; // enum values not consecutive
|
2025-04-30 18:42:23 +03:30
|
|
|
if (!std.mem.eql(u8, @tagName(v), field_names[i])) break; // fields out of order
|
2022-09-20 14:40:43 +01:00
|
|
|
} else {
|
2025-04-30 18:42:23 +03:30
|
|
|
return EnumTag;
|
2022-09-20 14:40:43 +01:00
|
|
|
}
|
2025-04-30 18:42:23 +03:30
|
|
|
},
|
|
|
|
|
else => {},
|
2022-09-20 14:40:43 +01:00
|
|
|
}
|
|
|
|
|
|
2025-04-30 18:42:23 +03:30
|
|
|
const IntTag = std.math.IntFittingRange(0, field_names.len -| 1);
|
|
|
|
|
return @Enum(IntTag, .exhaustive, field_names, &std.simd.iota(IntTag, field_names.len));
|
2020-12-13 22:03:59 +11:00
|
|
|
}
|
|
|
|
|
|
2021-05-04 20:47:26 +03:00
|
|
|
fn expectEqualEnum(expected: anytype, actual: @TypeOf(expected)) !void {
|
2020-12-13 22:03:59 +11:00
|
|
|
// TODO: https://github.com/ziglang/zig/issues/7419
|
2024-08-28 02:35:53 +01:00
|
|
|
// testing.expectEqual(@typeInfo(expected).@"enum", @typeInfo(actual).@"enum");
|
2022-03-24 17:42:55 -07:00
|
|
|
try testing.expectEqual(
|
2024-08-28 02:35:53 +01:00
|
|
|
@typeInfo(expected).@"enum".tag_type,
|
|
|
|
|
@typeInfo(actual).@"enum".tag_type,
|
2022-03-24 17:42:55 -07:00
|
|
|
);
|
|
|
|
|
// For comparing decls and fields, we cannot use the meta eql function here
|
|
|
|
|
// because the language does not guarantee that the slice pointers for field names
|
|
|
|
|
// and decl names will be the same.
|
|
|
|
|
comptime {
|
2024-08-28 02:35:53 +01:00
|
|
|
const expected_fields = @typeInfo(expected).@"enum".fields;
|
|
|
|
|
const actual_fields = @typeInfo(actual).@"enum".fields;
|
2022-03-24 17:42:55 -07:00
|
|
|
if (expected_fields.len != actual_fields.len) return error.FailedTest;
|
2023-02-18 09:02:57 -07:00
|
|
|
for (expected_fields, 0..) |expected_field, i| {
|
2022-03-24 17:42:55 -07:00
|
|
|
const actual_field = actual_fields[i];
|
|
|
|
|
try testing.expectEqual(expected_field.value, actual_field.value);
|
|
|
|
|
try testing.expectEqualStrings(expected_field.name, actual_field.name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
comptime {
|
2024-08-28 02:35:53 +01:00
|
|
|
const expected_decls = @typeInfo(expected).@"enum".decls;
|
|
|
|
|
const actual_decls = @typeInfo(actual).@"enum".decls;
|
2022-03-24 17:42:55 -07:00
|
|
|
if (expected_decls.len != actual_decls.len) return error.FailedTest;
|
2023-02-18 09:02:57 -07:00
|
|
|
for (expected_decls, 0..) |expected_decl, i| {
|
2022-03-24 17:42:55 -07:00
|
|
|
const actual_decl = actual_decls[i];
|
|
|
|
|
try testing.expectEqualStrings(expected_decl.name, actual_decl.name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
try testing.expectEqual(
|
2024-08-28 02:35:53 +01:00
|
|
|
@typeInfo(expected).@"enum".is_exhaustive,
|
|
|
|
|
@typeInfo(actual).@"enum".is_exhaustive,
|
2022-03-24 17:42:55 -07:00
|
|
|
);
|
2020-12-13 22:03:59 +11:00
|
|
|
}
|
|
|
|
|
|
2024-02-26 15:14:43 -08:00
|
|
|
test FieldEnum {
|
2022-12-06 20:35:50 -07:00
|
|
|
try expectEqualEnum(enum {}, FieldEnum(struct {}));
|
2021-05-04 20:47:26 +03:00
|
|
|
try expectEqualEnum(enum { a }, FieldEnum(struct { a: u8 }));
|
|
|
|
|
try expectEqualEnum(enum { a, b, c }, FieldEnum(struct { a: u8, b: void, c: f32 }));
|
|
|
|
|
try expectEqualEnum(enum { a, b, c }, FieldEnum(union { a: u8, b: void, c: f32 }));
|
2022-09-20 14:40:43 +01:00
|
|
|
|
|
|
|
|
const Tagged = union(enum) { a: u8, b: void, c: f32 };
|
|
|
|
|
try testing.expectEqual(Tag(Tagged), FieldEnum(Tagged));
|
|
|
|
|
|
2023-08-21 14:27:34 -07:00
|
|
|
const Tag2 = enum { a, b, c };
|
2022-09-20 14:40:43 +01:00
|
|
|
const Tagged2 = union(Tag2) { a: u8, b: void, c: f32 };
|
2023-08-21 14:27:34 -07:00
|
|
|
try testing.expect(Tag(Tagged2) == FieldEnum(Tagged2));
|
2022-09-20 14:40:43 +01:00
|
|
|
|
|
|
|
|
const Tag3 = enum(u8) { a, b, c = 7 };
|
|
|
|
|
const Tagged3 = union(Tag3) { a: u8, b: void, c: f32 };
|
|
|
|
|
try testing.expect(Tag(Tagged3) != FieldEnum(Tagged3));
|
2020-12-13 22:03:59 +11:00
|
|
|
}
|
|
|
|
|
|
2022-01-17 17:50:23 -08:00
|
|
|
pub fn DeclEnum(comptime T: type) type {
|
2025-04-30 18:42:23 +03:30
|
|
|
const decls = declarations(T);
|
|
|
|
|
var names: [decls.len][]const u8 = undefined;
|
|
|
|
|
for (&names, decls) |*name, decl| name.* = decl.name;
|
|
|
|
|
const IntTag = std.math.IntFittingRange(0, decls.len -| 1);
|
|
|
|
|
return @Enum(IntTag, .exhaustive, &names, &std.simd.iota(IntTag, decls.len));
|
2022-01-17 17:50:23 -08:00
|
|
|
}
|
|
|
|
|
|
2024-02-26 15:14:43 -08:00
|
|
|
test DeclEnum {
|
2022-01-17 17:50:23 -08:00
|
|
|
const A = struct {
|
2023-07-25 11:15:59 -04:00
|
|
|
pub const a: u8 = 0;
|
2022-01-17 17:50:23 -08:00
|
|
|
};
|
|
|
|
|
const B = union {
|
|
|
|
|
foo: void,
|
|
|
|
|
|
2023-07-25 11:15:59 -04:00
|
|
|
pub const a: u8 = 0;
|
|
|
|
|
pub const b: void = {};
|
|
|
|
|
pub const c: f32 = 0;
|
2022-01-17 17:50:23 -08:00
|
|
|
};
|
|
|
|
|
const C = enum {
|
|
|
|
|
bar,
|
|
|
|
|
|
2023-07-25 11:15:59 -04:00
|
|
|
pub const a: u8 = 0;
|
|
|
|
|
pub const b: void = {};
|
|
|
|
|
pub const c: f32 = 0;
|
2022-01-17 17:50:23 -08:00
|
|
|
};
|
2024-09-07 15:20:55 +01:00
|
|
|
const D = struct {};
|
|
|
|
|
|
2022-01-17 17:50:23 -08:00
|
|
|
try expectEqualEnum(enum { a }, DeclEnum(A));
|
|
|
|
|
try expectEqualEnum(enum { a, b, c }, DeclEnum(B));
|
|
|
|
|
try expectEqualEnum(enum { a, b, c }, DeclEnum(C));
|
2024-09-07 15:20:55 +01:00
|
|
|
try expectEqualEnum(enum {}, DeclEnum(D));
|
2022-01-17 17:50:23 -08:00
|
|
|
}
|
|
|
|
|
|
2021-01-11 10:56:18 -07:00
|
|
|
pub fn Tag(comptime T: type) type {
|
2018-10-19 16:19:22 -05:00
|
|
|
return switch (@typeInfo(T)) {
|
2024-08-28 02:35:53 +01:00
|
|
|
.@"enum" => |info| info.tag_type,
|
|
|
|
|
.@"union" => |info| info.tag_type orelse @compileError(@typeName(T) ++ " has no tag type"),
|
2018-10-19 16:19:22 -05:00
|
|
|
else => @compileError("expected enum or union type, found '" ++ @typeName(T) ++ "'"),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-26 15:14:43 -08:00
|
|
|
test Tag {
|
2018-11-13 05:08:37 -08:00
|
|
|
const E = enum(u8) {
|
2018-10-19 16:19:22 -05:00
|
|
|
C = 33,
|
|
|
|
|
D,
|
|
|
|
|
};
|
2018-11-13 05:08:37 -08:00
|
|
|
const U = union(E) {
|
2018-10-19 16:19:22 -05:00
|
|
|
C: u8,
|
|
|
|
|
D: u16,
|
|
|
|
|
};
|
|
|
|
|
|
2021-05-04 20:47:26 +03:00
|
|
|
try testing.expect(Tag(E) == u8);
|
|
|
|
|
try testing.expect(Tag(U) == E);
|
2018-10-19 16:19:22 -05:00
|
|
|
}
|
|
|
|
|
|
2024-07-28 18:43:04 -06:00
|
|
|
/// Returns the active tag of a tagged union
|
2021-01-11 11:19:24 -07:00
|
|
|
pub fn activeTag(u: anytype) Tag(@TypeOf(u)) {
|
2019-12-09 21:56:19 +01:00
|
|
|
const T = @TypeOf(u);
|
2021-01-11 11:19:24 -07:00
|
|
|
return @as(Tag(T), u);
|
2018-10-19 16:19:22 -05:00
|
|
|
}
|
|
|
|
|
|
2024-02-26 15:14:43 -08:00
|
|
|
test activeTag {
|
2018-11-13 05:08:37 -08:00
|
|
|
const UE = enum {
|
2018-10-19 16:19:22 -05:00
|
|
|
Int,
|
|
|
|
|
Float,
|
|
|
|
|
};
|
2018-10-19 23:27:16 +02:00
|
|
|
|
2018-11-13 05:08:37 -08:00
|
|
|
const U = union(UE) {
|
2018-10-19 16:19:22 -05:00
|
|
|
Int: u32,
|
|
|
|
|
Float: f32,
|
|
|
|
|
};
|
2018-10-19 23:27:16 +02:00
|
|
|
|
2018-11-13 05:08:37 -08:00
|
|
|
var u = U{ .Int = 32 };
|
2021-05-04 20:47:26 +03:00
|
|
|
try testing.expect(activeTag(u) == UE.Int);
|
2018-10-19 16:19:22 -05:00
|
|
|
|
2018-11-13 05:08:37 -08:00
|
|
|
u = U{ .Float = 112.9876 };
|
2021-05-04 20:47:26 +03:00
|
|
|
try testing.expect(activeTag(u) == UE.Float);
|
2018-10-19 16:19:22 -05:00
|
|
|
}
|
|
|
|
|
|
2025-03-21 12:42:29 -05:00
|
|
|
/// Deprecated: Use @FieldType(U, tag_name)
|
2021-01-11 10:58:33 -07:00
|
|
|
const TagPayloadType = TagPayload;
|
|
|
|
|
|
2025-03-21 12:42:29 -05:00
|
|
|
/// Deprecated: Use @FieldType(U, tag_name)
|
2022-12-16 18:06:00 -07:00
|
|
|
pub fn TagPayloadByName(comptime U: type, comptime tag_name: []const u8) type {
|
2024-08-28 02:35:53 +01:00
|
|
|
const info = @typeInfo(U).@"union";
|
2020-08-26 12:20:27 -06:00
|
|
|
|
2018-11-23 10:02:14 -06:00
|
|
|
inline for (info.fields) |field_info| {
|
2022-12-16 18:06:00 -07:00
|
|
|
if (comptime mem.eql(u8, field_info.name, tag_name))
|
2022-12-13 22:30:06 +01:00
|
|
|
return field_info.type;
|
2018-11-23 10:02:14 -06:00
|
|
|
}
|
2020-08-26 12:20:27 -06:00
|
|
|
|
2024-04-22 14:02:32 -04:00
|
|
|
@compileError("no field '" ++ tag_name ++ "' in union '" ++ @typeName(U) ++ "'");
|
2018-11-23 10:02:14 -06:00
|
|
|
}
|
|
|
|
|
|
2025-03-21 12:42:29 -05:00
|
|
|
/// Deprecated: Use @FieldType(U, @tagName(tag))
|
2022-12-16 18:06:00 -07:00
|
|
|
pub fn TagPayload(comptime U: type, comptime tag: Tag(U)) type {
|
|
|
|
|
return TagPayloadByName(U, @tagName(tag));
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-26 15:14:43 -08:00
|
|
|
test TagPayload {
|
2018-11-23 10:02:14 -06:00
|
|
|
const Event = union(enum) {
|
|
|
|
|
Moved: struct {
|
|
|
|
|
from: i32,
|
|
|
|
|
to: i32,
|
|
|
|
|
},
|
|
|
|
|
};
|
2021-01-11 10:58:33 -07:00
|
|
|
const MovedEvent = TagPayload(Event, Event.Moved);
|
2023-11-10 05:27:17 +00:00
|
|
|
const e: Event = .{ .Moved = undefined };
|
2021-05-04 20:47:26 +03:00
|
|
|
try testing.expect(MovedEvent == @TypeOf(e.Moved));
|
2018-11-23 10:02:14 -06:00
|
|
|
}
|
|
|
|
|
|
2024-11-25 00:29:44 +02:00
|
|
|
/// Compares two of any type for equality. Containers that do not support comparison
|
|
|
|
|
/// on their own are compared on a field-by-field basis. Pointers are not followed.
|
2020-07-11 14:09:04 +03:00
|
|
|
pub fn eql(a: anytype, b: @TypeOf(a)) bool {
|
2019-12-09 21:56:19 +01:00
|
|
|
const T = @TypeOf(a);
|
2018-10-19 23:27:16 +02:00
|
|
|
|
2020-02-13 23:20:40 +01:00
|
|
|
switch (@typeInfo(T)) {
|
2024-08-28 02:35:53 +01:00
|
|
|
.@"struct" => |info| {
|
2024-11-25 00:29:44 +02:00
|
|
|
if (info.layout == .@"packed") return a == b;
|
|
|
|
|
|
2018-10-19 23:27:16 +02:00
|
|
|
inline for (info.fields) |field_info| {
|
|
|
|
|
if (!eql(@field(a, field_info.name), @field(b, field_info.name))) return false;
|
2018-10-19 16:19:22 -05:00
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
},
|
2024-08-28 02:35:53 +01:00
|
|
|
.error_union => {
|
2018-10-19 23:27:16 +02:00
|
|
|
if (a) |a_p| {
|
|
|
|
|
if (b) |b_p| return eql(a_p, b_p) else |_| return false;
|
|
|
|
|
} else |a_e| {
|
|
|
|
|
if (b) |_| return false else |b_e| return a_e == b_e;
|
2018-10-19 16:19:22 -05:00
|
|
|
}
|
|
|
|
|
},
|
2024-08-28 02:35:53 +01:00
|
|
|
.@"union" => |info| {
|
2021-01-11 10:56:18 -07:00
|
|
|
if (info.tag_type) |UnionTag| {
|
2024-06-18 19:04:16 +02:00
|
|
|
const tag_a: UnionTag = a;
|
|
|
|
|
const tag_b: UnionTag = b;
|
2018-10-19 23:27:16 +02:00
|
|
|
if (tag_a != tag_b) return false;
|
|
|
|
|
|
2024-06-18 19:04:16 +02:00
|
|
|
return switch (a) {
|
|
|
|
|
inline else => |val, tag| return eql(val, @field(b, @tagName(tag))),
|
|
|
|
|
};
|
2018-10-19 16:19:22 -05:00
|
|
|
}
|
2018-10-19 23:27:16 +02:00
|
|
|
|
2018-10-19 16:19:22 -05:00
|
|
|
@compileError("cannot compare untagged union type " ++ @typeName(T));
|
|
|
|
|
},
|
2024-08-28 02:35:53 +01:00
|
|
|
.array => {
|
2018-10-19 23:27:16 +02:00
|
|
|
if (a.len != b.len) return false;
|
2023-02-18 09:02:57 -07:00
|
|
|
for (a, 0..) |e, i|
|
2018-10-19 23:27:16 +02:00
|
|
|
if (!eql(e, b[i])) return false;
|
2018-10-19 16:19:22 -05:00
|
|
|
return true;
|
|
|
|
|
},
|
2025-09-05 15:00:30 -07:00
|
|
|
.vector => return @reduce(.And, a == b),
|
2024-08-28 02:35:53 +01:00
|
|
|
.pointer => |info| {
|
2020-02-13 23:20:40 +01:00
|
|
|
return switch (info.size) {
|
2025-01-15 17:53:05 +00:00
|
|
|
.one, .many, .c => a == b,
|
|
|
|
|
.slice => a.ptr == b.ptr and a.len == b.len,
|
2020-02-13 23:20:40 +01:00
|
|
|
};
|
2018-10-19 16:19:22 -05:00
|
|
|
},
|
2024-08-28 02:35:53 +01:00
|
|
|
.optional => {
|
2019-02-11 16:07:40 -05:00
|
|
|
if (a == null and b == null) return true;
|
|
|
|
|
if (a == null or b == null) return false;
|
2018-11-23 10:02:14 -06:00
|
|
|
return eql(a.?, b.?);
|
|
|
|
|
},
|
2018-10-19 16:19:22 -05:00
|
|
|
else => return a == b,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-26 15:14:43 -08:00
|
|
|
test eql {
|
2018-11-13 05:08:37 -08:00
|
|
|
const S = struct {
|
2018-10-19 16:19:22 -05:00
|
|
|
a: u32,
|
|
|
|
|
b: f64,
|
|
|
|
|
c: [5]u8,
|
|
|
|
|
};
|
2018-10-19 23:27:16 +02:00
|
|
|
|
2018-11-13 05:08:37 -08:00
|
|
|
const U = union(enum) {
|
2018-10-19 16:19:22 -05:00
|
|
|
s: S,
|
2018-11-23 10:02:14 -06:00
|
|
|
f: ?f32,
|
2018-10-19 16:19:22 -05:00
|
|
|
};
|
2018-10-19 23:27:16 +02:00
|
|
|
|
2018-11-13 05:08:37 -08:00
|
|
|
const s_1 = S{
|
2018-10-19 16:19:22 -05:00
|
|
|
.a = 134,
|
|
|
|
|
.b = 123.3,
|
2019-11-19 20:29:08 -05:00
|
|
|
.c = "12345".*,
|
2018-10-19 16:19:22 -05:00
|
|
|
};
|
2018-10-19 23:27:16 +02:00
|
|
|
|
2020-11-03 20:10:02 +01:00
|
|
|
var s_3 = S{
|
2018-10-19 16:19:22 -05:00
|
|
|
.a = 134,
|
|
|
|
|
.b = 123.3,
|
2019-11-19 20:29:08 -05:00
|
|
|
.c = "12345".*,
|
2018-10-19 16:19:22 -05:00
|
|
|
};
|
2018-10-19 23:27:16 +02:00
|
|
|
|
2018-11-13 05:08:37 -08:00
|
|
|
const u_1 = U{ .f = 24 };
|
|
|
|
|
const u_2 = U{ .s = s_1 };
|
|
|
|
|
const u_3 = U{ .f = 24 };
|
2018-10-19 23:27:16 +02:00
|
|
|
|
2021-05-04 20:47:26 +03:00
|
|
|
try testing.expect(eql(s_1, s_3));
|
|
|
|
|
try testing.expect(eql(&s_1, &s_1));
|
|
|
|
|
try testing.expect(!eql(&s_1, &s_3));
|
|
|
|
|
try testing.expect(eql(u_1, u_3));
|
|
|
|
|
try testing.expect(!eql(u_1, u_2));
|
2018-10-19 23:27:16 +02:00
|
|
|
|
2023-11-10 05:27:17 +00:00
|
|
|
const a1 = "abcdef".*;
|
|
|
|
|
const a2 = "abcdef".*;
|
|
|
|
|
const a3 = "ghijkl".*;
|
2018-10-19 23:27:16 +02:00
|
|
|
|
2021-05-04 20:47:26 +03:00
|
|
|
try testing.expect(eql(a1, a2));
|
|
|
|
|
try testing.expect(!eql(a1, a3));
|
2018-10-19 23:27:16 +02:00
|
|
|
|
2018-11-13 05:08:37 -08:00
|
|
|
const EU = struct {
|
2018-10-19 23:27:16 +02:00
|
|
|
fn tst(err: bool) !u8 {
|
|
|
|
|
if (err) return error.Error;
|
2019-11-06 23:25:57 -05:00
|
|
|
return @as(u8, 5);
|
2018-10-19 16:19:22 -05:00
|
|
|
}
|
|
|
|
|
};
|
2018-10-19 23:27:16 +02:00
|
|
|
|
2021-05-04 20:47:26 +03:00
|
|
|
try testing.expect(eql(EU.tst(true), EU.tst(true)));
|
|
|
|
|
try testing.expect(eql(EU.tst(false), EU.tst(false)));
|
|
|
|
|
try testing.expect(!eql(EU.tst(false), EU.tst(true)));
|
2020-01-07 19:14:37 +05:00
|
|
|
|
2024-06-18 19:04:16 +02:00
|
|
|
const CU = union(enum) {
|
|
|
|
|
a: void,
|
|
|
|
|
b: void,
|
|
|
|
|
c: comptime_int,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
try testing.expect(eql(CU{ .a = {} }, .a));
|
|
|
|
|
try testing.expect(!eql(CU{ .a = {} }, .b));
|
2025-10-16 22:11:51 +02:00
|
|
|
|
|
|
|
|
if (builtin.cpu.arch == .hexagon) return error.SkipZigTest;
|
|
|
|
|
|
|
|
|
|
const V = @Vector(4, u32);
|
|
|
|
|
const v1: V = @splat(1);
|
|
|
|
|
const v2: V = @splat(1);
|
|
|
|
|
const v3: V = @splat(2);
|
|
|
|
|
|
|
|
|
|
try testing.expect(eql(v1, v2));
|
|
|
|
|
try testing.expect(!eql(v1, v3));
|
2018-10-19 23:27:16 +02:00
|
|
|
}
|
2018-11-18 20:18:24 -05:00
|
|
|
|
2025-05-11 17:38:16 +09:00
|
|
|
/// Deprecated: use `std.enums.fromInt` instead and handle null.
|
2018-11-18 20:18:24 -05:00
|
|
|
pub const IntToEnumError = error{InvalidEnumTag};
|
|
|
|
|
|
2025-05-11 17:38:16 +09:00
|
|
|
/// Deprecated: use `std.enums.fromInt` instead and handle null instead of an error.
|
2021-01-11 10:56:18 -07:00
|
|
|
pub fn intToEnum(comptime EnumTag: type, tag_int: anytype) IntToEnumError!EnumTag {
|
2025-05-11 17:38:16 +09:00
|
|
|
return std.enums.fromInt(EnumTag, tag_int) orelse return error.InvalidEnumTag;
|
2018-11-18 20:18:24 -05:00
|
|
|
}
|
2019-10-16 01:29:16 -04:00
|
|
|
|
|
|
|
|
/// Given a type and a name, return the field index according to source order.
|
|
|
|
|
/// Returns `null` if the field is not found.
|
|
|
|
|
pub fn fieldIndex(comptime T: type, comptime name: []const u8) ?comptime_int {
|
2023-02-18 09:02:57 -07:00
|
|
|
inline for (fields(T), 0..) |field, i| {
|
2019-10-16 01:29:16 -04:00
|
|
|
if (mem.eql(u8, field.name, name))
|
2019-12-05 20:20:38 +02:00
|
|
|
return i;
|
2019-10-16 01:29:16 -04:00
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2019-10-16 19:16:39 -04:00
|
|
|
|
2020-01-19 02:40:35 -05:00
|
|
|
/// Returns a slice of pointers to public declarations of a namespace.
|
|
|
|
|
pub fn declList(comptime Namespace: type, comptime Decl: type) []const *const Decl {
|
|
|
|
|
const S = struct {
|
2020-06-12 16:32:02 +02:00
|
|
|
fn declNameLessThan(context: void, lhs: *const Decl, rhs: *const Decl) bool {
|
2021-06-19 21:10:22 -04:00
|
|
|
_ = context;
|
2020-01-19 02:40:35 -05:00
|
|
|
return mem.lessThan(u8, lhs.name, rhs.name);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
comptime {
|
|
|
|
|
const decls = declarations(Namespace);
|
|
|
|
|
var array: [decls.len]*const Decl = undefined;
|
2023-02-18 09:02:57 -07:00
|
|
|
for (decls, 0..) |decl, i| {
|
2020-01-19 02:40:35 -05:00
|
|
|
array[i] = &@field(Namespace, decl.name);
|
|
|
|
|
}
|
2023-05-23 15:33:12 +03:30
|
|
|
mem.sort(*const Decl, &array, {}, S.declNameLessThan);
|
2020-01-19 02:40:35 -05:00
|
|
|
return &array;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-02-24 23:39:03 +02:00
|
|
|
|
2025-04-30 18:42:23 +03:30
|
|
|
/// Deprecated: use @Int
|
2021-10-04 23:47:27 -07:00
|
|
|
pub fn Int(comptime signedness: std.builtin.Signedness, comptime bit_count: u16) type {
|
2025-04-30 18:42:23 +03:30
|
|
|
return @Int(signedness, bit_count);
|
2020-02-24 23:39:03 +02:00
|
|
|
}
|
2020-04-27 23:45:54 -06:00
|
|
|
|
2021-11-21 21:27:51 +00:00
|
|
|
pub fn Float(comptime bit_count: u8) type {
|
2025-04-30 18:42:23 +03:30
|
|
|
return switch (bit_count) {
|
|
|
|
|
16 => f16,
|
|
|
|
|
32 => f32,
|
|
|
|
|
64 => f64,
|
|
|
|
|
80 => f80,
|
|
|
|
|
128 => f128,
|
|
|
|
|
else => @compileError("invalid float bit count"),
|
|
|
|
|
};
|
2021-11-21 21:27:51 +00:00
|
|
|
}
|
2024-02-26 15:14:43 -08:00
|
|
|
test Float {
|
2021-11-21 21:27:51 +00:00
|
|
|
try testing.expectEqual(f16, Float(16));
|
|
|
|
|
try testing.expectEqual(f32, Float(32));
|
|
|
|
|
try testing.expectEqual(f64, Float(64));
|
2025-04-30 18:42:23 +03:30
|
|
|
try testing.expectEqual(f80, Float(80));
|
2021-11-21 21:27:51 +00:00
|
|
|
try testing.expectEqual(f128, Float(128));
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-25 09:16:43 +02:00
|
|
|
/// For a given function type, returns a tuple type which fields will
|
|
|
|
|
/// correspond to the argument types.
|
|
|
|
|
///
|
|
|
|
|
/// Examples:
|
2023-07-04 10:34:31 -04:00
|
|
|
/// - `ArgsTuple(fn () void)` ⇒ `tuple { }`
|
|
|
|
|
/// - `ArgsTuple(fn (a: u32) u32)` ⇒ `tuple { u32 }`
|
|
|
|
|
/// - `ArgsTuple(fn (a: u32, b: f16) noreturn)` ⇒ `tuple { u32, f16 }`
|
2020-09-25 09:16:43 +02:00
|
|
|
pub fn ArgsTuple(comptime Function: type) type {
|
|
|
|
|
const info = @typeInfo(Function);
|
2024-08-28 02:35:53 +01:00
|
|
|
if (info != .@"fn")
|
2020-09-25 09:16:43 +02:00
|
|
|
@compileError("ArgsTuple expects a function type");
|
|
|
|
|
|
2024-08-28 02:35:53 +01:00
|
|
|
const function_info = info.@"fn";
|
2020-09-25 09:16:43 +02:00
|
|
|
if (function_info.is_var_args)
|
|
|
|
|
@compileError("Cannot create ArgsTuple for variadic function");
|
|
|
|
|
|
2022-12-13 21:54:04 +01:00
|
|
|
var argument_field_list: [function_info.params.len]type = undefined;
|
2023-02-18 09:02:57 -07:00
|
|
|
inline for (function_info.params, 0..) |arg, i| {
|
2023-05-19 12:48:28 -07:00
|
|
|
const T = arg.type orelse @compileError("cannot create ArgsTuple for function with an 'anytype' parameter");
|
2022-08-03 15:17:21 +02:00
|
|
|
argument_field_list[i] = T;
|
2020-09-25 09:16:43 +02:00
|
|
|
}
|
|
|
|
|
|
2025-04-30 18:42:23 +03:30
|
|
|
return Tuple(&argument_field_list);
|
2020-09-25 09:16:43 +02:00
|
|
|
}
|
|
|
|
|
|
2025-04-30 18:42:23 +03:30
|
|
|
/// Deprecated; use `@Tuple` instead.
|
2020-09-25 09:27:00 +02:00
|
|
|
///
|
2025-04-30 18:42:23 +03:30
|
|
|
/// To be removed after Zig 0.16.0 releases.
|
2020-09-28 12:24:22 +02:00
|
|
|
pub fn Tuple(comptime types: []const type) type {
|
2025-04-30 18:42:23 +03:30
|
|
|
return @Tuple(types);
|
2020-09-25 09:27:00 +02:00
|
|
|
}
|
2020-09-25 09:16:43 +02:00
|
|
|
|
2020-09-29 16:19:44 +03:00
|
|
|
const TupleTester = struct {
|
|
|
|
|
fn assertTypeEqual(comptime Expected: type, comptime Actual: type) void {
|
|
|
|
|
if (Expected != Actual)
|
|
|
|
|
@compileError("Expected type " ++ @typeName(Expected) ++ ", but got type " ++ @typeName(Actual));
|
|
|
|
|
}
|
2020-09-25 09:16:43 +02:00
|
|
|
|
2020-09-29 16:19:44 +03:00
|
|
|
fn assertTuple(comptime expected: anytype, comptime Actual: type) void {
|
|
|
|
|
const info = @typeInfo(Actual);
|
2024-08-28 02:35:53 +01:00
|
|
|
if (info != .@"struct")
|
2020-09-29 16:19:44 +03:00
|
|
|
@compileError("Expected struct type");
|
2024-08-28 02:35:53 +01:00
|
|
|
if (!info.@"struct".is_tuple)
|
2020-09-29 16:19:44 +03:00
|
|
|
@compileError("Struct type must be a tuple type");
|
2020-09-25 09:27:00 +02:00
|
|
|
|
2020-09-29 16:19:44 +03:00
|
|
|
const fields_list = std.meta.fields(Actual);
|
|
|
|
|
if (expected.len != fields_list.len)
|
|
|
|
|
@compileError("Argument count mismatch");
|
2020-09-25 09:27:00 +02:00
|
|
|
|
2023-02-18 09:02:57 -07:00
|
|
|
inline for (fields_list, 0..) |fld, i| {
|
2022-12-13 22:30:06 +01:00
|
|
|
if (expected[i] != fld.type) {
|
|
|
|
|
@compileError("Field " ++ fld.name ++ " expected to be type " ++ @typeName(expected[i]) ++ ", but was type " ++ @typeName(fld.type));
|
2020-09-25 09:16:43 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-09-29 16:19:44 +03:00
|
|
|
}
|
|
|
|
|
};
|
2020-09-25 09:27:00 +02:00
|
|
|
|
2024-02-26 15:14:43 -08:00
|
|
|
test ArgsTuple {
|
2020-09-29 16:19:44 +03:00
|
|
|
TupleTester.assertTuple(.{}, ArgsTuple(fn () void));
|
|
|
|
|
TupleTester.assertTuple(.{u32}, ArgsTuple(fn (a: u32) []const u8));
|
|
|
|
|
TupleTester.assertTuple(.{ u32, f16 }, ArgsTuple(fn (a: u32, b: f16) noreturn));
|
2020-10-02 09:37:01 +02:00
|
|
|
TupleTester.assertTuple(.{ u32, f16, []const u8, void }, ArgsTuple(fn (a: u32, b: f16, c: []const u8, void) noreturn));
|
2023-05-19 12:48:28 -07:00
|
|
|
TupleTester.assertTuple(.{u32}, ArgsTuple(fn (comptime a: u32) []const u8));
|
2020-09-29 16:19:44 +03:00
|
|
|
}
|
2020-09-25 09:16:43 +02:00
|
|
|
|
2024-02-26 15:14:43 -08:00
|
|
|
test Tuple {
|
2020-09-29 16:19:44 +03:00
|
|
|
TupleTester.assertTuple(.{}, Tuple(&[_]type{}));
|
|
|
|
|
TupleTester.assertTuple(.{u32}, Tuple(&[_]type{u32}));
|
|
|
|
|
TupleTester.assertTuple(.{ u32, f16 }, Tuple(&[_]type{ u32, f16 }));
|
2020-10-02 09:37:01 +02:00
|
|
|
TupleTester.assertTuple(.{ u32, f16, []const u8, void }, Tuple(&[_]type{ u32, f16, []const u8, void }));
|
2020-09-25 09:16:43 +02:00
|
|
|
}
|
2020-12-18 15:38:38 -07:00
|
|
|
|
2022-08-03 15:17:21 +02:00
|
|
|
test "Tuple deduplication" {
|
|
|
|
|
const T1 = std.meta.Tuple(&.{ u32, f32, i8 });
|
|
|
|
|
const T2 = std.meta.Tuple(&.{ u32, f32, i8 });
|
|
|
|
|
const T3 = std.meta.Tuple(&.{ u32, f32, i7 });
|
|
|
|
|
|
|
|
|
|
if (T1 != T2) {
|
|
|
|
|
@compileError("std.meta.Tuple doesn't deduplicate tuple types.");
|
|
|
|
|
}
|
|
|
|
|
if (T1 == T3) {
|
|
|
|
|
@compileError("std.meta.Tuple fails to generate different types.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
test "ArgsTuple forwarding" {
|
|
|
|
|
const T1 = std.meta.Tuple(&.{ u32, f32, i8 });
|
|
|
|
|
const T2 = std.meta.ArgsTuple(fn (u32, f32, i8) void);
|
2025-01-15 17:53:05 +00:00
|
|
|
const T3 = std.meta.ArgsTuple(fn (u32, f32, i8) callconv(.c) noreturn);
|
2022-08-03 15:17:21 +02:00
|
|
|
|
|
|
|
|
if (T1 != T2) {
|
|
|
|
|
@compileError("std.meta.ArgsTuple produces different types than std.meta.Tuple");
|
|
|
|
|
}
|
|
|
|
|
if (T1 != T3) {
|
|
|
|
|
@compileError("std.meta.ArgsTuple produces different types for the same argument lists.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-11 16:26:29 +02:00
|
|
|
/// Returns whether `error_union` contains an error.
|
|
|
|
|
pub fn isError(error_union: anytype) bool {
|
2021-04-11 20:16:17 +02:00
|
|
|
return if (error_union) |_| false else |_| true;
|
2021-04-11 16:26:29 +02:00
|
|
|
}
|
|
|
|
|
|
2024-02-26 15:14:43 -08:00
|
|
|
test isError {
|
2023-07-17 00:21:45 +02:00
|
|
|
try std.testing.expect(isError(math.divTrunc(u8, 5, 0)));
|
|
|
|
|
try std.testing.expect(!isError(math.divTrunc(u8, 5, 5)));
|
2021-04-11 16:26:29 +02:00
|
|
|
}
|
2023-11-21 15:23:44 -07:00
|
|
|
|
|
|
|
|
/// Returns true if a type has a namespace and the namespace contains `name`;
|
|
|
|
|
/// `false` otherwise. Result is always comptime-known.
|
|
|
|
|
pub inline fn hasFn(comptime T: type, comptime name: []const u8) bool {
|
|
|
|
|
switch (@typeInfo(T)) {
|
2024-08-28 02:35:53 +01:00
|
|
|
.@"struct", .@"union", .@"enum", .@"opaque" => {},
|
2023-11-21 15:23:44 -07:00
|
|
|
else => return false,
|
|
|
|
|
}
|
|
|
|
|
if (!@hasDecl(T, name))
|
|
|
|
|
return false;
|
|
|
|
|
|
2024-08-28 02:35:53 +01:00
|
|
|
return @typeInfo(@TypeOf(@field(T, name))) == .@"fn";
|
2023-11-21 15:23:44 -07:00
|
|
|
}
|
|
|
|
|
|
2024-02-26 15:14:43 -08:00
|
|
|
test hasFn {
|
2024-02-03 16:47:55 -05:00
|
|
|
const S1 = struct {
|
|
|
|
|
pub fn foo() void {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
try std.testing.expect(hasFn(S1, "foo"));
|
|
|
|
|
try std.testing.expect(!hasFn(S1, "bar"));
|
|
|
|
|
try std.testing.expect(!hasFn(*S1, "foo"));
|
|
|
|
|
|
|
|
|
|
const S2 = struct {
|
|
|
|
|
foo: fn () void,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
try std.testing.expect(!hasFn(S2, "foo"));
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-09 22:18:52 +07:00
|
|
|
/// Returns true if a type has a `name` method; `false` otherwise.
|
|
|
|
|
/// Result is always comptime-known.
|
|
|
|
|
pub inline fn hasMethod(comptime T: type, comptime name: []const u8) bool {
|
|
|
|
|
return switch (@typeInfo(T)) {
|
2024-08-28 02:35:53 +01:00
|
|
|
.pointer => |P| switch (P.size) {
|
2025-01-15 17:53:05 +00:00
|
|
|
.one => hasFn(P.child, name),
|
|
|
|
|
.many, .slice, .c => false,
|
2024-02-03 16:47:55 -05:00
|
|
|
},
|
2023-07-09 22:18:52 +07:00
|
|
|
else => hasFn(T, name),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-26 15:14:43 -08:00
|
|
|
test hasMethod {
|
2024-02-03 16:47:55 -05:00
|
|
|
try std.testing.expect(!hasMethod(u32, "foo"));
|
|
|
|
|
try std.testing.expect(!hasMethod([]u32, "len"));
|
|
|
|
|
try std.testing.expect(!hasMethod(struct { u32, u64 }, "len"));
|
|
|
|
|
|
|
|
|
|
const S1 = struct {
|
|
|
|
|
pub fn foo() void {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
try std.testing.expect(hasMethod(S1, "foo"));
|
|
|
|
|
try std.testing.expect(hasMethod(*S1, "foo"));
|
|
|
|
|
|
|
|
|
|
try std.testing.expect(!hasMethod(S1, "bar"));
|
|
|
|
|
try std.testing.expect(!hasMethod(*[1]S1, "foo"));
|
|
|
|
|
try std.testing.expect(!hasMethod(*[10]S1, "foo"));
|
|
|
|
|
try std.testing.expect(!hasMethod([]S1, "foo"));
|
|
|
|
|
|
|
|
|
|
const S2 = struct {
|
|
|
|
|
foo: fn () void,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
try std.testing.expect(!hasMethod(S2, "foo"));
|
|
|
|
|
|
|
|
|
|
const U = union {
|
|
|
|
|
pub fn foo() void {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
try std.testing.expect(hasMethod(U, "foo"));
|
|
|
|
|
try std.testing.expect(hasMethod(*U, "foo"));
|
|
|
|
|
try std.testing.expect(!hasMethod(U, "bar"));
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-21 15:23:44 -07:00
|
|
|
/// True if every value of the type `T` has a unique bit pattern representing it.
|
|
|
|
|
/// In other words, `T` has no unused bits and no padding.
|
|
|
|
|
/// Result is always comptime-known.
|
|
|
|
|
pub inline fn hasUniqueRepresentation(comptime T: type) bool {
|
|
|
|
|
return switch (@typeInfo(T)) {
|
|
|
|
|
else => false, // TODO can we know if it's true for some of these types ?
|
|
|
|
|
|
2024-08-28 02:35:53 +01:00
|
|
|
.@"anyframe",
|
|
|
|
|
.@"enum",
|
|
|
|
|
.error_set,
|
|
|
|
|
.@"fn",
|
2023-11-21 15:23:44 -07:00
|
|
|
=> true,
|
|
|
|
|
|
2024-08-28 02:35:53 +01:00
|
|
|
.bool => false,
|
2023-11-21 15:23:44 -07:00
|
|
|
|
2024-08-28 02:35:53 +01:00
|
|
|
.int => |info| @sizeOf(T) * 8 == info.bits,
|
2023-11-21 15:23:44 -07:00
|
|
|
|
2025-01-15 17:53:05 +00:00
|
|
|
.pointer => |info| info.size != .slice,
|
2023-11-21 15:23:44 -07:00
|
|
|
|
2024-08-28 02:35:53 +01:00
|
|
|
.optional => |info| switch (@typeInfo(info.child)) {
|
|
|
|
|
.pointer => |ptr| !ptr.is_allowzero and switch (ptr.size) {
|
2025-01-15 17:53:05 +00:00
|
|
|
.slice, .c => false,
|
|
|
|
|
.one, .many => true,
|
2024-06-22 23:33:45 -04:00
|
|
|
},
|
|
|
|
|
else => false,
|
|
|
|
|
},
|
|
|
|
|
|
2024-08-28 02:35:53 +01:00
|
|
|
.array => |info| hasUniqueRepresentation(info.child),
|
2023-11-21 15:23:44 -07:00
|
|
|
|
2024-08-28 02:35:53 +01:00
|
|
|
.@"struct" => |info| {
|
2024-06-02 16:29:10 -07:00
|
|
|
if (info.layout == .@"packed") return @sizeOf(T) * 8 == @bitSizeOf(T);
|
|
|
|
|
|
2023-11-21 15:23:44 -07:00
|
|
|
var sum_size = @as(usize, 0);
|
|
|
|
|
|
|
|
|
|
inline for (info.fields) |field| {
|
2025-01-26 20:23:34 +02:00
|
|
|
if (field.is_comptime) continue;
|
2023-11-21 15:23:44 -07:00
|
|
|
if (!hasUniqueRepresentation(field.type)) return false;
|
|
|
|
|
sum_size += @sizeOf(field.type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return @sizeOf(T) == sum_size;
|
|
|
|
|
},
|
|
|
|
|
|
2024-08-28 02:35:53 +01:00
|
|
|
.vector => |info| hasUniqueRepresentation(info.child) and
|
2023-11-21 15:23:44 -07:00
|
|
|
@sizeOf(T) == @sizeOf(info.child) * info.len,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-26 15:14:43 -08:00
|
|
|
test hasUniqueRepresentation {
|
2023-11-21 15:23:44 -07:00
|
|
|
const TestStruct1 = struct {
|
|
|
|
|
a: u32,
|
|
|
|
|
b: u32,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
try testing.expect(hasUniqueRepresentation(TestStruct1));
|
|
|
|
|
|
|
|
|
|
const TestStruct2 = struct {
|
|
|
|
|
a: u32,
|
|
|
|
|
b: u16,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
try testing.expect(!hasUniqueRepresentation(TestStruct2));
|
|
|
|
|
|
|
|
|
|
const TestStruct3 = struct {
|
|
|
|
|
a: u32,
|
|
|
|
|
b: u32,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
try testing.expect(hasUniqueRepresentation(TestStruct3));
|
|
|
|
|
|
|
|
|
|
const TestStruct4 = struct { a: []const u8 };
|
|
|
|
|
|
|
|
|
|
try testing.expect(!hasUniqueRepresentation(TestStruct4));
|
|
|
|
|
|
|
|
|
|
const TestStruct5 = struct { a: TestStruct4 };
|
|
|
|
|
|
|
|
|
|
try testing.expect(!hasUniqueRepresentation(TestStruct5));
|
|
|
|
|
|
2024-06-02 16:29:10 -07:00
|
|
|
const TestStruct6 = packed struct(u8) {
|
|
|
|
|
@"0": bool,
|
|
|
|
|
@"1": bool,
|
|
|
|
|
@"2": bool,
|
|
|
|
|
@"3": bool,
|
|
|
|
|
@"4": bool,
|
|
|
|
|
@"5": bool,
|
|
|
|
|
@"6": bool,
|
|
|
|
|
@"7": bool,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
try testing.expect(hasUniqueRepresentation(TestStruct6));
|
|
|
|
|
|
2023-11-21 15:23:44 -07:00
|
|
|
const TestUnion2 = extern union {
|
|
|
|
|
a: u32,
|
|
|
|
|
b: u16,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
try testing.expect(!hasUniqueRepresentation(TestUnion2));
|
|
|
|
|
|
|
|
|
|
const TestUnion3 = union {
|
|
|
|
|
a: u32,
|
|
|
|
|
b: u16,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
try testing.expect(!hasUniqueRepresentation(TestUnion3));
|
|
|
|
|
|
|
|
|
|
const TestUnion4 = union(enum) {
|
|
|
|
|
a: u32,
|
|
|
|
|
b: u16,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
try testing.expect(!hasUniqueRepresentation(TestUnion4));
|
|
|
|
|
|
|
|
|
|
inline for ([_]type{ i0, u8, i16, u32, i64 }) |T| {
|
|
|
|
|
try testing.expect(hasUniqueRepresentation(T));
|
|
|
|
|
}
|
|
|
|
|
inline for ([_]type{ i1, u9, i17, u33, i24 }) |T| {
|
|
|
|
|
try testing.expect(!hasUniqueRepresentation(T));
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-22 23:33:45 -04:00
|
|
|
try testing.expect(hasUniqueRepresentation(*u8));
|
|
|
|
|
try testing.expect(hasUniqueRepresentation(*const u8));
|
|
|
|
|
try testing.expect(hasUniqueRepresentation(?*u8));
|
|
|
|
|
try testing.expect(hasUniqueRepresentation(?*const u8));
|
|
|
|
|
|
2023-11-21 15:23:44 -07:00
|
|
|
try testing.expect(!hasUniqueRepresentation([]u8));
|
|
|
|
|
try testing.expect(!hasUniqueRepresentation([]const u8));
|
2024-06-22 23:33:45 -04:00
|
|
|
try testing.expect(!hasUniqueRepresentation(?[]u8));
|
|
|
|
|
try testing.expect(!hasUniqueRepresentation(?[]const u8));
|
2023-11-21 15:23:44 -07:00
|
|
|
|
2024-02-14 06:05:01 +01:00
|
|
|
try testing.expect(hasUniqueRepresentation(@Vector(std.simd.suggestVectorLength(u8) orelse 1, u8)));
|
|
|
|
|
try testing.expect(@sizeOf(@Vector(3, u8)) == 3 or !hasUniqueRepresentation(@Vector(3, u8)));
|
2025-01-26 20:23:34 +02:00
|
|
|
|
|
|
|
|
const StructWithComptimeFields = struct {
|
|
|
|
|
comptime should_be_ignored: u64 = 42,
|
|
|
|
|
comptime should_also_be_ignored: [*:0]const u8 = "hope you're having a good day :)",
|
|
|
|
|
field: u32,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
try testing.expect(hasUniqueRepresentation(StructWithComptimeFields));
|
2023-11-21 15:23:44 -07:00
|
|
|
}
|