-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: std.math.{min,max}Int return T #20574
Comments
Just a thought: if #3806 were to be implemented, would it make more sense to change the functions to essentially look like this? pub fn minInt(comptime T: type) @Int(@typeInfo(T).Int.min, @typeInfo(T).Int.min + 1) {
return @typeInfo(T).Int.min;
}
pub fn maxInt(comptime T: type) @Int(@typeInfo(T).Int.max_exclusive - 1, @typeInfo(T).Int.max_exclusive) {
return @typeInfo(T).Int.max_exclusive - 1;
} ( |
What would be the harm in simply allowing a
I'm currently working with ( All that being said, implementing |
This phrasing doesn't make sense, as it talks about bitcasting to an integer, i.e.
Could you elaborate on your use case? I'm struggling to understand these two paragraphs. I don't understand what your hypothetical builtin |
Right, I didn't mean including floats as result or intermediate types (I forgot
I can't think of a case where it would lead to a bug.
No, but it trivially coerces to integer types which do. In my mind these integer types are the canonical bit representation for
Thank you for the clarification, I'll open a separate issue then.
Return a
Here's a demonstration of the difference (even though I'm pretty sure we generally don't like using Zig types to encode comptime-ness this way, so the real solution will instead be #3806):
/// returned type has no runtime bits
fn OPV(x: anytype) type {
return struct {
pub fn get(_: @This()) @TypeOf(x) {
return x;
}
};
}
fn RuntimeValue(X: type) type {
return struct {
data: X,
pub fn get(self: @This()) X {
return self.data;
}
};
}
/// returns an instance of OPV if x is comptime-known, otherwise an instance of RuntimeValue
fn wrap(x: anytype) if (@TypeOf(x) == comptime_int or @TypeOf(x) == u0 or @TypeOf(x) == i0) OPV(x) else RuntimeValue(@TypeOf(x)) {
if (@TypeOf(x) == comptime_int or @TypeOf(x) == u0 or @TypeOf(x) == i0) return .{};
return .{ .data = x };
}
fn runtimeTest(v: anytype, expected: comptime_int) void {
if (v.get() != expected) unreachable;
}
fn comptimeTest(v: anytype, expected: comptime_int) void {
comptime if (v.get() != expected) unreachable; //.get() is comptime-available for OPV, but not for RuntimeValue
}
test {
const maxInt = @import("std").math.maxInt;
const max0 = maxInt(u0);
comptime if (wrap(max0).get() != 0) unreachable;
const w0 = wrap(@as(u0, max0)); //wrap can return OPV because its argument is of a type without runtime bits
runtimeTest(w0, 0);
comptimeTest(w0, 0);
const max8 = maxInt(u8);
comptime if (wrap(max8).get() != 255) unreachable; //allowed - wrap returns an OPV; it can tell its argument is comptime-known by its type
const w8 = wrap(@as(u8, max8)); //wrap has no way to return OPV instead of RuntimeValue for comptime-known argument value
runtimeTest(w8, 255); //checking at runtime is allowed
//comptimeTest(w8, 255); //not allowed, re-enable for compile error
} |
std.math.minInt
andstd.math.maxInt
are useful functions for finding the bounds of an integer. They have a few use cases, with the following being the most common:0
is not appropriateif (x < std.math.minInt(i16) or x > std.max.maxInt(i16)) return error.OutOfBounds;
Looking through uses of these functions in
std
, most usages appear to align with one of the two use cases above. In particular, the first use is incredibly common, particularly formaxInt
; it is used instd.Progress
,std.zig.Zir
,std.c
, and so on. Unfortunately, particularly for this use case, the signature ofmaxInt
can sometimes be awkward. Suppose you are trying to@bitCast
the result ofmaxInt
to apacked struct(u32)
to act as a special value (I have personally tried this a few times in the compiler codebase). Then, sincemaxInt
returns acomptime_int
(rather than au32
), you can't do so without an@as
coercion, i.e.@bitCast(@as(u32, std.math.maxInt(u32)))
!Proposal
Change the signature of
minInt
andmaxInt
topub inline fn minInt(comptime T: type) T
. Theinline
annotation preserves the behavior that the result is comptime-known, so this change is mostly transparent to use sites.From a quick-ish look through all uses of
maxInt
(which is used by far more frequently thanminInt
) in the standard library, I don't think this would break a single use case -- for instance, it doesn't impact the "in-bounds" checks mentioned previously. Meanwhile, it would improve the aforementioned use case of passing the value to@bitCast
. This change also has the minor advantage that it acts as implicit documentation that the result is within the integer's range; i.e. thatmaxInt(u32)
is(1 << 32) - 1
rather than1 << 32
.This seems more-or-less like a no-brainer to me.
The text was updated successfully, but these errors were encountered: