math.MaxInt64 and friends are untyped constants. This causes confusion when using the constants on 32-bit systems. For example, on a 32-bit system,
var V = math.MaxInt64
does not compile: constant 9223372036854775807 overflows int.
This confusion has been reported at #19621 and #23086.
This can not be changed for Go 1 without breaking backward compatibility. For Go 2 we could use explicit types for the constants, so that the type of math.MaxInt64 is int64, and so forth. I believe that would cause less confusion in the long run.
It could, however, break currently working code that expects values like math.MaxInt8 to take on type int. In this proposal they would be type int8. So some existing code may need to be changed to add explicit conversions to int. It should be possible to automate this conversion using go fix.
Wouldn't this change break some optimizations that rely on math.MaxInt64 being a const? If it is a mutable global variable, the compiler can no longer perform certain classes of value-range propagation optimizations, right?
@dsnet, he's proposing making it a typed constant, not a variable.
Ah, my mistake. I saw the var V = math.MaxInt64 and got confused.
Untyped constants are valuable in computing values of expressions composed of other untyped constants and within quite large limits it's guaranteed to always work. var foo = anyUntypedConstant, OTOH, was never guaranteed nor IMHO intended to always work.
What about implicit constants ?
n := 1
n &= 0xFFFF0000
@cznic Do you have any examples from real code?
@flanglet I'm not sure what you mean but that seems like a different problem. I'm talking very specifically about the constants defined in the math package.
This seems like something to be solved by a linter.
@jimmyfrasche Are you talking about the original problem in this issue? There is no need for a linter for that; the compiler gives an error. This proposal is a suggestion for a way to remove that error as a stumbling block for people just learning how to use Go. I can certainly understand the argument that this isn't worth changing, but a linter won't help.
A linter could say "this works on your computer because it's 64bit but won't compile on 32bit arches because that constant will overflow int". That would at least help with people creating accidentally 64bit only programs, at least.
Maybe that and a better error message like: constant 18446744073709551615 (requires 64 bit integer) overflows int (32 bit integer)
@ianlancetaylor isn't this something the compiler could produce a better error message with instead of using typed constants? It's quite nice—and IMO clearer—being able to omit the type conversions.
The confusion seems to come from
I think a better error message should
int/uint for the target GOARCH when they're involvedThose are all orthogonal and each useful in its own right. In the case of var V = math.MaxInt64 they'd combine into something like
untyped constant math.MaxInt64 implicitly converted to int but overflows int (32 bit)
That makes it much clearer what's going on and why.
If you don't know all of the rules at play resulting in that error it gives you all the information you need to go look them up.
Even if you do know all that, it's still much friendlier and requires less thought to see where you went wrong. Error messages shouldn't make you think "huh?"—they should make you think "oh, duh!"
I'm sure that those would require extra bookkeeping or backtracking in the compiler and that some of those enhancements would be easier than others but they seem like they'd go a long way to making all errors involving untyped constants easier for everyone to deal with.
Another: #23163
This proposal seems like it would break comparisons against smaller-than-64-bit constants.
For example, at the moment you can check the value of an int easily before converting it to an int32:
x, err := strconv.Atoi(…)
if err != nil {
return …
}
if x > math.MaxInt32 || x < math.MinInt32 {
return …
}
x32 := int32(x)
On the other hand, if math.MaxInt32 is an int32, the condition stutters on the type:
if x > int(math.MaxInt32) || x < int(math.MinInt32) {
There are some other ways to address that stutter, of course. We could allow non-narrowing integer conversions (e.g. int32 to int) to occur implicitly, or by make it easier to do narrowing range-checks by some other means (e.g. the , ok conversions proposed in https://github.com/golang/go/issues/19624).
Still, I think the impact on range checks is likely to be a significant downside.
The arguments above seem convincing. Closing in favor of better compiler error messages.
Most helpful comment
The confusion seems to come from
I think a better error message should
int/uintfor the target GOARCH when they're involvedThose are all orthogonal and each useful in its own right. In the case of
var V = math.MaxInt64they'd combine into something likeThat makes it much clearer what's going on and why.
If you don't know all of the rules at play resulting in that error it gives you all the information you need to go look them up.
Even if you do know all that, it's still much friendlier and requires less thought to see where you went wrong. Error messages shouldn't make you think "huh?"—they should make you think "oh, duh!"
I'm sure that those would require extra bookkeeping or backtracking in the compiler and that some of those enhancements would be easier than others but they seem like they'd go a long way to making all errors involving untyped constants easier for everyone to deal with.