There is a difference in evaluation between a constant and a non-constant expression that arises from exact constants:
Numeric constants are exact values ββof arbitrary precision and do not overflow .
Typed constant expressions cannot overflow; if the result cannot be represented by its type, this is a compile-time error (this can be detected at compile time).
The same does not apply to mutable expressions, since it cannot be detected at compile time (it could only be detected at run time). Operations on variables can overflow.
In your first example, ONE is a typed constant of type int . This is a constant expression:
ONE << (unsafe.Sizeof(x)*8 - 1)
The shift expression is a constant, the following applies: Spec: Constant expressions:
If the left operand of the constant shift expression is an untyped constant, the result is an integer constant; otherwise, it is a constant of the same type as the left operand, which must be an integer type .
So, the result of the shift expression must fit in int , because it is a constant expression; but since it is not, it is a compile-time error.
In your second example, ONE not a constant, it is an int variable. Thus, the shift expression here can and will exceed, which will lead to the expected negative value.
Notes:
If you change ONE in the second example to a constant instead of a variable, you will get the same error (since the expression in the initializer will be a constant expression). If you change ONE to a variable in the first example, it will not work, since variables cannot be used in constant expressions (this must be a constant expression, because it initializes a constant).
Constant expressions for finding min-max values
You can use the following solution, which gives the maximum and minimum values ββof the uint and int types:
const ( MaxUint = ^uint(0) MinUint = 0 MaxInt = int(MaxUint >> 1) MinInt = -MaxInt - 1 ) func main() { fmt.Printf("uint: %d..%d\n", MinUint, MaxUint) fmt.Printf("int: %d..%d\n", MinInt, MaxInt) }
Conclusion (try on the Go Playground ):
uint: 0..4294967295 int: -2147483648..2147483647
The logic behind this lies in Spec: constant expressions:
The mask used by the unitary bitwise complement operator ^ matches the rule for non-constants: the mask is all 1s for unsigned constants and -1 for signed and untyped constants.
Thus, the typed constant expression ^uint(0) is of type uint and is the maximum value of uint : it has all its bits set to 1 . Given that integers are represented using 2 additions : shifting this to the left by 1 , you get the value of max int , from which the value of min int -MaxInt - 1 ( -1 because of the value 0 ).
Reasoning for different behaviors
Why is there no overflow for constant expressions and overflow for non-constant expressions?
The latter is easy: overflow occurs in most other (programming) languages. Thus, this behavior is consistent with other languages ββand has its advantages.
The real question is the first: why overflow for constant expressions is not allowed?
The constants in Go are greater than the values ββof typed variables: they are exact values ββof arbitrary precision. Staying in exact words, if you have a value that you want to assign to a typed constant, which allows you to overflow and assign a completely different value, is not true.
Forward, this type, checking and prohibiting overflow, can break errors like this:
type Char byte var c1 Char = 'a'
What's going on here? c1 Char = 'a' works because 'a' is a rune constant, and rune is an alias for int32 , and 'a' has a numeric value of 97 , which fits into the byte valid range (which is 0..255 ).
But c2 Char = 'δΈ' leads to a compile-time error, since rune 'δΈ' has a numerical value 19990 , which does not fit into byte . If overflow is allowed, your code will compile and assign a 22 numeric value ( '\x16' ) to c2 , but obviously this was not your intention. By not allowing overflow, this error easily breaks during compilation.
To check the results:
var c1 Char = 'a' fmt.Printf("%d %q %c\n", c1, c1, c1) // var c2 Char = 'δΈ' // Compile-time error: constant 19990 overflows Char r := 'δΈ' var c2 Char = Char(r) fmt.Printf("%d %q %c\n", c2, c2, c2)
Conclusion (try on the Go Playground ):
97 'a' a 22 '\x16'
To learn more about constants and their philosophy, read the blog post: Go Blog: Constants
And a few more questions (+ answers) that are related and / or interesting:
Golang: end-to-end int overflow
How does Go do arithmetic on constants?
Find the address of a constant in go
Why do these two float64 have different meanings?
How to change float64 number to uint64 correctly?
Give powers 10 as constants compactly