Source file src/cmd/compile/internal/walk/convert.go

     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 walk
     6  
     7  import (
     8  	"encoding/binary"
     9  	"go/constant"
    10  
    11  	"cmd/compile/internal/base"
    12  	"cmd/compile/internal/ir"
    13  	"cmd/compile/internal/reflectdata"
    14  	"cmd/compile/internal/ssagen"
    15  	"cmd/compile/internal/typecheck"
    16  	"cmd/compile/internal/types"
    17  	"cmd/internal/sys"
    18  )
    19  
    20  // walkConv walks an OCONV or OCONVNOP (but not OCONVIFACE) node.
    21  func walkConv(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
    22  	n.X = walkExpr(n.X, init)
    23  	if n.Op() == ir.OCONVNOP && n.Type() == n.X.Type() {
    24  		return n.X
    25  	}
    26  	if n.Op() == ir.OCONVNOP && ir.ShouldCheckPtr(ir.CurFunc, 1) {
    27  		if n.Type().IsUnsafePtr() && n.X.Type().IsUintptr() { // uintptr to unsafe.Pointer
    28  			return walkCheckPtrArithmetic(n, init)
    29  		}
    30  	}
    31  	param, result := rtconvfn(n.X.Type(), n.Type())
    32  	if param == types.Txxx {
    33  		return n
    34  	}
    35  	fn := types.BasicTypeNames[param] + "to" + types.BasicTypeNames[result]
    36  	return typecheck.Conv(mkcall(fn, types.Types[result], init, typecheck.Conv(n.X, types.Types[param])), n.Type())
    37  }
    38  
    39  // walkConvInterface walks an OCONVIFACE node.
    40  func walkConvInterface(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
    41  
    42  	n.X = walkExpr(n.X, init)
    43  
    44  	fromType := n.X.Type()
    45  	toType := n.Type()
    46  	if !fromType.IsInterface() && !ir.IsBlank(ir.CurFunc.Nname) {
    47  		// skip unnamed functions (func _())
    48  		if fromType.HasShape() {
    49  			// Unified IR uses OCONVIFACE for converting all derived types
    50  			// to interface type. Avoid assertion failure in
    51  			// MarkTypeUsedInInterface, because we've marked used types
    52  			// separately anyway.
    53  		} else {
    54  			reflectdata.MarkTypeUsedInInterface(fromType, ir.CurFunc.LSym)
    55  		}
    56  	}
    57  
    58  	if !fromType.IsInterface() {
    59  		typeWord := reflectdata.ConvIfaceTypeWord(base.Pos, n)
    60  		l := ir.NewBinaryExpr(base.Pos, ir.OMAKEFACE, typeWord, dataWord(n, init))
    61  		l.SetType(toType)
    62  		l.SetTypecheck(n.Typecheck())
    63  		return l
    64  	}
    65  	if fromType.IsEmptyInterface() {
    66  		base.Fatalf("OCONVIFACE can't operate on an empty interface")
    67  	}
    68  
    69  	// Evaluate the input interface.
    70  	c := typecheck.TempAt(base.Pos, ir.CurFunc, fromType)
    71  	init.Append(ir.NewAssignStmt(base.Pos, c, n.X))
    72  
    73  	if toType.IsEmptyInterface() {
    74  		// Implement interface to empty interface conversion:
    75  		//
    76  		// var res *uint8
    77  		// res = (*uint8)(unsafe.Pointer(itab))
    78  		// if res != nil {
    79  		//    res = res.type
    80  		// }
    81  
    82  		// Grab its parts.
    83  		itab := ir.NewUnaryExpr(base.Pos, ir.OITAB, c)
    84  		itab.SetType(types.Types[types.TUINTPTR].PtrTo())
    85  		itab.SetTypecheck(1)
    86  		data := ir.NewUnaryExpr(n.Pos(), ir.OIDATA, c)
    87  		data.SetType(types.Types[types.TUINT8].PtrTo()) // Type is generic pointer - we're just passing it through.
    88  		data.SetTypecheck(1)
    89  
    90  		typeWord := typecheck.TempAt(base.Pos, ir.CurFunc, types.NewPtr(types.Types[types.TUINT8]))
    91  		init.Append(ir.NewAssignStmt(base.Pos, typeWord, typecheck.Conv(typecheck.Conv(itab, types.Types[types.TUNSAFEPTR]), typeWord.Type())))
    92  		nif := ir.NewIfStmt(base.Pos, typecheck.Expr(ir.NewBinaryExpr(base.Pos, ir.ONE, typeWord, typecheck.NodNil())), nil, nil)
    93  		nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, typeWord, itabType(typeWord))}
    94  		init.Append(nif)
    95  
    96  		// Build the result.
    97  		// e = iface{typeWord, data}
    98  		e := ir.NewBinaryExpr(base.Pos, ir.OMAKEFACE, typeWord, data)
    99  		e.SetType(toType) // assign type manually, typecheck doesn't understand OEFACE.
   100  		e.SetTypecheck(1)
   101  		return e
   102  	}
   103  
   104  	// Must be converting I2I (more specific to less specific interface).
   105  	// Use the same code as e, _ = c.(T).
   106  	var rhs ir.Node
   107  	if n.TypeWord == nil || n.TypeWord.Op() == ir.OADDR && n.TypeWord.(*ir.AddrExpr).X.Op() == ir.OLINKSYMOFFSET {
   108  		// Fixed (not loaded from a dictionary) type.
   109  		ta := ir.NewTypeAssertExpr(base.Pos, c, toType)
   110  		ta.SetOp(ir.ODOTTYPE2)
   111  		// Allocate a descriptor for this conversion to pass to the runtime.
   112  		ta.Descriptor = makeTypeAssertDescriptor(toType, true)
   113  		rhs = ta
   114  	} else {
   115  		ta := ir.NewDynamicTypeAssertExpr(base.Pos, ir.ODYNAMICDOTTYPE2, c, n.TypeWord)
   116  		rhs = ta
   117  	}
   118  	rhs.SetType(toType)
   119  	rhs.SetTypecheck(1)
   120  
   121  	res := typecheck.TempAt(base.Pos, ir.CurFunc, toType)
   122  	as := ir.NewAssignListStmt(base.Pos, ir.OAS2DOTTYPE, []ir.Node{res, ir.BlankNode}, []ir.Node{rhs})
   123  	init.Append(as)
   124  	return res
   125  }
   126  
   127  // Returns the data word (the second word) used to represent conv.X in
   128  // an interface.
   129  func dataWord(conv *ir.ConvExpr, init *ir.Nodes) ir.Node {
   130  	pos, n := conv.Pos(), conv.X
   131  	fromType := n.Type()
   132  
   133  	// If it's a pointer, it is its own representation.
   134  	if types.IsDirectIface(fromType) {
   135  		return n
   136  	}
   137  
   138  	isInteger := fromType.IsInteger()
   139  	isBool := fromType.IsBoolean()
   140  	if sc := fromType.SoleComponent(); sc != nil {
   141  		isInteger = sc.IsInteger()
   142  		isBool = sc.IsBoolean()
   143  	}
   144  
   145  	diagnose := func(msg string, n ir.Node) {
   146  		if base.Debug.EscapeDebug > 0 {
   147  			// This output is most useful with -gcflags=-W=2 or similar because
   148  			// it often prints a temp variable name.
   149  			base.WarnfAt(n.Pos(), "convert: %s: %v", msg, n)
   150  		}
   151  	}
   152  
   153  	// Try a bunch of cases to avoid an allocation.
   154  	var value ir.Node
   155  	switch {
   156  	case fromType.Size() == 0:
   157  		// n is zero-sized. Use zerobase.
   158  		diagnose("using global for zero-sized interface value", n)
   159  		cheapExpr(n, init) // Evaluate n for side-effects. See issue 19246.
   160  		value = ir.NewLinksymExpr(base.Pos, ir.Syms.Zerobase, types.Types[types.TUINTPTR])
   161  	case isBool || fromType.Size() == 1 && isInteger:
   162  		// n is a bool/byte. Use staticuint64s[n * 8] on little-endian
   163  		// and staticuint64s[n * 8 + 7] on big-endian.
   164  		diagnose("using global for single-byte interface value", n)
   165  		n = cheapExpr(n, init)
   166  		n = soleComponent(init, n)
   167  		// byteindex widens n so that the multiplication doesn't overflow.
   168  		index := ir.NewBinaryExpr(base.Pos, ir.OLSH, byteindex(n), ir.NewInt(base.Pos, 3))
   169  		if ssagen.Arch.LinkArch.ByteOrder == binary.BigEndian {
   170  			index = ir.NewBinaryExpr(base.Pos, ir.OADD, index, ir.NewInt(base.Pos, 7))
   171  		}
   172  		// The actual type is [256]uint64, but we use [256*8]uint8 so we can address
   173  		// individual bytes.
   174  		staticuint64s := ir.NewLinksymExpr(base.Pos, ir.Syms.Staticuint64s, types.NewArray(types.Types[types.TUINT8], 256*8))
   175  		xe := ir.NewIndexExpr(base.Pos, staticuint64s, index)
   176  		xe.SetBounded(true)
   177  		value = xe
   178  	case n.Op() == ir.OLINKSYMOFFSET && n.(*ir.LinksymOffsetExpr).Linksym == ir.Syms.ZeroVal && n.(*ir.LinksymOffsetExpr).Offset_ == 0:
   179  		// n is using zeroVal, so we can use n directly.
   180  		// (Note that n does not have a proper pos in this case, so using conv for the diagnostic instead.)
   181  		diagnose("using global for zero value interface value", conv)
   182  		value = n
   183  	case n.Op() == ir.ONAME && n.(*ir.Name).Class == ir.PEXTERN && n.(*ir.Name).Readonly():
   184  		// n is a readonly global; use it directly.
   185  		diagnose("using global for interface value", n)
   186  		value = n
   187  	case conv.Esc() == ir.EscNone && fromType.Size() <= 1024:
   188  		// n does not escape. Use a stack temporary initialized to n.
   189  		diagnose("using stack temporary for interface value", n)
   190  		value = typecheck.TempAt(base.Pos, ir.CurFunc, fromType)
   191  		init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, value, n)))
   192  	}
   193  	if value != nil {
   194  		// The interface data word is &value.
   195  		return typecheck.Expr(typecheck.NodAddr(value))
   196  	}
   197  
   198  	// Time to do an allocation. We'll call into the runtime for that.
   199  	fnname, argType, needsaddr := dataWordFuncName(fromType)
   200  	var fn *ir.Name
   201  
   202  	var args []ir.Node
   203  	if needsaddr {
   204  		// Types of large or unknown size are passed by reference.
   205  		// Orderexpr arranged for n to be a temporary for all
   206  		// the conversions it could see. Comparison of an interface
   207  		// with a non-interface, especially in a switch on interface value
   208  		// with non-interface cases, is not visible to order.stmt, so we
   209  		// have to fall back on allocating a temp here.
   210  		if !ir.IsAddressable(n) {
   211  			n = copyExpr(n, fromType, init)
   212  		}
   213  		fn = typecheck.LookupRuntime(fnname, fromType)
   214  		args = []ir.Node{reflectdata.ConvIfaceSrcRType(base.Pos, conv), typecheck.NodAddr(n)}
   215  	} else {
   216  		// Use a specialized conversion routine that takes the type being
   217  		// converted by value, not by pointer.
   218  		fn = typecheck.LookupRuntime(fnname)
   219  		var arg ir.Node
   220  		switch {
   221  		case fromType == argType:
   222  			// already in the right type, nothing to do
   223  			arg = n
   224  		case fromType.Kind() == argType.Kind(),
   225  			fromType.IsPtrShaped() && argType.IsPtrShaped():
   226  			// can directly convert (e.g. named type to underlying type, or one pointer to another)
   227  			// TODO: never happens because pointers are directIface?
   228  			arg = ir.NewConvExpr(pos, ir.OCONVNOP, argType, n)
   229  		case fromType.IsInteger() && argType.IsInteger():
   230  			// can directly convert (e.g. int32 to uint32)
   231  			arg = ir.NewConvExpr(pos, ir.OCONV, argType, n)
   232  		default:
   233  			// unsafe cast through memory
   234  			arg = copyExpr(n, fromType, init)
   235  			var addr ir.Node = typecheck.NodAddr(arg)
   236  			addr = ir.NewConvExpr(pos, ir.OCONVNOP, argType.PtrTo(), addr)
   237  			arg = ir.NewStarExpr(pos, addr)
   238  			arg.SetType(argType)
   239  		}
   240  		args = []ir.Node{arg}
   241  	}
   242  	call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil)
   243  	call.Args = args
   244  	return safeExpr(walkExpr(typecheck.Expr(call), init), init)
   245  }
   246  
   247  // walkBytesRunesToString walks an OBYTES2STR or ORUNES2STR node.
   248  func walkBytesRunesToString(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
   249  	a := typecheck.NodNil()
   250  	if n.Esc() == ir.EscNone {
   251  		// Create temporary buffer for string on stack.
   252  		a = stackBufAddr(tmpstringbufsize, types.Types[types.TUINT8])
   253  	}
   254  	if n.Op() == ir.ORUNES2STR {
   255  		// slicerunetostring(*[32]byte, []rune) string
   256  		return mkcall("slicerunetostring", n.Type(), init, a, n.X)
   257  	}
   258  	// slicebytetostring(*[32]byte, ptr *byte, n int) string
   259  	n.X = cheapExpr(n.X, init)
   260  	ptr, len := backingArrayPtrLen(n.X)
   261  	return mkcall("slicebytetostring", n.Type(), init, a, ptr, len)
   262  }
   263  
   264  // walkBytesToStringTemp walks an OBYTES2STRTMP node.
   265  func walkBytesToStringTemp(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
   266  	n.X = walkExpr(n.X, init)
   267  	if !base.Flag.Cfg.Instrumenting {
   268  		// Let the backend handle OBYTES2STRTMP directly
   269  		// to avoid a function call to slicebytetostringtmp.
   270  		return n
   271  	}
   272  	// slicebytetostringtmp(ptr *byte, n int) string
   273  	n.X = cheapExpr(n.X, init)
   274  	ptr, len := backingArrayPtrLen(n.X)
   275  	return mkcall("slicebytetostringtmp", n.Type(), init, ptr, len)
   276  }
   277  
   278  // walkRuneToString walks an ORUNESTR node.
   279  func walkRuneToString(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
   280  	a := typecheck.NodNil()
   281  	if n.Esc() == ir.EscNone {
   282  		a = stackBufAddr(4, types.Types[types.TUINT8])
   283  	}
   284  	// intstring(*[4]byte, rune)
   285  	return mkcall("intstring", n.Type(), init, a, typecheck.Conv(n.X, types.Types[types.TINT64]))
   286  }
   287  
   288  // walkStringToBytes walks an OSTR2BYTES node.
   289  func walkStringToBytes(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
   290  	s := n.X
   291  
   292  	if expr, ok := s.(*ir.AddStringExpr); ok {
   293  		return walkAddString(expr, init, n)
   294  	}
   295  
   296  	if ir.IsConst(s, constant.String) {
   297  		sc := ir.StringVal(s)
   298  
   299  		// Allocate a [n]byte of the right size.
   300  		t := types.NewArray(types.Types[types.TUINT8], int64(len(sc)))
   301  		var a ir.Node
   302  		if n.Esc() == ir.EscNone && len(sc) <= int(ir.MaxImplicitStackVarSize) {
   303  			a = stackBufAddr(t.NumElem(), t.Elem())
   304  		} else {
   305  			types.CalcSize(t)
   306  			a = ir.NewUnaryExpr(base.Pos, ir.ONEW, nil)
   307  			a.SetType(types.NewPtr(t))
   308  			a.SetTypecheck(1)
   309  			a.MarkNonNil()
   310  		}
   311  		p := typecheck.TempAt(base.Pos, ir.CurFunc, t.PtrTo()) // *[n]byte
   312  		init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, p, a)))
   313  
   314  		// Copy from the static string data to the [n]byte.
   315  		if len(sc) > 0 {
   316  			sptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, s)
   317  			sptr.SetBounded(true)
   318  			as := ir.NewAssignStmt(base.Pos, ir.NewStarExpr(base.Pos, p), ir.NewStarExpr(base.Pos, typecheck.ConvNop(sptr, t.PtrTo())))
   319  			appendWalkStmt(init, as)
   320  		}
   321  
   322  		// Slice the [n]byte to a []byte.
   323  		slice := ir.NewSliceExpr(n.Pos(), ir.OSLICEARR, p, nil, nil, nil)
   324  		slice.SetType(n.Type())
   325  		slice.SetTypecheck(1)
   326  		return walkExpr(slice, init)
   327  	}
   328  
   329  	a := typecheck.NodNil()
   330  	if n.Esc() == ir.EscNone {
   331  		// Create temporary buffer for slice on stack.
   332  		a = stackBufAddr(tmpstringbufsize, types.Types[types.TUINT8])
   333  	}
   334  	// stringtoslicebyte(*32[byte], string) []byte
   335  	return mkcall("stringtoslicebyte", n.Type(), init, a, typecheck.Conv(s, types.Types[types.TSTRING]))
   336  }
   337  
   338  // walkStringToBytesTemp walks an OSTR2BYTESTMP node.
   339  func walkStringToBytesTemp(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
   340  	// []byte(string) conversion that creates a slice
   341  	// referring to the actual string bytes.
   342  	// This conversion is handled later by the backend and
   343  	// is only for use by internal compiler optimizations
   344  	// that know that the slice won't be mutated.
   345  	// The only such case today is:
   346  	// for i, c := range []byte(string)
   347  	n.X = walkExpr(n.X, init)
   348  	return n
   349  }
   350  
   351  // walkStringToRunes walks an OSTR2RUNES node.
   352  func walkStringToRunes(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
   353  	a := typecheck.NodNil()
   354  	if n.Esc() == ir.EscNone {
   355  		// Create temporary buffer for slice on stack.
   356  		a = stackBufAddr(tmpstringbufsize, types.Types[types.TINT32])
   357  	}
   358  	// stringtoslicerune(*[32]rune, string) []rune
   359  	return mkcall("stringtoslicerune", n.Type(), init, a, typecheck.Conv(n.X, types.Types[types.TSTRING]))
   360  }
   361  
   362  // dataWordFuncName returns the name of the function used to convert a value of type "from"
   363  // to the data word of an interface.
   364  // argType is the type the argument needs to be coerced to.
   365  // needsaddr reports whether the value should be passed (needaddr==false) or its address (needsaddr==true).
   366  func dataWordFuncName(from *types.Type) (fnname string, argType *types.Type, needsaddr bool) {
   367  	if from.IsInterface() {
   368  		base.Fatalf("can only handle non-interfaces")
   369  	}
   370  	switch {
   371  	case from.Size() == 2 && uint8(from.Alignment()) == 2:
   372  		return "convT16", types.Types[types.TUINT16], false
   373  	case from.Size() == 4 && uint8(from.Alignment()) == 4 && !from.HasPointers():
   374  		return "convT32", types.Types[types.TUINT32], false
   375  	case from.Size() == 8 && uint8(from.Alignment()) == uint8(types.Types[types.TUINT64].Alignment()) && !from.HasPointers():
   376  		return "convT64", types.Types[types.TUINT64], false
   377  	}
   378  	if sc := from.SoleComponent(); sc != nil {
   379  		switch {
   380  		case sc.IsString():
   381  			return "convTstring", types.Types[types.TSTRING], false
   382  		case sc.IsSlice():
   383  			return "convTslice", types.NewSlice(types.Types[types.TUINT8]), false // the element type doesn't matter
   384  		}
   385  	}
   386  
   387  	if from.HasPointers() {
   388  		return "convT", types.Types[types.TUNSAFEPTR], true
   389  	}
   390  	return "convTnoptr", types.Types[types.TUNSAFEPTR], true
   391  }
   392  
   393  // rtconvfn returns the parameter and result types that will be used by a
   394  // runtime function to convert from type src to type dst. The runtime function
   395  // name can be derived from the names of the returned types.
   396  //
   397  // If no such function is necessary, it returns (Txxx, Txxx).
   398  func rtconvfn(src, dst *types.Type) (param, result types.Kind) {
   399  	if ssagen.Arch.SoftFloat {
   400  		return types.Txxx, types.Txxx
   401  	}
   402  
   403  	switch ssagen.Arch.LinkArch.Family {
   404  	case sys.ARM, sys.MIPS:
   405  		if src.IsFloat() {
   406  			switch dst.Kind() {
   407  			case types.TINT64, types.TUINT64:
   408  				return types.TFLOAT64, dst.Kind()
   409  			}
   410  		}
   411  		if dst.IsFloat() {
   412  			switch src.Kind() {
   413  			case types.TINT64, types.TUINT64:
   414  				return src.Kind(), dst.Kind()
   415  			}
   416  		}
   417  
   418  	case sys.I386:
   419  		if src.IsFloat() {
   420  			switch dst.Kind() {
   421  			case types.TINT64, types.TUINT64:
   422  				return types.TFLOAT64, dst.Kind()
   423  			case types.TUINT32, types.TUINT, types.TUINTPTR:
   424  				return types.TFLOAT64, types.TUINT32
   425  			}
   426  		}
   427  		if dst.IsFloat() {
   428  			switch src.Kind() {
   429  			case types.TINT64, types.TUINT64:
   430  				return src.Kind(), dst.Kind()
   431  			case types.TUINT32, types.TUINT, types.TUINTPTR:
   432  				return types.TUINT32, types.TFLOAT64
   433  			}
   434  		}
   435  	}
   436  	return types.Txxx, types.Txxx
   437  }
   438  
   439  func soleComponent(init *ir.Nodes, n ir.Node) ir.Node {
   440  	if n.Type().SoleComponent() == nil {
   441  		return n
   442  	}
   443  	// Keep in sync with cmd/compile/internal/types/type.go:Type.SoleComponent.
   444  	for {
   445  		switch {
   446  		case n.Type().IsStruct():
   447  			if n.Type().Field(0).Sym.IsBlank() {
   448  				// Treat blank fields as the zero value as the Go language requires.
   449  				n = typecheck.TempAt(base.Pos, ir.CurFunc, n.Type().Field(0).Type)
   450  				appendWalkStmt(init, ir.NewAssignStmt(base.Pos, n, nil))
   451  				continue
   452  			}
   453  			n = typecheck.DotField(n.Pos(), n, 0)
   454  		case n.Type().IsArray():
   455  			n = typecheck.Expr(ir.NewIndexExpr(n.Pos(), n, ir.NewInt(base.Pos, 0)))
   456  		default:
   457  			return n
   458  		}
   459  	}
   460  }
   461  
   462  // byteindex converts n, which is byte-sized, to an int used to index into an array.
   463  // We cannot use conv, because we allow converting bool to int here,
   464  // which is forbidden in user code.
   465  func byteindex(n ir.Node) ir.Node {
   466  	// We cannot convert from bool to int directly.
   467  	// While converting from int8 to int is possible, it would yield
   468  	// the wrong result for negative values.
   469  	// Reinterpreting the value as an unsigned byte solves both cases.
   470  	if !types.Identical(n.Type(), types.Types[types.TUINT8]) {
   471  		n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n)
   472  		n.SetType(types.Types[types.TUINT8])
   473  		n.SetTypecheck(1)
   474  	}
   475  	n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n)
   476  	n.SetType(types.Types[types.TINT])
   477  	n.SetTypecheck(1)
   478  	return n
   479  }
   480  
   481  func walkCheckPtrArithmetic(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
   482  	// Calling cheapExpr(n, init) below leads to a recursive call to
   483  	// walkExpr, which leads us back here again. Use n.Checkptr to
   484  	// prevent infinite loops.
   485  	if n.CheckPtr() {
   486  		return n
   487  	}
   488  	n.SetCheckPtr(true)
   489  	defer n.SetCheckPtr(false)
   490  
   491  	// TODO(mdempsky): Make stricter. We only need to exempt
   492  	// reflect.Value.Pointer and reflect.Value.UnsafeAddr.
   493  	switch n.X.Op() {
   494  	case ir.OCALLMETH:
   495  		base.FatalfAt(n.X.Pos(), "OCALLMETH missed by typecheck")
   496  	case ir.OCALLFUNC, ir.OCALLINTER:
   497  		return n
   498  	}
   499  
   500  	if n.X.Op() == ir.ODOTPTR && ir.IsReflectHeaderDataField(n.X) {
   501  		return n
   502  	}
   503  
   504  	// Find original unsafe.Pointer operands involved in this
   505  	// arithmetic expression.
   506  	//
   507  	// "It is valid both to add and to subtract offsets from a
   508  	// pointer in this way. It is also valid to use &^ to round
   509  	// pointers, usually for alignment."
   510  	var originals []ir.Node
   511  	var walk func(n ir.Node)
   512  	walk = func(n ir.Node) {
   513  		switch n.Op() {
   514  		case ir.OADD:
   515  			n := n.(*ir.BinaryExpr)
   516  			walk(n.X)
   517  			walk(n.Y)
   518  		case ir.OSUB, ir.OANDNOT:
   519  			n := n.(*ir.BinaryExpr)
   520  			walk(n.X)
   521  		case ir.OCONVNOP:
   522  			n := n.(*ir.ConvExpr)
   523  			if n.X.Type().IsUnsafePtr() {
   524  				n.X = cheapExpr(n.X, init)
   525  				originals = append(originals, typecheck.ConvNop(n.X, types.Types[types.TUNSAFEPTR]))
   526  			}
   527  		}
   528  	}
   529  	walk(n.X)
   530  
   531  	cheap := cheapExpr(n, init)
   532  
   533  	slice := typecheck.MakeDotArgs(base.Pos, types.NewSlice(types.Types[types.TUNSAFEPTR]), originals)
   534  	slice.SetEsc(ir.EscNone)
   535  
   536  	init.Append(mkcall("checkptrArithmetic", nil, init, typecheck.ConvNop(cheap, types.Types[types.TUNSAFEPTR]), slice))
   537  	// TODO(khr): Mark backing store of slice as dead. This will allow us to reuse
   538  	// the backing store for multiple calls to checkptrArithmetic.
   539  
   540  	return cheap
   541  }
   542  
   543  // walkSliceToArray walks an OSLICE2ARR expression.
   544  func walkSliceToArray(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
   545  	// Replace T(x) with *(*T)(x).
   546  	conv := typecheck.Expr(ir.NewConvExpr(base.Pos, ir.OCONV, types.NewPtr(n.Type()), n.X)).(*ir.ConvExpr)
   547  	deref := typecheck.Expr(ir.NewStarExpr(base.Pos, conv)).(*ir.StarExpr)
   548  
   549  	// The OSLICE2ARRPTR conversion handles checking the slice length,
   550  	// so the dereference can't fail.
   551  	//
   552  	// However, this is more than just an optimization: if T is a
   553  	// zero-length array, then x (and thus (*T)(x)) can be nil, but T(x)
   554  	// should *not* panic. So suppressing the nil check here is
   555  	// necessary for correctness in that case.
   556  	deref.SetBounded(true)
   557  
   558  	return walkExpr(deref, init)
   559  }
   560  

View as plain text