-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
unable to infer expression type when adding u32 to if-else number literals #137
Comments
Also this should work: const x = u32(switch (foo) {
3 => 1,
4 => 2,
else => 5,
}); |
i had some problems when defining consts and then using them to make values for variables :
i resolved this casting the const to the appropriate type every time i need to use them. in the code example here, i think the compiler does not know what type to use for TINY_QUANTUM_SIZE, so it cant do the negation and the and operation on 32 nor 64 bits, same with any other bitwise operator, I would be a good idea to define literals as java/c++ does :
|
@thejoshwolfe I don't currently have an elegant way to solve this problem. Do you know how it should work? Currently there is no concept of "expected type". Instead there is implicit casting (passing null to ?i32 parameter) and peer type resolution ( fn foo(b: bool) {
const a = if (b) 1 else 2;
bar(a)
} What should the above code do? |
My guess would be that the type of |
Consider this code: fn foo(condition: bool) {
const a = if(condition) 50 else 100; // narrowest signed type is i8
const b = a + 100; // crashes the program due to integer overflow
} This seems like it would be really easy to accidentally cause a crash (or undefined behavior in release mode). Also, I'm considering adding integers with arbitrary bit width, so then it would make the choice kind of weird. If the numbers are 0 and 1, do we choose only 1 bit? |
Despite the Unfortunately, the proposal in the above paragraph would mean that the expression Java has a solution to this dilemma, which is assume number literals are s32. this works great for casual math, but breaks down when you deal with arithmetic that would overflow s32 in subtle ways, and it's also pretty silly when you try to pass a 0 as a byte argument: you get a "int not implicitly castable to byte" compile error unless you explicitly cast So here's a different proposal. Similar to the integer literal type that zig currently uses at compile time for constants like
Note that despite the presence of negative numbers in an expression, the concept of signed or unsigned in the inputs does not infect the output. When it comes time to implicitly cast one of these bounded integers to a runtime type, like u8, then the rule is simply that both the bounds have to fit into the destination type. For peer type resolution with a runtime type (like u32), the bounded integer must implicitly cast to the peer type. A suggestion for implementing this is that the current concept of number literals can be implemented as "integer between 15 and 15". This would fit nicely into a case where you multiply a bounded integer by 0. I tried to write down the algorithms for how the (min,max) would be calculated for various operators, but the multiplicative ones are pretty tricky. Worst case for any operator is simply calculate all 4 possible values and pick the min and max of the set. This entire proposal would only work on the assumption that more extreme inputs result in more extreme outputs for all operators, which I believe is true as long as overflow is not allowed. We may want to carefully study every single operator individually to make sure this is true. The above describes how the compiler would track the bounds of the values, but what about the generated runtime code? All operators need types at runtime. We can prevent overflow by enforcing that the inputs and output of every intermediate operation fit into some runtime type, and then using appropriate operators and type casts to make it all work. I'm not super confident about this, but I think it will work. One implication of this is that bounded integers are not allowed to be arbitrary precision; they all need to work at runtime. This might be an argument for not implementing number literals as bounded integers with equal upper and lower bounds. This proposal cannot accurately predict when the runtime will do integer division by 0. But as long as that's undefined behavior, we shouldn't have any problem with an inability to detect that. (Of course, we could still detect if you would definitely divide by 0, just like we can now.) |
Asked to put this here by Andrew... Examples of possibly troublesome type unifications.
|
- bitlen - abs - neg - isZero - sign - popcount
I want to note that rust has not solved this problem, because their number literals are of type fn thing(x: i32) {
let y = 2147483646 + match x {
3 => 1,
4 => 2,
_ => 3
};
println!("{}", y);
}
fn main() {
thing(1);
}
Zig at least catches this with a compile error. |
does not resolve
does I think I was stuck on assuming a character literal was u8 since a string literal is [*]const u8.
Might be better worded that the type must be known. The value is obviously known since it is literal. FYI the elm programming language is exemplary in compiler error messages that provide guidance. |
Related: #2749 |
Related: var i: isize = 4; // adjust
var x: E = if(i < 0) .A else .B where |
I presumed the same as well; you are not alone. This is unexpected and not consistent. |
Yikes, a 5+ year old issue! Doesn't every newcomer run into this on the first day? My recommendations for addressing this issue in priority order:
The following code "day one" code breaks in Zig:
The current error, cannot store runtime value in type 'comptime__int', makes no sense: the error occurs inside a pure expression, there is no storage. Unfortunately, the most likely naive interpretation is that this error message refers to the initialization of the constant (conflating 'comptime' with 'const', and 'storage' with 'initilization'). This creates a garden path which misinforms beginners that, "constants can't be initialized with runtime expressions" (untrue). In fact Zig does support such expressions, the real issue is that the compiler (currently) requires a type annotation on some literals with unresolved types:
This error is surprising, since the programmer understands that every leaf in the expression tree is a Even a slight variations such as annotating the
Finally, it should be possible work around this issue without a cast, but I could not find a syntax for type annotation in the Zig docs. Something like this:
Type annotations can be a very useful sanity checks for a programmer, even when they are not explicitly required by the compiler. Sorry for the long message. Thanks for all the hard work! I'm enjoying exploring Zig. |
Regarding "Finally, it should be possible work around this issue without a cast, but I could not find a syntax for type annotation in the Zig docs.".
const oflags =
std.os.O_CLOEXEC |
std.os.O_NOATIME |
if (flags.read_only) @as(u32, std.os.O_RDONLY) else (std.os.O_RDWR | std.os.O_CREAT); |
Good. In the semantics of Zig, I was wrong to say literals are unresolved types (although maybe they should be), so a cast does makes sense, and Type annotation would be a useful tool as an assertion to make sure the programmer and the type checker agree on what is going on without casting, but that is a separate issue. |
|
The text was updated successfully, but these errors were encountered: