The minmax modernizer rewrites if/else patterns into calls to min/max, but this changes evaluation semantics when the arguments have side effects. In the if/else form, only one branch executes; with max(a, b), both arguments are always evaluated.
Before (go fix):
package main
import "fmt"
func main() {
n := 0
f := func() int { n++; return n }
x := f()
if x < f() {
x = f()
}
fmt.Println(x)
}
Output: 3 (f called 3 times: once for initial x=1, once for comparison yielding 2, once in the branch yielding x=3)
After (GOTOOLCHAIN=go1.26.0 go fix):
package main
import "fmt"
func main() {
n := 0
f := func() int { n++; return n }
x := max(f(), f())
fmt.Println(x)
}
Output: 2 (f called only twice: max(1, 2) = 2)
The original code calls f() three times (initial assignment, comparison, and branch), while the modernized code calls f() only twice. The modernizer should not apply the transformation when the expressions being compared have side effects.
CC @adonovan
The
minmaxmodernizer rewritesif/elsepatterns into calls tomin/max, but this changes evaluation semantics when the arguments have side effects. In theif/elseform, only one branch executes; withmax(a, b), both arguments are always evaluated.Before (
go fix):Output:
3(f called 3 times: once for initial x=1, once for comparison yielding 2, once in the branch yielding x=3)After (
GOTOOLCHAIN=go1.26.0 go fix):Output:
2(f called only twice: max(1, 2) = 2)The original code calls
f()three times (initial assignment, comparison, and branch), while the modernized code callsf()only twice. The modernizer should not apply the transformation when the expressions being compared have side effects.CC @adonovan