go subr 源码

  • 2022-07-15
  • 浏览 (909)

golang subr 代码

文件路径:/src/cmd/compile/internal/typecheck/subr.go

// Copyright 2009 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 typecheck

import (
	"bytes"
	"fmt"
	"sort"
	"strings"

	"cmd/compile/internal/base"
	"cmd/compile/internal/ir"
	"cmd/compile/internal/types"
	"cmd/internal/objabi"
	"cmd/internal/src"
)

func AssignConv(n ir.Node, t *types.Type, context string) ir.Node {
	return assignconvfn(n, t, func() string { return context })
}

// LookupNum returns types.LocalPkg.LookupNum(prefix, n).
func LookupNum(prefix string, n int) *types.Sym {
	return types.LocalPkg.LookupNum(prefix, n)
}

// Given funarg struct list, return list of fn args.
func NewFuncParams(tl *types.Type, mustname bool) []*ir.Field {
	var args []*ir.Field
	gen := 0
	for _, t := range tl.Fields().Slice() {
		s := t.Sym
		if mustname && (s == nil || s.Name == "_") {
			// invent a name so that we can refer to it in the trampoline
			s = LookupNum(".anon", gen)
			gen++
		} else if s != nil && s.Pkg != types.LocalPkg {
			// TODO(mdempsky): Preserve original position, name, and package.
			s = Lookup(s.Name)
		}
		a := ir.NewField(base.Pos, s, t.Type)
		a.Pos = t.Pos
		a.IsDDD = t.IsDDD()
		args = append(args, a)
	}

	return args
}

// newname returns a new ONAME Node associated with symbol s.
func NewName(s *types.Sym) *ir.Name {
	n := ir.NewNameAt(base.Pos, s)
	n.Curfn = ir.CurFunc
	return n
}

// NodAddr returns a node representing &n at base.Pos.
func NodAddr(n ir.Node) *ir.AddrExpr {
	return NodAddrAt(base.Pos, n)
}

// nodAddrPos returns a node representing &n at position pos.
func NodAddrAt(pos src.XPos, n ir.Node) *ir.AddrExpr {
	n = markAddrOf(n)
	return ir.NewAddrExpr(pos, n)
}

func markAddrOf(n ir.Node) ir.Node {
	if IncrementalAddrtaken {
		// We can only do incremental addrtaken computation when it is ok
		// to typecheck the argument of the OADDR. That's only safe after the
		// main typecheck has completed, and not loading the inlined body.
		// The argument to OADDR needs to be typechecked because &x[i] takes
		// the address of x if x is an array, but not if x is a slice.
		// Note: OuterValue doesn't work correctly until n is typechecked.
		n = typecheck(n, ctxExpr)
		if x := ir.OuterValue(n); x.Op() == ir.ONAME {
			x.Name().SetAddrtaken(true)
		}
	} else {
		// Remember that we built an OADDR without computing the Addrtaken bit for
		// its argument. We'll do that later in bulk using computeAddrtaken.
		DirtyAddrtaken = true
	}
	return n
}

// If IncrementalAddrtaken is false, we do not compute Addrtaken for an OADDR Node
// when it is built. The Addrtaken bits are set in bulk by computeAddrtaken.
// If IncrementalAddrtaken is true, then when an OADDR Node is built the Addrtaken
// field of its argument is updated immediately.
var IncrementalAddrtaken = false

// If DirtyAddrtaken is true, then there are OADDR whose corresponding arguments
// have not yet been marked as Addrtaken.
var DirtyAddrtaken = false

func ComputeAddrtaken(top []ir.Node) {
	for _, n := range top {
		var doVisit func(n ir.Node)
		doVisit = func(n ir.Node) {
			if n.Op() == ir.OADDR {
				if x := ir.OuterValue(n.(*ir.AddrExpr).X); x.Op() == ir.ONAME {
					x.Name().SetAddrtaken(true)
					if x.Name().IsClosureVar() {
						// Mark the original variable as Addrtaken so that capturevars
						// knows not to pass it by value.
						x.Name().Defn.Name().SetAddrtaken(true)
					}
				}
			}
			if n.Op() == ir.OCLOSURE {
				ir.VisitList(n.(*ir.ClosureExpr).Func.Body, doVisit)
			}
		}
		ir.Visit(n, doVisit)
	}
}

func NodNil() ir.Node {
	n := ir.NewNilExpr(base.Pos)
	n.SetType(types.Types[types.TNIL])
	return n
}

// AddImplicitDots finds missing fields in obj.field that
// will give the shortest unique addressing and
// modifies the tree with missing field names.
func AddImplicitDots(n *ir.SelectorExpr) *ir.SelectorExpr {
	n.X = typecheck(n.X, ctxType|ctxExpr)
	t := n.X.Type()
	if t == nil {
		return n
	}

	if n.X.Op() == ir.OTYPE {
		return n
	}

	s := n.Sel
	if s == nil {
		return n
	}

	switch path, ambig := dotpath(s, t, nil, false); {
	case path != nil:
		// rebuild elided dots
		for c := len(path) - 1; c >= 0; c-- {
			dot := ir.NewSelectorExpr(n.Pos(), ir.ODOT, n.X, path[c].field.Sym)
			dot.SetImplicit(true)
			dot.SetType(path[c].field.Type)
			n.X = dot
		}
	case ambig:
		base.Errorf("ambiguous selector %v", n)
		n.X = nil
	}

	return n
}

// CalcMethods calculates all the methods (including embedding) of a non-interface
// type t.
func CalcMethods(t *types.Type) {
	if t == nil || t.AllMethods().Len() != 0 {
		return
	}

	// mark top-level method symbols
	// so that expand1 doesn't consider them.
	for _, f := range t.Methods().Slice() {
		f.Sym.SetUniq(true)
	}

	// generate all reachable methods
	slist = slist[:0]
	expand1(t, true)

	// check each method to be uniquely reachable
	var ms []*types.Field
	for i, sl := range slist {
		slist[i].field = nil
		sl.field.Sym.SetUniq(false)

		var f *types.Field
		path, _ := dotpath(sl.field.Sym, t, &f, false)
		if path == nil {
			continue
		}

		// dotpath may have dug out arbitrary fields, we only want methods.
		if !f.IsMethod() {
			continue
		}

		// add it to the base type method list
		f = f.Copy()
		f.Embedded = 1 // needs a trampoline
		for _, d := range path {
			if d.field.Type.IsPtr() {
				f.Embedded = 2
				break
			}
		}
		ms = append(ms, f)
	}

	for _, f := range t.Methods().Slice() {
		f.Sym.SetUniq(false)
	}

	ms = append(ms, t.Methods().Slice()...)
	sort.Sort(types.MethodsByName(ms))
	t.SetAllMethods(ms)
}

