github.com/rsc/tmp@v0.0.0-20240517235954-6deaab19748b/bootstrap/internal/gc/range.go (about)

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