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 "internal/buildcfg"
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 check.expr(nil, &x, rangeVar)
30
31
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
53
54 check.openScope(rangeStmt, "range")
55 defer check.closeScope()
56
57
58
59
60
61 lhs := [2]ast.Expr{sKey, sValue}
62 rhs := [2]Type{key, val}
63
64 rangeOverInt := isInteger(x.typ)
65
66 if isDef {
67
68 var vars []*Var
69 for i, lhs := range lhs {
70 if lhs == nil {
71 continue
72 }
73
74
75 var obj *Var
76 if ident, _ := lhs.(*ast.Ident); ident != nil {
77
78 name := ident.Name
79 obj = newVar(LocalVar, ident.Pos(), check.pkg, name, nil)
80 check.recordDef(ident, obj)
81
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)
88 }
89 assert(obj.typ == nil)
90
91
92 typ := rhs[i]
93 if typ == nil || typ == Typ[Invalid] {
94
95 obj.typ = Typ[Invalid]
96 check.usedVars[obj] = true
97 continue
98 }
99
100 if rangeOverInt {
101 assert(i == 0)
102 check.initVar(obj, &x, "range clause")
103 } else {
104 var y operand
105 y.mode = value
106 y.expr = lhs
107 y.typ = typ
108 check.initVar(obj, &y, "assignment")
109 }
110 assert(obj.typ != nil)
111 }
112
113
114 if len(vars) > 0 {
115 scopePos := rangeStmt.Body.Pos()
116 for _, obj := range vars {
117 check.declare(check.scope, nil , obj, scopePos)
118 }
119 } else {
120 check.error(noNewVarPos, NoNewVar, "no new variables on left side of :=")
121 }
122 } else if sKey != nil {
123
124 for i, lhs := range lhs {
125 if lhs == nil {
126 continue
127 }
128
129
130 typ := rhs[i]
131 if typ == nil || typ == Typ[Invalid] {
132 continue
133 }
134
135 if rangeOverInt {
136 assert(i == 0)
137 check.assignVar(lhs, nil, &x, "range clause")
138
139
140
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
148 y.typ = typ
149 check.assignVar(lhs, nil, &y, "assignment")
150 }
151 }
152 } else if rangeOverInt {
153
154
155
156
157
158
159 check.assignment(&x, nil, "range clause")
160 }
161
162 check.stmt(inner, rangeStmt.Body)
163 }
164
165
166
167
168
169
170
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
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
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
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
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
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
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