// adddot1 returns the number of fields or methods named s at depth d in Type t.
// If exactly one exists, it will be returned in *save (if save is not nil),
// and dotlist will contain the path of embedded fields traversed to find it,
// in reverse order. If none exist, more will indicate whether t contains any
// embedded fields at depth d, so callers can decide whether to retry at
// a greater depth.
func adddot1(s *types.Sym, t *types.Type, d int, save **types.Field, ignorecase bool) (c int, more bool) {
	if t.Recur() {
		return
	}
	t.SetRecur(true)
	defer t.SetRecur(false)

	var u *types.Type
	d--
	if d < 0 {
		// We've reached our target depth. If t has any fields/methods
		// named s, then we're done. Otherwise, we still need to check
		// below for embedded fields.
		c = lookdot0(s, t, save, ignorecase)
		if c != 0 {
			return c, false
		}
	}

	u = t
	if u.IsPtr() {
		u = u.Elem()
	}
	if !u.IsStruct() && !u.IsInterface() {
		return c, false
	}

	var fields *types.Fields
	if u.IsStruct() {
		fields = u.Fields()
	} else {
		fields = u.AllMethods()
	}
	for _, f := range fields.Slice() {
		if f.Embedded == 0 || f.Sym == nil {
			continue
		}
		if d < 0 {
			// Found an embedded field at target depth.
			return c, true
		}
		a, more1 := adddot1(s, f.Type, d, save, ignorecase)
		if a != 0 && c == 0 {
			dotlist[d].field = f
		}
		c += a
		if more1 {
			more = true
		}
	}

	return c, more
}

// dotlist is used by adddot1 to record the path of embedded fields
// used to access a target field or method.
// Must be non-nil so that dotpath returns a non-nil slice even if d is zero.
var dotlist = make([]dlist, 10)

// Convert node n for assignment to type t.
func assignconvfn(n ir.Node, t *types.Type, context func() string) ir.Node {
	if n == nil || n.Type() == nil {
		return n
	}

	if t.Kind() == types.TBLANK && n.Type().Kind() == types.TNIL {
		base.Errorf("use of untyped nil")
	}

	n = convlit1(n, t, false, context)
	if n.Type() == nil {
		return n
	}
	if t.Kind() == types.TBLANK {
		return n
	}

	// Convert ideal bool from comparison to plain bool
	// if the next step is non-bool (like interface{}).
	if n.Type() == types.UntypedBool && !t.IsBoolean() {
		if n.Op() == ir.ONAME || n.Op() == ir.OLITERAL {
			r := ir.NewConvExpr(base.Pos, ir.OCONVNOP, nil, n)
			r.SetType(types.Types[types.TBOOL])
			r.SetTypecheck(1)
			r.SetImplicit(true)
			n = r
		}
	}

	if types.Identical(n.Type(), t) {
		return n
	}

	op, why := Assignop(n.Type(), t)
	if op == ir.OXXX {
		base.Errorf("cannot use %L as type %v in %s%s", n, t, context(), why)
		op = ir.OCONV
	}

	r := ir.NewConvExpr(base.Pos, op, t, n)
	r.SetTypecheck(1)
	r.SetImplicit(true)
	return r
}

// Is type src assignment compatible to type dst?
// If so, return op code to use in conversion.
// If not, return OXXX. In this case, the string return parameter may
// hold a reason why. In all other cases, it'll be the empty string.
func Assignop(src, dst *types.Type) (ir.Op, string) {
	if src == dst {
		return ir.OCONVNOP, ""
	}
	if src == nil || dst == nil || src.Kind() == types.TFORW || dst.Kind() == types.TFORW || src.Underlying() == nil || dst.Underlying() == nil {
		return ir.OXXX, ""
	}

	// 1. src type is identical to dst.
	if types.Identical(src, dst) {
		return ir.OCONVNOP, ""
	}
	return Assignop1(src, dst)
}

