// assignVar checks the assignment lhs = rhs (if x == nil), or lhs = x (if x != nil).
// If x != nil, it must be the evaluation of rhs (and rhs will be ignored).
// If the assignment check fails and x != nil, x.mode is set to invalid.
-func (check *Checker) assignVar(lhs, rhs syntax.Expr, x *operand) {
+func (check *Checker) assignVar(lhs, rhs syntax.Expr, x *operand, context string) {
T := check.lhsVar(lhs) // nil if lhs is _
if !isValid(T) {
if x != nil {
check.expr(target, x, rhs)
}
- context := "assignment"
- if T == nil {
+ if T == nil && context == "assignment" {
context = "assignment to _ identifier"
}
check.assignment(x, T, context)
// each value can be assigned to its corresponding variable.
if l == r && !isCall {
for i, lhs := range lhs {
- check.assignVar(lhs, orig_rhs[i], nil)
+ check.assignVar(lhs, orig_rhs[i], nil, "assignment")
}
return
}
r = len(rhs)
if l == r {
for i, lhs := range lhs {
- check.assignVar(lhs, nil, rhs[i])
+ check.assignVar(lhs, nil, rhs[i], "assignment")
}
// Only record comma-ok expression if both assignments succeeded
// (go.dev/issue/59371).
check.errorf(s.Lhs, NonNumericIncDec, invalidOp+"%s%s%s (non-numeric type %s)", s.Lhs, s.Op, s.Op, x.typ)
return
}
- check.assignVar(s.Lhs, nil, &x)
+ check.assignVar(s.Lhs, nil, &x, "assignment")
return
}
var x operand
check.binary(&x, nil, lhs[0], rhs[0], s.Op)
- check.assignVar(lhs[0], nil, &x)
+ check.assignVar(lhs[0], nil, &x, "assignment")
case *syntax.CallStmt:
kind := "go"
func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *syntax.RangeClause) {
// Convert syntax form to local variables.
- type expr = syntax.Expr
+ type Expr = syntax.Expr
type identType = syntax.Name
identName := func(n *identType) string { return n.Value }
sKey := rclause.Lhs // possibly nil
// (irregular assignment, cannot easily map to existing assignment checks)
// lhs expressions and initialization value (rhs) types
- lhs := [2]expr{sKey, sValue}
- rhs := [2]Type{key, val} // key, val may be nil
+ 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)
if isDef {
// short variable declaration
}
// initialize lhs variable
- if typ := rhs[i]; typ != nil {
+ if constIntRange {
+ check.initVar(obj, &x, "range clause")
+ } else if typ := rhs[i]; typ != nil {
x.mode = value
x.expr = lhs // we don't have a better rhs expression to use here
x.typ = typ
- check.initVar(obj, &x, "range clause")
+ check.initVar(obj, &x, "assignment") // error is on variable, use "assignment" not "range clause"
} else {
obj.typ = Typ[Invalid]
obj.used = true // don't complain about unused variable
} else {
check.error(noNewVarPos, NoNewVar, "no new variables on left side of :=")
}
- } else {
+ } else if sKey != nil /* lhs[0] != nil */ {
// ordinary assignment
for i, lhs := range lhs {
if lhs == nil {
continue
}
- if typ := rhs[i]; typ != nil {
+
+ if constIntRange {
+ check.assignVar(lhs, nil, &x, "range clause")
+ } else if typ := rhs[i]; typ != nil {
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)
+ check.assignVar(lhs, nil, &x, "assignment") // error is on variable, use "assignment" not "range clause"
}
}
+ } else if constIntRange {
+ // 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.
+ check.assignment(&x, nil, "range clause")
}
check.stmt(inner, s.Body)
// assignVar checks the assignment lhs = rhs (if x == nil), or lhs = x (if x != nil).
// If x != nil, it must be the evaluation of rhs (and rhs will be ignored).
// If the assignment check fails and x != nil, x.mode is set to invalid.
-func (check *Checker) assignVar(lhs, rhs ast.Expr, x *operand) {
+func (check *Checker) assignVar(lhs, rhs ast.Expr, x *operand, context string) {
T := check.lhsVar(lhs) // nil if lhs is _
if !isValid(T) {
if x != nil {
check.expr(target, x, rhs)
}
- context := "assignment"
- if T == nil {
+ if T == nil && context == "assignment" {
context = "assignment to _ identifier"
}
check.assignment(x, T, context)
// each value can be assigned to its corresponding variable.
if l == r && !isCall {
for i, lhs := range lhs {
- check.assignVar(lhs, orig_rhs[i], nil)
+ check.assignVar(lhs, orig_rhs[i], nil, "assignment")
}
return
}
r = len(rhs)
if l == r {
for i, lhs := range lhs {
- check.assignVar(lhs, nil, rhs[i])
+ check.assignVar(lhs, nil, rhs[i], "assignment")
}
// Only record comma-ok expression if both assignments succeeded
// (go.dev/issue/59371).
if x.mode == invalid {
return
}
- check.assignVar(s.X, nil, &x)
+ check.assignVar(s.X, nil, &x, "assignment")
case *ast.AssignStmt:
switch s.Tok {
if x.mode == invalid {
return
}
- check.assignVar(s.Lhs[0], nil, &x)
+ check.assignVar(s.Lhs[0], nil, &x, "assignment")
}
case *ast.GoStmt:
func (check *Checker) rangeStmt(inner stmtContext, s *ast.RangeStmt) {
// Convert go/ast form to local variables.
- type expr = ast.Expr
+ type Expr = ast.Expr
type identType = ast.Ident
identName := func(n *identType) string { return n.Name }
sKey, sValue := s.Key, s.Value
// (irregular assignment, cannot easily map to existing assignment checks)
// lhs expressions and initialization value (rhs) types
- lhs := [2]expr{sKey, sValue}
- rhs := [2]Type{key, val} // key, val may be nil
+ 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)
if isDef {
// short variable declaration
}
// initialize lhs variable
- if typ := rhs[i]; typ != nil {
+ if constIntRange {
+ check.initVar(obj, &x, "range clause")
+ } else if typ := rhs[i]; typ != nil {
x.mode = value
x.expr = lhs // we don't have a better rhs expression to use here
x.typ = typ
- check.initVar(obj, &x, "range clause")
+ check.initVar(obj, &x, "assignment") // error is on variable, use "assignment" not "range clause"
} else {
obj.typ = Typ[Invalid]
obj.used = true // don't complain about unused variable
} else {
check.error(noNewVarPos, NoNewVar, "no new variables on left side of :=")
}
- } else {
+ } else if sKey != nil /* lhs[0] != nil */ {
// ordinary assignment
for i, lhs := range lhs {
if lhs == nil {
continue
}
- if typ := rhs[i]; typ != nil {
+
+ if constIntRange {
+ check.assignVar(lhs, nil, &x, "range clause")
+ } else if typ := rhs[i]; typ != nil {
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)
+ check.assignVar(lhs, nil, &x, "assignment") // error is on variable, use "assignment" not "range clause"
}
}
+ } else if constIntRange {
+ // 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.
+ check.assignment(&x, nil, "range clause")
}
check.stmt(inner, s.Body)
package p
+// test framework assumes 64-bit int/uint sizes by default
+const (
+ maxInt = 1<<63 - 1
+ maxUint = 1<<64 - 1
+)
+
type MyInt int32
func _() {
for i, j /* ERROR "range over 10 (untyped int constant) permits only one iteration variable" */ := range 10 {
_, _ = i, j
}
- for i /* ERROR "cannot use i (value of type MyInt) as int value in assignment" */ = range MyInt(10) {
+ for i = range MyInt /* ERROR "cannot use MyInt(10) (constant 10 of type MyInt) as int value in range clause" */ (10) {
_ = i
}
for mi := range MyInt(10) {
for range x { // ok
}
}
+
+func issue65133() {
+ for range maxInt {
+ }
+ for range maxInt /* ERROR "cannot use maxInt + 1 (untyped int constant 9223372036854775808) as int value in range clause (overflows)" */ + 1 {
+ }
+ for range maxUint /* ERROR "cannot use maxUint (untyped int constant 18446744073709551615) as int value in range clause (overflows)" */ {
+ }
+
+ for i := range maxInt {
+ _ = i
+ }
+ for i := range maxInt /* ERROR "cannot use maxInt + 1 (untyped int constant 9223372036854775808) as int value in range clause (overflows)" */ + 1 {
+ _ = i
+ }
+ for i := range maxUint /* ERROR "cannot use maxUint (untyped int constant 18446744073709551615) as int value in range clause (overflows)" */ {
+ _ = i
+ }
+
+ var i int
+ _ = i
+ for i = range maxInt {
+ }
+ for i = range maxInt /* ERROR "cannot use maxInt + 1 (untyped int constant 9223372036854775808) as int value in range clause (overflows)" */ + 1 {
+ }
+ for i = range maxUint /* ERROR "cannot use maxUint (untyped int constant 18446744073709551615) as int value in range clause (overflows)" */ {
+ }
+
+ var j uint
+ _ = j
+ for j = range maxInt {
+ }
+ for j = range maxInt + 1 {
+ }
+ for j = range maxUint {
+ }
+ for j = range maxUint /* ERROR "cannot use maxUint + 1 (untyped int constant 18446744073709551616) as uint value in range clause (overflows)" */ + 1 {
+ }
+
+ for range 256 {
+ }
+ for _ = range 256 {
+ }
+ for i = range 256 {
+ }
+ for i := range 256 {
+ _ = i
+ }
+
+ var u8 uint8
+ _ = u8
+ for u8 = range - /* ERROR "cannot use -1 (untyped int constant) as uint8 value in range clause (overflows)" */ 1 {
+ }
+ for u8 = range 0 {
+ }
+ for u8 = range 255 {
+ }
+ for u8 = range 256 /* ERROR "cannot use 256 (untyped int constant) as uint8 value in range clause (overflows)" */ {
+ }
+}