src/cmd/compile/internal/ir/expr.go GO 1,324 lines View on github.com → Search inside
1// Copyright 2020 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45package ir67import (8	"bytes"9	"cmd/compile/internal/base"10	"cmd/compile/internal/types"11	"cmd/internal/obj"12	"cmd/internal/src"13	"fmt"14	"go/constant"15	"go/token"16)1718// An Expr is a Node that can appear as an expression.19type Expr interface {20	Node21	isExpr()22}2324// A miniExpr is a miniNode with extra fields common to expressions.25// TODO(rsc): Once we are sure about the contents, compact the bools26// into a bit field and leave extra bits available for implementations27// embedding miniExpr. Right now there are ~24 unused bits sitting here.28type miniExpr struct {29	miniNode30	flags bitset831	typ   *types.Type32	init  Nodes // TODO(rsc): Don't require every Node to have an init33}3435const (36	miniExprNonNil = 1 << iota37	miniExprTransient38	miniExprBounded39	miniExprImplicit // for use by implementations; not supported by every Expr40	miniExprCheckPtr41)4243func (*miniExpr) isExpr() {}4445func (n *miniExpr) Type() *types.Type     { return n.typ }46func (n *miniExpr) SetType(x *types.Type) { n.typ = x }47func (n *miniExpr) NonNil() bool          { return n.flags&miniExprNonNil != 0 }48func (n *miniExpr) MarkNonNil()           { n.flags |= miniExprNonNil }49func (n *miniExpr) Transient() bool       { return n.flags&miniExprTransient != 0 }50func (n *miniExpr) SetTransient(b bool)   { n.flags.set(miniExprTransient, b) }51func (n *miniExpr) Bounded() bool         { return n.flags&miniExprBounded != 0 }52func (n *miniExpr) SetBounded(b bool)     { n.flags.set(miniExprBounded, b) }53func (n *miniExpr) Init() Nodes           { return n.init }54func (n *miniExpr) PtrInit() *Nodes       { return &n.init }55func (n *miniExpr) SetInit(x Nodes)       { n.init = x }5657// An AddStringExpr is a string concatenation List[0] + List[1] + ... + List[len(List)-1].58type AddStringExpr struct {59	miniExpr60	List     Nodes61	Prealloc *Name62}6364func NewAddStringExpr(pos src.XPos, list []Node) *AddStringExpr {65	n := &AddStringExpr{}66	n.pos = pos67	n.op = OADDSTR68	n.List = list69	return n70}7172// An AddrExpr is an address-of expression &X.73// It may end up being a normal address-of or an allocation of a composite literal.74type AddrExpr struct {75	miniExpr76	X        Node77	Prealloc *Name // preallocated storage if any78}7980func NewAddrExpr(pos src.XPos, x Node) *AddrExpr {81	if x == nil || x.Typecheck() != 1 {82		base.FatalfAt(pos, "missed typecheck: %L", x)83	}84	n := &AddrExpr{X: x}85	n.pos = pos8687	switch x.Op() {88	case OARRAYLIT, OMAPLIT, OSLICELIT, OSTRUCTLIT:89		n.op = OPTRLIT9091	default:92		n.op = OADDR93		if r, ok := OuterValue(x).(*Name); ok && r.Op() == ONAME {94			r.SetAddrtaken(true)9596			// If r is a closure variable, we need to mark its canonical97			// variable as addrtaken too, so that closure conversion98			// captures it by reference.99			//100			// Exception: if we've already marked the variable as101			// capture-by-value, then that means this variable isn't102			// logically modified, and we must be taking its address to pass103			// to a runtime function that won't mutate it. In that case, we104			// only need to make sure our own copy is addressable.105			if r.IsClosureVar() && !r.Byval() {106				r.Canonical().SetAddrtaken(true)107			}108		}109	}110111	n.SetType(types.NewPtr(x.Type()))112	n.SetTypecheck(1)113114	return n115}116117func (n *AddrExpr) Implicit() bool     { return n.flags&miniExprImplicit != 0 }118func (n *AddrExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) }119120func (n *AddrExpr) SetOp(op Op) {121	switch op {122	default:123		panic(n.no("SetOp " + op.String()))124	case OADDR, OPTRLIT:125		n.op = op126	}127}128129// A BasicLit is a literal of basic type.130type BasicLit struct {131	miniExpr132	val constant.Value133}134135// NewBasicLit returns an OLITERAL representing val with the given type.136func NewBasicLit(pos src.XPos, typ *types.Type, val constant.Value) Node {137	AssertValidTypeForConst(typ, val)138139	n := &BasicLit{val: val}140	n.op = OLITERAL141	n.pos = pos142	n.SetType(typ)143	n.SetTypecheck(1)144	return n145}146147func (n *BasicLit) Val() constant.Value       { return n.val }148func (n *BasicLit) SetVal(val constant.Value) { n.val = val }149150// NewConstExpr returns an OLITERAL representing val, copying the151// position and type from orig.152func NewConstExpr(val constant.Value, orig Node) Node {153	return NewBasicLit(orig.Pos(), orig.Type(), val)154}155156// A BinaryExpr is a binary expression X Op Y,157// or Op(X, Y) for builtin functions that do not become calls.158type BinaryExpr struct {159	miniExpr160	X     Node161	Y     Node162	RType Node `mknode:"-"` // see reflectdata/helpers.go163}164165func NewBinaryExpr(pos src.XPos, op Op, x, y Node) *BinaryExpr {166	n := &BinaryExpr{X: x, Y: y}167	n.pos = pos168	n.SetOp(op)169	return n170}171172func (n *BinaryExpr) SetOp(op Op) {173	switch op {174	default:175		panic(n.no("SetOp " + op.String()))176	case OADD, OADDSTR, OAND, OANDNOT, ODIV, OEQ, OGE, OGT, OLE,177		OLSH, OLT, OMOD, OMUL, ONE, OOR, ORSH, OSUB, OXOR,178		OCOPY, OCOMPLEX, OUNSAFEADD, OUNSAFESLICE, OUNSAFESTRING,179		OMAKEFACE:180		n.op = op181	}182}183184// A CallExpr is a function call Fun(Args).185type CallExpr struct {186	miniExpr187	Fun           Node188	Args          Nodes189	DeferAt       Node190	RType         Node    `mknode:"-"` // see reflectdata/helpers.go191	KeepAlive     []*Name // vars to be kept alive until call returns192	IsDDD         bool193	GoDefer       bool // whether this call is part of a go or defer statement194	NoInline      bool // whether this call must not be inlined195	UseBuf        bool // use stack buffer for backing store (OAPPEND only)196	AppendNoAlias bool // backing store proven to be unaliased (OAPPEND only)197	// whether it's a runtime.KeepAlive call the compiler generates to198	// keep a variable alive. See #73137.199	IsCompilerVarLive bool200	Reshape           bool201}202203func NewCallExpr(pos src.XPos, op Op, fun Node, args []Node) *CallExpr {204	n := &CallExpr{Fun: fun}205	n.pos = pos206	n.SetOp(op)207	n.Args = args208	return n209}210211func (*CallExpr) isStmt() {}212213func (n *CallExpr) SetOp(op Op) {214	switch op {215	default:216		panic(n.no("SetOp " + op.String()))217	case OAPPEND,218		OCALL, OCALLFUNC, OCALLINTER, OCALLMETH,219		ODELETE,220		OGETG, OGETCALLERSP,221		OMAKE, OMAX, OMIN, OPRINT, OPRINTLN,222		ORECOVER:223		n.op = op224	}225}226227// A ClosureExpr is a function literal expression.228type ClosureExpr struct {229	miniExpr230	Func     *Func `mknode:"-"`231	Prealloc *Name232	IsGoWrap bool // whether this is wrapper closure of a go statement233}234235// A CompLitExpr is a composite literal Type{Vals}.236// Before type-checking, the type is Ntype.237type CompLitExpr struct {238	miniExpr239	List     Nodes // initialized values240	RType    Node  `mknode:"-"` // *runtime._type for OMAPLIT map types241	Prealloc *Name242	// For OSLICELIT, Len is the backing array length.243	// For OMAPLIT, Len is the number of entries that we've removed from List and244	// generated explicit mapassign calls for. This is used to inform the map alloc hint.245	Len int64246}247248func NewCompLitExpr(pos src.XPos, op Op, typ *types.Type, list []Node) *CompLitExpr {249	n := &CompLitExpr{List: list}250	n.pos = pos251	n.SetOp(op)252	if typ != nil {253		n.SetType(typ)254	}255	return n256}257258func (n *CompLitExpr) Implicit() bool     { return n.flags&miniExprImplicit != 0 }259func (n *CompLitExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) }260261func (n *CompLitExpr) SetOp(op Op) {262	switch op {263	default:264		panic(n.no("SetOp " + op.String()))265	case OARRAYLIT, OCOMPLIT, OMAPLIT, OSTRUCTLIT, OSLICELIT:266		n.op = op267	}268}269270// A ConvExpr is a conversion Type(X).271// It may end up being a value or a type.272type ConvExpr struct {273	miniExpr274	X Node275276	// For implementing OCONVIFACE expressions.277	//278	// TypeWord is an expression yielding a *runtime._type or279	// *runtime.itab value to go in the type word of the iface/eface280	// result. See reflectdata.ConvIfaceTypeWord for further details.281	//282	// SrcRType is an expression yielding a *runtime._type value for X,283	// if it's not pointer-shaped and needs to be heap allocated.284	TypeWord Node `mknode:"-"`285	SrcRType Node `mknode:"-"`286287	// For -d=checkptr instrumentation of conversions from288	// unsafe.Pointer to *Elem or *[Len]Elem.289	//290	// TODO(mdempsky): We only ever need one of these, but currently we291	// don't decide which one until walk. Longer term, it probably makes292	// sense to have a dedicated IR op for `(*[Len]Elem)(ptr)[:n:m]`293	// expressions.294	ElemRType     Node `mknode:"-"`295	ElemElemRType Node `mknode:"-"`296}297298func NewConvExpr(pos src.XPos, op Op, typ *types.Type, x Node) *ConvExpr {299	n := &ConvExpr{X: x}300	n.pos = pos301	n.typ = typ302	n.SetOp(op)303	return n304}305306func (n *ConvExpr) Implicit() bool     { return n.flags&miniExprImplicit != 0 }307func (n *ConvExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) }308func (n *ConvExpr) CheckPtr() bool     { return n.flags&miniExprCheckPtr != 0 }309func (n *ConvExpr) SetCheckPtr(b bool) { n.flags.set(miniExprCheckPtr, b) }310311func (n *ConvExpr) SetOp(op Op) {312	switch op {313	default:314		panic(n.no("SetOp " + op.String()))315	case OCONV, OCONVIFACE, OCONVNOP, OBYTES2STR, OBYTES2STRTMP, ORUNES2STR, OSTR2BYTES, OSTR2BYTESTMP, OSTR2RUNES, ORUNESTR, OSLICE2ARR, OSLICE2ARRPTR:316		n.op = op317	}318}319320// An IndexExpr is an index expression X[Index].321type IndexExpr struct {322	miniExpr323	X        Node324	Index    Node325	RType    Node `mknode:"-"` // see reflectdata/helpers.go326	Assigned bool327}328329func NewIndexExpr(pos src.XPos, x, index Node) *IndexExpr {330	n := &IndexExpr{X: x, Index: index}331	n.pos = pos332	n.op = OINDEX333	return n334}335336func (n *IndexExpr) SetOp(op Op) {337	switch op {338	default:339		panic(n.no("SetOp " + op.String()))340	case OINDEX, OINDEXMAP:341		n.op = op342	}343}344345// A KeyExpr is a Key: Value composite literal key.346type KeyExpr struct {347	miniExpr348	Key   Node349	Value Node350}351352func NewKeyExpr(pos src.XPos, key, value Node) *KeyExpr {353	n := &KeyExpr{Key: key, Value: value}354	n.pos = pos355	n.op = OKEY356	return n357}358359// A StructKeyExpr is a Field: Value composite literal key.360type StructKeyExpr struct {361	miniExpr362	Field *types.Field363	Value Node364}365366func NewStructKeyExpr(pos src.XPos, field *types.Field, value Node) *StructKeyExpr {367	n := &StructKeyExpr{Field: field, Value: value}368	n.pos = pos369	n.op = OSTRUCTKEY370	return n371}372373func (n *StructKeyExpr) Sym() *types.Sym { return n.Field.Sym }374375// An InlinedCallExpr is an inlined function call.376type InlinedCallExpr struct {377	miniExpr378	Body       Nodes379	ReturnVars Nodes // must be side-effect free380	Reshape    bool381}382383func NewInlinedCallExpr(pos src.XPos, body, retvars []Node) *InlinedCallExpr {384	n := &InlinedCallExpr{}385	n.pos = pos386	n.op = OINLCALL387	n.Body = body388	n.ReturnVars = retvars389	return n390}391392func (n *InlinedCallExpr) SingleResult() Node {393	if have := len(n.ReturnVars); have != 1 {394		base.FatalfAt(n.Pos(), "inlined call has %v results, expected 1", have)395	}396397	// If the type of the call is not a shape, but the type of the return value398	// is a shape, we need to do an implicit conversion, so the real type399	// of n is maintained.400	needImplicitConv := !n.Type().HasShape() && n.ReturnVars[0].Type().HasShape()401	if n.Reshape { // or if the inlined call expr needs reshaping.402		needImplicitConv = true403	}404405	if needImplicitConv {406		r := NewConvExpr(n.Pos(), OCONVNOP, n.Type(), n.ReturnVars[0])407		r.SetTypecheck(1)408		return r409	}410	return n.ReturnVars[0]411}412413// A LogicalExpr is an expression X Op Y where Op is && or ||.414// It is separate from BinaryExpr to make room for statements415// that must be executed before Y but after X.416type LogicalExpr struct {417	miniExpr418	X Node419	Y Node420}421422func NewLogicalExpr(pos src.XPos, op Op, x, y Node) *LogicalExpr {423	n := &LogicalExpr{X: x, Y: y}424	n.pos = pos425	n.SetOp(op)426	return n427}428429func (n *LogicalExpr) SetOp(op Op) {430	switch op {431	default:432		panic(n.no("SetOp " + op.String()))433	case OANDAND, OOROR:434		n.op = op435	}436}437438// A MakeExpr is a make expression: make(Type[, Len[, Cap]]).439// Op is OMAKECHAN, OMAKEMAP, OMAKESLICE, or OMAKESLICECOPY,440// but *not* OMAKE (that's a pre-typechecking CallExpr).441type MakeExpr struct {442	miniExpr443	RType Node `mknode:"-"` // see reflectdata/helpers.go444	Len   Node445	Cap   Node446}447448func NewMakeExpr(pos src.XPos, op Op, len, cap Node) *MakeExpr {449	n := &MakeExpr{Len: len, Cap: cap}450	n.pos = pos451	n.SetOp(op)452	return n453}454455func (n *MakeExpr) SetOp(op Op) {456	switch op {457	default:458		panic(n.no("SetOp " + op.String()))459	case OMAKECHAN, OMAKEMAP, OMAKESLICE, OMAKESLICECOPY:460		n.op = op461	}462}463464// A NilExpr represents the predefined untyped constant nil.465type NilExpr struct {466	miniExpr467}468469func NewNilExpr(pos src.XPos, typ *types.Type) *NilExpr {470	if typ == nil {471		base.FatalfAt(pos, "missing type")472	}473	n := &NilExpr{}474	n.pos = pos475	n.op = ONIL476	n.SetType(typ)477	n.SetTypecheck(1)478	return n479}480481// A ParenExpr is a parenthesized expression (X).482// It may end up being a value or a type.483type ParenExpr struct {484	miniExpr485	X Node486}487488func NewParenExpr(pos src.XPos, x Node) *ParenExpr {489	n := &ParenExpr{X: x}490	n.op = OPAREN491	n.pos = pos492	return n493}494495func (n *ParenExpr) Implicit() bool     { return n.flags&miniExprImplicit != 0 }496func (n *ParenExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) }497498// A ResultExpr represents a direct access to a result.499type ResultExpr struct {500	miniExpr501	Index int64 // index of the result expr.502}503504func NewResultExpr(pos src.XPos, typ *types.Type, index int64) *ResultExpr {505	n := &ResultExpr{Index: index}506	n.pos = pos507	n.op = ORESULT508	n.typ = typ509	return n510}511512// A LinksymOffsetExpr refers to an offset within a global variable.513// It is like a SelectorExpr but without the field name.514type LinksymOffsetExpr struct {515	miniExpr516	Linksym *obj.LSym517	Offset_ int64518}519520func NewLinksymOffsetExpr(pos src.XPos, lsym *obj.LSym, offset int64, typ *types.Type) *LinksymOffsetExpr {521	if typ == nil {522		base.FatalfAt(pos, "nil type")523	}524	n := &LinksymOffsetExpr{Linksym: lsym, Offset_: offset}525	n.typ = typ526	n.op = OLINKSYMOFFSET527	n.SetTypecheck(1)528	return n529}530531// NewLinksymExpr is NewLinksymOffsetExpr, but with offset fixed at 0.532func NewLinksymExpr(pos src.XPos, lsym *obj.LSym, typ *types.Type) *LinksymOffsetExpr {533	return NewLinksymOffsetExpr(pos, lsym, 0, typ)534}535536// NewNameOffsetExpr is NewLinksymOffsetExpr, but taking a *Name537// representing a global variable instead of an *obj.LSym directly.538func NewNameOffsetExpr(pos src.XPos, name *Name, offset int64, typ *types.Type) *LinksymOffsetExpr {539	if name == nil || IsBlank(name) || !(name.Op() == ONAME && name.Class == PEXTERN) {540		base.FatalfAt(pos, "cannot take offset of nil, blank name or non-global variable: %v", name)541	}542	return NewLinksymOffsetExpr(pos, name.Linksym(), offset, typ)543}544545// A SelectorExpr is a selector expression X.Sel.546type SelectorExpr struct {547	miniExpr548	X Node549	// Sel is the name of the field or method being selected, without (in the550	// case of methods) any preceding type specifier. If the field/method is551	// exported, than the Sym uses the local package regardless of the package552	// of the containing type.553	Sel *types.Sym554	// The actual selected field - may not be filled in until typechecking.555	Selection *types.Field556	Prealloc  *Name // preallocated storage for OMETHVALUE, if any557}558559func NewSelectorExpr(pos src.XPos, op Op, x Node, sel *types.Sym) *SelectorExpr {560	n := &SelectorExpr{X: x, Sel: sel}561	n.pos = pos562	n.SetOp(op)563	return n564}565566func (n *SelectorExpr) SetOp(op Op) {567	switch op {568	default:569		panic(n.no("SetOp " + op.String()))570	case OXDOT, ODOT, ODOTPTR, ODOTMETH, ODOTINTER, OMETHVALUE, OMETHEXPR:571		n.op = op572	}573}574575func (n *SelectorExpr) Sym() *types.Sym    { return n.Sel }576func (n *SelectorExpr) Implicit() bool     { return n.flags&miniExprImplicit != 0 }577func (n *SelectorExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) }578func (n *SelectorExpr) Offset() int64      { return n.Selection.Offset }579580func (n *SelectorExpr) FuncName() *Name {581	if n.Op() != OMETHEXPR {582		panic(n.no("FuncName"))583	}584	fn := NewNameAt(n.Selection.Pos, MethodSym(n.X.Type(), n.Sel), n.Type())585	fn.Class = PFUNC586	if n.Selection.Nname != nil {587		// TODO(austin): Nname is nil for interface method588		// expressions (I.M), so we can't attach a Func to589		// those here.590		fn.Func = n.Selection.Nname.(*Name).Func591	}592	return fn593}594595// A SliceExpr is a slice expression X[Low:High] or X[Low:High:Max].596type SliceExpr struct {597	miniExpr598	X    Node599	Low  Node600	High Node601	Max  Node602}603604func NewSliceExpr(pos src.XPos, op Op, x, low, high, max Node) *SliceExpr {605	n := &SliceExpr{X: x, Low: low, High: high, Max: max}606	n.pos = pos607	n.op = op608	return n609}610611func (n *SliceExpr) SetOp(op Op) {612	switch op {613	default:614		panic(n.no("SetOp " + op.String()))615	case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR:616		n.op = op617	}618}619620// IsSlice3 reports whether o is a slice3 op (OSLICE3, OSLICE3ARR).621// o must be a slicing op.622func (o Op) IsSlice3() bool {623	switch o {624	case OSLICE, OSLICEARR, OSLICESTR:625		return false626	case OSLICE3, OSLICE3ARR:627		return true628	}629	base.Fatalf("IsSlice3 op %v", o)630	return false631}632633// A SliceHeaderExpr constructs a slice header from its parts.634type SliceHeaderExpr struct {635	miniExpr636	Ptr Node637	Len Node638	Cap Node639}640641func NewSliceHeaderExpr(pos src.XPos, typ *types.Type, ptr, len, cap Node) *SliceHeaderExpr {642	n := &SliceHeaderExpr{Ptr: ptr, Len: len, Cap: cap}643	n.pos = pos644	n.op = OSLICEHEADER645	n.typ = typ646	return n647}648649// A StringHeaderExpr expression constructs a string header from its parts.650type StringHeaderExpr struct {651	miniExpr652	Ptr Node653	Len Node654}655656func NewStringHeaderExpr(pos src.XPos, ptr, len Node) *StringHeaderExpr {657	n := &StringHeaderExpr{Ptr: ptr, Len: len}658	n.pos = pos659	n.op = OSTRINGHEADER660	n.typ = types.Types[types.TSTRING]661	return n662}663664// A StarExpr is a dereference expression *X.665// It may end up being a value or a type.666type StarExpr struct {667	miniExpr668	X Node669}670671func NewStarExpr(pos src.XPos, x Node) *StarExpr {672	n := &StarExpr{X: x}673	n.op = ODEREF674	n.pos = pos675	return n676}677678func (n *StarExpr) Implicit() bool     { return n.flags&miniExprImplicit != 0 }679func (n *StarExpr) SetImplicit(b bool) { n.flags.set(miniExprImplicit, b) }680681// A TypeAssertExpr is a selector expression X.(Type).682// Before type-checking, the type is Ntype.683type TypeAssertExpr struct {684	miniExpr685	X Node686687	// Runtime type information provided by walkDotType for688	// assertions from non-empty interface to concrete type.689	ITab Node `mknode:"-"` // *runtime.itab for Type implementing X's type690691	// An internal/abi.TypeAssert descriptor to pass to the runtime.692	Descriptor *obj.LSym693694	// When set to true, if this assert would panic, then use a nil pointer panic695	// instead of an interface conversion panic.696	// It must not be set for type assertions using the commaok form.697	UseNilPanic bool698}699700func NewTypeAssertExpr(pos src.XPos, x Node, typ *types.Type) *TypeAssertExpr {701	n := &TypeAssertExpr{X: x}702	n.pos = pos703	n.op = ODOTTYPE704	if typ != nil {705		n.SetType(typ)706	}707	return n708}709710func (n *TypeAssertExpr) SetOp(op Op) {711	switch op {712	default:713		panic(n.no("SetOp " + op.String()))714	case ODOTTYPE, ODOTTYPE2:715		n.op = op716	}717}718719// A DynamicTypeAssertExpr asserts that X is of dynamic type RType.720type DynamicTypeAssertExpr struct {721	miniExpr722	X Node723724	// SrcRType is an expression that yields a *runtime._type value725	// representing X's type. It's used in failed assertion panic726	// messages.727	SrcRType Node728729	// RType is an expression that yields a *runtime._type value730	// representing the asserted type.731	//732	// BUG(mdempsky): If ITab is non-nil, RType may be nil.733	RType Node734735	// ITab is an expression that yields a *runtime.itab value736	// representing the asserted type within the assertee expression's737	// original interface type.738	//739	// ITab is only used for assertions from non-empty interface type to740	// a concrete (i.e., non-interface) type. For all other assertions,741	// ITab is nil.742	ITab Node743}744745func NewDynamicTypeAssertExpr(pos src.XPos, op Op, x, rtype Node) *DynamicTypeAssertExpr {746	n := &DynamicTypeAssertExpr{X: x, RType: rtype}747	n.pos = pos748	n.op = op749	return n750}751752func (n *DynamicTypeAssertExpr) SetOp(op Op) {753	switch op {754	default:755		panic(n.no("SetOp " + op.String()))756	case ODYNAMICDOTTYPE, ODYNAMICDOTTYPE2:757		n.op = op758	}759}760761// A UnaryExpr is a unary expression Op X,762// or Op(X) for a builtin function that does not end up being a call.763type UnaryExpr struct {764	miniExpr765	X Node766}767768func NewUnaryExpr(pos src.XPos, op Op, x Node) *UnaryExpr {769	n := &UnaryExpr{X: x}770	n.pos = pos771	n.SetOp(op)772	return n773}774775func (n *UnaryExpr) SetOp(op Op) {776	switch op {777	default:778		panic(n.no("SetOp " + op.String()))779	case OBITNOT, ONEG, ONOT, OPLUS, ORECV,780		OCAP, OCLEAR, OCLOSE, OIMAG, OLEN, ONEW, OPANIC, OREAL,781		OCHECKNIL, OCFUNC, OIDATA, OITAB, OSPTR,782		OUNSAFESTRINGDATA, OUNSAFESLICEDATA:783		n.op = op784	}785}786787func IsZero(n Node) bool {788	switch n.Op() {789	case ONIL:790		return true791792	case OLITERAL:793		switch u := n.Val(); u.Kind() {794		case constant.String:795			return constant.StringVal(u) == ""796		case constant.Bool:797			return !constant.BoolVal(u)798		default:799			return constant.Sign(u) == 0800		}801802	case OARRAYLIT:803		n := n.(*CompLitExpr)804		for _, n1 := range n.List {805			if n1.Op() == OKEY {806				n1 = n1.(*KeyExpr).Value807			}808			if !IsZero(n1) {809				return false810			}811		}812		return true813814	case OSTRUCTLIT:815		n := n.(*CompLitExpr)816		for _, n1 := range n.List {817			n1 := n1.(*StructKeyExpr)818			if !IsZero(n1.Value) {819				return false820			}821		}822		return true823	}824825	return false826}827828// lvalue etc829func IsAddressable(n Node) bool {830	switch n.Op() {831	case OINDEX:832		n := n.(*IndexExpr)833		if n.X.Type() != nil && n.X.Type().IsArray() {834			return IsAddressable(n.X)835		}836		if n.X.Type() != nil && n.X.Type().IsString() {837			return false838		}839		fallthrough840	case ODEREF, ODOTPTR:841		return true842843	case ODOT:844		n := n.(*SelectorExpr)845		return IsAddressable(n.X)846847	case ONAME:848		n := n.(*Name)849		if n.Class == PFUNC {850			return false851		}852		return true853854	case OLINKSYMOFFSET:855		return true856	}857858	return false859}860861// StaticValue analyzes n to find the earliest expression that always862// evaluates to the same value as n, which might be from an enclosing863// function.864//865// For example, given:866//867//	var x int = g()868//	func() {869//		y := x870//		*p = int(y)871//	}872//873// calling StaticValue on the "int(y)" expression returns the outer874// "g()" expression.875//876// NOTE: StaticValue can return a result with a different type than877// n's type because it can traverse through OCONVNOP operations.878// TODO: consider reapplying OCONVNOP operations to the result. See https://go.dev/cl/676517.879func StaticValue(n Node) Node {880	for {881		switch n1 := n.(type) {882		case *ConvExpr:883			if n1.Op() == OCONVNOP {884				n = n1.X885				continue886			}887		case *InlinedCallExpr:888			if n1.Op() == OINLCALL {889				n = n1.SingleResult()890				continue891			}892		case *ParenExpr:893			n = n1.X894			continue895		}896897		n1 := staticValue1(n)898		if n1 == nil {899			return n900		}901		n = n1902	}903}904905func staticValue1(nn Node) Node {906	if nn.Op() != ONAME {907		return nil908	}909	n := nn.(*Name).Canonical()910	if n.Class != PAUTO {911		return nil912	}913914	defn := n.Defn915	if defn == nil {916		return nil917	}918919	var rhs Node920FindRHS:921	switch defn.Op() {922	case OAS:923		defn := defn.(*AssignStmt)924		rhs = defn.Y925	case OAS2:926		defn := defn.(*AssignListStmt)927		for i, lhs := range defn.Lhs {928			if lhs == n {929				rhs = defn.Rhs[i]930				break FindRHS931			}932		}933		base.FatalfAt(defn.Pos(), "%v missing from LHS of %v", n, defn)934	default:935		return nil936	}937	if rhs == nil {938		if n.AutoTemp() {939			return nil940		}941		base.FatalfAt(defn.Pos(), "RHS is nil: %v", defn)942	}943944	if Reassigned(n) {945		return nil946	}947948	return rhs949}950951// Reassigned takes an ONAME node, walks the function in which it is952// defined, and returns a boolean indicating whether the name has any953// assignments other than its declaration.954// NB: global variables are always considered to be re-assigned.955// TODO: handle initial declaration not including an assignment and956// followed by a single assignment?957// NOTE: any changes made here should also be made in the corresponding958// code in the ReassignOracle.Init method.959func Reassigned(name *Name) bool {960	if name.Op() != ONAME {961		base.Fatalf("reassigned %v", name)962	}963	// no way to reliably check for no-reassignment of globals, assume it can be964	if name.Curfn == nil {965		return true966	}967968	if name.Addrtaken() {969		return true // conservatively assume it's reassigned indirectly970	}971972	// TODO(mdempsky): This is inefficient and becoming increasingly973	// unwieldy. Figure out a way to generalize escape analysis's974	// reassignment detection for use by inlining and devirtualization.975976	// isName reports whether n is a reference to name.977	isName := func(x Node) bool {978		if x == nil {979			return false980		}981		n, ok := OuterValue(x).(*Name)982		return ok && n.Canonical() == name983	}984985	var do func(n Node) bool986	do = func(n Node) bool {987		switch n.Op() {988		case OAS:989			n := n.(*AssignStmt)990			if isName(n.X) && n != name.Defn {991				return true992			}993		case OAS2, OAS2FUNC, OAS2MAPR, OAS2DOTTYPE, OAS2RECV, OSELRECV2:994			n := n.(*AssignListStmt)995			for _, p := range n.Lhs {996				if isName(p) && n != name.Defn {997					return true998				}999			}1000		case OASOP:1001			n := n.(*AssignOpStmt)1002			if isName(n.X) {1003				return true1004			}1005		case OADDR:1006			n := n.(*AddrExpr)1007			if isName(n.X) {1008				base.FatalfAt(n.Pos(), "%v not marked addrtaken", name)1009			}1010		case ORANGE:1011			n := n.(*RangeStmt)1012			if isName(n.Key) || isName(n.Value) {1013				return true1014			}1015		case OCLOSURE:1016			n := n.(*ClosureExpr)1017			if Any(n.Func, do) {1018				return true1019			}1020		}1021		return false1022	}1023	return Any(name.Curfn, do)1024}10251026// StaticCalleeName returns the ONAME/PFUNC for n, if known.1027func StaticCalleeName(n Node) *Name {1028	switch n.Op() {1029	case OMETHEXPR:1030		n := n.(*SelectorExpr)1031		return MethodExprName(n)1032	case ONAME:1033		n := n.(*Name)1034		if n.Class == PFUNC {1035			return n1036		}1037	case OCLOSURE:1038		return n.(*ClosureExpr).Func.Nname1039	}1040	return nil1041}10421043// IsIntrinsicCall reports whether the compiler back end will treat the call as an intrinsic operation.1044var IsIntrinsicCall = func(*CallExpr) bool { return false }10451046// IsIntrinsicSym reports whether the compiler back end will treat a call to this symbol as an intrinsic operation.1047var IsIntrinsicSym = func(*types.Sym) bool { return false }10481049// SameSafeExpr checks whether it is safe to reuse one of l and r1050// instead of computing both. SameSafeExpr assumes that l and r are1051// used in the same statement or expression. In order for it to be1052// safe to reuse l or r, they must:1053//   - be the same expression1054//   - not have side-effects (no function calls, no channel ops);1055//     however, panics are ok1056//   - not cause inappropriate aliasing; e.g. two string to []byte1057//     conversions, must result in two distinct slices1058//1059// The handling of OINDEXMAP is subtle. OINDEXMAP can occur both1060// as an lvalue (map assignment) and an rvalue (map access). This is1061// currently OK, since the only place SameSafeExpr gets used on an1062// lvalue expression is for OSLICE and OAPPEND optimizations, and it1063// is correct in those settings.1064func SameSafeExpr(l Node, r Node) bool {1065	for l.Op() == OCONVNOP {1066		l = l.(*ConvExpr).X1067	}1068	for r.Op() == OCONVNOP {1069		r = r.(*ConvExpr).X1070	}1071	if l.Op() != r.Op() || !types.Identical(l.Type(), r.Type()) {1072		return false1073	}10741075	switch l.Op() {1076	case ONAME:1077		return l == r10781079	case ODOT, ODOTPTR:1080		l := l.(*SelectorExpr)1081		r := r.(*SelectorExpr)1082		return l.Sel != nil && r.Sel != nil && l.Sel == r.Sel && SameSafeExpr(l.X, r.X)10831084	case ODEREF:1085		l := l.(*StarExpr)1086		r := r.(*StarExpr)1087		return SameSafeExpr(l.X, r.X)10881089	case ONOT, OBITNOT, OPLUS, ONEG:1090		l := l.(*UnaryExpr)1091		r := r.(*UnaryExpr)1092		return SameSafeExpr(l.X, r.X)10931094	case OCONV:1095		l := l.(*ConvExpr)1096		r := r.(*ConvExpr)1097		// Some conversions can't be reused, such as []byte(str).1098		// Allow only numeric-ish types. This is a bit conservative.1099		return types.IsSimple[l.Type().Kind()] && SameSafeExpr(l.X, r.X)11001101	case OINDEX, OINDEXMAP:1102		l := l.(*IndexExpr)1103		r := r.(*IndexExpr)1104		return SameSafeExpr(l.X, r.X) && SameSafeExpr(l.Index, r.Index)11051106	case OADD, OSUB, OOR, OXOR, OMUL, OLSH, ORSH, OAND, OANDNOT, ODIV, OMOD:1107		l := l.(*BinaryExpr)1108		r := r.(*BinaryExpr)1109		return SameSafeExpr(l.X, r.X) && SameSafeExpr(l.Y, r.Y)11101111	case OLITERAL:1112		return constant.Compare(l.Val(), token.EQL, r.Val())11131114	case ONIL:1115		return true1116	}11171118	return false1119}11201121// ShouldCheckPtr reports whether pointer checking should be enabled for1122// function fn at a given level. See debugHelpFooter for defined1123// levels.1124func ShouldCheckPtr(fn *Func, level int) bool {1125	return base.Debug.Checkptr >= level && fn.Pragma&NoCheckPtr == 01126}11271128// ShouldAsanCheckPtr reports whether pointer checking should be enabled for1129// function fn when -asan is enabled.1130func ShouldAsanCheckPtr(fn *Func) bool {1131	return base.Flag.ASan && fn.Pragma&NoCheckPtr == 01132}11331134// IsReflectHeaderDataField reports whether l is an expression p.Data1135// where p has type reflect.SliceHeader or reflect.StringHeader.1136func IsReflectHeaderDataField(l Node) bool {1137	if l.Type() != types.Types[types.TUINTPTR] {1138		return false1139	}11401141	var tsym *types.Sym1142	switch l.Op() {1143	case ODOT:1144		l := l.(*SelectorExpr)1145		tsym = l.X.Type().Sym()1146	case ODOTPTR:1147		l := l.(*SelectorExpr)1148		tsym = l.X.Type().Elem().Sym()1149	default:1150		return false1151	}11521153	if tsym == nil || l.Sym().Name != "Data" || tsym.Pkg.Path != "reflect" {1154		return false1155	}1156	return tsym.Name == "SliceHeader" || tsym.Name == "StringHeader"1157}11581159func ParamNames(ft *types.Type) []Node {1160	args := make([]Node, ft.NumParams())1161	for i, f := range ft.Params() {1162		args[i] = f.Nname.(*Name)1163	}1164	return args1165}11661167func RecvParamNames(ft *types.Type) []Node {1168	args := make([]Node, ft.NumRecvs()+ft.NumParams())1169	for i, f := range ft.RecvParams() {1170		args[i] = f.Nname.(*Name)1171	}1172	return args1173}11741175// MethodSym returns the method symbol representing a method name1176// associated with a specific receiver type.1177//1178// Method symbols can be used to distinguish the same method appearing1179// in different method sets. For example, T.M and (*T).M have distinct1180// method symbols.1181//1182// The returned symbol will be marked as a function.1183func MethodSym(recv *types.Type, msym *types.Sym) *types.Sym {1184	sym := MethodSymSuffix(recv, msym, "")1185	sym.SetFunc(true)1186	return sym1187}11881189// MethodSymSuffix is like MethodSym, but allows attaching a1190// distinguisher suffix. To avoid collisions, the suffix must not1191// start with a letter, number, or period.1192func MethodSymSuffix(recv *types.Type, msym *types.Sym, suffix string) *types.Sym {1193	if msym.IsBlank() {1194		base.Fatalf("blank method name")1195	}11961197	rsym := recv.Sym()1198	if recv.IsPtr() {1199		if rsym != nil {1200			base.Fatalf("declared pointer receiver type: %v", recv)1201		}1202		rsym = recv.Elem().Sym()1203	}12041205	// Find the package the receiver type appeared in. For1206	// anonymous receiver types (i.e., anonymous structs with1207	// embedded fields), use the "go" pseudo-package instead.1208	rpkg := Pkgs.Go1209	if rsym != nil {1210		rpkg = rsym.Pkg1211	}12121213	var b bytes.Buffer1214	if recv.IsPtr() {1215		// The parentheses aren't really necessary, but1216		// they're pretty traditional at this point.1217		fmt.Fprintf(&b, "(%-S)", recv)1218	} else {1219		fmt.Fprintf(&b, "%-S", recv)1220	}12211222	// A particular receiver type may have multiple non-exported1223	// methods with the same name. To disambiguate them, include a1224	// package qualifier for names that came from a different1225	// package than the receiver type.1226	if !types.IsExported(msym.Name) && msym.Pkg != rpkg {1227		b.WriteString(".")1228		b.WriteString(msym.Pkg.Prefix)1229	}12301231	b.WriteString(".")1232	b.WriteString(msym.Name)1233	b.WriteString(suffix)1234	return rpkg.LookupBytes(b.Bytes())1235}12361237// LookupMethodSelector returns the types.Sym of the selector for a method1238// named in local symbol name, as well as the types.Sym of the receiver.1239//1240// TODO(prattmic): this does not attempt to handle method suffixes (wrappers).1241func LookupMethodSelector(pkg *types.Pkg, name string) (typ, meth *types.Sym, err error) {1242	typeName, methName := splitType(name)1243	if typeName == "" {1244		return nil, nil, fmt.Errorf("%s doesn't contain type split", name)1245	}12461247	if len(typeName) > 3 && typeName[:2] == "(*" && typeName[len(typeName)-1] == ')' {1248		// Symbol name is for a pointer receiver method. We just want1249		// the base type name.1250		typeName = typeName[2 : len(typeName)-1]1251	}12521253	typ = pkg.Lookup(typeName)1254	meth = pkg.Selector(methName)1255	return typ, meth, nil1256}12571258// splitType splits a local symbol name into type and method (fn). If this a1259// free function, typ == "".1260//1261// N.B. closures and methods can be ambiguous (e.g., bar.func1). These cases1262// are returned as methods.1263func splitType(name string) (typ, fn string) {1264	// Types are split on the first dot, ignoring everything inside1265	// brackets (instantiation of type parameter, usually including1266	// "go.shape").1267	bracket := 01268	for i, r := range name {1269		if r == '.' && bracket == 0 {1270			return name[:i], name[i+1:]1271		}1272		if r == '[' {1273			bracket++1274		}1275		if r == ']' {1276			bracket--1277		}1278	}1279	return "", name1280}12811282// MethodExprName returns the ONAME representing the method1283// referenced by expression n, which must be a method selector,1284// method expression, or method value.1285func MethodExprName(n Node) *Name {1286	name, _ := MethodExprFunc(n).Nname.(*Name)1287	return name1288}12891290// MethodExprFunc is like MethodExprName, but returns the types.Field instead.1291func MethodExprFunc(n Node) *types.Field {1292	switch n.Op() {1293	case ODOTMETH, OMETHEXPR, OMETHVALUE:1294		return n.(*SelectorExpr).Selection1295	}1296	base.Fatalf("unexpected node: %v (%v)", n, n.Op())1297	panic("unreachable")1298}12991300// A MoveToHeapExpr takes a slice as input and moves it to the1301// heap (by copying the backing store if it is not already1302// on the heap).1303type MoveToHeapExpr struct {1304	miniExpr1305	Slice Node1306	// An expression that evaluates to a *runtime._type1307	// that represents the slice element type.1308	RType Node1309	// If PreserveCapacity is true, the capacity of1310	// the resulting slice, and all of the elements in1311	// [len:cap], must be preserved.1312	// If PreserveCapacity is false, the resulting1313	// slice may have any capacity >= len, with any1314	// elements in the resulting [len:cap] range zeroed.1315	PreserveCapacity bool1316}13171318func NewMoveToHeapExpr(pos src.XPos, slice Node) *MoveToHeapExpr {1319	n := &MoveToHeapExpr{Slice: slice}1320	n.pos = pos1321	n.op = OMOVE2HEAP1322	return n1323}

Code quality findings 6

Use of unsafe package detected; ensure it’s necessary, justified in comments, and bounds-checked to avoid memory corruption
warning safety unsafe-package
// unsafe.Pointer to *Elem or *[Len]Elem.
Type switch without default case; unhandled types will silently do nothing. Add a default case for safety
info correctness unchecked-type-switch
switch n1 := n.(type) {
Range over slice copies each element by value; use index or pointer receiver for large structs to avoid copies
info performance copy-large-struct
for i, lhs := range defn.Lhs {
Range over slice copies each element by value; use index or pointer receiver for large structs to avoid copies
info performance copy-large-struct
for i, f := range ft.Params() {
Range over slice copies each element by value; use index or pointer receiver for large structs to avoid copies
info performance copy-large-struct
for i, f := range ft.RecvParams() {
Range over slice copies each element by value; use index or pointer receiver for large structs to avoid copies
info performance copy-large-struct
for i, r := range name {

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.