func Assignop1(src, dst *types.Type) (ir.Op, string) {
	// 2. src and dst have identical underlying types and
	//   a. either src or dst is not a named type, or
	//   b. both are empty interface types, or
	//   c. at least one is a gcshape type.
	// For assignable but different non-empty interface types,
	// we want to recompute the itab. Recomputing the itab ensures
	// that itabs are unique (thus an interface with a compile-time
	// type I has an itab with interface type I).
	if types.Identical(src.Underlying(), dst.Underlying()) {
		if src.IsEmptyInterface() {
			// Conversion between two empty interfaces
			// requires no code.
			return ir.OCONVNOP, ""
		}
		if (src.Sym() == nil || dst.Sym() == nil) && !src.IsInterface() {
			// Conversion between two types, at least one unnamed,
			// needs no conversion. The exception is nonempty interfaces
			// which need to have their itab updated.
			return ir.OCONVNOP, ""
		}
		if src.IsShape() || dst.IsShape() {
			// Conversion between a shape type and one of the types
			// it represents also needs no conversion.
			return ir.OCONVNOP, ""
		}
	}

	// 3. dst is an interface type and src implements dst.
	if dst.IsInterface() && src.Kind() != types.TNIL {
		var missing, have *types.Field
		var ptr int
		if src.IsShape() {
			// Shape types implement things they have already
			// been typechecked to implement, even if they
			// don't have the methods for them.
			return ir.OCONVIFACE, ""
		}
		if implements(src, dst, &missing, &have, &ptr) {
			return ir.OCONVIFACE, ""
		}

		var why string
		if isptrto(src, types.TINTER) {
			why = fmt.Sprintf(":\n\t%v is pointer to interface, not interface", src)
		} else if have != nil && have.Sym == missing.Sym && have.Nointerface() {
			why = fmt.Sprintf(":\n\t%v does not implement %v (%v method is marked 'nointerface')", src, dst, missing.Sym)
		} else if have != nil && have.Sym == missing.Sym {
			why = fmt.Sprintf(":\n\t%v does not implement %v (wrong type for %v method)\n"+
				"\t\thave %v%S\n\t\twant %v%S", src, dst, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
		} else if ptr != 0 {
			why = fmt.Sprintf(":\n\t%v does not implement %v (%v method has pointer receiver)", src, dst, missing.Sym)
		} else if have != nil {
			why = fmt.Sprintf(":\n\t%v does not implement %v (missing %v method)\n"+
				"\t\thave %v%S\n\t\twant %v%S", src, dst, missing.Sym, have.Sym, have.Type, missing.Sym, missing.Type)
		} else {
			why = fmt.Sprintf(":\n\t%v does not implement %v (missing %v method)", src, dst, missing.Sym)
		}

		return ir.OXXX, why
	}

	if isptrto(dst, types.TINTER) {
		why := fmt.Sprintf(":\n\t%v is pointer to interface, not interface", dst)
		return ir.OXXX, why
	}

	if src.IsInterface() && dst.Kind() != types.TBLANK {
		var missing, have *types.Field
		var ptr int
		var why string
		if implements(dst, src, &missing, &have, &ptr) {
			why = ": need type assertion"
		}
		return ir.OXXX, why
	}

	// 4. src is a bidirectional channel value, dst is a channel type,
	// src and dst have identical element types, and
	// either src or dst is not a named type.
	if src.IsChan() && src.ChanDir() == types.Cboth && dst.IsChan() {
		if types.Identical(src.Elem(), dst.Elem()) && (src.Sym() == nil || dst.Sym() == nil) {
			return ir.OCONVNOP, ""
		}
	}

	// 5. src is the predeclared identifier nil and dst is a nillable type.
	if src.Kind() == types.TNIL {
		switch dst.Kind() {
		case types.TPTR,
			types.TFUNC,
			types.TMAP,
			types.TCHAN,
			types.TINTER,
			types.TSLICE:
			return ir.OCONVNOP, ""
		}
	}

	// 6. rule about untyped constants - already converted by DefaultLit.

	// 7. Any typed value can be assigned to the blank identifier.
	if dst.Kind() == types.TBLANK {
		return ir.OCONVNOP, ""
	}

	return ir.OXXX, ""
}

// Can we convert a value of type src to a value of type dst?
// If so, return op code to use in conversion (maybe OCONVNOP).
// If not, return OXXX. In this case, the string return parameter may
// hold a reason why. In all other cases, it'll be the empty string.
// srcConstant indicates whether the value of type src is a constant.
func Convertop(srcConstant bool, src, dst *types.Type) (ir.Op, string) {
	if src == dst {
		return ir.OCONVNOP, ""
	}
	if src == nil || dst == nil {
		return ir.OXXX, ""
	}

	// Conversions from regular to go:notinheap are not allowed
	// (unless it's unsafe.Pointer). These are runtime-specific
	// rules.
	// (a) Disallow (*T) to (*U) where T is go:notinheap but U isn't.
	if src.IsPtr() && dst.IsPtr() && dst.Elem().NotInHeap() && !src.Elem().NotInHeap() {
		why := fmt.Sprintf(":\n\t%v is incomplete (or unallocatable), but %v is not", dst.Elem(), src.Elem())
		return ir.OXXX, why
	}
	// (b) Disallow string to []T where T is go:notinheap.
	if src.IsString() && dst.IsSlice() && dst.Elem().NotInHeap() && (dst.Elem().Kind() == types.ByteType.Kind() || dst.Elem().Kind() == types.RuneType.Kind()) {
		why := fmt.Sprintf(":\n\t%v is incomplete (or unallocatable)", dst.Elem())
		return ir.OXXX, why
	}

	// 1. src can be assigned to dst.
	op, why := Assignop(src, dst)
	if op != ir.OXXX {
		return op, why
	}

	// The rules for interfaces are no different in conversions
	// than assignments. If interfaces are involved, stop now
	// with the good message from assignop.
	// Otherwise clear the error.
	if src.IsInterface() || dst.IsInterface() {
		return ir.OXXX, why
	}

	// 2. Ignoring struct tags, src and dst have identical underlying types.
	if types.IdenticalIgnoreTags(src.Underlying(), dst.Underlying()) {
		return ir.OCONVNOP, ""
	}

	// 3. src and dst are unnamed pointer types and, ignoring struct tags,
	// their base types have identical underlying types.
	if src.IsPtr() && dst.IsPtr() && src.Sym() == nil && dst.Sym() == nil {
		if types.IdenticalIgnoreTags(src.Elem().Underlying(), dst.Elem().Underlying()) {
			return ir.OCONVNOP, ""
		}
	}

	// 4. src and dst are both integer or floating point types.
	if (src.IsInteger() || src.IsFloat()) && (dst.IsInteger() || dst.IsFloat()) {
		if types.SimType[src.Kind()] == types.SimType[dst.Kind()] {
			return ir.OCONVNOP, ""
		}
		return ir.OCONV, ""
	}

	// 5. src and dst are both complex types.
	if src.IsComplex() && dst.IsComplex() {
		if types.SimType[src.Kind()] == types.SimType[dst.Kind()] {
			return ir.OCONVNOP, ""
		}
		return ir.OCONV, ""
	}

	// Special case for constant conversions: any numeric
	// conversion is potentially okay. We'll validate further
	// within evconst. See #38117.
	if srcConstant && (src.IsInteger() || src.IsFloat() || src.IsComplex()) && (dst.IsInteger() || dst.IsFloat() || dst.IsComplex()) {
		return ir.OCONV, ""
	}

	// 6. src is an integer or has type []byte or []rune
	// and dst is a string type.
	if src.IsInteger() && dst.IsString() {
		return ir.ORUNESTR, ""
	}

	if src.IsSlice() && dst.IsString() {
		if src.Elem().Kind() == types.ByteType.Kind() {
			return ir.OBYTES2STR, ""
		}
		if src.Elem().Kind() == types.RuneType.Kind() {
			return ir.ORUNES2STR, ""
		}
	}

	// 7. src is a string and dst is []byte or []rune.
	// String to slice.
	if src.IsString() && dst.IsSlice() {
		if dst.Elem().Kind() == types.ByteType.Kind() {
			return ir.OSTR2BYTES, ""
		}
		if dst.Elem().Kind() == types.RuneType.Kind() {
			return ir.OSTR2RUNES, ""
		}
	}

	// 8. src is a pointer or uintptr and dst is unsafe.Pointer.
	if (src.IsPtr() || src.IsUintptr()) && dst.IsUnsafePtr() {
		return ir.OCONVNOP, ""
	}

	// 9. src is unsafe.Pointer and dst is a pointer or uintptr.
	if src.IsUnsafePtr() && (dst.IsPtr() || dst.IsUintptr()) {
		return ir.OCONVNOP, ""
	}

	// 10. src is map and dst is a pointer to corresponding hmap.
	// This rule is needed for the implementation detail that
	// go gc maps are implemented as a pointer to a hmap struct.
	if src.Kind() == types.TMAP && dst.IsPtr() &&
		src.MapType().Hmap == dst.Elem() {
		return ir.OCONVNOP, ""
	}

	// 11. src is a slice and dst is a pointer-to-array.
	// They must have same element type.
	if src.IsSlice() && dst.IsPtr() && dst.Elem().IsArray() &&
		types.Identical(src.Elem(), dst.Elem().Elem()) {
		return ir.OSLICE2ARRPTR, ""
	}

	return ir.OXXX, ""
}

// Code to resolve elided DOTs in embedded types.

// A dlist stores a pointer to a TFIELD Type embedded within
// a TSTRUCT or TINTER Type.
type dlist struct {
	field *types.Field
}

// dotpath computes the unique shortest explicit selector path to fully qualify
// a selection expression x.f, where x is of type t and f is the symbol s.
// If no such path exists, dotpath returns nil.
// If there are multiple shortest paths to the same depth, ambig is true.
func dotpath(s *types.Sym, t *types.Type, save **types.Field, ignorecase bool) (path []dlist, ambig bool) {
	// The embedding of types within structs imposes a tree structure onto
	// types: structs parent the types they embed, and types parent their
	// fields or methods. Our goal here is to find the shortest path to
	// a field or method named s in the subtree rooted at t. To accomplish
	// that, we iteratively perform depth-first searches of increasing depth
	// until we either find the named field/method or exhaust the tree.
	for d := 0; ; d++ {
		if d > len(dotlist) {
			dotlist = append(dotlist, dlist{})
		}
		if c, more := adddot1(s, t, d, save, ignorecase); c == 1 {
			return dotlist[:d], false
		} else if c > 1 {
			return nil, true
		} else if !more {
			return nil, false
		}
	}
}

func expand0(t *types.Type) {
	u := t
	if u.IsPtr() {
		u = u.Elem()
	}

	if u.IsInterface() {
		for _, f := range u.AllMethods().Slice() {
			if f.Sym.Uniq() {
				continue
			}
			f.Sym.SetUniq(true)
			slist = append(slist, symlink{field: f})
		}

		return
	}

	u = types.ReceiverBaseType(t)
	if u != nil {
		for _, f := range u.Methods().Slice() {
			if f.Sym.Uniq() {
				continue
			}
			f.Sym.SetUniq(true)
			slist = append(slist, symlink{field: f})
		}
	}
}

func expand1(t *types.Type, top bool) {
	if t.Recur() {
		return
	}
	t.SetRecur(true)

	if !top {
		expand0(t)
	}

	u := t
	if u.IsPtr() {
		u = u.Elem()
	}

	if u.IsStruct() || u.IsInterface() {
		var fields *types.Fields
		if u.IsStruct() {
			fields = u.Fields()
		} else {
			fields = u.AllMethods()
		}
		for _, f := range fields.Slice() {
			if f.Embedded == 0 {
				continue
			}
			if f.Sym == nil {
				continue
			}
			expand1(f.Type, false)
		}
	}

	t.SetRecur(false)
}

func ifacelookdot(s *types.Sym, t *types.Type, ignorecase bool) (m *types.Field, followptr bool) {
	if t == nil {
		return nil, false
	}

	path, ambig := dotpath(s, t, &m, ignorecase)
	if path == nil {
		if ambig {
			base.Errorf("%v.%v is ambiguous", t, s)
		}
		return nil, false
	}

	for _, d := range path {
		if d.field.Type.IsPtr() {
			followptr = true
			break
		}
	}

	if !m.IsMethod() {
		base.Errorf("%v.%v is a field, not a method", t, s)
		return nil, followptr
	}

	return m, followptr
}

// implements reports whether t implements the interface iface. t can be
// an interface, a type parameter, or a concrete type. If implements returns
// false, it stores a method of iface that is not implemented in *m. If the
// method name matches but the type is wrong, it additionally stores the type
// of the method (on t) in *samename.
func implements(t, iface *types.Type, m, samename **types.Field, ptr *int) bool {
	t0 := t
	if t == nil {
		return false
	}

	if t.IsInterface() || t.IsTypeParam() {
		if t.IsTypeParam() {
			// If t is a simple type parameter T, its type and underlying is the same.
			// If t is a type definition:'type P[T any] T', its type is P[T] and its
			// underlying is T. Therefore we use 't.Underlying() != t' to distinguish them.
			if t.Underlying() != t {
				CalcMethods(t)
			} else {
				// A typeparam satisfies an interface if its type bound
				// has all the methods of that interface.
				t = t.Bound()
			}
		}
		i := 0
		tms := t.AllMethods().Slice()
		for _, im := range iface.AllMethods().Slice() {
			for i < len(tms) && tms[i].Sym != im.Sym {
				i++
			}
			if i == len(tms) {
				*m = im
				*samename = nil
				*ptr = 0
				return false
			}
			tm := tms[i]
			if !types.Identical(tm.Type, im.Type) {
				*m = im
				*samename = tm
				*ptr = 0
				return false
			}
		}

		return true
	}

	t = types.ReceiverBaseType(t)
	var tms []*types.Field
	if t != nil {
		CalcMethods(t)
		tms = t.AllMethods().Slice()
	}
	i := 0
	for _, im := range iface.AllMethods().Slice() {
		for i < len(tms) && tms[i].Sym != im.Sym {
			i++
		}
		if i == len(tms) {
			*m = im
			*samename, _ = ifacelookdot(im.Sym, t, true)
			*ptr = 0
			return false
		}
		tm := tms[i]
		if tm.Nointerface() || !types.Identical(tm.Type, im.Type) {
			*m = im
			*samename = tm
			*ptr = 0
			return false
		}
		followptr := tm.Embedded == 2

		// if pointer receiver in method,
		// the method does not exist for value types.
		rcvr := tm.Type.Recv().Type
		if rcvr.IsPtr() && !t0.IsPtr() && !followptr && !types.IsInterfaceMethod(tm.Type) {
			if false && base.Flag.LowerR != 0 {
				base.Errorf("interface pointer mismatch")
			}

			*m = im
			*samename = nil
			*ptr = 1
			return false
		}
	}

	return true
}

func isptrto(t *types.Type, et types.Kind) bool {
	if t == nil {
		return false
	}
	if !t.IsPtr() {
		return false
	}
	t = t.Elem()
	if t == nil {
		return false
	}
	if t.Kind() != et {
		return false
	}
	return true
}

// lookdot0 returns the number of fields or methods named s associated
// with Type t. If exactly one exists, it will be returned in *save
// (if save is not nil).
func lookdot0(s *types.Sym, t *types.Type, save **types.Field, ignorecase bool) int {
	u := t
	if u.IsPtr() {
		u = u.Elem()
	}

	c := 0
	if u.IsStruct() || u.IsInterface() {
		var fields *types.Fields
		if u.IsStruct() {
			fields = u.Fields()
		} else {
			fields = u.AllMethods()
		}
		for _, f := range fields.Slice() {
			if f.Sym == s || (ignorecase && f.IsMethod() && strings.EqualFold(f.Sym.Name, s.Name)) {
				if save != nil {
					*save = f
				}
				c++
			}
		}
	}

	u = t
	if t.Sym() != nil && t.IsPtr() && !t.Elem().IsPtr() {
		// If t is a defined pointer type, then x.m is shorthand for (*x).m.
		u = t.Elem()
	}
	u = types.ReceiverBaseType(u)
	if u != nil {
		for _, f := range u.Methods().Slice() {
			if f.Embedded == 0 && (f.Sym == s || (ignorecase && strings.EqualFold(f.Sym.Name, s.Name))) {
				if save != nil {
					*save = f
				}
				c++
			}
		}
	}

	return c
}

var slist []symlink

// Code to help generate trampoline functions for methods on embedded
// types. These are approx the same as the corresponding AddImplicitDots
// routines except that they expect to be called with unique tasks and
// they return the actual methods.

type symlink struct {
	field *types.Field
}

// TypesOf converts a list of nodes to a list
// of types of those nodes.
func TypesOf(x []ir.Ntype) []*types.Type {
	r := make([]*types.Type, len(x))
	for i, n := range x {
		r[i] = n.Type()
	}
	return r
}

// addTargs writes out the targs to buffer b as a comma-separated list enclosed by
// brackets.
func addTargs(b *bytes.Buffer, targs []*types.Type) {
	b.WriteByte('[')
	for i, targ := range targs {
		if i > 0 {
			b.WriteByte(',')
		}
		// Make sure that type arguments (including type params), are
		// uniquely specified. LinkString() eliminates all spaces
		// and includes the package path (local package path is "" before
		// linker substitution).
		tstring := targ.LinkString()
		b.WriteString(tstring)
	}
	b.WriteString("]")
}

// InstTypeName creates a name for an instantiated type, based on the name of the
// generic type and the type args.
func InstTypeName(name string, targs []*types.Type) string {
	b := bytes.NewBufferString(name)
	addTargs(b, targs)
	return b.String()
}

// makeInstName1 returns the name of the generic function instantiated with the
// given types, which can have type params or shapes, or be concrete types. name is
// the name of the generic function or method.
func makeInstName1(name string, targs []*types.Type, hasBrackets bool) string {
	b := bytes.NewBufferString("")
	i := strings.Index(name, "[")
	assert(hasBrackets == (i >= 0))
	if i >= 0 {
		b.WriteString(name[0:i])
	} else {
		b.WriteString(name)
	}
	addTargs(b, targs)
	if i >= 0 {
		i2 := strings.LastIndex(name[i:], "]")
		assert(i2 >= 0)
		b.WriteString(name[i+i2+1:])
	}
	return b.String()
}

// MakeFuncInstSym makes the unique sym for a stenciled generic function or method,
// based on the name of the function gf and the targs. It replaces any
// existing bracket type list in the name. MakeInstName asserts that gf has
// brackets in its name if and only if hasBrackets is true.
//
// Names of declared generic functions have no brackets originally, so hasBrackets
// should be false. Names of generic methods already have brackets, since the new
// type parameter is specified in the generic type of the receiver (e.g. func
// (func (v *value[T]).set(...) { ... } has the original name (*value[T]).set.
//
// The standard naming is something like: 'genFn[int,bool]' for functions and
// '(*genType[int,bool]).methodName' for methods
//
// isMethodNode specifies if the name of a method node is being generated (as opposed
// to a name of an instantiation of generic function or name of the shape-based
// function that helps implement a method of an instantiated type). For method nodes
// on shape types, we prepend "nofunc.", because method nodes for shape types will
// have no body, and we want to avoid a name conflict with the shape-based function
// that helps implement the same method for fully-instantiated types. Function names
// are also created at the end of (*Tsubster).typ1, so we append "nofunc" there as
// well, as needed.
func MakeFuncInstSym(gf *types.Sym, targs []*types.Type, isMethodNode, hasBrackets bool) *types.Sym {
	nm := makeInstName1(gf.Name, targs, hasBrackets)
	if targs[0].HasShape() && isMethodNode {
		nm = "nofunc." + nm
	}
	return gf.Pkg.Lookup(nm)
}

func MakeDictSym(gf *types.Sym, targs []*types.Type, hasBrackets bool) *types.Sym {
	for _, targ := range targs {
		if targ.HasTParam() {
			fmt.Printf("FUNCTION %s\n", gf.Name)
			for _, targ := range targs {
				fmt.Printf("  PARAM %+v\n", targ)
			}
			panic("dictionary should always have concrete type args")
		}
	}
	name := makeInstName1(gf.Name, targs, hasBrackets)
	name = fmt.Sprintf("%s.%s", objabi.GlobalDictPrefix, name)
	return gf.Pkg.Lookup(name)
}

func assert(p bool) {
	base.Assert(p)
}

// List of newly fully-instantiated types who should have their methods generated.
var instTypeList []*types.Type

// NeedInstType adds a new fully-instantiated type to instTypeList.
func NeedInstType(t *types.Type) {
	instTypeList = append(instTypeList, t)
}

// GetInstTypeList returns the current contents of instTypeList.
func GetInstTypeList() []*types.Type {
	r := instTypeList
	return r
}

// ClearInstTypeList clears the contents of instTypeList.
func ClearInstTypeList() {
	instTypeList = nil
}

// General type substituter, for replacing typeparams with type args.
type Tsubster struct {
	Tparams []*types.Type
	Targs   []*types.Type
	// If non-nil, the substitution map from name nodes in the generic function to the
	// name nodes in the new stenciled function.
	Vars map[*ir.Name]*ir.Name
	// If non-nil, function to substitute an incomplete (TFORW) type.
	SubstForwFunc func(*types.Type) *types.Type
	// Prevent endless recursion on functions. See #51832.
	Funcs map[*types.Type]bool
}

// Typ computes the type obtained by substituting any type parameter or shape in t
// that appears in subst.Tparams with the corresponding type argument in subst.Targs.
// If t contains no type parameters, the result is t; otherwise the result is a new
// type. It deals with recursive types by using TFORW types and finding partially or
// fully created types via sym.Def.
func (ts *Tsubster) Typ(t *types.Type) *types.Type {
	// Defer the CheckSize calls until we have fully-defined
	// (possibly-recursive) top-level type.
	types.DeferCheckSize()
	r := ts.typ1(t)
	types.ResumeCheckSize()
	return r
}

func (ts *Tsubster) typ1(t *types.Type) *types.Type {
	hasParamOrShape := t.HasTParam() || t.HasShape()
	if !hasParamOrShape && t.Kind() != types.TFUNC {
		// Note: function types need to be copied regardless, as the
		// types of closures may contain declarations that need
		// to be copied. See #45738.
		return t
	}

	if t.IsTypeParam() || t.IsShape() {
		for i, tp := range ts.Tparams {
			if tp == t {
				return ts.Targs[i]
			}
		}
		// If t is a simple typeparam T, then t has the name/symbol 'T'
		// and t.Underlying() == t.
		//
		// However, consider the type definition: 'type P[T any] T'. We
		// might use this definition so we can have a variant of type T
		// that we can add new methods to. Suppose t is a reference to
		// P[T]. t has the name 'P[T]', but its kind is TTYPEPARAM,
		// because P[T] is defined as T. If we look at t.Underlying(), it
		// is different, because the name of t.Underlying() is 'T' rather
		// than 'P[T]'. But the kind of t.Underlying() is also TTYPEPARAM.
		// In this case, we do the needed recursive substitution in the
		// case statement below.
		if t.Underlying() == t {
			// t is a simple typeparam that didn't match anything in tparam
			return t
		}
		// t is a more complex typeparam (e.g. P[T], as above, whose
		// definition is just T).
		assert(t.Sym() != nil)
	}

	var newsym *types.Sym
	var neededTargs []*types.Type
	var targsChanged bool // == are there any substitutions from this
	var forw *types.Type

	if t.Sym() != nil && hasParamOrShape {
		// Need to test for t.HasTParam() again because of special TFUNC case above.
		// Translate the type params for this type according to
		// the tparam/targs mapping from subst.
		neededTargs = make([]*types.Type, len(t.RParams()))
		for i, rparam := range t.RParams() {
			neededTargs[i] = ts.typ1(rparam)
			if !types.IdenticalStrict(neededTargs[i], rparam) {
				targsChanged = true
			}
		}
		// For a named (defined) type, we have to change the name of the
		// type as well. We do this first, so we can look up if we've
		// already seen this type during this substitution or other
		// definitions/substitutions.
		genName := genericTypeName(t.Sym())
		newsym = t.Sym().Pkg.Lookup(InstTypeName(genName, neededTargs))
		if newsym.Def != nil {
			// We've already created this instantiated defined type.
			return newsym.Def.Type()
		}

		// In order to deal with recursive generic types, create a TFORW
		// type initially and set the Def field of its sym, so it can be
		// found if this type appears recursively within the type.
		forw = NewIncompleteNamedType(t.Pos(), newsym)
		//println("Creating new type by sub", newsym.Name, forw.HasTParam())
		forw.SetRParams(neededTargs)
		// Copy the OrigType from the re-instantiated type (which is the sym of
		// the base generic type).
		assert(t.OrigType() != nil)
		forw.SetOrigType(t.OrigType())
	}

	var newt *types.Type

	switch t.Kind() {
	case types.TTYPEPARAM:
		if t.Sym() == newsym && !targsChanged {
			// The substitution did not change the type.
			return t
		}
		// Substitute the underlying typeparam (e.g. T in P[T], see
		// the example describing type P[T] above).
		newt = ts.typ1(t.Underlying())
		assert(newt != t)

	case types.TARRAY:
		elem := t.Elem()
		newelem := ts.typ1(elem)
		if newelem != elem || targsChanged {
			newt = types.NewArray(newelem, t.NumElem())
		}

	case types.TPTR:
		elem := t.Elem()
		newelem := ts.typ1(elem)
		if newelem != elem || targsChanged {
			newt = types.NewPtr(newelem)
		}

	case types.TSLICE:
		elem := t.Elem()
		newelem := ts.typ1(elem)
		if newelem != elem || targsChanged {
			newt = types.NewSlice(newelem)
		}

	case types.TSTRUCT:
		newt = ts.tstruct(t, targsChanged)
		if newt == t {
			newt = nil
		}

	case types.TFUNC:
		// watch out for endless recursion on plain function types that mention themselves, e.g. "type T func() T"
		if !hasParamOrShape {
			if ts.Funcs[t] { // Visit such function types only once.
				return t
			}
			if ts.Funcs == nil {
				// allocate lazily
				ts.Funcs = make(map[*types.Type]bool)
			}
			ts.Funcs[t] = true
		}
		newrecvs := ts.tstruct(t.Recvs(), false)
		newparams := ts.tstruct(t.Params(), false)
		newresults := ts.tstruct(t.Results(), false)
		// Translate the tparams of a signature.
		newtparams := ts.tstruct(t.TParams(), false)
		if newrecvs != t.Recvs() || newparams != t.Params() ||
			newresults != t.Results() || newtparams != t.TParams() || targsChanged {
			// If any types have changed, then the all the fields of
			// of recv, params, and results must be copied, because they have
			// offset fields that are dependent, and so must have an
			// independent copy for each new signature.
			var newrecv *types.Field
			if newrecvs.NumFields() > 0 {
				if newrecvs == t.Recvs() {
					newrecvs = ts.tstruct(t.Recvs(), true)
				}
				newrecv = newrecvs.Field(0)
			}
			if newparams == t.Params() {
				newparams = ts.tstruct(t.Params(), true)
			}
			if newresults == t.Results() {
				newresults = ts.tstruct(t.Results(), true)
			}
			var tparamfields []*types.Field
			if newtparams.HasTParam() {
				tparamfields = newtparams.FieldSlice()
			} else {
				// Completely remove the tparams from the resulting
				// signature, if the tparams are now concrete types.
				tparamfields = nil
			}
			newt = types.NewSignature(t.Pkg(), newrecv, tparamfields,
				newparams.FieldSlice(), newresults.FieldSlice())
		}
		if !hasParamOrShape {
			delete(ts.Funcs, t)
		}

	case types.TINTER:
		newt = ts.tinter(t, targsChanged)
		if newt == t {
			newt = nil
		}

	case types.TMAP:
		newkey := ts.typ1(t.Key())
		newval := ts.typ1(t.Elem())
		if newkey != t.Key() || newval != t.Elem() || targsChanged {
			newt = types.NewMap(newkey, newval)
		}

	case types.TCHAN:
		elem := t.Elem()
		newelem := ts.typ1(elem)
		if newelem != elem || targsChanged {
			newt = types.NewChan(newelem, t.ChanDir())
		}
	case types.TFORW:
		if ts.SubstForwFunc != nil {
			return ts.SubstForwFunc(forw)
		} else {
			assert(false)
		}
	case types.TINT, types.TINT8, types.TINT16, types.TINT32, types.TINT64,
		types.TUINT, types.TUINT8, types.TUINT16, types.TUINT32, types.TUINT64,
		types.TUINTPTR, types.TBOOL, types.TSTRING, types.TFLOAT32, types.TFLOAT64, types.TCOMPLEX64, types.TCOMPLEX128, types.TUNSAFEPTR:
		newt = t.Underlying()
	case types.TUNION:
		nt := t.NumTerms()
		newterms := make([]*types.Type, nt)
		tildes := make([]bool, nt)
		changed := false
		for i := 0; i < nt; i++ {
			term, tilde := t.Term(i)
			tildes[i] = tilde
			newterms[i] = ts.typ1(term)
			if newterms[i] != term {
				changed = true
			}
		}
		if changed {
			newt = types.NewUnion(newterms, tildes)
		}
	default:
		panic(fmt.Sprintf("Bad type in (*TSubster).Typ: %v", t.Kind()))
	}
	if newt == nil {
		// Even though there were typeparams in the type, there may be no
		// change if this is a function type for a function call (which will
		// have its own tparams/targs in the function instantiation).
		return t
	}

	if forw != nil {
		forw.SetUnderlying(newt)
		newt = forw
	}

	if !newt.HasTParam() && !newt.IsFuncArgStruct() {
		// Calculate the size of any new types created. These will be
		// deferred until the top-level ts.Typ() or g.typ() (if this is
		// called from g.fillinMethods()).
		types.CheckSize(newt)
	}

	if t.Kind() != types.TINTER && t.Methods().Len() > 0 {
		// Fill in the method info for the new type.
		var newfields []*types.Field
		newfields = make([]*types.Field, t.Methods().Len())
		for i, f := range t.Methods().Slice() {
			t2 := ts.typ1(f.Type)
			oldsym := f.Nname.Sym()

			// Use the name of the substituted receiver to create the
			// method name, since the receiver name may have many levels
			// of nesting (brackets) with type names to be substituted.
			recvType := t2.Recv().Type
			var nm string
			if recvType.IsPtr() {
				recvType = recvType.Elem()
				nm = "(*" + recvType.Sym().Name + ")." + f.Sym.Name
			} else {
				nm = recvType.Sym().Name + "." + f.Sym.Name
			}
			if recvType.RParams()[0].HasShape() {
				// We add "nofunc" to methods of shape type to avoid
				// conflict with the name of the shape-based helper
				// function. See header comment of MakeFuncInstSym.
				nm = "nofunc." + nm
			}
			newsym := oldsym.Pkg.Lookup(nm)
			var nname *ir.Name
			if newsym.Def != nil {
				nname = newsym.Def.(*ir.Name)
			} else {
				nname = ir.NewNameAt(f.Pos, newsym)
				nname.SetType(t2)
				ir.MarkFunc(nname)
				newsym.Def = nname
			}
			newfields[i] = types.NewField(f.Pos, f.Sym, t2)
			newfields[i].Nname = nname
		}
		newt.Methods().Set(newfields)
		if !newt.HasTParam() && !newt.HasShape() {
			// Generate all the methods for a new fully-instantiated type.

			NeedInstType(newt)
		}
	}
	return newt
}

// tstruct substitutes type params in types of the fields of a structure type. For
// each field, tstruct copies the Nname, and translates it if Nname is in
// ts.vars. To always force the creation of a new (top-level) struct,
// regardless of whether anything changed with the types or names of the struct's
// fields, set force to true.
func (ts *Tsubster) tstruct(t *types.Type, force bool) *types.Type {
	if t.NumFields() == 0 {
		if t.HasTParam() || t.HasShape() {
			// For an empty struct, we need to return a new type, if
			// substituting from a generic type or shape type, since it
			// will change HasTParam/HasShape flags.
			return types.NewStruct(t.Pkg(), nil)
		}
		return t
	}
	var newfields []*types.Field
	if force {
		newfields = make([]*types.Field, t.NumFields())
	}
	for i, f := range t.Fields().Slice() {
		t2 := ts.typ1(f.Type)
		if (t2 != f.Type || f.Nname != nil) && newfields == nil {
			newfields = make([]*types.Field, t.NumFields())
			for j := 0; j < i; j++ {
				newfields[j] = t.Field(j)
			}
		}
		if newfields != nil {
			newfields[i] = types.NewField(f.Pos, f.Sym, t2)
			newfields[i].Embedded = f.Embedded
			newfields[i].Note = f.Note
			if f.IsDDD() {
				newfields[i].SetIsDDD(true)
			}
			if f.Nointerface() {
				newfields[i].SetNointerface(true)
			}
			if f.Nname != nil && ts.Vars != nil {
				v := ts.Vars[f.Nname.(*ir.Name)]
				if v != nil {
					// This is the case where we are
					// translating the type of the function we
					// are substituting, so its dcls are in
					// the subst.ts.vars table, and we want to
					// change to reference the new dcl.
					newfields[i].Nname = v
				} else {
					// This is the case where we are
					// translating the type of a function
					// reference inside the function we are
					// substituting, so we leave the Nname
					// value as is.
					newfields[i].Nname = f.Nname
				}
			}
		}
	}
	if newfields != nil {
		news := types.NewStruct(t.Pkg(), newfields)
		news.StructType().Funarg = t.StructType().Funarg
		return news
	}
	return t

}

// tinter substitutes type params in types of the methods of an interface type.
func (ts *Tsubster) tinter(t *types.Type, force bool) *types.Type {
	if t.Methods().Len() == 0 {
		if t.HasTParam() || t.HasShape() {
			// For an empty interface, we need to return a new type, if
			// substituting from a generic type or shape type, since
			// since it will change HasTParam/HasShape flags.
			return types.NewInterface(t.Pkg(), nil, false)
		}
		return t
	}
	var newfields []*types.Field
	if force {
		newfields = make([]*types.Field, t.Methods().Len())
	}
	for i, f := range t.Methods().Slice() {
		t2 := ts.typ1(f.Type)
		if (t2 != f.Type || f.Nname != nil) && newfields == nil {
			newfields = make([]*types.Field, t.Methods().Len())
			for j := 0; j < i; j++ {
				newfields[j] = t.Methods().Index(j)
			}
		}
		if newfields != nil {
			newfields[i] = types.NewField(f.Pos, f.Sym, t2)
		}
	}
	if newfields != nil {
		return types.NewInterface(t.Pkg(), newfields, t.IsImplicit())
	}
	return t
}

// genericSym returns the name of the base generic type for the type named by
// sym. It simply returns the name obtained by removing everything after the
// first bracket ("[").
func genericTypeName(sym *types.Sym) string {
	return sym.Name[0:strings.Index(sym.Name, "[")]
}

// getShapes appends the list of the shape types that are used within type t to
// listp. The type traversal is simplified for two reasons: (1) we can always stop a
// type traversal when t.HasShape() is false; and (2) shape types can't appear inside
// a named type, except for the type args of a generic type. So, the traversal will
// always stop before we have to deal with recursive types.
func getShapes(t *types.Type, listp *[]*types.Type) {
	if !t.HasShape() {
		return
	}
	if t.IsShape() {
		*listp = append(*listp, t)
		return
	}

	if t.Sym() != nil {
		// A named type can't have shapes in it, except for type args of a
		// generic type. We will have to deal with this differently once we
		// alloc local types in generic functions (#47631).
		for _, rparam := range t.RParams() {
			getShapes(rparam, listp)
		}
		return
	}

	switch t.Kind() {
	case types.TARRAY, types.TPTR, types.TSLICE, types.TCHAN:
		getShapes(t.Elem(), listp)

	case types.TSTRUCT:
		for _, f := range t.FieldSlice() {
			getShapes(f.Type, listp)
		}

	case types.TFUNC:
		for _, f := range t.Recvs().FieldSlice() {
			getShapes(f.Type, listp)
		}
		for _, f := range t.Params().FieldSlice() {
			getShapes(f.Type, listp)
		}
		for _, f := range t.Results().FieldSlice() {
			getShapes(f.Type, listp)
		}
		for _, f := range t.TParams().FieldSlice() {
			getShapes(f.Type, listp)
		}

	case types.TINTER:
		for _, f := range t.Methods().Slice() {
			getShapes(f.Type, listp)
		}

	case types.TMAP:
		getShapes(t.Key(), listp)
		getShapes(t.Elem(), listp)

	default:
		panic(fmt.Sprintf("Bad type in getShapes: %v", t.Kind()))
	}

}

// Shapify takes a concrete type and a type param index, and returns a GCshape type that can
// be used in place of the input type and still generate identical code.
// No methods are added - all methods calls directly on a shape should
// be done by converting to an interface using the dictionary.
//
// For now, we only consider two types to have the same shape, if they have exactly
// the same underlying type or they are both pointer types.
//
// tparam is the associated typeparam - it must be TTYPEPARAM type. If there is a
// structural type for the associated type param (not common), then a pointer type t
// is mapped to its underlying type, rather than being merged with other pointers.
//
// Shape types are also distinguished by the index of the type in a type param/arg
// list. We need to do this so we can distinguish and substitute properly for two
// type params in the same function that have the same shape for a particular
// instantiation.
func Shapify(t *types.Type, index int, tparam *types.Type) *types.Type {
	assert(!t.IsShape())
	if t.HasShape() {
		// We are sometimes dealing with types from a shape instantiation
		// that were constructed from existing shape types, so t may
		// sometimes have shape types inside it. In that case, we find all
		// those shape types with getShapes() and replace them with their
		// underlying type.
		//
		// If we don't do this, we may create extra unneeded shape types that
		// have these other shape types embedded in them. This may lead to
		// generating extra shape instantiations, and a mismatch between the
		// instantiations that we used in generating dictionaries and the
		// instantations that are actually called. (#51303).
		list := []*types.Type{}
		getShapes(t, &list)
		list2 := make([]*types.Type, len(list))
		for i, shape := range list {
			list2[i] = shape.Underlying()
		}
		ts := Tsubster{
			Tparams: list,
			Targs:   list2,
		}
		t = ts.Typ(t)
	}
	// Map all types with the same underlying type to the same shape.
	u := t.Underlying()

	// All pointers have the same shape.
	// TODO: Make unsafe.Pointer the same shape as normal pointers.
	// Note: pointers to arrays are special because of slice-to-array-pointer
	// conversions. See issue 49295.
	if u.Kind() == types.TPTR && u.Elem().Kind() != types.TARRAY &&
		tparam.Bound().StructuralType() == nil && !u.Elem().NotInHeap() {
		u = types.Types[types.TUINT8].PtrTo()
	}

	submap := shapeMap[index]
	if submap == nil {
		submap = map[*types.Type]*types.Type{}
		shapeMap[index] = submap
	}
	if s := submap[u]; s != nil {
		return s
	}

	// LinkString specifies the type uniquely, but has no spaces.
	nm := fmt.Sprintf("%s_%d", u.LinkString(), index)
	sym := types.ShapePkg.Lookup(nm)
	if sym.Def != nil {
		// Use any existing type with the same name
		submap[u] = sym.Def.Type()
		return submap[u]
	}
	name := ir.NewDeclNameAt(u.Pos(), ir.OTYPE, sym)
	s := types.NewNamed(name)
	sym.Def = name
	s.SetUnderlying(u)
	s.SetIsShape(true)
	s.SetHasShape(true)
	types.CalcSize(s)
	name.SetType(s)
	name.SetTypecheck(1)
	submap[u] = s
	return s
}

var shapeMap = map[int]map[*types.Type]*types.Type{}

相关信息

go 源码目录

相关文章

go bexport 源码

go builtin 源码

go builtin_test 源码

go const 源码

go crawler 源码

go dcl 源码

go export 源码

go expr 源码

go func 源码

go iexport 源码

0  赞