mirror of https://github.com/golang/go.git
go/types, types2: report error for floating-point iteration variable
While at it, slightly improve documentation and code. Also, add additional test cases for #66561. Updates #66561. Fixes #67027. Change-Id: I682b0e9227e065d6bbd199871c2e1ecff13edc66 Reviewed-on: https://go-review.googlesource.com/c/go/+/580937 Reviewed-by: Robert Griesemer <gri@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Robert Findley <rfindley@google.com> Auto-Submit: Robert Griesemer <gri@google.com>
This commit is contained in:
parent
a523152ea1
commit
26ed0528c7
|
|
@ -898,7 +898,7 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
|
|||
lhs := [2]Expr{sKey, sValue} // sKey, sValue may be nil
|
||||
rhs := [2]Type{key, val} // key, val may be nil
|
||||
|
||||
constIntRange := x.mode == constant_ && isInteger(x.typ)
|
||||
rangeOverInt := isInteger(x.typ)
|
||||
|
||||
if isDef {
|
||||
// short variable declaration
|
||||
|
|
@ -933,14 +933,15 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
|
|||
continue
|
||||
}
|
||||
|
||||
// initialize lhs variable
|
||||
if constIntRange {
|
||||
if rangeOverInt {
|
||||
assert(i == 0) // at most one iteration variable (rhs[1] == nil for rangeOverInt)
|
||||
check.initVar(obj, &x, "range clause")
|
||||
} else {
|
||||
x.mode = value
|
||||
x.expr = lhs // we don't have a better rhs expression to use here
|
||||
x.typ = typ
|
||||
check.initVar(obj, &x, "assignment") // error is on variable, use "assignment" not "range clause"
|
||||
var y operand
|
||||
y.mode = value
|
||||
y.expr = lhs // we don't have a better rhs expression to use here
|
||||
y.typ = typ
|
||||
check.initVar(obj, &y, "assignment") // error is on variable, use "assignment" not "range clause"
|
||||
}
|
||||
assert(obj.typ != nil)
|
||||
}
|
||||
|
|
@ -967,21 +968,30 @@ func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *s
|
|||
continue
|
||||
}
|
||||
|
||||
if constIntRange {
|
||||
if rangeOverInt {
|
||||
assert(i == 0) // at most one iteration variable (rhs[1] == nil for rangeOverInt)
|
||||
check.assignVar(lhs, nil, &x, "range clause")
|
||||
// If the assignment succeeded, if x was untyped before, it now
|
||||
// has a type inferred via the assignment. It must be an integer.
|
||||
// (go.dev/issues/67027)
|
||||
if x.mode != invalid && !isInteger(x.typ) {
|
||||
check.softErrorf(lhs, InvalidRangeExpr, "cannot use iteration variable of type %s", x.typ)
|
||||
}
|
||||
} else {
|
||||
x.mode = value
|
||||
x.expr = lhs // we don't have a better rhs expression to use here
|
||||
x.typ = typ
|
||||
check.assignVar(lhs, nil, &x, "assignment") // error is on variable, use "assignment" not "range clause"
|
||||
var y operand
|
||||
y.mode = value
|
||||
y.expr = lhs // we don't have a better rhs expression to use here
|
||||
y.typ = typ
|
||||
check.assignVar(lhs, nil, &y, "assignment") // error is on variable, use "assignment" not "range clause"
|
||||
}
|
||||
}
|
||||
} else if constIntRange {
|
||||
} else if rangeOverInt {
|
||||
// If we don't have any iteration variables, we still need to
|
||||
// check that a (possibly untyped) integer range expression x
|
||||
// is valid.
|
||||
// We do this by checking the assignment _ = x. This ensures
|
||||
// that an untyped x can be converted to a value of type int.
|
||||
// that an untyped x can be converted to a value of its default
|
||||
// type (rune or int).
|
||||
check.assignment(&x, nil, "range clause")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -898,7 +898,7 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) {
|
|||
lhs := [2]Expr{sKey, sValue} // sKey, sValue may be nil
|
||||
rhs := [2]Type{key, val} // key, val may be nil
|
||||
|
||||
constIntRange := x.mode == constant_ && isInteger(x.typ)
|
||||
rangeOverInt := isInteger(x.typ)
|
||||
|
||||
if isDef {
|
||||
// short variable declaration
|
||||
|
|
@ -933,14 +933,15 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) {
|
|||
continue
|
||||
}
|
||||
|
||||
// initialize lhs variable
|
||||
if constIntRange {
|
||||
if rangeOverInt {
|
||||
assert(i == 0) // at most one iteration variable (rhs[1] == nil for rangeOverInt)
|
||||
check.initVar(obj, &x, "range clause")
|
||||
} else {
|
||||
x.mode = value
|
||||
x.expr = lhs // we don't have a better rhs expression to use here
|
||||
x.typ = typ
|
||||
check.initVar(obj, &x, "assignment") // error is on variable, use "assignment" not "range clause"
|
||||
var y operand
|
||||
y.mode = value
|
||||
y.expr = lhs // we don't have a better rhs expression to use here
|
||||
y.typ = typ
|
||||
check.initVar(obj, &y, "assignment") // error is on variable, use "assignment" not "range clause"
|
||||
}
|
||||
assert(obj.typ != nil)
|
||||
}
|
||||
|
|
@ -967,21 +968,30 @@ func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) {
|
|||
continue
|
||||
}
|
||||
|
||||
if constIntRange {
|
||||
if rangeOverInt {
|
||||
assert(i == 0) // at most one iteration variable (rhs[1] == nil for rangeOverInt)
|
||||
check.assignVar(lhs, nil, &x, "range clause")
|
||||
// If the assignment succeeded, if x was untyped before, it now
|
||||
// has a type inferred via the assignment. It must be an integer.
|
||||
// (go.dev/issues/67027)
|
||||
if x.mode != invalid && !isInteger(x.typ) {
|
||||
check.softErrorf(lhs, InvalidRangeExpr, "cannot use iteration variable of type %s", x.typ)
|
||||
}
|
||||
} else {
|
||||
x.mode = value
|
||||
x.expr = lhs // we don't have a better rhs expression to use here
|
||||
x.typ = typ
|
||||
check.assignVar(lhs, nil, &x, "assignment") // error is on variable, use "assignment" not "range clause"
|
||||
var y operand
|
||||
y.mode = value
|
||||
y.expr = lhs // we don't have a better rhs expression to use here
|
||||
y.typ = typ
|
||||
check.assignVar(lhs, nil, &y, "assignment") // error is on variable, use "assignment" not "range clause"
|
||||
}
|
||||
}
|
||||
} else if constIntRange {
|
||||
} else if rangeOverInt {
|
||||
// If we don't have any iteration variables, we still need to
|
||||
// check that a (possibly untyped) integer range expression x
|
||||
// is valid.
|
||||
// We do this by checking the assignment _ = x. This ensures
|
||||
// that an untyped x can be converted to a value of type int.
|
||||
// that an untyped x can be converted to a value of its default
|
||||
// type (rune or int).
|
||||
check.assignment(&x, nil, "range clause")
|
||||
}
|
||||
|
||||
|
|
@ -1011,6 +1021,7 @@ func rangeKeyVal(typ Type, allowVersion func(goVersion) bool) (key, val Type, ca
|
|||
return Typ[Int], universeRune, "", false, true // use 'rune' name
|
||||
}
|
||||
if isInteger(typ) {
|
||||
// untyped numeric constants may be representable as integer values
|
||||
if allowVersion != nil && !allowVersion(go1_22) {
|
||||
return bad("requires go1.22 or later")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -953,10 +953,10 @@ func issue10148() {
|
|||
for y /* ERROR "declared and not used" */ := range "" {
|
||||
_ = "" /* ERROR "mismatched types untyped string and untyped int" */ + 1
|
||||
}
|
||||
for range 1.5 /* ERROR "cannot range over 1.5" */ {
|
||||
for range 1.5 /* ERROR "cannot range over 1.5 (untyped float constant)" */ {
|
||||
_ = "" /* ERROR "mismatched types untyped string and untyped int" */ + 1
|
||||
}
|
||||
for y := range 1.5 /* ERROR "cannot range over 1.5" */ {
|
||||
for y := range 1.5 /* ERROR "cannot range over 1.5 (untyped float constant)" */ {
|
||||
_ = "" /* ERROR "mismatched types untyped string and untyped int" */ + 1
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,3 +129,62 @@ func issue65133() {
|
|||
for u8 = range 256 /* ERROR "cannot use 256 (untyped int constant) as uint8 value in range clause (overflows)" */ {
|
||||
}
|
||||
}
|
||||
|
||||
func issue64471() {
|
||||
for i := range 'a' {
|
||||
var _ *rune = &i // ensure i has type rune
|
||||
}
|
||||
}
|
||||
|
||||
func issue66561() {
|
||||
for range 10.0 /* ERROR "cannot range over 10.0 (untyped float constant 10)" */ {
|
||||
}
|
||||
for range 1e3 /* ERROR "cannot range over 1e3 (untyped float constant 1000)" */ {
|
||||
}
|
||||
for range 1 /* ERROR "cannot range over 1 + 0i (untyped complex constant (1 + 0i))" */ + 0i {
|
||||
}
|
||||
|
||||
for range 1.1 /* ERROR "cannot range over 1.1 (untyped float constant)" */ {
|
||||
}
|
||||
for range 1i /* ERROR "cannot range over 1i (untyped complex constant (0 + 1i))" */ {
|
||||
}
|
||||
|
||||
for i := range 10.0 /* ERROR "cannot range over 10.0 (untyped float constant 10)" */ {
|
||||
_ = i
|
||||
}
|
||||
for i := range 1e3 /* ERROR "cannot range over 1e3 (untyped float constant 1000)" */ {
|
||||
_ = i
|
||||
}
|
||||
for i := range 1 /* ERROR "cannot range over 1 + 0i (untyped complex constant (1 + 0i))" */ + 0i {
|
||||
_ = i
|
||||
}
|
||||
|
||||
for i := range 1.1 /* ERROR "cannot range over 1.1 (untyped float constant)" */ {
|
||||
_ = i
|
||||
}
|
||||
for i := range 1i /* ERROR "cannot range over 1i (untyped complex constant (0 + 1i))" */ {
|
||||
_ = i
|
||||
}
|
||||
|
||||
var j float64
|
||||
_ = j
|
||||
for j /* ERROR "cannot use iteration variable of type float64" */ = range 1 {
|
||||
}
|
||||
for j = range 1.1 /* ERROR "cannot range over 1.1 (untyped float constant)" */ {
|
||||
}
|
||||
for j = range 10.0 /* ERROR "cannot range over 10.0 (untyped float constant 10)" */ {
|
||||
}
|
||||
|
||||
// There shouldn't be assignment errors if there are more iteration variables than permitted.
|
||||
var i int
|
||||
_ = i
|
||||
for i, j /* ERROR "range over 10 (untyped int constant) permits only one iteration variable" */ = range 10 {
|
||||
}
|
||||
}
|
||||
|
||||
func issue67027() {
|
||||
var i float64
|
||||
_ = i
|
||||
for i /* ERROR "cannot use iteration variable of type float64" */ = range 10 {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue