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