Source file src/go/types/range.go

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

View as plain text