github.com/euank/go@v0.0.0-20160829210321-495514729181/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, TSLICE:
    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, TSLICE:
   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  	case TMAP:
   221  		// orderstmt allocated the iterator for us.
   222  		// we only use a once, so no copy needed.
   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  	case TCHAN:
   258  		// orderstmt arranged for a copy of the channel variable.
   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  		// Zero hv1. This prevents hv1 from being the sole, inaccessible
   282  		// reference to an otherwise GC-able value during the next channel receive.
   283  		// See issue 15281.
   284  		body = append(body, Nod(OAS, hv1, nil))
   285  
   286  	case TSTRING:
   287  		// orderstmt arranged for a copy of the string variable.
   288  		ha := a
   289  
   290  		ohv1 := temp(Types[TINT])
   291  
   292  		hv1 := temp(Types[TINT])
   293  		init = append(init, Nod(OAS, hv1, nil))
   294  
   295  		var a *Node
   296  		var hv2 *Node
   297  		if v2 == nil {
   298  			a = Nod(OAS, hv1, mkcall("stringiter", Types[TINT], nil, ha, hv1))
   299  		} else {
   300  			hv2 = temp(runetype)
   301  			a = Nod(OAS2, nil, nil)
   302  			a.List.Set([]*Node{hv1, hv2})
   303  			fn := syslook("stringiter2")
   304  			a.Rlist.Set1(mkcall1(fn, fn.Type.Results(), nil, ha, hv1))
   305  		}
   306  
   307  		n.Left = Nod(ONE, hv1, Nodintconst(0))
   308  		n.Left.Ninit.Set([]*Node{Nod(OAS, ohv1, hv1), a})
   309  
   310  		body = nil
   311  		if v1 != nil {
   312  			body = []*Node{Nod(OAS, v1, ohv1)}
   313  		}
   314  		if v2 != nil {
   315  			body = append(body, Nod(OAS, v2, hv2))
   316  		}
   317  	}
   318  
   319  	n.Op = OFOR
   320  	typecheckslice(init, Etop)
   321  	n.Ninit.Append(init...)
   322  	typecheckslice(n.Left.Ninit.Slice(), Etop)
   323  	n.Left = typecheck(n.Left, Erv)
   324  	n.Right = typecheck(n.Right, Etop)
   325  	typecheckslice(body, Etop)
   326  	n.Nbody.Set(append(body, n.Nbody.Slice()...))
   327  	n = walkstmt(n)
   328  
   329  	lineno = lno
   330  }
   331  
   332  // Lower n into runtime·memclr if possible, for
   333  // fast zeroing of slices and arrays (issue 5373).
   334  // Look for instances of
   335  //
   336  // for i := range a {
   337  // 	a[i] = zero
   338  // }
   339  //
   340  // in which the evaluation of a is side-effect-free.
   341  //
   342  // Parameters are as in walkrange: "for v1, v2 = range a".
   343  func memclrrange(n, v1, v2, a *Node) bool {
   344  	if Debug['N'] != 0 || instrumenting {
   345  		return false
   346  	}
   347  	if v1 == nil || v2 != nil {
   348  		return false
   349  	}
   350  	if n.Nbody.Len() == 0 || n.Nbody.First() == nil || n.Nbody.Len() > 1 {
   351  		return false
   352  	}
   353  	stmt := n.Nbody.First() // only stmt in body
   354  	if stmt.Op != OAS || stmt.Left.Op != OINDEX {
   355  		return false
   356  	}
   357  	if !samesafeexpr(stmt.Left.Left, a) || !samesafeexpr(stmt.Left.Right, v1) {
   358  		return false
   359  	}
   360  	elemsize := n.Type.Elem().Width
   361  	if elemsize <= 0 || !iszero(stmt.Right) {
   362  		return false
   363  	}
   364  
   365  	// Convert to
   366  	// if len(a) != 0 {
   367  	// 	hp = &a[0]
   368  	// 	hn = len(a)*sizeof(elem(a))
   369  	// 	memclr(hp, hn)
   370  	// 	i = len(a) - 1
   371  	// }
   372  	n.Op = OIF
   373  
   374  	n.Nbody.Set(nil)
   375  	n.Left = Nod(ONE, Nod(OLEN, a, nil), Nodintconst(0))
   376  
   377  	// hp = &a[0]
   378  	hp := temp(Ptrto(Types[TUINT8]))
   379  
   380  	tmp := Nod(OINDEX, a, Nodintconst(0))
   381  	tmp.Bounded = true
   382  	tmp = Nod(OADDR, tmp, nil)
   383  	tmp = Nod(OCONVNOP, tmp, nil)
   384  	tmp.Type = Ptrto(Types[TUINT8])
   385  	n.Nbody.Append(Nod(OAS, hp, tmp))
   386  
   387  	// hn = len(a) * sizeof(elem(a))
   388  	hn := temp(Types[TUINTPTR])
   389  
   390  	tmp = Nod(OLEN, a, nil)
   391  	tmp = Nod(OMUL, tmp, Nodintconst(elemsize))
   392  	tmp = conv(tmp, Types[TUINTPTR])
   393  	n.Nbody.Append(Nod(OAS, hn, tmp))
   394  
   395  	// memclr(hp, hn)
   396  	fn := mkcall("memclr", nil, nil, hp, hn)
   397  
   398  	n.Nbody.Append(fn)
   399  
   400  	// i = len(a) - 1
   401  	v1 = Nod(OAS, v1, Nod(OSUB, Nod(OLEN, a, nil), Nodintconst(1)))
   402  
   403  	n.Nbody.Append(v1)
   404  
   405  	n.Left = typecheck(n.Left, Erv)
   406  	typecheckslice(n.Nbody.Slice(), Etop)
   407  	n = walkstmt(n)
   408  	return true
   409  }