]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/walk/convert.go
cmd/compile: use cache in front of convI2I
[gostls13.git] / 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         // Try a bunch of cases to avoid an allocation.
145         var value ir.Node
146         switch {
147         case fromType.Size() == 0:
148                 // n is zero-sized. Use zerobase.
149                 cheapExpr(n, init) // Evaluate n for side-effects. See issue 19246.
150                 value = ir.NewLinksymExpr(base.Pos, ir.Syms.Zerobase, types.Types[types.TUINTPTR])
151         case isBool || fromType.Size() == 1 && isInteger:
152                 // n is a bool/byte. Use staticuint64s[n * 8] on little-endian
153                 // and staticuint64s[n * 8 + 7] on big-endian.
154                 n = cheapExpr(n, init)
155                 n = soleComponent(init, n)
156                 // byteindex widens n so that the multiplication doesn't overflow.
157                 index := ir.NewBinaryExpr(base.Pos, ir.OLSH, byteindex(n), ir.NewInt(base.Pos, 3))
158                 if ssagen.Arch.LinkArch.ByteOrder == binary.BigEndian {
159                         index = ir.NewBinaryExpr(base.Pos, ir.OADD, index, ir.NewInt(base.Pos, 7))
160                 }
161                 // The actual type is [256]uint64, but we use [256*8]uint8 so we can address
162                 // individual bytes.
163                 staticuint64s := ir.NewLinksymExpr(base.Pos, ir.Syms.Staticuint64s, types.NewArray(types.Types[types.TUINT8], 256*8))
164                 xe := ir.NewIndexExpr(base.Pos, staticuint64s, index)
165                 xe.SetBounded(true)
166                 value = xe
167         case n.Op() == ir.ONAME && n.(*ir.Name).Class == ir.PEXTERN && n.(*ir.Name).Readonly():
168                 // n is a readonly global; use it directly.
169                 value = n
170         case conv.Esc() == ir.EscNone && fromType.Size() <= 1024:
171                 // n does not escape. Use a stack temporary initialized to n.
172                 value = typecheck.TempAt(base.Pos, ir.CurFunc, fromType)
173                 init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, value, n)))
174         }
175         if value != nil {
176                 // The interface data word is &value.
177                 return typecheck.Expr(typecheck.NodAddr(value))
178         }
179
180         // Time to do an allocation. We'll call into the runtime for that.
181         fnname, argType, needsaddr := dataWordFuncName(fromType)
182         var fn *ir.Name
183
184         var args []ir.Node
185         if needsaddr {
186                 // Types of large or unknown size are passed by reference.
187                 // Orderexpr arranged for n to be a temporary for all
188                 // the conversions it could see. Comparison of an interface
189                 // with a non-interface, especially in a switch on interface value
190                 // with non-interface cases, is not visible to order.stmt, so we
191                 // have to fall back on allocating a temp here.
192                 if !ir.IsAddressable(n) {
193                         n = copyExpr(n, fromType, init)
194                 }
195                 fn = typecheck.LookupRuntime(fnname, fromType)
196                 args = []ir.Node{reflectdata.ConvIfaceSrcRType(base.Pos, conv), typecheck.NodAddr(n)}
197         } else {
198                 // Use a specialized conversion routine that takes the type being
199                 // converted by value, not by pointer.
200                 fn = typecheck.LookupRuntime(fnname)
201                 var arg ir.Node
202                 switch {
203                 case fromType == argType:
204                         // already in the right type, nothing to do
205                         arg = n
206                 case fromType.Kind() == argType.Kind(),
207                         fromType.IsPtrShaped() && argType.IsPtrShaped():
208                         // can directly convert (e.g. named type to underlying type, or one pointer to another)
209                         // TODO: never happens because pointers are directIface?
210                         arg = ir.NewConvExpr(pos, ir.OCONVNOP, argType, n)
211                 case fromType.IsInteger() && argType.IsInteger():
212                         // can directly convert (e.g. int32 to uint32)
213                         arg = ir.NewConvExpr(pos, ir.OCONV, argType, n)
214                 default:
215                         // unsafe cast through memory
216                         arg = copyExpr(n, fromType, init)
217                         var addr ir.Node = typecheck.NodAddr(arg)
218                         addr = ir.NewConvExpr(pos, ir.OCONVNOP, argType.PtrTo(), addr)
219                         arg = ir.NewStarExpr(pos, addr)
220                         arg.SetType(argType)
221                 }
222                 args = []ir.Node{arg}
223         }
224         call := ir.NewCallExpr(base.Pos, ir.OCALL, fn, nil)
225         call.Args = args
226         return safeExpr(walkExpr(typecheck.Expr(call), init), init)
227 }
228
229 // walkBytesRunesToString walks an OBYTES2STR or ORUNES2STR node.
230 func walkBytesRunesToString(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
231         a := typecheck.NodNil()
232         if n.Esc() == ir.EscNone {
233                 // Create temporary buffer for string on stack.
234                 a = stackBufAddr(tmpstringbufsize, types.Types[types.TUINT8])
235         }
236         if n.Op() == ir.ORUNES2STR {
237                 // slicerunetostring(*[32]byte, []rune) string
238                 return mkcall("slicerunetostring", n.Type(), init, a, n.X)
239         }
240         // slicebytetostring(*[32]byte, ptr *byte, n int) string
241         n.X = cheapExpr(n.X, init)
242         ptr, len := backingArrayPtrLen(n.X)
243         return mkcall("slicebytetostring", n.Type(), init, a, ptr, len)
244 }
245
246 // walkBytesToStringTemp walks an OBYTES2STRTMP node.
247 func walkBytesToStringTemp(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
248         n.X = walkExpr(n.X, init)
249         if !base.Flag.Cfg.Instrumenting {
250                 // Let the backend handle OBYTES2STRTMP directly
251                 // to avoid a function call to slicebytetostringtmp.
252                 return n
253         }
254         // slicebytetostringtmp(ptr *byte, n int) string
255         n.X = cheapExpr(n.X, init)
256         ptr, len := backingArrayPtrLen(n.X)
257         return mkcall("slicebytetostringtmp", n.Type(), init, ptr, len)
258 }
259
260 // walkRuneToString walks an ORUNESTR node.
261 func walkRuneToString(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
262         a := typecheck.NodNil()
263         if n.Esc() == ir.EscNone {
264                 a = stackBufAddr(4, types.Types[types.TUINT8])
265         }
266         // intstring(*[4]byte, rune)
267         return mkcall("intstring", n.Type(), init, a, typecheck.Conv(n.X, types.Types[types.TINT64]))
268 }
269
270 // walkStringToBytes walks an OSTR2BYTES node.
271 func walkStringToBytes(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
272         s := n.X
273         if ir.IsConst(s, constant.String) {
274                 sc := ir.StringVal(s)
275
276                 // Allocate a [n]byte of the right size.
277                 t := types.NewArray(types.Types[types.TUINT8], int64(len(sc)))
278                 var a ir.Node
279                 if n.Esc() == ir.EscNone && len(sc) <= int(ir.MaxImplicitStackVarSize) {
280                         a = stackBufAddr(t.NumElem(), t.Elem())
281                 } else {
282                         types.CalcSize(t)
283                         a = ir.NewUnaryExpr(base.Pos, ir.ONEW, nil)
284                         a.SetType(types.NewPtr(t))
285                         a.SetTypecheck(1)
286                         a.MarkNonNil()
287                 }
288                 p := typecheck.TempAt(base.Pos, ir.CurFunc, t.PtrTo()) // *[n]byte
289                 init.Append(typecheck.Stmt(ir.NewAssignStmt(base.Pos, p, a)))
290
291                 // Copy from the static string data to the [n]byte.
292                 if len(sc) > 0 {
293                         sptr := ir.NewUnaryExpr(base.Pos, ir.OSPTR, s)
294                         sptr.SetBounded(true)
295                         as := ir.NewAssignStmt(base.Pos, ir.NewStarExpr(base.Pos, p), ir.NewStarExpr(base.Pos, typecheck.ConvNop(sptr, t.PtrTo())))
296                         appendWalkStmt(init, as)
297                 }
298
299                 // Slice the [n]byte to a []byte.
300                 slice := ir.NewSliceExpr(n.Pos(), ir.OSLICEARR, p, nil, nil, nil)
301                 slice.SetType(n.Type())
302                 slice.SetTypecheck(1)
303                 return walkExpr(slice, init)
304         }
305
306         a := typecheck.NodNil()
307         if n.Esc() == ir.EscNone {
308                 // Create temporary buffer for slice on stack.
309                 a = stackBufAddr(tmpstringbufsize, types.Types[types.TUINT8])
310         }
311         // stringtoslicebyte(*32[byte], string) []byte
312         return mkcall("stringtoslicebyte", n.Type(), init, a, typecheck.Conv(s, types.Types[types.TSTRING]))
313 }
314
315 // walkStringToBytesTemp walks an OSTR2BYTESTMP node.
316 func walkStringToBytesTemp(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
317         // []byte(string) conversion that creates a slice
318         // referring to the actual string bytes.
319         // This conversion is handled later by the backend and
320         // is only for use by internal compiler optimizations
321         // that know that the slice won't be mutated.
322         // The only such case today is:
323         // for i, c := range []byte(string)
324         n.X = walkExpr(n.X, init)
325         return n
326 }
327
328 // walkStringToRunes walks an OSTR2RUNES node.
329 func walkStringToRunes(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
330         a := typecheck.NodNil()
331         if n.Esc() == ir.EscNone {
332                 // Create temporary buffer for slice on stack.
333                 a = stackBufAddr(tmpstringbufsize, types.Types[types.TINT32])
334         }
335         // stringtoslicerune(*[32]rune, string) []rune
336         return mkcall("stringtoslicerune", n.Type(), init, a, typecheck.Conv(n.X, types.Types[types.TSTRING]))
337 }
338
339 // dataWordFuncName returns the name of the function used to convert a value of type "from"
340 // to the data word of an interface.
341 // argType is the type the argument needs to be coerced to.
342 // needsaddr reports whether the value should be passed (needaddr==false) or its address (needsaddr==true).
343 func dataWordFuncName(from *types.Type) (fnname string, argType *types.Type, needsaddr bool) {
344         if from.IsInterface() {
345                 base.Fatalf("can only handle non-interfaces")
346         }
347         switch {
348         case from.Size() == 2 && uint8(from.Alignment()) == 2:
349                 return "convT16", types.Types[types.TUINT16], false
350         case from.Size() == 4 && uint8(from.Alignment()) == 4 && !from.HasPointers():
351                 return "convT32", types.Types[types.TUINT32], false
352         case from.Size() == 8 && uint8(from.Alignment()) == uint8(types.Types[types.TUINT64].Alignment()) && !from.HasPointers():
353                 return "convT64", types.Types[types.TUINT64], false
354         }
355         if sc := from.SoleComponent(); sc != nil {
356                 switch {
357                 case sc.IsString():
358                         return "convTstring", types.Types[types.TSTRING], false
359                 case sc.IsSlice():
360                         return "convTslice", types.NewSlice(types.Types[types.TUINT8]), false // the element type doesn't matter
361                 }
362         }
363
364         if from.HasPointers() {
365                 return "convT", types.Types[types.TUNSAFEPTR], true
366         }
367         return "convTnoptr", types.Types[types.TUNSAFEPTR], true
368 }
369
370 // rtconvfn returns the parameter and result types that will be used by a
371 // runtime function to convert from type src to type dst. The runtime function
372 // name can be derived from the names of the returned types.
373 //
374 // If no such function is necessary, it returns (Txxx, Txxx).
375 func rtconvfn(src, dst *types.Type) (param, result types.Kind) {
376         if ssagen.Arch.SoftFloat {
377                 return types.Txxx, types.Txxx
378         }
379
380         switch ssagen.Arch.LinkArch.Family {
381         case sys.ARM, sys.MIPS:
382                 if src.IsFloat() {
383                         switch dst.Kind() {
384                         case types.TINT64, types.TUINT64:
385                                 return types.TFLOAT64, dst.Kind()
386                         }
387                 }
388                 if dst.IsFloat() {
389                         switch src.Kind() {
390                         case types.TINT64, types.TUINT64:
391                                 return src.Kind(), dst.Kind()
392                         }
393                 }
394
395         case sys.I386:
396                 if src.IsFloat() {
397                         switch dst.Kind() {
398                         case types.TINT64, types.TUINT64:
399                                 return types.TFLOAT64, dst.Kind()
400                         case types.TUINT32, types.TUINT, types.TUINTPTR:
401                                 return types.TFLOAT64, types.TUINT32
402                         }
403                 }
404                 if dst.IsFloat() {
405                         switch src.Kind() {
406                         case types.TINT64, types.TUINT64:
407                                 return src.Kind(), dst.Kind()
408                         case types.TUINT32, types.TUINT, types.TUINTPTR:
409                                 return types.TUINT32, types.TFLOAT64
410                         }
411                 }
412         }
413         return types.Txxx, types.Txxx
414 }
415
416 func soleComponent(init *ir.Nodes, n ir.Node) ir.Node {
417         if n.Type().SoleComponent() == nil {
418                 return n
419         }
420         // Keep in sync with cmd/compile/internal/types/type.go:Type.SoleComponent.
421         for {
422                 switch {
423                 case n.Type().IsStruct():
424                         if n.Type().Field(0).Sym.IsBlank() {
425                                 // Treat blank fields as the zero value as the Go language requires.
426                                 n = typecheck.TempAt(base.Pos, ir.CurFunc, n.Type().Field(0).Type)
427                                 appendWalkStmt(init, ir.NewAssignStmt(base.Pos, n, nil))
428                                 continue
429                         }
430                         n = typecheck.DotField(n.Pos(), n, 0)
431                 case n.Type().IsArray():
432                         n = typecheck.Expr(ir.NewIndexExpr(n.Pos(), n, ir.NewInt(base.Pos, 0)))
433                 default:
434                         return n
435                 }
436         }
437 }
438
439 // byteindex converts n, which is byte-sized, to an int used to index into an array.
440 // We cannot use conv, because we allow converting bool to int here,
441 // which is forbidden in user code.
442 func byteindex(n ir.Node) ir.Node {
443         // We cannot convert from bool to int directly.
444         // While converting from int8 to int is possible, it would yield
445         // the wrong result for negative values.
446         // Reinterpreting the value as an unsigned byte solves both cases.
447         if !types.Identical(n.Type(), types.Types[types.TUINT8]) {
448                 n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n)
449                 n.SetType(types.Types[types.TUINT8])
450                 n.SetTypecheck(1)
451         }
452         n = ir.NewConvExpr(base.Pos, ir.OCONV, nil, n)
453         n.SetType(types.Types[types.TINT])
454         n.SetTypecheck(1)
455         return n
456 }
457
458 func walkCheckPtrArithmetic(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
459         // Calling cheapExpr(n, init) below leads to a recursive call to
460         // walkExpr, which leads us back here again. Use n.Checkptr to
461         // prevent infinite loops.
462         if n.CheckPtr() {
463                 return n
464         }
465         n.SetCheckPtr(true)
466         defer n.SetCheckPtr(false)
467
468         // TODO(mdempsky): Make stricter. We only need to exempt
469         // reflect.Value.Pointer and reflect.Value.UnsafeAddr.
470         switch n.X.Op() {
471         case ir.OCALLMETH:
472                 base.FatalfAt(n.X.Pos(), "OCALLMETH missed by typecheck")
473         case ir.OCALLFUNC, ir.OCALLINTER:
474                 return n
475         }
476
477         if n.X.Op() == ir.ODOTPTR && ir.IsReflectHeaderDataField(n.X) {
478                 return n
479         }
480
481         // Find original unsafe.Pointer operands involved in this
482         // arithmetic expression.
483         //
484         // "It is valid both to add and to subtract offsets from a
485         // pointer in this way. It is also valid to use &^ to round
486         // pointers, usually for alignment."
487         var originals []ir.Node
488         var walk func(n ir.Node)
489         walk = func(n ir.Node) {
490                 switch n.Op() {
491                 case ir.OADD:
492                         n := n.(*ir.BinaryExpr)
493                         walk(n.X)
494                         walk(n.Y)
495                 case ir.OSUB, ir.OANDNOT:
496                         n := n.(*ir.BinaryExpr)
497                         walk(n.X)
498                 case ir.OCONVNOP:
499                         n := n.(*ir.ConvExpr)
500                         if n.X.Type().IsUnsafePtr() {
501                                 n.X = cheapExpr(n.X, init)
502                                 originals = append(originals, typecheck.ConvNop(n.X, types.Types[types.TUNSAFEPTR]))
503                         }
504                 }
505         }
506         walk(n.X)
507
508         cheap := cheapExpr(n, init)
509
510         slice := typecheck.MakeDotArgs(base.Pos, types.NewSlice(types.Types[types.TUNSAFEPTR]), originals)
511         slice.SetEsc(ir.EscNone)
512
513         init.Append(mkcall("checkptrArithmetic", nil, init, typecheck.ConvNop(cheap, types.Types[types.TUNSAFEPTR]), slice))
514         // TODO(khr): Mark backing store of slice as dead. This will allow us to reuse
515         // the backing store for multiple calls to checkptrArithmetic.
516
517         return cheap
518 }
519
520 // walkSliceToArray walks an OSLICE2ARR expression.
521 func walkSliceToArray(n *ir.ConvExpr, init *ir.Nodes) ir.Node {
522         // Replace T(x) with *(*T)(x).
523         conv := typecheck.Expr(ir.NewConvExpr(base.Pos, ir.OCONV, types.NewPtr(n.Type()), n.X)).(*ir.ConvExpr)
524         deref := typecheck.Expr(ir.NewStarExpr(base.Pos, conv)).(*ir.StarExpr)
525
526         // The OSLICE2ARRPTR conversion handles checking the slice length,
527         // so the dereference can't fail.
528         //
529         // However, this is more than just an optimization: if T is a
530         // zero-length array, then x (and thus (*T)(x)) can be nil, but T(x)
531         // should *not* panic. So suppressing the nil check here is
532         // necessary for correctness in that case.
533         deref.SetBounded(true)
534
535         return walkExpr(deref, init)
536 }