range.go raw

   1  // Code generated by "go test -run=Generate -write=all"; DO NOT EDIT.
   2  // Source: ../../cmd/compile/internal/types2/range.go
   3  
   4  // Copyright 2025 The Go Authors. All rights reserved.
   5  // Use of this source code is governed by a BSD-style
   6  // license that can be found in the LICENSE file.
   7  
   8  // This file implements typechecking of range statements.
   9  
  10  package types
  11  
  12  import (
  13  	"go/ast"
  14  	"go/constant"
  15  	"internal/buildcfg"
  16  	. "internal/types/errors"
  17  )
  18  
  19  // rangeStmt type-checks a range statement of form
  20  //
  21  //	for sKey, sValue = range rangeVar { ... }
  22  //
  23  // where sKey, sValue, sExtra may be nil. isDef indicates whether these
  24  // variables are assigned to only (=) or whether there is a short variable
  25  // declaration (:=). If the latter and there are no variables, an error is
  26  // reported at noNewVarPos.
  27  func (check *Checker) rangeStmt(inner stmtContext, rangeStmt *ast.RangeStmt, noNewVarPos positioner, sKey, sValue, sExtra, rangeVar ast.Expr, isDef bool) {
  28  	// check expression to iterate over
  29  	var x operand
  30  
  31  	// From the spec:
  32  	//   The range expression x is evaluated before beginning the loop,
  33  	//   with one exception: if at most one iteration variable is present
  34  	//   and x or len(x) is constant, the range expression is not evaluated.
  35  	// So we have to be careful not to evaluate the arg in the
  36  	// described situation.
  37  
  38  	check.hasCallOrRecv = false
  39  	check.expr(nil, &x, rangeVar)
  40  
  41  	if isTypes2 && x.mode != invalid && sValue == nil && !check.hasCallOrRecv {
  42  		if t, ok := arrayPtrDeref(under(x.typ)).(*Array); ok {
  43  			for {
  44  				// Put constant info on the thing inside parentheses.
  45  				// That's where (*../noder/writer).expr expects it.
  46  				// See issue 73476.
  47  				p, ok := rangeVar.(*ast.ParenExpr)
  48  				if !ok {
  49  					break
  50  				}
  51  				rangeVar = p.X
  52  			}
  53  			// Override type of rangeVar to be a constant
  54  			// (and thus side-effects will not be computed
  55  			// by the backend).
  56  			check.record(&operand{
  57  				mode: constant_,
  58  				expr: rangeVar,
  59  				typ:  Typ[Int],
  60  				val:  constant.MakeInt64(t.len),
  61  				id:   x.id,
  62  			})
  63  		}
  64  	}
  65  
  66  	// determine key/value types
  67  	var key, val Type
  68  	if x.mode != invalid {
  69  		k, v, cause, ok := rangeKeyVal(check, x.typ, func(v goVersion) bool {
  70  			return check.allowVersion(v)
  71  		})
  72  		switch {
  73  		case !ok && cause != "":
  74  			check.softErrorf(&x, InvalidRangeExpr, "cannot range over %s: %s", &x, cause)
  75  		case !ok:
  76  			check.softErrorf(&x, InvalidRangeExpr, "cannot range over %s", &x)
  77  		case k == nil && sKey != nil:
  78  			check.softErrorf(sKey, InvalidIterVar, "range over %s permits no iteration variables", &x)
  79  		case v == nil && sValue != nil:
  80  			check.softErrorf(sValue, InvalidIterVar, "range over %s permits only one iteration variable", &x)
  81  		case sExtra != nil:
  82  			check.softErrorf(sExtra, InvalidIterVar, "range clause permits at most two iteration variables")
  83  		}
  84  		key, val = k, v
  85  	}
  86  
  87  	// Open the for-statement block scope now, after the range clause.
  88  	// Iteration variables declared with := need to go in this scope (was go.dev/issue/51437).
  89  	check.openScope(rangeStmt, "range")
  90  	defer check.closeScope()
  91  
  92  	// check assignment to/declaration of iteration variables
  93  	// (irregular assignment, cannot easily map to existing assignment checks)
  94  
  95  	// lhs expressions and initialization value (rhs) types
  96  	lhs := [2]ast.Expr{sKey, sValue} // sKey, sValue may be nil
  97  	rhs := [2]Type{key, val}         // key, val may be nil
  98  
  99  	rangeOverInt := isInteger(x.typ)
 100  
 101  	if isDef {
 102  		// short variable declaration
 103  		var vars []*Var
 104  		for i, lhs := range lhs {
 105  			if lhs == nil {
 106  				continue
 107  			}
 108  
 109  			// determine lhs variable
 110  			var obj *Var
 111  			if ident, _ := lhs.(*ast.Ident); ident != nil {
 112  				// declare new variable
 113  				name := ident.Name
 114  				obj = newVar(LocalVar, ident.Pos(), check.pkg, name, nil)
 115  				check.recordDef(ident, obj)
 116  				// _ variables don't count as new variables
 117  				if name != "_" {
 118  					vars = append(vars, obj)
 119  				}
 120  			} else {
 121  				check.errorf(lhs, InvalidSyntaxTree, "cannot declare %s", lhs)
 122  				obj = newVar(LocalVar, lhs.Pos(), check.pkg, "_", nil) // dummy variable
 123  			}
 124  			assert(obj.typ == nil)
 125  
 126  			// initialize lhs iteration variable, if any
 127  			typ := rhs[i]
 128  			if typ == nil || typ == Typ[Invalid] {
 129  				// typ == Typ[Invalid] can happen if allowVersion fails.
 130  				obj.typ = Typ[Invalid]
 131  				check.usedVars[obj] = true // don't complain about unused variable
 132  				continue
 133  			}
 134  
 135  			if rangeOverInt {
 136  				assert(i == 0) // at most one iteration variable (rhs[1] == nil or Typ[Invalid] for rangeOverInt)
 137  				check.initVar(obj, &x, "range clause")
 138  			} else {
 139  				var y operand
 140  				y.mode = value
 141  				y.expr = lhs // we don't have a better rhs expression to use here
 142  				y.typ = typ
 143  				check.initVar(obj, &y, "assignment") // error is on variable, use "assignment" not "range clause"
 144  			}
 145  			assert(obj.typ != nil)
 146  		}
 147  
 148  		// declare variables
 149  		if len(vars) > 0 {
 150  			scopePos := rangeStmt.Body.Pos()
 151  			for _, obj := range vars {
 152  				check.declare(check.scope, nil /* recordDef already called */, obj, scopePos)
 153  			}
 154  		} else {
 155  			check.error(noNewVarPos, NoNewVar, "no new variables on left side of :=")
 156  		}
 157  	} else if sKey != nil /* lhs[0] != nil */ {
 158  		// ordinary assignment
 159  		for i, lhs := range lhs {
 160  			if lhs == nil {
 161  				continue
 162  			}
 163  
 164  			// assign to lhs iteration variable, if any
 165  			typ := rhs[i]
 166  			if typ == nil || typ == Typ[Invalid] {
 167  				continue
 168  			}
 169  
 170  			if rangeOverInt {
 171  				assert(i == 0) // at most one iteration variable (rhs[1] == nil or Typ[Invalid] for rangeOverInt)
 172  				check.assignVar(lhs, nil, &x, "range clause")
 173  				// If the assignment succeeded, if x was untyped before, it now
 174  				// has a type inferred via the assignment. It must be an integer.
 175  				// (go.dev/issues/67027)
 176  				if x.mode != invalid && !isInteger(x.typ) {
 177  					check.softErrorf(lhs, InvalidRangeExpr, "cannot use iteration variable of type %s", x.typ)
 178  				}
 179  			} else {
 180  				var y operand
 181  				y.mode = value
 182  				y.expr = lhs // we don't have a better rhs expression to use here
 183  				y.typ = typ
 184  				check.assignVar(lhs, nil, &y, "assignment") // error is on variable, use "assignment" not "range clause"
 185  			}
 186  		}
 187  	} else if rangeOverInt {
 188  		// If we don't have any iteration variables, we still need to
 189  		// check that a (possibly untyped) integer range expression x
 190  		// is valid.
 191  		// We do this by checking the assignment _ = x. This ensures
 192  		// that an untyped x can be converted to a value of its default
 193  		// type (rune or int).
 194  		check.assignment(&x, nil, "range clause")
 195  	}
 196  
 197  	check.stmt(inner, rangeStmt.Body)
 198  }
 199  
 200  // rangeKeyVal returns the key and value type produced by a range clause
 201  // over an expression of type orig.
 202  // If allowVersion != nil, it is used to check the required language version.
 203  // If the range clause is not permitted, rangeKeyVal returns ok = false.
 204  // When ok = false, rangeKeyVal may also return a reason in cause.
 205  // The check parameter is only used in case of an error; it may be nil.
 206  func rangeKeyVal(check *Checker, orig Type, allowVersion func(goVersion) bool) (key, val Type, cause string, ok bool) {
 207  	bad := func(cause string) (Type, Type, string, bool) {
 208  		return Typ[Invalid], Typ[Invalid], cause, false
 209  	}
 210  
 211  	rtyp, err := commonUnder(orig, func(t, u Type) *typeError {
 212  		// A channel must permit receive operations.
 213  		if ch, _ := u.(*Chan); ch != nil && ch.dir == SendOnly {
 214  			return typeErrorf("receive from send-only channel %s", t)
 215  		}
 216  		return nil
 217  	})
 218  	if rtyp == nil {
 219  		return bad(err.format(check))
 220  	}
 221  
 222  	switch typ := arrayPtrDeref(rtyp).(type) {
 223  	case *Basic:
 224  		if isString(typ) {
 225  			return Typ[Int], universeRune, "", true // use 'rune' name
 226  		}
 227  		if isInteger(typ) {
 228  			if allowVersion != nil && !allowVersion(go1_22) {
 229  				return bad("requires go1.22 or later")
 230  			}
 231  			return orig, nil, "", true
 232  		}
 233  	case *Array:
 234  		return Typ[Int], typ.elem, "", true
 235  	case *Slice:
 236  		return Typ[Int], typ.elem, "", true
 237  	case *Map:
 238  		return typ.key, typ.elem, "", true
 239  	case *Chan:
 240  		assert(typ.dir != SendOnly)
 241  		return typ.elem, nil, "", true
 242  	case *Signature:
 243  		if !buildcfg.Experiment.RangeFunc && allowVersion != nil && !allowVersion(go1_23) {
 244  			return bad("requires go1.23 or later")
 245  		}
 246  		// check iterator arity
 247  		switch {
 248  		case typ.Params().Len() != 1:
 249  			return bad("func must be func(yield func(...) bool): wrong argument count")
 250  		case typ.Results().Len() != 0:
 251  			return bad("func must be func(yield func(...) bool): unexpected results")
 252  		}
 253  		assert(typ.Recv() == nil)
 254  		// check iterator argument type
 255  		u, err := commonUnder(typ.Params().At(0).Type(), nil)
 256  		cb, _ := u.(*Signature)
 257  		switch {
 258  		case cb == nil:
 259  			if err != nil {
 260  				return bad(check.sprintf("func must be func(yield func(...) bool): in yield type, %s", err.format(check)))
 261  			} else {
 262  				return bad("func must be func(yield func(...) bool): argument is not func")
 263  			}
 264  		case cb.Params().Len() > 2:
 265  			return bad("func must be func(yield func(...) bool): yield func has too many parameters")
 266  		case cb.Results().Len() != 1 || !Identical(cb.Results().At(0).Type(), universeBool):
 267  			// see go.dev/issues/71131, go.dev/issues/71164
 268  			if cb.Results().Len() == 1 && isBoolean(cb.Results().At(0).Type()) {
 269  				return bad("func must be func(yield func(...) bool): yield func returns user-defined boolean, not bool")
 270  			} else {
 271  				return bad("func must be func(yield func(...) bool): yield func does not return bool")
 272  			}
 273  		}
 274  		assert(cb.Recv() == nil)
 275  		// determine key and value types, if any
 276  		if cb.Params().Len() >= 1 {
 277  			key = cb.Params().At(0).Type()
 278  		}
 279  		if cb.Params().Len() >= 2 {
 280  			val = cb.Params().At(1).Type()
 281  		}
 282  		return key, val, "", true
 283  	}
 284  	return
 285  }
 286