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