github.com/bgentry/go@v0.0.0-20150121062915-6cf5a733d54d/src/cmd/gc/range.c (about)

     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 <u.h>
    10  #include <libc.h>
    11  #include "go.h"
    12  
    13  void
    14  typecheckrange(Node *n)
    15  {
    16  	char *why;
    17  	Type *t, *t1, *t2;
    18  	Node *v1, *v2;
    19  	NodeList *ll;
    20  
    21  	// delicate little dance.  see typecheckas2
    22  	for(ll=n->list; ll; ll=ll->next)
    23  		if(ll->n->defn != n)
    24  			typecheck(&ll->n, Erv | Easgn);
    25  
    26  	typecheck(&n->right, Erv);
    27  	if((t = n->right->type) == T)
    28  		goto out;
    29  	if(isptr[t->etype] && isfixedarray(t->type))
    30  		t = t->type;
    31  	n->type = t;
    32  
    33  	switch(t->etype) {
    34  	default:
    35  		yyerror("cannot range over %lN", n->right);
    36  		goto out;
    37  
    38  	case TARRAY:
    39  		t1 = types[TINT];
    40  		t2 = t->type;
    41  		break;
    42  
    43  	case TMAP:
    44  		t1 = t->down;
    45  		t2 = t->type;
    46  		break;
    47  
    48  	case TCHAN:
    49  		if(!(t->chan & Crecv)) {
    50  			yyerror("invalid operation: range %N (receive from send-only type %T)", n->right, n->right->type);
    51  			goto out;
    52  		}
    53  		t1 = t->type;
    54  		t2 = nil;
    55  		if(count(n->list) == 2)
    56  			goto toomany;
    57  		break;
    58  
    59  	case TSTRING:
    60  		t1 = types[TINT];
    61  		t2 = runetype;
    62  		break;
    63  	}
    64  
    65  	if(count(n->list) > 2) {
    66  	toomany:
    67  		yyerror("too many variables in range");
    68  	}
    69  
    70  	v1 = N;
    71  	if(n->list)
    72  		v1 = n->list->n;
    73  	v2 = N;
    74  	if(n->list && n->list->next)
    75  		v2 = n->list->next->n;
    76  
    77  	// this is not only a optimization but also a requirement in the spec.
    78  	// "if the second iteration variable is the blank identifier, the range
    79  	// clause is equivalent to the same clause with only the first variable
    80  	// present."
    81  	if(isblank(v2)) {
    82  		if(v1 != N)
    83  			n->list = list1(v1);
    84  		v2 = N;
    85  	}
    86  
    87  	if(v1) {
    88  		if(v1->defn == n)
    89  			v1->type = t1;
    90  		else if(v1->type != T && assignop(t1, v1->type, &why) == 0)
    91  			yyerror("cannot assign type %T to %lN in range%s", t1, v1, why);
    92  	}
    93  	if(v2) {
    94  		if(v2->defn == n)
    95  			v2->type = t2;
    96  		else if(v2->type != T && assignop(t2, v2->type, &why) == 0)
    97  			yyerror("cannot assign type %T to %lN in range%s", t2, v2, why);
    98  	}
    99  
   100  out:
   101  	typechecklist(n->nbody, Etop);
   102  
   103  	// second half of dance
   104  	n->typecheck = 1;
   105  	for(ll=n->list; ll; ll=ll->next)
   106  		if(ll->n->typecheck == 0)
   107  			typecheck(&ll->n, Erv | Easgn);
   108  }
   109  
   110  void
   111  walkrange(Node *n)
   112  {
   113  	Node *ohv1, *hv1, *hv2;	// hidden (old) val 1, 2
   114  	Node *ha, *hit;	// hidden aggregate, iterator
   115  	Node *hn, *hp;	// hidden len, pointer
   116  	Node *hb;  // hidden bool
   117  	Node *a, *v1, *v2;	// not hidden aggregate, val 1, 2
   118  	Node *fn, *tmp;
   119  	Node *keyname, *valname;
   120  	Node *key, *val;
   121  	NodeList *body, *init;
   122  	Type *th, *t;
   123  	int lno;
   124  
   125  	t = n->type;
   126  	init = nil;
   127  
   128  	a = n->right;
   129  	lno = setlineno(a);
   130  
   131  	v1 = N;
   132  	if(n->list)
   133  		v1 = n->list->n;
   134  	v2 = N;
   135  	if(n->list && n->list->next && !isblank(n->list->next->n))
   136  		v2 = n->list->next->n;
   137  	// n->list has no meaning anymore, clear it
   138  	// to avoid erroneous processing by racewalk.
   139  	n->list = nil;
   140  	hv2 = N;
   141  
   142  	switch(t->etype) {
   143  	default:
   144  		fatal("walkrange");
   145  
   146  	case TARRAY:
   147  		// Lower n into runtime·memclr if possible, for
   148  		// fast zeroing of slices and arrays (issue 5373).
   149  		// Look for instances of
   150  		//
   151  		// for i := range a {
   152  		// 	a[i] = zero
   153  		// }
   154  		//
   155  		// in which the evaluation of a is side-effect-free.
   156  		if(!debug['N'])
   157  		if(!flag_race)
   158  		if(v1 != N)
   159  		if(v2 == N)
   160  		if(n->nbody != nil)
   161  		if(n->nbody->n != N)	// at least one statement in body
   162  		if(n->nbody->next == nil) {	// at most one statement in body
   163  			tmp = n->nbody->n;	// first statement of body
   164  			if(tmp->op == OAS)
   165  			if(tmp->left->op == OINDEX)
   166  			if(samesafeexpr(tmp->left->left, a))
   167  			if(samesafeexpr(tmp->left->right, v1))
   168  			if(t->type->width > 0)
   169  			if(iszero(tmp->right)) {
   170  				// Convert to
   171  				// if len(a) != 0 {
   172  				// 	hp = &a[0]
   173  				// 	hn = len(a)*sizeof(elem(a))
   174  				// 	memclr(hp, hn)
   175  				// 	i = len(a) - 1
   176  				// }
   177  				n->op = OIF;
   178  				n->nbody = nil;
   179  				n->ntest = nod(ONE, nod(OLEN, a, N), nodintconst(0));
   180  				n->nincr = nil;
   181  
   182  				// hp = &a[0]
   183  				hp = temp(ptrto(types[TUINT8]));
   184  				tmp = nod(OINDEX, a, nodintconst(0));
   185  				tmp->bounded = 1;
   186  				tmp = nod(OADDR, tmp, N);
   187  				tmp = nod(OCONVNOP, tmp, N);
   188  				tmp->type = ptrto(types[TUINT8]);
   189  				n->nbody = list(n->nbody, nod(OAS, hp, tmp));
   190  
   191  				// hn = len(a) * sizeof(elem(a))
   192  				hn = temp(types[TUINTPTR]);
   193  				tmp = nod(OLEN, a, N);
   194  				tmp = nod(OMUL, tmp, nodintconst(t->type->width));
   195  				tmp = conv(tmp, types[TUINTPTR]);
   196  				n->nbody = list(n->nbody, nod(OAS, hn, tmp));
   197  
   198  				// memclr(hp, hn)
   199  				fn = mkcall("memclr", T, nil, hp, hn);
   200  				n->nbody = list(n->nbody, fn);
   201  
   202  				// i = len(a) - 1
   203  				v1 = nod(OAS, v1, nod(OSUB, nod(OLEN, a, N), nodintconst(1)));
   204  				n->nbody = list(n->nbody, v1);
   205  
   206  				typecheck(&n->ntest, Erv);
   207  				typechecklist(n->nbody, Etop);
   208  				walkstmt(&n);
   209  				lineno = lno;
   210  				return;
   211  			}
   212  		}
   213  
   214  		// orderstmt arranged for a copy of the array/slice variable if needed.
   215  		ha = a;
   216  		hv1 = temp(types[TINT]);
   217  		hn = temp(types[TINT]);
   218  		hp = nil;
   219  
   220  		init = list(init, nod(OAS, hv1, N));
   221  		init = list(init, nod(OAS, hn, nod(OLEN, ha, N)));
   222  		if(v2) {
   223  			hp = temp(ptrto(n->type->type));
   224  			tmp = nod(OINDEX, ha, nodintconst(0));
   225  			tmp->bounded = 1;
   226  			init = list(init, nod(OAS, hp, nod(OADDR, tmp, N)));
   227  		}
   228  
   229  		n->ntest = nod(OLT, hv1, hn);
   230  		n->nincr = nod(OAS, hv1, nod(OADD, hv1, nodintconst(1)));
   231  		if(v1 == N)
   232  			body = nil;
   233  		else if(v2 == N)
   234  			body = list1(nod(OAS, v1, hv1));
   235  		else {
   236  			a = nod(OAS2, N, N);
   237  			a->list = list(list1(v1), v2);
   238  			a->rlist = list(list1(hv1), nod(OIND, hp, N));
   239  			body = list1(a);
   240  			
   241  			// Advance pointer as part of increment.
   242  			// We used to advance the pointer before executing the loop body,
   243  			// but doing so would make the pointer point past the end of the
   244  			// array during the final iteration, possibly causing another unrelated
   245  			// piece of memory not to be garbage collected until the loop finished.
   246  			// Advancing during the increment ensures that the pointer p only points
   247  			// pass the end of the array during the final "p++; i++; if(i >= len(x)) break;",
   248  			// after which p is dead, so it cannot confuse the collector.
   249  			tmp = nod(OADD, hp, nodintconst(t->type->width));
   250  			tmp->type = hp->type;
   251  			tmp->typecheck = 1;
   252  			tmp->right->type = types[tptr];
   253  			tmp->right->typecheck = 1;
   254  			a = nod(OAS, hp, tmp);
   255  			typecheck(&a, Etop);
   256  			n->nincr->ninit = list1(a);
   257  		}
   258  		break;
   259  
   260  	case TMAP:
   261  		// orderstmt allocated the iterator for us.
   262  		// we only use a once, so no copy needed.
   263  		ha = a;
   264  		th = hiter(t);
   265  		hit = n->alloc;
   266  		hit->type = th;
   267  		n->left = N;
   268  		keyname = newname(th->type->sym);  // depends on layout of iterator struct.  See reflect.c:hiter
   269  		valname = newname(th->type->down->sym); // ditto
   270  
   271  		fn = syslook("mapiterinit", 1);
   272  		argtype(fn, t->down);
   273  		argtype(fn, t->type);
   274  		argtype(fn, th);
   275  		init = list(init, mkcall1(fn, T, nil, typename(t), ha, nod(OADDR, hit, N)));
   276  		n->ntest = nod(ONE, nod(ODOT, hit, keyname), nodnil());
   277  
   278  		fn = syslook("mapiternext", 1);
   279  		argtype(fn, th);
   280  		n->nincr = mkcall1(fn, T, nil, nod(OADDR, hit, N));
   281  
   282  		key = nod(ODOT, hit, keyname);
   283  		key = nod(OIND, key, N);
   284  		if(v1 == N)
   285  			body = nil;
   286  		else if(v2 == N) {
   287  			body = list1(nod(OAS, v1, key));
   288  		} else {
   289  			val = nod(ODOT, hit, valname);
   290  			val = nod(OIND, val, N);
   291  			a = nod(OAS2, N, N);
   292  			a->list = list(list1(v1), v2);
   293  			a->rlist = list(list1(key), val);
   294  			body = list1(a);
   295  		}
   296  		break;
   297  
   298  	case TCHAN:
   299  		// orderstmt arranged for a copy of the channel variable.
   300  		ha = a;
   301  		n->ntest = N;
   302  		
   303  		hv1 = temp(t->type);
   304  		hv1->typecheck = 1;
   305  		if(haspointers(t->type))
   306  			init = list(init, nod(OAS, hv1, N));
   307  		hb = temp(types[TBOOL]);
   308  
   309  		n->ntest = nod(ONE, hb, nodbool(0));
   310  		a = nod(OAS2RECV, N, N);
   311  		a->typecheck = 1;
   312  		a->list = list(list1(hv1), hb);
   313  		a->rlist = list1(nod(ORECV, ha, N));
   314  		n->ntest->ninit = list1(a);
   315  		if(v1 == N)
   316  			body = nil;
   317  		else
   318  			body = list1(nod(OAS, v1, hv1));
   319  		break;
   320  
   321  	case TSTRING:
   322  		// orderstmt arranged for a copy of the string variable.
   323  		ha = a;
   324  
   325  		ohv1 = temp(types[TINT]);
   326  
   327  		hv1 = temp(types[TINT]);
   328  		init = list(init, nod(OAS, hv1, N));
   329  
   330  		if(v2 == N)
   331  			a = nod(OAS, hv1, mkcall("stringiter", types[TINT], nil, ha, hv1));
   332  		else {
   333  			hv2 = temp(runetype);
   334  			a = nod(OAS2, N, N);
   335  			a->list = list(list1(hv1), hv2);
   336  			fn = syslook("stringiter2", 0);
   337  			a->rlist = list1(mkcall1(fn, getoutargx(fn->type), nil, ha, hv1));
   338  		}
   339  		n->ntest = nod(ONE, hv1, nodintconst(0));
   340  		n->ntest->ninit = list(list1(nod(OAS, ohv1, hv1)), a);
   341  
   342  		
   343  		body = nil;
   344  		if(v1 != N)
   345  			body = list1(nod(OAS, v1, ohv1));
   346  		if(v2 != N)
   347  			body = list(body, nod(OAS, v2, hv2));
   348  		break;
   349  	}
   350  
   351  	n->op = OFOR;
   352  	typechecklist(init, Etop);
   353  	n->ninit = concat(n->ninit, init);
   354  	typechecklist(n->ntest->ninit, Etop);
   355  	typecheck(&n->ntest, Erv);
   356  	typecheck(&n->nincr, Etop);
   357  	typechecklist(body, Etop);
   358  	n->nbody = concat(body, n->nbody);
   359  	walkstmt(&n);
   360  	
   361  	lineno = lno;
   362  }
   363