Source file
src/go/types/range.go
1
2
3
4
5
6
7
8
9
10 package types
11
12 import (
13 "go/ast"
14 "go/constant"
15 . "internal/types/errors"
16 )
17
18
19
20
21
22
23
24
25
26 func (check *Checker) rangeStmt(inner stmtContext, rangeStmt *ast.RangeStmt, noNewVarPos positioner, sKey, sValue, sExtra, rangeVar ast.Expr, isDef bool) {
27
28 var x operand
29
30
31
32
33
34
35
36
37 check.hasCallOrRecv = false
38 check.expr(nil, &x, rangeVar)
39
40 if isTypes2 && x.mode != invalid && sValue == nil && !check.hasCallOrRecv {
41 if t, ok := arrayPtrDeref(under(x.typ)).(*Array); ok {
42 for {
43
44
45
46 p, ok := rangeVar.(*ast.ParenExpr)
47 if !ok {
48 break
49 }
50 rangeVar = p.X
51 }
52
53
54
55 check.record(&operand{
56 mode: constant_,
57 expr: rangeVar,
58 typ: Typ[Int],
59 val: constant.MakeInt64(t.len),
60 id: x.id,
61 })
62 }
63 }
64
65
66 var key, val Type
67 if x.mode != invalid {
68 k, v, cause, ok := rangeKeyVal(check, x.typ, func(v goVersion) bool {
69 return check.allowVersion(v)
70 })
71 switch {
72 case !ok && cause != "":
73 check.softErrorf(&x, InvalidRangeExpr, "cannot range over %s: %s", &x, cause)
74 case !ok:
75 check.softErrorf(&x, InvalidRangeExpr, "cannot range over %s", &x)
76 case k == nil && sKey != nil:
77 check.softErrorf(sKey, InvalidIterVar, "range over %s permits no iteration variables", &x)
78 case v == nil && sValue != nil:
79 check.softErrorf(sValue, InvalidIterVar, "range over %s permits only one iteration variable", &x)
80 case sExtra != nil:
81 check.softErrorf(sExtra, InvalidIterVar, "range clause permits at most two iteration variables")
82 }
83 key, val = k, v
84 }
85
86
87
88 check.openScope(rangeStmt, "range")
89 defer check.closeScope()
90
91
92
93
94
95 lhs := [2]ast.Expr{sKey, sValue}
96 rhs := [2]Type{key, val}
97
98 rangeOverInt := isInteger(x.typ)
99
100 if isDef {
101
102 var vars []*Var
103 for i, lhs := range lhs {
104 if lhs == nil {
105 continue
106 }
107
108
109 var obj *Var
110 if ident, _ := lhs.(*ast.Ident); ident != nil {
111
112 name := ident.Name
113 obj = newVar(LocalVar, ident.Pos(), check.pkg, name, nil)
114 check.recordDef(ident, obj)
115
116 if name != "_" {
117 vars = append(vars, obj)
118 }
119 } else {
120 check.errorf(lhs, InvalidSyntaxTree, "cannot declare %s", lhs)
121 obj = newVar(LocalVar, lhs.Pos(), check.pkg, "_", nil)
122 }
123 assert(obj.typ == nil)
124
125
126 typ := rhs[i]
127 if typ == nil || typ == Typ[Invalid] {
128
129 obj.typ = Typ[Invalid]
130 check.usedVars[obj] = true
131 continue
132 }
133
134 if rangeOverInt {
135 assert(i == 0)
136 check.initVar(obj, &x, "range clause")
137 } else {
138 var y operand
139 y.mode = value
140 y.expr = lhs
141 y.typ = typ
142 check.initVar(obj, &y, "assignment")
143 }
144 assert(obj.typ != nil)
145 }
146
147
148 if len(vars) > 0 {
149 scopePos := rangeStmt.Body.Pos()
150 for _, obj := range vars {
151 check.declare(check.scope, nil , obj, scopePos)
152 }
153 } else {
154 check.error(noNewVarPos, NoNewVar, "no new variables on left side of :=")
155 }
156 } else if sKey != nil {
157
158 for i, lhs := range lhs {
159 if lhs == nil {
160 continue
161 }
162
163
164 typ := rhs[i]
165 if typ == nil || typ == Typ[Invalid] {
166 continue
167 }
168
169 if rangeOverInt {
170 assert(i == 0)
171 check.assignVar(lhs, nil, &x, "range clause")
172
173
174
175 if x.mode != invalid && !isInteger(x.typ) {
176 check.softErrorf(lhs, InvalidRangeExpr, "cannot use iteration variable of type %s", x.typ)
177 }
178 } else {
179 var y operand
180 y.mode = value
181 y.expr = lhs
182 y.typ = typ
183 check.assignVar(lhs, nil, &y, "assignment")
184 }
185 }
186 } else if rangeOverInt {
187
188
189
190
191
192
193 check.assignment(&x, nil, "range clause")
194 }
195
196 check.stmt(inner, rangeStmt.Body)
197 }
198
199
200
201
202
203
204
205 func rangeKeyVal(check *Checker, orig Type, allowVersion func(goVersion) bool) (key, val Type, cause string, ok bool) {
206 bad := func(cause string) (Type, Type, string, bool) {
207 return Typ[Invalid], Typ[Invalid], cause, false
208 }
209
210 rtyp, err := commonUnder(orig, func(t, u Type) *typeError {
211
212 if ch, _ := u.(*Chan); ch != nil && ch.dir == SendOnly {
213 return typeErrorf("receive from send-only channel %s", t)
214 }
215 return nil
216 })
217 if rtyp == nil {
218 return bad(err.format(check))
219 }
220
221 switch typ := arrayPtrDeref(rtyp).(type) {
222 case *Basic:
223 if isString(typ) {
224 return Typ[Int], universeRune, "", true
225 }
226 if isInteger(typ) {
227 if allowVersion != nil && !allowVersion(go1_22) {
228 return bad("requires go1.22 or later")
229 }
230 return orig, nil, "", true
231 }
232 case *Array:
233 return Typ[Int], typ.elem, "", true
234 case *Slice:
235 return Typ[Int], typ.elem, "", true
236 case *Map:
237 return typ.key, typ.elem, "", true
238 case *Chan:
239 assert(typ.dir != SendOnly)
240 return typ.elem, nil, "", true
241 case *Signature:
242 if allowVersion != nil && !allowVersion(go1_23) {
243 return bad("requires go1.23 or later")
244 }
245
246 switch {
247 case typ.Params().Len() != 1:
248 return bad("func must be func(yield func(...) bool): wrong argument count")
249 case typ.Results().Len() != 0:
250 return bad("func must be func(yield func(...) bool): unexpected results")
251 }
252 assert(typ.Recv() == nil)
253
254 u, err := commonUnder(typ.Params().At(0).Type(), nil)
255 cb, _ := u.(*Signature)
256 switch {
257 case cb == nil:
258 if err != nil {
259 return bad(check.sprintf("func must be func(yield func(...) bool): in yield type, %s", err.format(check)))
260 } else {
261 return bad("func must be func(yield func(...) bool): argument is not func")
262 }
263 case cb.Params().Len() > 2:
264 return bad("func must be func(yield func(...) bool): yield func has too many parameters")
265 case cb.Results().Len() != 1 || !Identical(cb.Results().At(0).Type(), universeBool):
266
267 if cb.Results().Len() == 1 && isBoolean(cb.Results().At(0).Type()) {
268 return bad("func must be func(yield func(...) bool): yield func returns user-defined boolean, not bool")
269 } else {
270 return bad("func must be func(yield func(...) bool): yield func does not return bool")
271 }
272 }
273 assert(cb.Recv() == nil)
274
275 if cb.Params().Len() >= 1 {
276 key = cb.Params().At(0).Type()
277 }
278 if cb.Params().Len() >= 2 {
279 val = cb.Params().At(1).Type()
280 }
281 return key, val, "", true
282 }
283 return
284 }
285
View as plain text