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