github.com/peggyl/go@v0.0.0-20151008231540-ae315999c2d5/src/cmd/compile/internal/gc/range.go (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  package gc
     6  
     7  import "cmd/internal/obj"
     8  
     9  /*
    10   * range
    11   */
    12  func typecheckrange(n *Node) {
    13  	var toomany int
    14  	var why string
    15  	var t1 *Type
    16  	var t2 *Type
    17  	var v1 *Node
    18  	var v2 *Node
    19  
    20  	// Typechecking order is important here:
    21  	// 0. first typecheck range expression (slice/map/chan),
    22  	//	it is evaluated only once and so logically it is not part of the loop.
    23  	// 1. typcheck produced values,
    24  	//	this part can declare new vars and so it must be typechecked before body,
    25  	//	because body can contain a closure that captures the vars.
    26  	// 2. decldepth++ to denote loop body.
    27  	// 3. typecheck body.
    28  	// 4. decldepth--.
    29  
    30  	typecheck(&n.Right, Erv)
    31  
    32  	t := n.Right.Type
    33  	if t == nil {
    34  		goto out
    35  	}
    36  
    37  	// delicate little dance.  see typecheckas2
    38  	for ll := n.List; ll != nil; ll = ll.Next {
    39  		if ll.N.Name == nil || ll.N.Name.Defn != n {
    40  			typecheck(&ll.N, Erv|Easgn)
    41  		}
    42  	}
    43  
    44  	if Isptr[t.Etype] && Isfixedarray(t.Type) {
    45  		t = t.Type
    46  	}
    47  	n.Type = t
    48  
    49  	toomany = 0
    50  	switch t.Etype {
    51  	default:
    52  		Yyerror("cannot range over %v", Nconv(n.Right, obj.FmtLong))
    53  		goto out
    54  
    55  	case TARRAY:
    56  		t1 = Types[TINT]
    57  		t2 = t.Type
    58  
    59  	case TMAP:
    60  		t1 = t.Down
    61  		t2 = t.Type
    62  
    63  	case TCHAN:
    64  		if t.Chan&Crecv == 0 {
    65  			Yyerror("invalid operation: range %v (receive from send-only type %v)", n.Right, n.Right.Type)
    66  			goto out
    67  		}
    68  
    69  		t1 = t.Type
    70  		t2 = nil
    71  		if count(n.List) == 2 {
    72  			toomany = 1
    73  		}
    74  
    75  	case TSTRING:
    76  		t1 = Types[TINT]
    77  		t2 = runetype
    78  	}
    79  
    80  	if count(n.List) > 2 || toomany != 0 {
    81  		Yyerror("too many variables in range")
    82  	}
    83  
    84  	v1 = nil
    85  	if n.List != nil {
    86  		v1 = n.List.N
    87  	}
    88  	v2 = nil
    89  	if n.List != nil && n.List.Next != nil {
    90  		v2 = n.List.Next.N
    91  	}
    92  
    93  	// this is not only a optimization but also a requirement in the spec.
    94  	// "if the second iteration variable is the blank identifier, the range
    95  	// clause is equivalent to the same clause with only the first variable
    96  	// present."
    97  	if isblank(v2) {
    98  		if v1 != nil {
    99  			n.List = list1(v1)
   100  		}
   101  		v2 = nil
   102  	}
   103  
   104  	if v1 != nil {
   105  		if v1.Name != nil && v1.Name.Defn == n {
   106  			v1.Type = t1
   107  		} else if v1.Type != nil && assignop(t1, v1.Type, &why) == 0 {
   108  			Yyerror("cannot assign type %v to %v in range%s", t1, Nconv(v1, obj.FmtLong), why)
   109  		}
   110  		checkassign(n, v1)
   111  	}
   112  
   113  	if v2 != nil {
   114  		if v2.Name != nil && v2.Name.Defn == n {
   115  			v2.Type = t2
   116  		} else if v2.Type != nil && assignop(t2, v2.Type, &why) == 0 {
   117  			Yyerror("cannot assign type %v to %v in range%s", t2, Nconv(v2, obj.FmtLong), why)
   118  		}
   119  		checkassign(n, v2)
   120  	}
   121  
   122  	// second half of dance
   123  out:
   124  	n.Typecheck = 1
   125  
   126  	for ll := n.List; ll != nil; ll = ll.Next {
   127  		if ll.N.Typecheck == 0 {
   128  			typecheck(&ll.N, Erv|Easgn)
   129  		}
   130  	}
   131  
   132  	decldepth++
   133  	typechecklist(n.Nbody, Etop)
   134  	decldepth--
   135  }
   136  
   137  func walkrange(n *Node) {
   138  	// variable name conventions:
   139  	//	ohv1, hv1, hv2: hidden (old) val 1, 2
   140  	//	ha, hit: hidden aggregate, iterator
   141  	//	hn, hp: hidden len, pointer
   142  	//	hb: hidden bool
   143  	//	a, v1, v2: not hidden aggregate, val 1, 2
   144  
   145  	t := n.Type
   146  
   147  	a := n.Right
   148  	lno := int(setlineno(a))
   149  	n.Right = nil
   150  
   151  	var v1 *Node
   152  	if n.List != nil {
   153  		v1 = n.List.N
   154  	}
   155  	var v2 *Node
   156  	if n.List != nil && n.List.Next != nil && !isblank(n.List.Next.N) {
   157  		v2 = n.List.Next.N
   158  	}
   159  
   160  	// n->list has no meaning anymore, clear it
   161  	// to avoid erroneous processing by racewalk.
   162  	n.List = nil
   163  
   164  	var body *NodeList
   165  	var init *NodeList
   166  	switch t.Etype {
   167  	default:
   168  		Fatalf("walkrange")
   169  
   170  		// Lower n into runtime·memclr if possible, for
   171  	// fast zeroing of slices and arrays (issue 5373).
   172  	// Look for instances of
   173  	//
   174  	// for i := range a {
   175  	// 	a[i] = zero
   176  	// }
   177  	//
   178  	// in which the evaluation of a is side-effect-free.
   179  	case TARRAY:
   180  		if Debug['N'] == 0 {
   181  			if flag_race == 0 {
   182  				if v1 != nil {
   183  					if v2 == nil {
   184  						if n.Nbody != nil {
   185  							if n.Nbody.N != nil { // at least one statement in body
   186  								if n.Nbody.Next == nil { // at most one statement in body
   187  									tmp := n.Nbody.N // first statement of body
   188  									if tmp.Op == OAS {
   189  										if tmp.Left.Op == OINDEX {
   190  											if samesafeexpr(tmp.Left.Left, a) {
   191  												if samesafeexpr(tmp.Left.Right, v1) {
   192  													if t.Type.Width > 0 {
   193  														if iszero(tmp.Right) {
   194  															// Convert to
   195  															// if len(a) != 0 {
   196  															// 	hp = &a[0]
   197  															// 	hn = len(a)*sizeof(elem(a))
   198  															// 	memclr(hp, hn)
   199  															// 	i = len(a) - 1
   200  															// }
   201  															n.Op = OIF
   202  
   203  															n.Nbody = nil
   204  															n.Left = Nod(ONE, Nod(OLEN, a, nil), Nodintconst(0))
   205  
   206  															// hp = &a[0]
   207  															hp := temp(Ptrto(Types[TUINT8]))
   208  
   209  															tmp := Nod(OINDEX, a, Nodintconst(0))
   210  															tmp.Bounded = true
   211  															tmp = Nod(OADDR, tmp, nil)
   212  															tmp = Nod(OCONVNOP, tmp, nil)
   213  															tmp.Type = Ptrto(Types[TUINT8])
   214  															n.Nbody = list(n.Nbody, Nod(OAS, hp, tmp))
   215  
   216  															// hn = len(a) * sizeof(elem(a))
   217  															hn := temp(Types[TUINTPTR])
   218  
   219  															tmp = Nod(OLEN, a, nil)
   220  															tmp = Nod(OMUL, tmp, Nodintconst(t.Type.Width))
   221  															tmp = conv(tmp, Types[TUINTPTR])
   222  															n.Nbody = list(n.Nbody, Nod(OAS, hn, tmp))
   223  
   224  															// memclr(hp, hn)
   225  															fn := mkcall("memclr", nil, nil, hp, hn)
   226  
   227  															n.Nbody = list(n.Nbody, fn)
   228  
   229  															// i = len(a) - 1
   230  															v1 = Nod(OAS, v1, Nod(OSUB, Nod(OLEN, a, nil), Nodintconst(1)))
   231  
   232  															n.Nbody = list(n.Nbody, v1)
   233  
   234  															typecheck(&n.Left, Erv)
   235  															typechecklist(n.Nbody, Etop)
   236  															walkstmt(&n)
   237  															lineno = int32(lno)
   238  															return
   239  														}
   240  													}
   241  												}
   242  											}
   243  										}
   244  									}
   245  								}
   246  							}
   247  						}
   248  					}
   249  				}
   250  			}
   251  		}
   252  
   253  		// orderstmt arranged for a copy of the array/slice variable if needed.
   254  		ha := a
   255  
   256  		hv1 := temp(Types[TINT])
   257  		hn := temp(Types[TINT])
   258  		var hp *Node
   259  
   260  		init = list(init, Nod(OAS, hv1, nil))
   261  		init = list(init, Nod(OAS, hn, Nod(OLEN, ha, nil)))
   262  		if v2 != nil {
   263  			hp = temp(Ptrto(n.Type.Type))
   264  			tmp := Nod(OINDEX, ha, Nodintconst(0))
   265  			tmp.Bounded = true
   266  			init = list(init, Nod(OAS, hp, Nod(OADDR, tmp, nil)))
   267  		}
   268  
   269  		n.Left = Nod(OLT, hv1, hn)
   270  		n.Right = Nod(OAS, hv1, Nod(OADD, hv1, Nodintconst(1)))
   271  		if v1 == nil {
   272  			body = nil
   273  		} else if v2 == nil {
   274  			body = list1(Nod(OAS, v1, hv1))
   275  		} else {
   276  			a := Nod(OAS2, nil, nil)
   277  			a.List = list(list1(v1), v2)
   278  			a.Rlist = list(list1(hv1), Nod(OIND, hp, nil))
   279  			body = list1(a)
   280  
   281  			// Advance pointer as part of increment.
   282  			// We used to advance the pointer before executing the loop body,
   283  			// but doing so would make the pointer point past the end of the
   284  			// array during the final iteration, possibly causing another unrelated
   285  			// piece of memory not to be garbage collected until the loop finished.
   286  			// Advancing during the increment ensures that the pointer p only points
   287  			// pass the end of the array during the final "p++; i++; if(i >= len(x)) break;",
   288  			// after which p is dead, so it cannot confuse the collector.
   289  			tmp := Nod(OADD, hp, Nodintconst(t.Type.Width))
   290  
   291  			tmp.Type = hp.Type
   292  			tmp.Typecheck = 1
   293  			tmp.Right.Type = Types[Tptr]
   294  			tmp.Right.Typecheck = 1
   295  			a = Nod(OAS, hp, tmp)
   296  			typecheck(&a, Etop)
   297  			n.Right.Ninit = list1(a)
   298  		}
   299  
   300  		// orderstmt allocated the iterator for us.
   301  	// we only use a once, so no copy needed.
   302  	case TMAP:
   303  		ha := a
   304  
   305  		th := hiter(t)
   306  		hit := prealloc[n]
   307  		hit.Type = th
   308  		n.Left = nil
   309  		keyname := newname(th.Type.Sym)      // depends on layout of iterator struct.  See reflect.go:hiter
   310  		valname := newname(th.Type.Down.Sym) // ditto
   311  
   312  		fn := syslook("mapiterinit", 1)
   313  
   314  		substArgTypes(fn, t.Down, t.Type, th)
   315  		init = list(init, mkcall1(fn, nil, nil, typename(t), ha, Nod(OADDR, hit, nil)))
   316  		n.Left = Nod(ONE, Nod(ODOT, hit, keyname), nodnil())
   317  
   318  		fn = syslook("mapiternext", 1)
   319  		substArgTypes(fn, th)
   320  		n.Right = mkcall1(fn, nil, nil, Nod(OADDR, hit, nil))
   321  
   322  		key := Nod(ODOT, hit, keyname)
   323  		key = Nod(OIND, key, nil)
   324  		if v1 == nil {
   325  			body = nil
   326  		} else if v2 == nil {
   327  			body = list1(Nod(OAS, v1, key))
   328  		} else {
   329  			val := Nod(ODOT, hit, valname)
   330  			val = Nod(OIND, val, nil)
   331  			a := Nod(OAS2, nil, nil)
   332  			a.List = list(list1(v1), v2)
   333  			a.Rlist = list(list1(key), val)
   334  			body = list1(a)
   335  		}
   336  
   337  		// orderstmt arranged for a copy of the channel variable.
   338  	case TCHAN:
   339  		ha := a
   340  
   341  		n.Left = nil
   342  
   343  		hv1 := temp(t.Type)
   344  		hv1.Typecheck = 1
   345  		if haspointers(t.Type) {
   346  			init = list(init, Nod(OAS, hv1, nil))
   347  		}
   348  		hb := temp(Types[TBOOL])
   349  
   350  		n.Left = Nod(ONE, hb, Nodbool(false))
   351  		a := Nod(OAS2RECV, nil, nil)
   352  		a.Typecheck = 1
   353  		a.List = list(list1(hv1), hb)
   354  		a.Rlist = list1(Nod(ORECV, ha, nil))
   355  		n.Left.Ninit = list1(a)
   356  		if v1 == nil {
   357  			body = nil
   358  		} else {
   359  			body = list1(Nod(OAS, v1, hv1))
   360  		}
   361  
   362  		// orderstmt arranged for a copy of the string variable.
   363  	case TSTRING:
   364  		ha := a
   365  
   366  		ohv1 := temp(Types[TINT])
   367  
   368  		hv1 := temp(Types[TINT])
   369  		init = list(init, Nod(OAS, hv1, nil))
   370  
   371  		var a *Node
   372  		var hv2 *Node
   373  		if v2 == nil {
   374  			a = Nod(OAS, hv1, mkcall("stringiter", Types[TINT], nil, ha, hv1))
   375  		} else {
   376  			hv2 = temp(runetype)
   377  			a = Nod(OAS2, nil, nil)
   378  			a.List = list(list1(hv1), hv2)
   379  			fn := syslook("stringiter2", 0)
   380  			a.Rlist = list1(mkcall1(fn, getoutargx(fn.Type), nil, ha, hv1))
   381  		}
   382  
   383  		n.Left = Nod(ONE, hv1, Nodintconst(0))
   384  		n.Left.Ninit = list(list1(Nod(OAS, ohv1, hv1)), a)
   385  
   386  		body = nil
   387  		if v1 != nil {
   388  			body = list1(Nod(OAS, v1, ohv1))
   389  		}
   390  		if v2 != nil {
   391  			body = list(body, Nod(OAS, v2, hv2))
   392  		}
   393  	}
   394  
   395  	n.Op = OFOR
   396  	typechecklist(init, Etop)
   397  	n.Ninit = concat(n.Ninit, init)
   398  	typechecklist(n.Left.Ninit, Etop)
   399  	typecheck(&n.Left, Erv)
   400  	typecheck(&n.Right, Etop)
   401  	typechecklist(body, Etop)
   402  	n.Nbody = concat(body, n.Nbody)
   403  	walkstmt(&n)
   404  
   405  	lineno = int32(lno)
   406  }