| // Copyright 2020 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package ir |
| |
| import ( |
| "bytes" |
| "cmd/compile/internal/base" |
| "cmd/compile/internal/types" |
| "cmd/internal/obj" |
| "cmd/internal/src" |
| "fmt" |
| "go/constant" |
| "go/token" |
| ) |
| |
| // An Expr is a Node that can appear as an expression. |
| type Expr interface { |
| Node |
| isExpr() |
| } |
| |
| // A miniExpr is a miniNode with extra fields common to expressions. |
| // TODO(rsc): Once we are sure about the contents, compact the bools |
| // into a bit field and leave extra bits available for implementations |
| // embedding miniExpr. Right now there are ~60 unused bits sitting here. |
| type miniExpr struct { |
| miniNode |
| typ *types.Type |
| init Nodes // TODO(rsc): Don't require every Node to have an init |
| flags bitset8 |
| } |
| |
| const ( |
| miniExprNonNil = 1 << iota |
| miniExprTransient |
| miniExprBounded |
| miniExprImplicit // for use by implementations; not supported by every Expr |
| miniExprCheckPtr |
| ) |
| |
| func (*miniExpr) isExpr() {} |
| |
| func (n *miniExpr) Type() *types.Type { return n.typ } |
| func (n *miniExpr) SetType(x *types.Type) { n.typ = x } |
| func (n *miniExpr) NonNil() bool { return n.flags&miniExprNonNil != 0 } |
| func (n *miniExpr) MarkNonNil() { n.flags |= miniExprNonNil } |
| func (n *miniExpr) Transient() bool { return n.flags&miniExprTransient != 0 } |
| func (n *miniExpr) SetTransient(b bool) { n.flags.set(miniExprTransient, b) } |
| func (n *miniExpr) Bounded() bool { return n.flags&miniExprBounded != 0 } |
| func (n *miniExpr) SetBounded(b bool) { n.flags.set(miniExprBounded, b) } |
| func (n *miniExpr) Init() Nodes { return n.init } |
| func (n *miniExpr) PtrInit() *Nodes { return &n.init } |
| func (n *miniExpr) SetInit(x Nodes) { n.init = x } |
| |
| // An AddStringExpr is a string concatenation List[0] + List[1] + ... + List[len(List)-1]. |
| type AddStringExpr struct { |
| miniExpr |
| List Nodes |
| Prealloc *Name |
| } |
| |
| func NewAddStringExpr(pos src.XPos, list []Node) *AddStringExpr { |
| n := &AddStringExpr{} |
| n.pos = pos |
| n.op = OADDSTR |
| n.List = list |
| return n |
| } |
| |
| // An AddrExpr is an address-of expression &X. |
| // It may end up being a normal address-of or an allocation of a composite literal. |
| type AddrExpr struct { |
| miniExpr |
| X Node |
| Prealloc *Name // preallocated storage if any |
| } |
| |
| func NewAddrExpr(pos src.XPos, x Node) *AddrExpr { |
| if x == nil || x.Typecheck() != 1 { |
| base.FatalfAt(pos, "missed typecheck: %L", x) |
| } |
| n := &AddrExpr{X: x} |
| n.pos = pos |
| |
| switch x.Op() { |
| case OARRAYLIT, OMAPLIT, OSLICELIT, OSTRUCTLIT: |
| n.op = OPTRLIT |
| |
| default: |
| n.op = OADDR |
| if r, ok := OuterValue(x).(*Name); ok && r.Op() == ONAME { |
| r.SetAddrtaken(true) |
| |
| // If r is a closure variable, we need to mark its canonical |
| // variable as addrtaken too, so that closure conversion |
| // captures it by reference. |
| // |
| // Exception: if we've already marked the variable as |
| // capture-by-value, then that means this variable isn't |
| // logically modified, and we must be taking its address to pass |
| // to a runtime function that won't mutate it. In that case, we |
| // only need to make sure our own copy is addressable. |
| if r.IsClosureVar() && !r.Byval() { |
| r.Canonical().SetAddrtaken(true) |
| } |
| } |
| } |
| |
| n.SetType(types.NewPtr(x.Type())) |
| n.SetTypecheck(1) |
| |
| return n |
| } |
| |
| func (n *AddrExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 } |
| func (n *AddrExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) } |
| |
| func (n *AddrExpr) SetOp(op Op) { |
| switch op { |
| default: |
| panic(n.no("SetOp " + op.String())) |
| case OADDR, OPTRLIT: |
| n.op = op |
| } |
| } |
| |
| // A BasicLit is a literal of basic type. |
| type BasicLit struct { |
| miniExpr |
| val constant.Value |
| } |
| |
| // NewBasicLit returns an OLITERAL representing val with the given type. |
| func NewBasicLit(pos src.XPos, typ *types.Type, val constant.Value) Node { |
| AssertValidTypeForConst(typ, val) |
| |
| n := &BasicLit{val: val} |
| n.op = OLITERAL |
| n.pos = pos |
| n.SetType(typ) |
| n.SetTypecheck(1) |
| return n |
| } |
| |
| func (n *BasicLit) Val() constant.Value { return n.val } |
| func (n *BasicLit) SetVal(val constant.Value) { n.val = val } |
| |
| // NewConstExpr returns an OLITERAL representing val, copying the |
| // position and type from orig. |
| func NewConstExpr(val constant.Value, orig Node) Node { |
| return NewBasicLit(orig.Pos(), orig.Type(), val) |
| } |
| |
| // A BinaryExpr is a binary expression X Op Y, |
| // or Op(X, Y) for builtin functions that do not become calls. |
| type BinaryExpr struct { |
| miniExpr |
| X Node |
| Y Node |
| RType Node `mknode:"-"` // see reflectdata/helpers.go |
| } |
| |
| func NewBinaryExpr(pos src.XPos, op Op, x, y Node) *BinaryExpr { |
| n := &BinaryExpr{X: x, Y: y} |
| n.pos = pos |
| n.SetOp(op) |
| return n |
| } |
| |
| func (n *BinaryExpr) SetOp(op Op) { |
| switch op { |
| default: |
| panic(n.no("SetOp " + op.String())) |
| case OADD, OADDSTR, OAND, OANDNOT, ODIV, OEQ, OGE, OGT, OLE, |
| OLSH, OLT, OMOD, OMUL, ONE, OOR, ORSH, OSUB, OXOR, |
| OCOPY, OCOMPLEX, OUNSAFEADD, OUNSAFESLICE, OUNSAFESTRING, |
| OMAKEFACE: |
| n.op = op |
| } |
| } |
| |
| // A CallExpr is a function call Fun(Args). |
| type CallExpr struct { |
| miniExpr |
| Fun Node |
| Args Nodes |
| DeferAt Node |
| RType Node `mknode:"-"` // see reflectdata/helpers.go |
| KeepAlive []*Name // vars to be kept alive until call returns |
| IsDDD bool |
| GoDefer bool // whether this call is part of a go or defer statement |
| NoInline bool // whether this call must not be inlined |
| } |
| |
| func NewCallExpr(pos src.XPos, op Op, fun Node, args []Node) *CallExpr { |
| n := &CallExpr{Fun: fun} |
| n.pos = pos |
| n.SetOp(op) |
| n.Args = args |
| return n |
| } |
| |
| func (*CallExpr) isStmt() {} |
| |
| func (n *CallExpr) SetOp(op Op) { |
| switch op { |
| default: |
| panic(n.no("SetOp " + op.String())) |
| case OAPPEND, |
| OCALL, OCALLFUNC, OCALLINTER, OCALLMETH, |
| ODELETE, |
| OGETG, OGETCALLERSP, |
| OMAKE, OMAX, OMIN, OPRINT, OPRINTLN, |
| ORECOVER, ORECOVERFP: |
| n.op = op |
| } |
| } |
| |
| // A ClosureExpr is a function literal expression. |
| type ClosureExpr struct { |
| miniExpr |
| Func *Func `mknode:"-"` |
| Prealloc *Name |
| IsGoWrap bool // whether this is wrapper closure of a go statement |
| } |
| |
| // A CompLitExpr is a composite literal Type{Vals}. |
| // Before type-checking, the type is Ntype. |
| type CompLitExpr struct { |
| miniExpr |
| List Nodes // initialized values |
| RType Node `mknode:"-"` // *runtime._type for OMAPLIT map types |
| Prealloc *Name |
| // For OSLICELIT, Len is the backing array length. |
| // For OMAPLIT, Len is the number of entries that we've removed from List and |
| // generated explicit mapassign calls for. This is used to inform the map alloc hint. |
| Len int64 |
| } |
| |
| func NewCompLitExpr(pos src.XPos, op Op, typ *types.Type, list []Node) *CompLitExpr { |
| n := &CompLitExpr{List: list} |
| n.pos = pos |
| n.SetOp(op) |
| if typ != nil { |
| n.SetType(typ) |
| } |
| return n |
| } |
| |
| func (n *CompLitExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 } |
| func (n *CompLitExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) } |
| |
| func (n *CompLitExpr) SetOp(op Op) { |
| switch op { |
| default: |
| panic(n.no("SetOp " + op.String())) |
| case OARRAYLIT, OCOMPLIT, OMAPLIT, OSTRUCTLIT, OSLICELIT: |
| n.op = op |
| } |
| } |
| |
| // A ConvExpr is a conversion Type(X). |
| // It may end up being a value or a type. |
| type ConvExpr struct { |
| miniExpr |
| X Node |
| |
| // For implementing OCONVIFACE expressions. |
| // |
| // TypeWord is an expression yielding a *runtime._type or |
| // *runtime.itab value to go in the type word of the iface/eface |
| // result. See reflectdata.ConvIfaceTypeWord for further details. |
| // |
| // SrcRType is an expression yielding a *runtime._type value for X, |
| // if it's not pointer-shaped and needs to be heap allocated. |
| TypeWord Node `mknode:"-"` |
| SrcRType Node `mknode:"-"` |
| |
| // For -d=checkptr instrumentation of conversions from |
| // unsafe.Pointer to *Elem or *[Len]Elem. |
| // |
| // TODO(mdempsky): We only ever need one of these, but currently we |
| // don't decide which one until walk. Longer term, it probably makes |
| // sense to have a dedicated IR op for `(*[Len]Elem)(ptr)[:n:m]` |
| // expressions. |
| ElemRType Node `mknode:"-"` |
| ElemElemRType Node `mknode:"-"` |
| } |
| |
| func NewConvExpr(pos src.XPos, op Op, typ *types.Type, x Node) *ConvExpr { |
| n := &ConvExpr{X: x} |
| n.pos = pos |
| n.typ = typ |
| n.SetOp(op) |
| return n |
| } |
| |
| func (n *ConvExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 } |
| func (n *ConvExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) } |
| func (n *ConvExpr) CheckPtr() bool { return n.flags&miniExprCheckPtr != 0 } |
| func (n *ConvExpr) SetCheckPtr(b bool) { n.flags.set(miniExprCheckPtr, b) } |
| |
| func (n *ConvExpr) SetOp(op Op) { |
| switch op { |
| default: |
| panic(n.no("SetOp " + op.String())) |
| case OCONV, OCONVIFACE, OCONVNOP, OBYTES2STR, OBYTES2STRTMP, ORUNES2STR, OSTR2BYTES, OSTR2BYTESTMP, OSTR2RUNES, ORUNESTR, OSLICE2ARR, OSLICE2ARRPTR: |
| n.op = op |
| } |
| } |
| |
| // An IndexExpr is an index expression X[Index]. |
| type IndexExpr struct { |
| miniExpr |
| X Node |
| Index Node |
| RType Node `mknode:"-"` // see reflectdata/helpers.go |
| Assigned bool |
| } |
| |
| func NewIndexExpr(pos src.XPos, x, index Node) *IndexExpr { |
| n := &IndexExpr{X: x, Index: index} |
| n.pos = pos |
| n.op = OINDEX |
| return n |
| } |
| |
| func (n *IndexExpr) SetOp(op Op) { |
| switch op { |
| default: |
| panic(n.no("SetOp " + op.String())) |
| case OINDEX, OINDEXMAP: |
| n.op = op |
| } |
| } |
| |
| // A KeyExpr is a Key: Value composite literal key. |
| type KeyExpr struct { |
| miniExpr |
| Key Node |
| Value Node |
| } |
| |
| func NewKeyExpr(pos src.XPos, key, value Node) *KeyExpr { |
| n := &KeyExpr{Key: key, Value: value} |
| n.pos = pos |
| n.op = OKEY |
| return n |
| } |
| |
| // A StructKeyExpr is a Field: Value composite literal key. |
| type StructKeyExpr struct { |
| miniExpr |
| Field *types.Field |
| Value Node |
| } |
| |
| func NewStructKeyExpr(pos src.XPos, field *types.Field, value Node) *StructKeyExpr { |
| n := &StructKeyExpr{Field: field, Value: value} |
| n.pos = pos |
| n.op = OSTRUCTKEY |
| return n |
| } |
| |
| func (n *StructKeyExpr) Sym() *types.Sym { return n.Field.Sym } |
| |
| // An InlinedCallExpr is an inlined function call. |
| type InlinedCallExpr struct { |
| miniExpr |
| Body Nodes |
| ReturnVars Nodes // must be side-effect free |
| } |
| |
| func NewInlinedCallExpr(pos src.XPos, body, retvars []Node) *InlinedCallExpr { |
| n := &InlinedCallExpr{} |
| n.pos = pos |
| n.op = OINLCALL |
| n.Body = body |
| n.ReturnVars = retvars |
| return n |
| } |
| |
| func (n *InlinedCallExpr) SingleResult() Node { |
| if have := len(n.ReturnVars); have != 1 { |
| base.FatalfAt(n.Pos(), "inlined call has %v results, expected 1", have) |
| } |
| if !n.Type().HasShape() && n.ReturnVars[0].Type().HasShape() { |
| // If the type of the call is not a shape, but the type of the return value |
| // is a shape, we need to do an implicit conversion, so the real type |
| // of n is maintained. |
| r := NewConvExpr(n.Pos(), OCONVNOP, n.Type(), n.ReturnVars[0]) |
| r.SetTypecheck(1) |
| return r |
| } |
| return n.ReturnVars[0] |
| } |
| |
| // A LogicalExpr is an expression X Op Y where Op is && or ||. |
| // It is separate from BinaryExpr to make room for statements |
| // that must be executed before Y but after X. |
| type LogicalExpr struct { |
| miniExpr |
| X Node |
| Y Node |
| } |
| |
| func NewLogicalExpr(pos src.XPos, op Op, x, y Node) *LogicalExpr { |
| n := &LogicalExpr{X: x, Y: y} |
| n.pos = pos |
| n.SetOp(op) |
| return n |
| } |
| |
| func (n *LogicalExpr) SetOp(op Op) { |
| switch op { |
| default: |
| panic(n.no("SetOp " + op.String())) |
| case OANDAND, OOROR: |
| n.op = op |
| } |
| } |
| |
| // A MakeExpr is a make expression: make(Type[, Len[, Cap]]). |
| // Op is OMAKECHAN, OMAKEMAP, OMAKESLICE, or OMAKESLICECOPY, |
| // but *not* OMAKE (that's a pre-typechecking CallExpr). |
| type MakeExpr struct { |
| miniExpr |
| RType Node `mknode:"-"` // see reflectdata/helpers.go |
| Len Node |
| Cap Node |
| } |
| |
| func NewMakeExpr(pos src.XPos, op Op, len, cap Node) *MakeExpr { |
| n := &MakeExpr{Len: len, Cap: cap} |
| n.pos = pos |
| n.SetOp(op) |
| return n |
| } |
| |
| func (n *MakeExpr) SetOp(op Op) { |
| switch op { |
| default: |
| panic(n.no("SetOp " + op.String())) |
| case OMAKECHAN, OMAKEMAP, OMAKESLICE, OMAKESLICECOPY: |
| n.op = op |
| } |
| } |
| |
| // A NilExpr represents the predefined untyped constant nil. |
| type NilExpr struct { |
| miniExpr |
| } |
| |
| func NewNilExpr(pos src.XPos, typ *types.Type) *NilExpr { |
| if typ == nil { |
| base.FatalfAt(pos, "missing type") |
| } |
| n := &NilExpr{} |
| n.pos = pos |
| n.op = ONIL |
| n.SetType(typ) |
| n.SetTypecheck(1) |
| return n |
| } |
| |
| // A ParenExpr is a parenthesized expression (X). |
| // It may end up being a value or a type. |
| type ParenExpr struct { |
| miniExpr |
| X Node |
| } |
| |
| func NewParenExpr(pos src.XPos, x Node) *ParenExpr { |
| n := &ParenExpr{X: x} |
| n.op = OPAREN |
| n.pos = pos |
| return n |
| } |
| |
| func (n *ParenExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 } |
| func (n *ParenExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) } |
| |
| // A ResultExpr represents a direct access to a result. |
| type ResultExpr struct { |
| miniExpr |
| Index int64 // index of the result expr. |
| } |
| |
| func NewResultExpr(pos src.XPos, typ *types.Type, index int64) *ResultExpr { |
| n := &ResultExpr{Index: index} |
| n.pos = pos |
| n.op = ORESULT |
| n.typ = typ |
| return n |
| } |
| |
| // A LinksymOffsetExpr refers to an offset within a global variable. |
| // It is like a SelectorExpr but without the field name. |
| type LinksymOffsetExpr struct { |
| miniExpr |
| Linksym *obj.LSym |
| Offset_ int64 |
| } |
| |
| func NewLinksymOffsetExpr(pos src.XPos, lsym *obj.LSym, offset int64, typ *types.Type) *LinksymOffsetExpr { |
| if typ == nil { |
| base.FatalfAt(pos, "nil type") |
| } |
| n := &LinksymOffsetExpr{Linksym: lsym, Offset_: offset} |
| n.typ = typ |
| n.op = OLINKSYMOFFSET |
| n.SetTypecheck(1) |
| return n |
| } |
| |
| // NewLinksymExpr is NewLinksymOffsetExpr, but with offset fixed at 0. |
| func NewLinksymExpr(pos src.XPos, lsym *obj.LSym, typ *types.Type) *LinksymOffsetExpr { |
| return NewLinksymOffsetExpr(pos, lsym, 0, typ) |
| } |
| |
| // NewNameOffsetExpr is NewLinksymOffsetExpr, but taking a *Name |
| // representing a global variable instead of an *obj.LSym directly. |
| func NewNameOffsetExpr(pos src.XPos, name *Name, offset int64, typ *types.Type) *LinksymOffsetExpr { |
| if name == nil || IsBlank(name) || !(name.Op() == ONAME && name.Class == PEXTERN) { |
| base.FatalfAt(pos, "cannot take offset of nil, blank name or non-global variable: %v", name) |
| } |
| return NewLinksymOffsetExpr(pos, name.Linksym(), offset, typ) |
| } |
| |
| // A SelectorExpr is a selector expression X.Sel. |
| type SelectorExpr struct { |
| miniExpr |
| X Node |
| // Sel is the name of the field or method being selected, without (in the |
| // case of methods) any preceding type specifier. If the field/method is |
| // exported, than the Sym uses the local package regardless of the package |
| // of the containing type. |
| Sel *types.Sym |
| // The actual selected field - may not be filled in until typechecking. |
| Selection *types.Field |
| Prealloc *Name // preallocated storage for OMETHVALUE, if any |
| } |
| |
| func NewSelectorExpr(pos src.XPos, op Op, x Node, sel *types.Sym) *SelectorExpr { |
| n := &SelectorExpr{X: x, Sel: sel} |
| n.pos = pos |
| n.SetOp(op) |
| return n |
| } |
| |
| func (n *SelectorExpr) SetOp(op Op) { |
| switch op { |
| default: |
| panic(n.no("SetOp " + op.String())) |
| case OXDOT, ODOT, ODOTPTR, ODOTMETH, ODOTINTER, OMETHVALUE, OMETHEXPR: |
| n.op = op |
| } |
| } |
| |
| func (n *SelectorExpr) Sym() *types.Sym { return n.Sel } |
| func (n *SelectorExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 } |
| func (n *SelectorExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) } |
| func (n *SelectorExpr) Offset() int64 { return n.Selection.Offset } |
| |
| func (n *SelectorExpr) FuncName() *Name { |
| if n.Op() != OMETHEXPR { |
| panic(n.no("FuncName")) |
| } |
| fn := NewNameAt(n.Selection.Pos, MethodSym(n.X.Type(), n.Sel), n.Type()) |
| fn.Class = PFUNC |
| if n.Selection.Nname != nil { |
| // TODO(austin): Nname is nil for interface method |
| // expressions (I.M), so we can't attach a Func to |
| // those here. |
| fn.Func = n.Selection.Nname.(*Name).Func |
| } |
| return fn |
| } |
| |
| // A SliceExpr is a slice expression X[Low:High] or X[Low:High:Max]. |
| type SliceExpr struct { |
| miniExpr |
| X Node |
| Low Node |
| High Node |
| Max Node |
| } |
| |
| func NewSliceExpr(pos src.XPos, op Op, x, low, high, max Node) *SliceExpr { |
| n := &SliceExpr{X: x, Low: low, High: high, Max: max} |
| n.pos = pos |
| n.op = op |
| return n |
| } |
| |
| func (n *SliceExpr) SetOp(op Op) { |
| switch op { |
| default: |
| panic(n.no("SetOp " + op.String())) |
| case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR: |
| n.op = op |
| } |
| } |
| |
| // IsSlice3 reports whether o is a slice3 op (OSLICE3, OSLICE3ARR). |
| // o must be a slicing op. |
| func (o Op) IsSlice3() bool { |
| switch o { |
| case OSLICE, OSLICEARR, OSLICESTR: |
| return false |
| case OSLICE3, OSLICE3ARR: |
| return true |
| } |
| base.Fatalf("IsSlice3 op %v", o) |
| return false |
| } |
| |
| // A SliceHeader expression constructs a slice header from its parts. |
| type SliceHeaderExpr struct { |
| miniExpr |
| Ptr Node |
| Len Node |
| Cap Node |
| } |
| |
| func NewSliceHeaderExpr(pos src.XPos, typ *types.Type, ptr, len, cap Node) *SliceHeaderExpr { |
| n := &SliceHeaderExpr{Ptr: ptr, Len: len, Cap: cap} |
| n.pos = pos |
| n.op = OSLICEHEADER |
| n.typ = typ |
| return n |
| } |
| |
| // A StringHeaderExpr expression constructs a string header from its parts. |
| type StringHeaderExpr struct { |
| miniExpr |
| Ptr Node |
| Len Node |
| } |
| |
| func NewStringHeaderExpr(pos src.XPos, ptr, len Node) *StringHeaderExpr { |
| n := &StringHeaderExpr{Ptr: ptr, Len: len} |
| n.pos = pos |
| n.op = OSTRINGHEADER |
| n.typ = types.Types[types.TSTRING] |
| return n |
| } |
| |
| // A StarExpr is a dereference expression *X. |
| // It may end up being a value or a type. |
| type StarExpr struct { |
| miniExpr |
| X Node |
| } |
| |
| func NewStarExpr(pos src.XPos, x Node) *StarExpr { |
| n := &StarExpr{X: x} |
| n.op = ODEREF |
| n.pos = pos |
| return n |
| } |
| |
| func (n *StarExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 } |
| func (n *StarExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) } |
| |
| // A TypeAssertionExpr is a selector expression X.(Type). |
| // Before type-checking, the type is Ntype. |
| type TypeAssertExpr struct { |
| miniExpr |
| X Node |
| |
| // Runtime type information provided by walkDotType for |
| // assertions from non-empty interface to concrete type. |
| ITab Node `mknode:"-"` // *runtime.itab for Type implementing X's type |
| |
| // An internal/abi.TypeAssert descriptor to pass to the runtime. |
| Descriptor *obj.LSym |
| } |
| |
| func NewTypeAssertExpr(pos src.XPos, x Node, typ *types.Type) *TypeAssertExpr { |
| n := &TypeAssertExpr{X: x} |
| n.pos = pos |
| n.op = ODOTTYPE |
| if typ != nil { |
| n.SetType(typ) |
| } |
| return n |
| } |
| |
| func (n *TypeAssertExpr) SetOp(op Op) { |
| switch op { |
| default: |
| panic(n.no("SetOp " + op.String())) |
| case ODOTTYPE, ODOTTYPE2: |
| n.op = op |
| } |
| } |
| |
| // A DynamicTypeAssertExpr asserts that X is of dynamic type RType. |
| type DynamicTypeAssertExpr struct { |
| miniExpr |
| X Node |
| |
| // SrcRType is an expression that yields a *runtime._type value |
| // representing X's type. It's used in failed assertion panic |
| // messages. |
| SrcRType Node |
| |
| // RType is an expression that yields a *runtime._type value |
| // representing the asserted type. |
| // |
| // BUG(mdempsky): If ITab is non-nil, RType may be nil. |
| RType Node |
| |
| // ITab is an expression that yields a *runtime.itab value |
| // representing the asserted type within the assertee expression's |
| // original interface type. |
| // |
| // ITab is only used for assertions from non-empty interface type to |
| // a concrete (i.e., non-interface) type. For all other assertions, |
| // ITab is nil. |
| ITab Node |
| } |
| |
| func NewDynamicTypeAssertExpr(pos src.XPos, op Op, x, rtype Node) *DynamicTypeAssertExpr { |
| n := &DynamicTypeAssertExpr{X: x, RType: rtype} |
| n.pos = pos |
| n.op = op |
| return n |
| } |
| |
| func (n *DynamicTypeAssertExpr) SetOp(op Op) { |
| switch op { |
| default: |
| panic(n.no("SetOp " + op.String())) |
| case ODYNAMICDOTTYPE, ODYNAMICDOTTYPE2: |
| n.op = op |
| } |
| } |
| |
| // A UnaryExpr is a unary expression Op X, |
| // or Op(X) for a builtin function that does not end up being a call. |
| type UnaryExpr struct { |
| miniExpr |
| X Node |
| } |
| |
| func NewUnaryExpr(pos src.XPos, op Op, x Node) *UnaryExpr { |
| n := &UnaryExpr{X: x} |
| n.pos = pos |
| n.SetOp(op) |
| return n |
| } |
| |
| func (n *UnaryExpr) SetOp(op Op) { |
| switch op { |
| default: |
| panic(n.no("SetOp " + op.String())) |
| case OBITNOT, ONEG, ONOT, OPLUS, ORECV, |
| OCAP, OCLEAR, OCLOSE, OIMAG, OLEN, ONEW, OPANIC, OREAL, |
| OCHECKNIL, OCFUNC, OIDATA, OITAB, OSPTR, |
| OUNSAFESTRINGDATA, OUNSAFESLICEDATA: |
| n.op = op |
| } |
| } |
| |
| func IsZero(n Node) bool { |
| switch n.Op() { |
| case ONIL: |
| return true |
| |
| case OLITERAL: |
| switch u := n.Val(); u.Kind() { |
| case constant.String: |
| return constant.StringVal(u) == "" |
| case constant.Bool: |
| return !constant.BoolVal(u) |
| default: |
| return constant.Sign(u) == 0 |
| } |
| |
| case OARRAYLIT: |
| n := n.(*CompLitExpr) |
| for _, n1 := range n.List { |
| if n1.Op() == OKEY { |
| n1 = n1.(*KeyExpr).Value |
| } |
| if !IsZero(n1) { |
| return false |
| } |
| } |
| return true |
| |
| case OSTRUCTLIT: |
| n := n.(*CompLitExpr) |
| for _, n1 := range n.List { |
| n1 := n1.(*StructKeyExpr) |
| if !IsZero(n1.Value) { |
| return false |
| } |
| } |
| return true |
| } |
| |
| return false |
| } |
| |
| // lvalue etc |
| func IsAddressable(n Node) bool { |
| switch n.Op() { |
| case OINDEX: |
| n := n.(*IndexExpr) |
| if n.X.Type() != nil && n.X.Type().IsArray() { |
| return IsAddressable(n.X) |
| } |
| if n.X.Type() != nil && n.X.Type().IsString() { |
| return false |
| } |
| fallthrough |
| case ODEREF, ODOTPTR: |
| return true |
| |
| case ODOT: |
| n := n.(*SelectorExpr) |
| return IsAddressable(n.X) |
| |
| case ONAME: |
| n := n.(*Name) |
| if n.Class == PFUNC { |
| return false |
| } |
| return true |
| |
| case OLINKSYMOFFSET: |
| return true |
| } |
| |
| return false |
| } |
| |
| // StaticValue analyzes n to find the earliest expression that always |
| // evaluates to the same value as n, which might be from an enclosing |
| // function. |
| // |
| // For example, given: |
| // |
| // var x int = g() |
| // func() { |
| // y := x |
| // *p = int(y) |
| // } |
| // |
| // calling StaticValue on the "int(y)" expression returns the outer |
| // "g()" expression. |
| func StaticValue(n Node) Node { |
| for { |
| switch n1 := n.(type) { |
| case *ConvExpr: |
| if n1.Op() == OCONVNOP { |
| n = n1.X |
| continue |
| } |
| case *InlinedCallExpr: |
| if n1.Op() == OINLCALL { |
| n = n1.SingleResult() |
| continue |
| } |
| case *ParenExpr: |
| n = n1.X |
| continue |
| } |
| |
| n1 := staticValue1(n) |
| if n1 == nil { |
| return n |
| } |
| n = n1 |
| } |
| } |
| |
| func staticValue1(nn Node) Node { |
| if nn.Op() != ONAME { |
| return nil |
| } |
| n := nn.(*Name).Canonical() |
| if n.Class != PAUTO { |
| return nil |
| } |
| |
| defn := n.Defn |
| if defn == nil { |
| return nil |
| } |
| |
| var rhs Node |
| FindRHS: |
| switch defn.Op() { |
| case OAS: |
| defn := defn.(*AssignStmt) |
| rhs = defn.Y |
| case OAS2: |
| defn := defn.(*AssignListStmt) |
| for i, lhs := range defn.Lhs { |
| if lhs == n { |
| rhs = defn.Rhs[i] |
| break FindRHS |
| } |
| } |
| base.Fatalf("%v missing from LHS of %v", n, defn) |
| default: |
| return nil |
| } |
| if rhs == nil { |
| base.Fatalf("RHS is nil: %v", defn) |
| } |
| |
| if Reassigned(n) { |
| return nil |
| } |
| |
| return rhs |
| } |
| |
| // Reassigned takes an ONAME node, walks the function in which it is |
| // defined, and returns a boolean indicating whether the name has any |
| // assignments other than its declaration. |
| // NB: global variables are always considered to be re-assigned. |
| // TODO: handle initial declaration not including an assignment and |
| // followed by a single assignment? |
| // NOTE: any changes made here should also be made in the corresponding |
| // code in the ReassignOracle.Init method. |
| func Reassigned(name *Name) bool { |
| if name.Op() != ONAME { |
| base.Fatalf("reassigned %v", name) |
| } |
| // no way to reliably check for no-reassignment of globals, assume it can be |
| if name.Curfn == nil { |
| return true |
| } |
| |
| if name.Addrtaken() { |
| return true // conservatively assume it's reassigned indirectly |
| } |
| |
| // TODO(mdempsky): This is inefficient and becoming increasingly |
| // unwieldy. Figure out a way to generalize escape analysis's |
| // reassignment detection for use by inlining and devirtualization. |
| |
| // isName reports whether n is a reference to name. |
| isName := func(x Node) bool { |
| if x == nil { |
| return false |
| } |
| n, ok := OuterValue(x).(*Name) |
| return ok && n.Canonical() == name |
| } |
| |
| var do func(n Node) bool |
| do = func(n Node) bool { |
| switch n.Op() { |
| case OAS: |
| n := n.(*AssignStmt) |
| if isName(n.X) && n != name.Defn { |
| return true |
| } |
| case OAS2, OAS2FUNC, OAS2MAPR, OAS2DOTTYPE, OAS2RECV, OSELRECV2: |
| n := n.(*AssignListStmt) |
| for _, p := range n.Lhs { |
| if isName(p) && n != name.Defn { |
| return true |
| } |
| } |
| case OASOP: |
| n := n.(*AssignOpStmt) |
| if isName(n.X) { |
| return true |
| } |
| case OADDR: |
| n := n.(*AddrExpr) |
| if isName(n.X) { |
| base.FatalfAt(n.Pos(), "%v not marked addrtaken", name) |
| } |
| case ORANGE: |
| n := n.(*RangeStmt) |
| if isName(n.Key) || isName(n.Value) { |
| return true |
| } |
| case OCLOSURE: |
| n := n.(*ClosureExpr) |
| if Any(n.Func, do) { |
| return true |
| } |
| } |
| return false |
| } |
| return Any(name.Curfn, do) |
| } |
| |
| // StaticCalleeName returns the ONAME/PFUNC for n, if known. |
| func StaticCalleeName(n Node) *Name { |
| switch n.Op() { |
| case OMETHEXPR: |
| n := n.(*SelectorExpr) |
| return MethodExprName(n) |
| case ONAME: |
| n := n.(*Name) |
| if n.Class == PFUNC { |
| return n |
| } |
| case OCLOSURE: |
| return n.(*ClosureExpr).Func.Nname |
| } |
| return nil |
| } |
| |
| // IsIntrinsicCall reports whether the compiler back end will treat the call as an intrinsic operation. |
| var IsIntrinsicCall = func(*CallExpr) bool { return false } |
| |
| // SameSafeExpr checks whether it is safe to reuse one of l and r |
| // instead of computing both. SameSafeExpr assumes that l and r are |
| // used in the same statement or expression. In order for it to be |
| // safe to reuse l or r, they must: |
| // - be the same expression |
| // - not have side-effects (no function calls, no channel ops); |
| // however, panics are ok |
| // - not cause inappropriate aliasing; e.g. two string to []byte |
| // conversions, must result in two distinct slices |
| // |
| // The handling of OINDEXMAP is subtle. OINDEXMAP can occur both |
| // as an lvalue (map assignment) and an rvalue (map access). This is |
| // currently OK, since the only place SameSafeExpr gets used on an |
| // lvalue expression is for OSLICE and OAPPEND optimizations, and it |
| // is correct in those settings. |
| func SameSafeExpr(l Node, r Node) bool { |
| for l.Op() == OCONVNOP { |
| l = l.(*ConvExpr).X |
| } |
| for r.Op() == OCONVNOP { |
| r = r.(*ConvExpr).X |
| } |
| if l.Op() != r.Op() || !types.Identical(l.Type(), r.Type()) { |
| return false |
| } |
| |
| switch l.Op() { |
| case ONAME: |
| return l == r |
| |
| case ODOT, ODOTPTR: |
| l := l.(*SelectorExpr) |
| r := r.(*SelectorExpr) |
| return l.Sel != nil && r.Sel != nil && l.Sel == r.Sel && SameSafeExpr(l.X, r.X) |
| |
| case ODEREF: |
| l := l.(*StarExpr) |
| r := r.(*StarExpr) |
| return SameSafeExpr(l.X, r.X) |
| |
| case ONOT, OBITNOT, OPLUS, ONEG: |
| l := l.(*UnaryExpr) |
| r := r.(*UnaryExpr) |
| return SameSafeExpr(l.X, r.X) |
| |
| case OCONV: |
| l := l.(*ConvExpr) |
| r := r.(*ConvExpr) |
| // Some conversions can't be reused, such as []byte(str). |
| // Allow only numeric-ish types. This is a bit conservative. |
| return types.IsSimple[l.Type().Kind()] && SameSafeExpr(l.X, r.X) |
| |
| case OINDEX, OINDEXMAP: |
| l := l.(*IndexExpr) |
| r := r.(*IndexExpr) |
| return SameSafeExpr(l.X, r.X) && SameSafeExpr(l.Index, r.Index) |
| |
| case OADD, OSUB, OOR, OXOR, OMUL, OLSH, ORSH, OAND, OANDNOT, ODIV, OMOD: |
| l := l.(*BinaryExpr) |
| r := r.(*BinaryExpr) |
| return SameSafeExpr(l.X, r.X) && SameSafeExpr(l.Y, r.Y) |
| |
| case OLITERAL: |
| return constant.Compare(l.Val(), token.EQL, r.Val()) |
| |
| case ONIL: |
| return true |
| } |
| |
| return false |
| } |
| |
| // ShouldCheckPtr reports whether pointer checking should be enabled for |
| // function fn at a given level. See debugHelpFooter for defined |
| // levels. |
| func ShouldCheckPtr(fn *Func, level int) bool { |
| return base.Debug.Checkptr >= level && fn.Pragma&NoCheckPtr == 0 |
| } |
| |
| // ShouldAsanCheckPtr reports whether pointer checking should be enabled for |
| // function fn when -asan is enabled. |
| func ShouldAsanCheckPtr(fn *Func) bool { |
| return base.Flag.ASan && fn.Pragma&NoCheckPtr == 0 |
| } |
| |
| // IsReflectHeaderDataField reports whether l is an expression p.Data |
| // where p has type reflect.SliceHeader or reflect.StringHeader. |
| func IsReflectHeaderDataField(l Node) bool { |
| if l.Type() != types.Types[types.TUINTPTR] { |
| return false |
| } |
| |
| var tsym *types.Sym |
| switch l.Op() { |
| case ODOT: |
| l := l.(*SelectorExpr) |
| tsym = l.X.Type().Sym() |
| case ODOTPTR: |
| l := l.(*SelectorExpr) |
| tsym = l.X.Type().Elem().Sym() |
| default: |
| return false |
| } |
| |
| if tsym == nil || l.Sym().Name != "Data" || tsym.Pkg.Path != "reflect" { |
| return false |
| } |
| return tsym.Name == "SliceHeader" || tsym.Name == "StringHeader" |
| } |
| |
| func ParamNames(ft *types.Type) []Node { |
| args := make([]Node, ft.NumParams()) |
| for i, f := range ft.Params() { |
| args[i] = f.Nname.(*Name) |
| } |
| return args |
| } |
| |
| // MethodSym returns the method symbol representing a method name |
| // associated with a specific receiver type. |
| // |
| // Method symbols can be used to distinguish the same method appearing |
| // in different method sets. For example, T.M and (*T).M have distinct |
| // method symbols. |
| // |
| // The returned symbol will be marked as a function. |
| func MethodSym(recv *types.Type, msym *types.Sym) *types.Sym { |
| sym := MethodSymSuffix(recv, msym, "") |
| sym.SetFunc(true) |
| return sym |
| } |
| |
| // MethodSymSuffix is like MethodSym, but allows attaching a |
| // distinguisher suffix. To avoid collisions, the suffix must not |
| // start with a letter, number, or period. |
| func MethodSymSuffix(recv *types.Type, msym *types.Sym, suffix string) *types.Sym { |
| if msym.IsBlank() { |
| base.Fatalf("blank method name") |
| } |
| |
| rsym := recv.Sym() |
| if recv.IsPtr() { |
| if rsym != nil { |
| base.Fatalf("declared pointer receiver type: %v", recv) |
| } |
| rsym = recv.Elem().Sym() |
| } |
| |
| // Find the package the receiver type appeared in. For |
| // anonymous receiver types (i.e., anonymous structs with |
| // embedded fields), use the "go" pseudo-package instead. |
| rpkg := Pkgs.Go |
| if rsym != nil { |
| rpkg = rsym.Pkg |
| } |
| |
| var b bytes.Buffer |
| if recv.IsPtr() { |
| // The parentheses aren't really necessary, but |
| // they're pretty traditional at this point. |
| fmt.Fprintf(&b, "(%-S)", recv) |
| } else { |
| fmt.Fprintf(&b, "%-S", recv) |
| } |
| |
| // A particular receiver type may have multiple non-exported |
| // methods with the same name. To disambiguate them, include a |
| // package qualifier for names that came from a different |
| // package than the receiver type. |
| if !types.IsExported(msym.Name) && msym.Pkg != rpkg { |
| b.WriteString(".") |
| b.WriteString(msym.Pkg.Prefix) |
| } |
| |
| b.WriteString(".") |
| b.WriteString(msym.Name) |
| b.WriteString(suffix) |
| return rpkg.LookupBytes(b.Bytes()) |
| } |
| |
| // LookupMethodSelector returns the types.Sym of the selector for a method |
| // named in local symbol name, as well as the types.Sym of the receiver. |
| // |
| // TODO(prattmic): this does not attempt to handle method suffixes (wrappers). |
| func LookupMethodSelector(pkg *types.Pkg, name string) (typ, meth *types.Sym, err error) { |
| typeName, methName := splitType(name) |
| if typeName == "" { |
| return nil, nil, fmt.Errorf("%s doesn't contain type split", name) |
| } |
| |
| if len(typeName) > 3 && typeName[:2] == "(*" && typeName[len(typeName)-1] == ')' { |
| // Symbol name is for a pointer receiver method. We just want |
| // the base type name. |
| typeName = typeName[2 : len(typeName)-1] |
| } |
| |
| typ = pkg.Lookup(typeName) |
| meth = pkg.Selector(methName) |
| return typ, meth, nil |
| } |
| |
| // splitType splits a local symbol name into type and method (fn). If this a |
| // free function, typ == "". |
| // |
| // N.B. closures and methods can be ambiguous (e.g., bar.func1). These cases |
| // are returned as methods. |
| func splitType(name string) (typ, fn string) { |
| // Types are split on the first dot, ignoring everything inside |
| // brackets (instantiation of type parameter, usually including |
| // "go.shape"). |
| bracket := 0 |
| for i, r := range name { |
| if r == '.' && bracket == 0 { |
| return name[:i], name[i+1:] |
| } |
| if r == '[' { |
| bracket++ |
| } |
| if r == ']' { |
| bracket-- |
| } |
| } |
| return "", name |
| } |
| |
| // MethodExprName returns the ONAME representing the method |
| // referenced by expression n, which must be a method selector, |
| // method expression, or method value. |
| func MethodExprName(n Node) *Name { |
| name, _ := MethodExprFunc(n).Nname.(*Name) |
| return name |
| } |
| |
| // MethodExprFunc is like MethodExprName, but returns the types.Field instead. |
| func MethodExprFunc(n Node) *types.Field { |
| switch n.Op() { |
| case ODOTMETH, OMETHEXPR, OMETHVALUE: |
| return n.(*SelectorExpr).Selection |
| } |
| base.Fatalf("unexpected node: %v (%v)", n, n.Op()) |
| panic("unreachable") |
| } |