1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 /*
6 * range
7 */
8
9 #include "go.h"
10
11 void
12 typecheckrange(Node *n)
13 {
14 char *why;
15 Type *t, *t1, *t2;
16 Node *v1, *v2;
17 NodeList *ll;
18
19 // delicate little dance. see typecheckas2
20 for(ll=n->list; ll; ll=ll->next)
21 if(ll->n->defn != n)
22 typecheck(&ll->n, Erv | Easgn);
23
24 typecheck(&n->right, Erv);
25 if((t = n->right->type) == T)
26 goto out;
27 if(isptr[t->etype] && isfixedarray(t->type))
28 t = t->type;
29 n->type = t;
30
31 switch(t->etype) {
32 default:
33 yyerror("cannot range over %+N", n->right);
34 goto out;
35
36 case TARRAY:
37 t1 = types[TINT];
38 t2 = t->type;
39 break;
40
41 case TMAP:
42 t1 = t->down;
43 t2 = t->type;
44 break;
45
46 case TCHAN:
47 t1 = t->type;
48 t2 = nil;
49 if(count(n->list) == 2)
50 goto toomany;
51 break;
52
53 case TSTRING:
54 t1 = types[TINT];
55 t2 = types[TINT];
56 break;
57 }
58
59 if(count(n->list) > 2) {
60 toomany:
61 yyerror("too many variables in range");
62 }
63
64 v1 = n->list->n;
65 v2 = N;
66 if(n->list->next)
67 v2 = n->list->next->n;
68
69 if(v1->defn == n)
70 v1->type = t1;
71 else if(v1->type != T && assignop(t1, v1->type, &why) == 0)
72 yyerror("cannot assign type %T to %+N in range%s", t1, v1, why);
73 if(v2) {
74 if(v2->defn == n)
75 v2->type = t2;
76 else if(v2->type != T && assignop(t2, v2->type, &why) == 0)
77 yyerror("cannot assign type %T to %+N in range%s", t2, v2, why);
78 }
79
80 out:
81 typechecklist(n->nbody, Etop);
82
83 // second half of dance
84 n->typecheck = 1;
85 for(ll=n->list; ll; ll=ll->next)
86 if(ll->n->typecheck == 0)
87 typecheck(&ll->n, Erv | Easgn);
88 }
89
90 void
91 walkrange(Node *n)
92 {
93 Node *ohv1, *hv1, *hv2; // hidden (old) val 1, 2
94 Node *ha, *hit; // hidden aggregate, iterator
95 Node *hn, *hp; // hidden len, pointer
96 Node *hb; // hidden bool
97 Node *a, *v1, *v2; // not hidden aggregate, val 1, 2
98 Node *fn, *tmp;
99 NodeList *body, *init;
100 Type *th, *t;
101 int lno;
102
103 t = n->type;
104 init = nil;
105
106 a = n->right;
107 lno = setlineno(a);
108 if(t->etype == TSTRING && !eqtype(t, types[TSTRING])) {
109 a = nod(OCONV, n->right, N);
110 a->type = types[TSTRING];
111 }
112
113 v1 = n->list->n;
114 hv1 = N;
115
116 v2 = N;
117 if(n->list->next)
118 v2 = n->list->next->n;
119 hv2 = N;
120
121 if(v2 == N && t->etype == TARRAY) {
122 // will have just one reference to argument.
123 // no need to make a potentially expensive copy.
124 ha = a;
125 } else {
126 ha = nod(OXXX, N, N);
127 tempname(ha, a->type);
128 init = list(init, nod(OAS, ha, a));
129 }
130
131 switch(t->etype) {
132 default:
133 fatal("walkrange");
134
135 case TARRAY:
136 hv1 = nod(OXXX, N, n);
137 tempname(hv1, types[TINT]);
138 hn = nod(OXXX, N, N);
139 tempname(hn, types[TINT]);
140 hp = nil;
141
142 init = list(init, nod(OAS, hv1, N));
143 init = list(init, nod(OAS, hn, nod(OLEN, ha, N)));
144 if(v2) {
145 hp = nod(OXXX, N, N);
146 tempname(hp, ptrto(n->type->type));
147 tmp = nod(OINDEX, ha, nodintconst(0));
148 tmp->etype = 1; // no bounds check
149 init = list(init, nod(OAS, hp, nod(OADDR, tmp, N)));
150 }
151
152 n->ntest = nod(OLT, hv1, hn);
153 n->nincr = nod(OASOP, hv1, nodintconst(1));
154 n->nincr->etype = OADD;
155 body = list1(nod(OAS, v1, hv1));
156 if(v2) {
157 body = list(body, nod(OAS, v2, nod(OIND, hp, N)));
158 tmp = nod(OADD, hp, nodintconst(t->type->width));
159 tmp->type = hp->type;
160 tmp->typecheck = 1;
161 tmp->right->type = types[tptr];
162 tmp->right->typecheck = 1;
163 body = list(body, nod(OAS, hp, tmp));
164 }
165 break;
166
167 case TMAP:
168 th = typ(TARRAY);
169 th->type = ptrto(types[TUINT8]);
170 th->bound = (sizeof(struct Hiter) + widthptr - 1) / widthptr;
171 hit = nod(OXXX, N, N);
172 tempname(hit, th);
173
174 fn = syslook("mapiterinit", 1);
175 argtype(fn, t->down);
176 argtype(fn, t->type);
177 argtype(fn, th);
178 init = list(init, mkcall1(fn, T, nil, typename(t), ha, nod(OADDR, hit, N)));
179 n->ntest = nod(ONE, nod(OINDEX, hit, nodintconst(0)), nodnil());
180
181 fn = syslook("mapiternext", 1);
182 argtype(fn, th);
183 n->nincr = mkcall1(fn, T, nil, nod(OADDR, hit, N));
184
185 if(v2 == N) {
186 fn = syslook("mapiter1", 1);
187 argtype(fn, th);
188 argtype(fn, t->down);
189 a = nod(OAS, v1, mkcall1(fn, t->down, nil, nod(OADDR, hit, N)));
190 } else {
191 fn = syslook("mapiter2", 1);
192 argtype(fn, th);
193 argtype(fn, t->down);
194 argtype(fn, t->type);
195 a = nod(OAS2, N, N);
196 a->list = list(list1(v1), v2);
197 a->rlist = list1(mkcall1(fn, getoutargx(fn->type), nil, nod(OADDR, hit, N)));
198 }
199 body = list1(a);
200 break;
201
202 case TCHAN:
203 hv1 = nod(OXXX, N, n);
204 tempname(hv1, t->type);
205 hb = nod(OXXX, N, N);
206 tempname(hb, types[TBOOL]);
207
208 n->ntest = nod(ONE, hb, nodbool(0));
209 a = nod(OAS2RECV, N, N);
210 a->typecheck = 1;
211 a->list = list(list1(hv1), hb);
212 a->rlist = list1(nod(ORECV, ha, N));
213 n->ntest->ninit = list1(a);
214 body = list1(nod(OAS, v1, hv1));
215 break;
216
217 case TSTRING:
218 ohv1 = nod(OXXX, N, N);
219 tempname(ohv1, types[TINT]);
220
221 hv1 = nod(OXXX, N, N);
222 tempname(hv1, types[TINT]);
223 init = list(init, nod(OAS, hv1, N));
224
225 if(v2 == N)
226 a = nod(OAS, hv1, mkcall("stringiter", types[TINT], nil, ha, hv1));
227 else {
228 hv2 = nod(OXXX, N, N);
229 tempname(hv2, types[TINT]);
230 a = nod(OAS2, N, N);
231 a->list = list(list1(hv1), hv2);
232 fn = syslook("stringiter2", 0);
233 a->rlist = list1(mkcall1(fn, getoutargx(fn->type), nil, ha, hv1));
234 }
235 n->ntest = nod(ONE, hv1, nodintconst(0));
236 n->ntest->ninit = list(list1(nod(OAS, ohv1, hv1)), a);
237
238 body = list1(nod(OAS, v1, ohv1));
239 if(v2 != N)
240 body = list(body, nod(OAS, v2, hv2));
241 break;
242 }
243
244 n->op = OFOR;
245 typechecklist(init, Etop);
246 n->ninit = concat(n->ninit, init);
247 typechecklist(n->ntest->ninit, Etop);
248 typecheck(&n->ntest, Erv);
249 typecheck(&n->nincr, Etop);
250 typechecklist(body, Etop);
251 n->nbody = concat(body, n->nbody);
252 walkstmt(&n);
253
254 lineno = lno;
255 }
256