Weird state for int after multiplication with 0
Acton Version
0.24.1.20250219.1.43.32
Steps to Reproduce and Observed Behavior
Multiplication by 0 produces a bigint 0 value that is in an odd state that prevents it from converting to u32 correctly.
foo = 6 * 0
foo32 = u32(foo)
Unhandled exception in actor: u32_bug.main[-12]:
ValueError: u32(): value 0 out of range for type u32
The value compares equal to a literal 0 and replacing it with a literal 0 causes it to convert to u32 correctly.
foo = 6 * 0
# injecting this conditional gets rid of the exception
if foo == 0:
foo = 0
foo32 = u32(foo)
Expected Behavior
Multiplying by 0 should produce a 0 value completely equivalent to a literal 0.
@sydow I think this is for you!
in u32 (and presumably other uX) we check size of the int, which I think is the number of limbs.
B_u32 B_u32G_new(B_atom a, B_int base) {
B_int b = B_intG_new(a, base);
unsigned long n = b->val.n[0];
long sz = b->val.size;
if (sz > 1 || sz < 0 || n > UINT_MAX) {
char errmsg[1024];
snprintf(errmsg, sizeof(errmsg), "u32(): value %s out of range for type u32",get_str(&b->val));
$RAISE((B_BaseException)$NEW(B_ValueError,to$str(errmsg)));
}
return toB_u32(n);
}
since we did a multiplication just before, I presume that we have allocated extra limbs in order to hold the potentially larger result. The result is in fact not multi-limb but we haven't shrunk down for some reason? not sure what the intention is...
I cannot reproduce this (using actonc version 0.24.1.20250219.16.4.30). Extending to a complete program
foo = 6 * 0
foo32 = u32(foo)
actor main(env):
print(foo, foo32)
env.exit(0)
it compiles and runs correctly (printing 0 0). Moving the two assignments to the actor body doesn't change anything.
I have reproduced it on my end with actonc 0.24.1.20250220.12.11.46, even after a cache purge, running actonc on the following complete source file:
actor main(env):
foo = 6 * 0
#if foo == 0:
# foo = 0
foo32 = u32(foo)
print(foo, foo32)
env.exit(0)
Here is this output of ldd with the resulting executable, on the off-chance that it becomes relevant:
linux-vdso.so.1 (0x00007ffd53ad5000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007b171c3fc000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007b171c3f7000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007b171c000000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007b171c3f2000)
/lib64/ld-linux-x86-64.so.2 (0x00007b171c4ed000)
I added some debug information to the exception message producing the following puzzling result:
Unhandled exception in actor: u32_bug.main[-12]:
ValueError: u32(): value 0 out of range for type u32 (size 0) (sz > 1: 0) (sz < 0: 0) (n > UINT_MAX: 1) (n: 123506963419088) (UINT_MAX: 18446744073709551615)
Interesting!
I note three things:
-
I would have expected the constant UINT_MAX to be 2^32-1, but on your system it is 2^64-1 (which I would expect to be the value of ULONG_MAX). I guess this shows that we must change to names that include bit length, i.e. UINT32_MAX and UINT64_MAX, etc. We have known that for long,...
-
It is still puzzling that n > UINT_MAX returns 1 with the given values for n and UINT_MAX. The LHS is an UINT64 and the RHS an UINT32. But it isn't on your system... Hmm; I love C.
-
There is an additional logical error in the code for C function u32: we should only check if n>UINT32_MAX if sz > 0 (or, equivalently if sz==1). If sz==0, n can have whatever value. Phuuh!
I will change this in all u64, u32, u16, i64, i32 and i16 tomorrow. I hope stdint.h gives these MAX and MIN constants on all plarforms? We should of course change int, short and long to properly sized types everywhere, but I don't promise to do that now.
And I need to understand how to do the check if n>UINT32_MAX, etc.
I would like to suggest that we add assert statements to like all of these functions. I think we would catch a lot of these kind of things so much quicker. The more asserts the better. Document our assumptions using asserts