Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 21 additions & 24 deletions sdk/src/Command.zig
Original file line number Diff line number Diff line change
Expand Up @@ -222,24 +222,24 @@ pub fn Params(comptime T: type) type {
};
}

fn buildParams(comptime T: type) [std.meta.fieldNames(T).len]Param {
const fields = @import("zig017_fields.zig").fields(T);
var out: [fields.len]Param = undefined;
for (fields, 0..) |f, i| {
if (!settings.isCell(f.type)) @compileError(
"sdk.Command.Params: field '" ++ f.name ++ "' of " ++ @typeName(T) ++
" must be an argument cell — `" ++ f.name ++ ": sdk.Command.Arg(" ++
@typeName(f.type) ++ ", .{ .description = \"…\" })`. Every parameter needs a description.",
fn buildParams(comptime T: type) [@typeInfo(T).@"struct".field_names.len]Param {
const info = @typeInfo(T).@"struct";
var out: [info.field_names.len]Param = undefined;
for (info.field_names, info.field_types, info.field_attrs, 0..) |f_name, Cell, cell_attrs, i| {
if (!settings.isCell(Cell)) @compileError(
"sdk.Command.Params: field '" ++ f_name ++ "' of " ++ @typeName(T) ++
" must be an argument cell — `" ++ f_name ++ ": sdk.Command.Arg(" ++
@typeName(Cell) ++ ", .{ .description = \"…\" })`. Every parameter needs a description.",
);
const opts = f.type.setting_options;
const default: ?f.type.Payload = if (f.defaultValue()) |cell| cell.v else null;
const opts = Cell.setting_options;
const default: ?Cell.Payload = if (cell_attrs.defaultValue(Cell)) |cell| cell.v else null;
out[i] = .{
.key = f.name,
.label = opts.name orelse settings.deriveLabel(f.name),
.key = f_name,
.label = opts.name orelse settings.deriveLabel(f_name),
.description = opts.description,
.kind = settings.kindFor(f.type.Payload, opts),
.kind = settings.kindFor(Cell.Payload, opts),
.required = default == null,
.default = if (default) |d| defaultZon(f.type.Payload, d) else null,
.default = if (default) |d| defaultZon(Cell.Payload, d) else null,
};
}
return out;
Expand All @@ -261,23 +261,20 @@ fn defaultZon(comptime P: type, comptime value: P) ?[]const u8 {

/// `T`'s fields with bare payload types, each keeping its cell's default if it has one.
fn Plain(comptime T: type) type {
const fields = @import("zig017_fields.zig").fields(T);
var names: [fields.len][:0]const u8 = undefined;
var types: [fields.len]type = undefined;
var attrs: [fields.len]std.lang.Type.Struct.FieldAttributes = undefined;
for (fields, 0..) |f, i| {
const P = f.type.Payload;
names[i] = f.name;
const info = @typeInfo(T).@"struct";
var types: [info.field_names.len]type = undefined;
var attrs: [info.field_names.len]std.lang.Type.Struct.FieldAttributes = undefined;
for (info.field_types, info.field_attrs, 0..) |Cell, cell_attrs, i| {
const P = Cell.Payload;
types[i] = P;
attrs[i] = if (f.defaultValue()) |cell| blk: {
attrs[i] = if (cell_attrs.defaultValue(Cell)) |cell| blk: {
const payload_default: P = cell.v;
break :blk .{ .default_value_ptr = @ptrCast(&payload_default) };
} else .{};
}
const frozen_names = names;
const frozen_types = types;
const frozen_attrs = attrs;
return @Struct(.auto, null, &frozen_names, &frozen_types, &frozen_attrs);
return @Struct(.auto, null, info.field_names, &frozen_types, &frozen_attrs);
}

test "Params: descriptors, defaults and parsing" {
Expand Down
33 changes: 33 additions & 0 deletions sdk/src/fingerprint.zig
Original file line number Diff line number Diff line change
Expand Up @@ -331,3 +331,36 @@ test "fingerprint catches function-pointer signature changes" {
const v2 = comptime hashAll(seed, .{V2}, 6);
try std.testing.expect(v1 != v2);
}

test "both hashes are the ones Zig 0.16 gave for the same types" {
// Computed by Zig 0.16 with this file as it was before 0.17 made `@typeInfo` struct-of-arrays
// (fizzy `main` at 7b538aa): the 0.17 loops hash the same facts in the same order. A type of
// every kind the hashes look into, at a depth that reaches them all.
const Tag = enum(u8) { a, b, c };
const Inner = extern struct { x: i16, y: f32 };
const Packed = packed struct { lo: u4, hi: u12 };
const Choice = union(enum) { none, int: i64, pair: [2]u16 };
const Opaque = opaque {};
const Probe = struct {
tag: Tag,
inner: Inner,
bits: Packed,
choice: Choice,
maybe: ?*const Inner,
many: [3]u16,
name: []const u8,
raw: [*]u8,
len: usize,
delta: isize,
ratio: f64,
on: bool,
hook: *const fn (state: *Opaque, n: u32, s: []const u8) callconv(.c) bool,
plain: *const fn (f32) void,
result: ?*const fn (?*anyopaque) callconv(.c) u64,
};
// Fixed widths only: `hashAll` folds in sizes, which this keeps the same on every target.
const Fixed = extern struct { a: u8, b: u32, c: i64, d: [4]u16, e: f32, t: Tag };
@setEvalBranchQuota(1_000_000);
try std.testing.expectEqual(@as(u64, 0x2ae9f3848d7d3d9f), comptime hashAllShape(seed, .{ Probe, Fixed }, 6));
try std.testing.expectEqual(@as(u64, 0xc0b59f8f6303fb84), comptime hashAll(seed, .{Fixed}, 6));
}
Loading
Loading