| // 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 Expr[0] + Exprs[1] + ... + Expr[len(Expr)-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 { |
| n := &AddrExpr{X: x} |
| n.op = OADDR |
| n.pos = pos |
| 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 |
| } |
| |
| func NewBasicLit(pos src.XPos, val constant.Value) Node { |
| n := &BasicLit{val: val} |
| n.op = OLITERAL |
| n.pos = pos |
| if k := val.Kind(); k != constant.Unknown { |
| n.SetType(idealType(k)) |
| } |
| return n |
| } |
| |
| func (n *BasicLit) Val() constant.Value { return n.val } |
| func (n *BasicLit) SetVal(val constant.Value) { n.val = 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 |
| } |
| |
| 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, |
| OEFACE: |
| n.op = op |
| } |
| } |
| |
| // A CallExpr is a function call X(Args). |
| type CallExpr struct { |
| miniExpr |
| origNode |
| X Node |
| Args Nodes |
| KeepAlive []*Name // vars to be kept alive until call returns |
| IsDDD bool |
| NoInline bool |
| } |
| |
| func NewCallExpr(pos src.XPos, op Op, fun Node, args []Node) *CallExpr { |
| n := &CallExpr{X: fun} |
| n.pos = pos |
| n.orig = n |
| 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, OGETCALLERPC, OGETCALLERSP, |
| OMAKE, OPRINT, OPRINTN, |
| 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 |
| } |
| |
| // Deprecated: Use NewClosureFunc instead. |
| func NewClosureExpr(pos src.XPos, fn *Func) *ClosureExpr { |
| n := &ClosureExpr{Func: fn} |
| n.op = OCLOSURE |
| n.pos = pos |
| return n |
| } |
| |
| // A CompLitExpr is a composite literal Type{Vals}. |
| // Before type-checking, the type is Ntype. |
| type CompLitExpr struct { |
| miniExpr |
| origNode |
| Ntype Ntype |
| List Nodes // initialized values |
| Prealloc *Name |
| Len int64 // backing array length for OSLICELIT |
| } |
| |
| func NewCompLitExpr(pos src.XPos, op Op, typ Ntype, list []Node) *CompLitExpr { |
| n := &CompLitExpr{Ntype: typ} |
| n.pos = pos |
| n.SetOp(op) |
| n.List = list |
| n.orig = n |
| 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 |
| } |
| } |
| |
| type ConstExpr struct { |
| miniExpr |
| origNode |
| val constant.Value |
| } |
| |
| func NewConstExpr(val constant.Value, orig Node) Node { |
| n := &ConstExpr{val: val} |
| n.op = OLITERAL |
| n.pos = orig.Pos() |
| n.orig = orig |
| n.SetType(orig.Type()) |
| n.SetTypecheck(orig.Typecheck()) |
| n.SetDiag(orig.Diag()) |
| return n |
| } |
| |
| func (n *ConstExpr) Sym() *types.Sym { return n.orig.Sym() } |
| func (n *ConstExpr) Val() constant.Value { return n.val } |
| |
| // A ConvExpr is a conversion Type(X). |
| // It may end up being a value or a type. |
| type ConvExpr struct { |
| miniExpr |
| X Node |
| } |
| |
| 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, OCONVIDATA, OCONVNOP, OBYTES2STR, OBYTES2STRTMP, ORUNES2STR, OSTR2BYTES, OSTR2BYTESTMP, OSTR2RUNES, ORUNESTR, OSLICE2ARRPTR: |
| n.op = op |
| } |
| } |
| |
| // An IndexExpr is an index expression X[Index]. |
| type IndexExpr struct { |
| miniExpr |
| X Node |
| Index Node |
| 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 an 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 a 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 |
| 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. |
| // (It may be copied and assigned a type, though.) |
| type NilExpr struct { |
| miniExpr |
| Sym_ *types.Sym // TODO: Remove |
| } |
| |
| func NewNilExpr(pos src.XPos) *NilExpr { |
| n := &NilExpr{} |
| n.pos = pos |
| n.op = ONIL |
| return n |
| } |
| |
| func (n *NilExpr) Sym() *types.Sym { return n.Sym_ } |
| func (n *NilExpr) SetSym(x *types.Sym) { n.Sym_ = x } |
| |
| // 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) } |
| |
| func (*ParenExpr) CanBeNtype() {} |
| |
| // SetOTYPE changes n to be an OTYPE node returning t, |
| // like all the type nodes in type.go. |
| func (n *ParenExpr) SetOTYPE(t *types.Type) { |
| n.op = OTYPE |
| n.typ = t |
| t.SetNod(n) |
| } |
| |
| // A RawOrigExpr represents an arbitrary Go expression as a string value. |
| // When printed in diagnostics, the string value is written out exactly as-is. |
| type RawOrigExpr struct { |
| miniExpr |
| Raw string |
| } |
| |
| func NewRawOrigExpr(pos src.XPos, op Op, raw string) *RawOrigExpr { |
| n := &RawOrigExpr{Raw: raw} |
| n.pos = pos |
| n.op = op |
| return n |
| } |
| |
| // 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 { |
| n := &LinksymOffsetExpr{Linksym: lsym, Offset_: offset} |
| n.typ = typ |
| n.op = OLINKSYMOFFSET |
| 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)) |
| fn.Class = PFUNC |
| fn.SetType(n.Type()) |
| 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. reflectdata.methodWrapper generates the |
| // Func. |
| fn.Func = n.Selection.Nname.(*Name).Func |
| } |
| return fn |
| } |
| |
| // Before type-checking, bytes.Buffer is a SelectorExpr. |
| // After type-checking it becomes a Name. |
| func (*SelectorExpr) CanBeNtype() {} |
| |
| // 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 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) } |
| |
| func (*StarExpr) CanBeNtype() {} |
| |
| // SetOTYPE changes n to be an OTYPE node returning t, |
| // like all the type nodes in type.go. |
| func (n *StarExpr) SetOTYPE(t *types.Type) { |
| n.op = OTYPE |
| n.X = nil |
| n.typ = t |
| t.SetNod(n) |
| } |
| |
| // A TypeAssertionExpr is a selector expression X.(Type). |
| // Before type-checking, the type is Ntype. |
| type TypeAssertExpr struct { |
| miniExpr |
| X Node |
| Ntype Ntype |
| |
| // Runtime type information provided by walkDotType for |
| // assertions from non-empty interface to concrete type. |
| Itab *AddrExpr `mknode:"-"` // *runtime.itab for Type implementing X's type |
| } |
| |
| func NewTypeAssertExpr(pos src.XPos, x Node, typ Ntype) *TypeAssertExpr { |
| n := &TypeAssertExpr{X: x, Ntype: typ} |
| n.pos = pos |
| n.op = ODOTTYPE |
| 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 T. |
| type DynamicTypeAssertExpr struct { |
| miniExpr |
| X Node |
| // N = not an interface |
| // E = empty interface |
| // I = nonempty interface |
| // For E->N, T is a *runtime.type for N |
| // For I->N, T is a *runtime.itab for N+I |
| // For E->I, T is a *runtime.type for I |
| // For I->I, ditto |
| // For I->E, T is a *runtime.type for interface{} (unnecessary, but just to fill in the slot) |
| // For E->E, ditto |
| T Node |
| } |
| |
| func NewDynamicTypeAssertExpr(pos src.XPos, op Op, x, t Node) *DynamicTypeAssertExpr { |
| n := &DynamicTypeAssertExpr{X: x, T: t} |
| 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, |
| OALIGNOF, OCAP, OCLOSE, OIMAG, OLEN, ONEW, |
| OOFFSETOF, OPANIC, OREAL, OSIZEOF, |
| OCHECKNIL, OCFUNC, OIDATA, OITAB, OSPTR, OVARDEF, OVARKILL, OVARLIVE: |
| n.op = op |
| } |
| } |
| |
| // Probably temporary: using Implicit() flag to mark generic function nodes that |
| // are called to make getGfInfo analysis easier in one pre-order pass. |
| func (n *InstExpr) Implicit() bool { return n.flags&miniExprImplicit != 0 } |
| func (n *InstExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) } |
| |
| // An InstExpr is a generic function or type instantiation. |
| type InstExpr struct { |
| miniExpr |
| X Node |
| Targs []Node |
| } |
| |
| func NewInstExpr(pos src.XPos, op Op, x Node, targs []Node) *InstExpr { |
| n := &InstExpr{X: x, Targs: targs} |
| n.pos = pos |
| n.op = op |
| return n |
| } |
| |
| 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 |
| } |
| |
| func StaticValue(n Node) Node { |
| for { |
| if n.Op() == OCONVNOP { |
| n = n.(*ConvExpr).X |
| continue |
| } |
| |
| if n.Op() == OINLCALL { |
| n = n.(*InlinedCallExpr).SingleResult() |
| continue |
| } |
| |
| n1 := staticValue1(n) |
| if n1 == nil { |
| return n |
| } |
| n = n1 |
| } |
| } |
| |
| // staticValue1 implements a simple SSA-like optimization. If n is a local variable |
| // that is initialized and never reassigned, staticValue1 returns the initializer |
| // expression. Otherwise, it returns nil. |
| func staticValue1(nn Node) Node { |
| if nn.Op() != ONAME { |
| return nil |
| } |
| n := nn.(*Name) |
| 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. |
| // The second return value is the first such assignment encountered in the walk, if any. It is mostly |
| // useful for -m output documenting the reason for inhibited optimizations. |
| // NB: global variables are always considered to be re-assigned. |
| // TODO: handle initial declaration not including an assignment and followed by a single assignment? |
| 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 |
| } |
| |
| // 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 { |
| n, ok := 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 OADDR: |
| n := n.(*AddrExpr) |
| if isName(OuterValue(n.X)) { |
| return true |
| } |
| case OCLOSURE: |
| n := n.(*ClosureExpr) |
| if Any(n.Func, do) { |
| return true |
| } |
| } |
| return false |
| } |
| return Any(name.Curfn, do) |
| } |
| |
| // 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 { |
| 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 OCONVNOP: |
| l := l.(*ConvExpr) |
| r := r.(*ConvExpr) |
| 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 |
| } |
| |
| // 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().FieldSlice() { |
| args[i] = AsNode(f.Nname) |
| } |
| 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()) |
| } |
| |
| // 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") |
| } |