commit 16cc65242fd6e2fc9883fb87e5e78e1d1641db3e (tree)
parent 400319872ba2fd1707a90db232e9c790450f37eb
Author: Andrew Kelley <andrew@ziglang.org>
Date: Sat, 15 Oct 2022 12:11:55 -0400
Merge pull request #12918 from jacobly0/math-cast-comptime-int
std.math: fix behavior relating to comptime_int arguments
Diffstat:
5 files changed, 65 insertions(+), 6 deletions(-)
diff --git a/build.zig b/build.zig
@@ -554,6 +554,8 @@ fn addCmakeCfgOptionsToExe(
}) catch unreachable);
assert(cfg.lld_include_dir.len != 0);
exe.addIncludePath(cfg.lld_include_dir);
+ exe.addIncludePath(cfg.llvm_include_dir);
+ exe.addLibraryPath(cfg.llvm_lib_dir);
addCMakeLibraryList(exe, cfg.clang_libraries);
addCMakeLibraryList(exe, cfg.lld_libraries);
addCMakeLibraryList(exe, cfg.llvm_libraries);
@@ -684,6 +686,8 @@ const CMakeConfig = struct {
lld_include_dir: []const u8,
lld_libraries: []const u8,
clang_libraries: []const u8,
+ llvm_lib_dir: []const u8,
+ llvm_include_dir: []const u8,
llvm_libraries: []const u8,
dia_guids_lib: []const u8,
};
@@ -745,6 +749,8 @@ fn parseConfigH(b: *Builder, config_h_text: []const u8) ?CMakeConfig {
.lld_include_dir = undefined,
.lld_libraries = undefined,
.clang_libraries = undefined,
+ .llvm_lib_dir = undefined,
+ .llvm_include_dir = undefined,
.llvm_libraries = undefined,
.dia_guids_lib = undefined,
};
@@ -782,6 +788,14 @@ fn parseConfigH(b: *Builder, config_h_text: []const u8) ?CMakeConfig {
.prefix = "#define ZIG_DIA_GUIDS_LIB ",
.field = "dia_guids_lib",
},
+ .{
+ .prefix = "#define ZIG_LLVM_INCLUDE_PATH ",
+ .field = "llvm_include_dir",
+ },
+ .{
+ .prefix = "#define ZIG_LLVM_LIB_PATH ",
+ .field = "llvm_lib_dir",
+ },
// .prefix = ZIG_LLVM_LINK_MODE parsed manually below
};
diff --git a/lib/std/math.zig b/lib/std/math.zig
@@ -1063,10 +1063,11 @@ test "negateCast" {
/// return null.
pub fn cast(comptime T: type, x: anytype) ?T {
comptime assert(@typeInfo(T) == .Int); // must pass an integer
- comptime assert(@typeInfo(@TypeOf(x)) == .Int); // must pass an integer
- if (maxInt(@TypeOf(x)) > maxInt(T) and x > maxInt(T)) {
+ const is_comptime = @TypeOf(x) == comptime_int;
+ comptime assert(is_comptime or @typeInfo(@TypeOf(x)) == .Int); // must pass an integer
+ if ((is_comptime or maxInt(@TypeOf(x)) > maxInt(T)) and x > maxInt(T)) {
return null;
- } else if (minInt(@TypeOf(x)) < minInt(T) and x < minInt(T)) {
+ } else if ((is_comptime or minInt(@TypeOf(x)) < minInt(T)) and x < minInt(T)) {
return null;
} else {
return @intCast(T, x);
@@ -1074,12 +1075,18 @@ pub fn cast(comptime T: type, x: anytype) ?T {
}
test "cast" {
+ try testing.expect(cast(u8, 300) == null);
try testing.expect(cast(u8, @as(u32, 300)) == null);
+ try testing.expect(cast(i8, -200) == null);
try testing.expect(cast(i8, @as(i32, -200)) == null);
+ try testing.expect(cast(u8, -1) == null);
try testing.expect(cast(u8, @as(i8, -1)) == null);
+ try testing.expect(cast(u64, -1) == null);
try testing.expect(cast(u64, @as(i8, -1)) == null);
+ try testing.expect(cast(u8, 255).? == @as(u8, 255));
try testing.expect(cast(u8, @as(u32, 255)).? == @as(u8, 255));
+ try testing.expect(@TypeOf(cast(u8, 255).?) == u8);
try testing.expect(@TypeOf(cast(u8, @as(u32, 255)).?) == u8);
}
diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig
@@ -21,6 +21,9 @@ const debug_safety = false;
/// Returns the number of limbs needed to store `scalar`, which must be a
/// primitive integer value.
+/// Note: A comptime-known upper bound of this value that may be used
+/// instead if `scalar` is not already comptime-known is
+/// `calcTwosCompLimbCount(@typeInfo(@TypeOf(scalar)).Int.bits)`
pub fn calcLimbLen(scalar: anytype) usize {
if (scalar == 0) {
return 1;
@@ -391,7 +394,18 @@ pub const Mutable = struct {
/// Asserts the result fits in `r`. An upper bound on the number of limbs needed by
/// r is `math.max(a.limbs.len, calcLimbLen(scalar)) + 1`.
pub fn addScalar(r: *Mutable, a: Const, scalar: anytype) void {
- var limbs: [calcLimbLen(scalar)]Limb = undefined;
+ // Normally we could just determine the number of limbs needed with calcLimbLen,
+ // but that is not comptime-known when scalar is not a comptime_int. Instead, we
+ // use calcTwosCompLimbCount for a non-comptime_int scalar, which can be pessimistic
+ // in the case that scalar happens to be small in magnitude within its type, but it
+ // is well worth being able to use the stack and not needing an allocator passed in.
+ // Note that Mutable.init still sets len to calcLimbLen(scalar) in any case.
+ const limb_len = comptime switch (@typeInfo(@TypeOf(scalar))) {
+ .ComptimeInt => calcLimbLen(scalar),
+ .Int => |info| calcTwosCompLimbCount(info.bits),
+ else => @compileError("expected scalar to be an int"),
+ };
+ var limbs: [limb_len]Limb = undefined;
const operand = init(&limbs, scalar).toConst();
return add(r, a, operand);
}
@@ -2303,7 +2317,18 @@ pub const Const = struct {
/// Same as `order` but the right-hand operand is a primitive integer.
pub fn orderAgainstScalar(lhs: Const, scalar: anytype) math.Order {
- var limbs: [calcLimbLen(scalar)]Limb = undefined;
+ // Normally we could just determine the number of limbs needed with calcLimbLen,
+ // but that is not comptime-known when scalar is not a comptime_int. Instead, we
+ // use calcTwosCompLimbCount for a non-comptime_int scalar, which can be pessimistic
+ // in the case that scalar happens to be small in magnitude within its type, but it
+ // is well worth being able to use the stack and not needing an allocator passed in.
+ // Note that Mutable.init still sets len to calcLimbLen(scalar) in any case.
+ const limb_len = comptime switch (@typeInfo(@TypeOf(scalar))) {
+ .ComptimeInt => calcLimbLen(scalar),
+ .Int => |info| calcTwosCompLimbCount(info.bits),
+ else => @compileError("expected scalar to be an int"),
+ };
+ var limbs: [limb_len]Limb = undefined;
const rhs = Mutable.init(&limbs, scalar);
return order(lhs, rhs.toConst());
}
diff --git a/lib/std/math/big/int_test.zig b/lib/std/math/big/int_test.zig
@@ -573,7 +573,7 @@ test "big.int add sign" {
try testing.expect((try a.to(i32)) == -3);
}
-test "big.int add scalar" {
+test "big.int add comptime scalar" {
var a = try Managed.initSet(testing.allocator, 50);
defer a.deinit();
@@ -584,6 +584,17 @@ test "big.int add scalar" {
try testing.expect((try b.to(u32)) == 55);
}
+test "big.int add scalar" {
+ var a = try Managed.initSet(testing.allocator, 123);
+ defer a.deinit();
+
+ var b = try Managed.init(testing.allocator);
+ defer b.deinit();
+ try b.addScalar(&a, @as(u32, 31));
+
+ try testing.expect((try b.to(u32)) == 154);
+}
+
test "big.int addWrap single-single, unsigned" {
var a = try Managed.initSet(testing.allocator, maxInt(u17));
defer a.deinit();
diff --git a/src/stage1/config.h.in b/src/stage1/config.h.in
@@ -22,6 +22,8 @@
#define ZIG_LLD_INCLUDE_PATH "@LLD_INCLUDE_DIRS@"
#define ZIG_LLD_LIBRARIES "@LLD_LIBRARIES@"
#define ZIG_CLANG_LIBRARIES "@CLANG_LIBRARIES@"
+#define ZIG_LLVM_INCLUDE_PATH "@LLVM_INCLUDE_DIRS@"
+#define ZIG_LLVM_LIB_PATH "@LLVM_LIBDIRS@"
#define ZIG_LLVM_LIBRARIES "@LLVM_LIBRARIES@"
#define ZIG_DIA_GUIDS_LIB "@ZIG_DIA_GUIDS_LIB_ESCAPED@"