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