go iimport 源码
golang iimport 代码
文件路径:/src/cmd/compile/internal/typecheck/iimport.go
// Copyright 2018 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.
// Indexed package import.
// See iexport.go for the export data format.
package typecheck
import (
"encoding/binary"
"fmt"
"go/constant"
"math/big"
"os"
"strings"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/src"
)
// An iimporterAndOffset identifies an importer and an offset within
// its data section.
type iimporterAndOffset struct {
p *iimporter
off uint64
}
var (
// DeclImporter maps from imported identifiers to an importer
// and offset where that identifier's declaration can be read.
DeclImporter = map[*types.Sym]iimporterAndOffset{}
// inlineImporter is like DeclImporter, but for inline bodies
// for function and method symbols.
inlineImporter = map[*types.Sym]iimporterAndOffset{}
)
// expandDecl returns immediately if n is already a Name node. Otherwise, n should
// be an Ident node, and expandDecl reads in the definition of the specified
// identifier from the appropriate package.
func expandDecl(n ir.Node) ir.Node {
if n, ok := n.(*ir.Name); ok {
return n
}
id := n.(*ir.Ident)
if n := id.Sym().PkgDef(); n != nil {
return n.(*ir.Name)
}
r := importReaderFor(id.Sym(), DeclImporter)
if r == nil {
// Can happen if user tries to reference an undeclared name.
return n
}
return r.doDecl(n.Sym())
}
// ImportBody reads in the dcls and body of an imported function (which should not
// yet have been read in).
func ImportBody(fn *ir.Func) {
if fn.Inl.Body != nil {
base.Fatalf("%v already has inline body", fn)
}
r := importReaderFor(fn.Nname.Sym(), inlineImporter)
if r == nil {
base.Fatalf("missing import reader for %v", fn)
}
if inimport {
base.Fatalf("recursive inimport")
}
inimport = true
r.doInline(fn)
inimport = false
}
// HaveInlineBody reports whether we have fn's inline body available
// for inlining.
func HaveInlineBody(fn *ir.Func) bool {
if fn.Inl == nil {
return false
}
// Unified IR is much more conservative about pruning unreachable
// methods (at the cost of increased build artifact size).
if base.Debug.Unified != 0 {
return true
}
if fn.Inl.Body != nil {
return true
}
_, ok := inlineImporter[fn.Nname.Sym()]
return ok
}
func importReaderFor(sym *types.Sym, importers map[*types.Sym]iimporterAndOffset) *importReader {
x, ok := importers[sym]
if !ok {
return nil
}
return x.p.newReader(x.off, sym.Pkg)
}
type intReader struct {
*strings.Reader
pkg *types.Pkg
}
func (r *intReader) int64() int64 {
i, err := binary.ReadVarint(r.Reader)
if err != nil {
base.Errorf("import %q: read error: %v", r.pkg.Path, err)
base.ErrorExit()
}
return i
}
func (r *intReader) uint64() uint64 {
i, err := binary.ReadUvarint(r.Reader)
if err != nil {
base.Errorf("import %q: read error: %v", r.pkg.Path, err)
base.ErrorExit()
}
return i
}
func ReadImports(pkg *types.Pkg, data string) {
ird := &intReader{strings.NewReader(data), pkg}
version := ird.uint64()
switch version {
case iexportVersionGo1_18, iexportVersionPosCol, iexportVersionGo1_11:
default:
base.Errorf("import %q: unknown export format version %d", pkg.Path, version)
base.ErrorExit()
}
sLen := int64(ird.uint64())
dLen := int64(ird.uint64())
// TODO(mdempsky): Replace os.SEEK_CUR with io.SeekCurrent after
// #44505 is fixed.
whence, _ := ird.Seek(0, os.SEEK_CUR)
stringData := data[whence : whence+sLen]
declData := data[whence+sLen : whence+sLen+dLen]
ird.Seek(sLen+dLen, os.SEEK_CUR)
p := &iimporter{
exportVersion: version,
ipkg: pkg,
pkgCache: map[uint64]*types.Pkg{},
posBaseCache: map[uint64]*src.PosBase{},
typCache: map[uint64]*types.Type{},
stringData: stringData,
declData: declData,
}
for i, pt := range predeclared() {
p.typCache[uint64(i)] = pt
}
// Declaration index.
for nPkgs := ird.uint64(); nPkgs > 0; nPkgs-- {
pkg := p.pkgAt(ird.uint64())
pkgName := p.stringAt(ird.uint64())
pkgHeight := int(ird.uint64())
if pkg.Name == "" {
pkg.Name = pkgName
pkg.Height = pkgHeight
types.NumImport[pkgName]++
// TODO(mdempsky): This belongs somewhere else.
pkg.Lookup("_").Def = ir.BlankNode
} else {
if pkg.Name != pkgName {
base.Fatalf("conflicting package names %v and %v for path %q", pkg.Name, pkgName, pkg.Path)
}
if pkg.Height != pkgHeight {
base.Fatalf("conflicting package heights %v and %v for path %q", pkg.Height, pkgHeight, pkg.Path)
}
}
for nSyms := ird.uint64(); nSyms > 0; nSyms-- {
s := pkg.Lookup(p.stringAt(ird.uint64()))
off := ird.uint64()
if _, ok := DeclImporter[s]; !ok {
DeclImporter[s] = iimporterAndOffset{p, off}
}
}
}
// Inline body index.
for nPkgs := ird.uint64(); nPkgs > 0; nPkgs-- {
pkg := p.pkgAt(ird.uint64())
for nSyms := ird.uint64(); nSyms > 0; nSyms-- {
s := pkg.Lookup(p.stringAt(ird.uint64()))
off := ird.uint64()
if _, ok := inlineImporter[s]; !ok {
inlineImporter[s] = iimporterAndOffset{p, off}
}
}
}
}
type iimporter struct {
exportVersion uint64
ipkg *types.Pkg
pkgCache map[uint64]*types.Pkg
posBaseCache map[uint64]*src.PosBase
typCache map[uint64]*types.Type
stringData string
declData string
}
func (p *iimporter) stringAt(off uint64) string {
var x [binary.MaxVarintLen64]byte
n := copy(x[:], p.stringData[off:])
slen, n := binary.Uvarint(x[:n])
if n <= 0 {
base.Fatalf("varint failed")
}
spos := off + uint64(n)
return p.stringData[spos : spos+slen]
}
func (p *iimporter) posBaseAt(off uint64) *src.PosBase {
if posBase, ok := p.posBaseCache[off]; ok {
return posBase
}
file := p.stringAt(off)
posBase := src.NewFileBase(file, file)
p.posBaseCache[off] = posBase
return posBase
}
func (p *iimporter) pkgAt(off uint64) *types.Pkg {
if pkg, ok := p.pkgCache[off]; ok {
return pkg
}
pkg := p.ipkg
if pkgPath := p.stringAt(off); pkgPath != "" {
pkg = types.NewPkg(pkgPath, "")
}
p.pkgCache[off] = pkg
return pkg
}
// An importReader keeps state for reading an individual imported
// object (declaration or inline body).
type importReader struct {
strings.Reader
p *iimporter
currPkg *types.Pkg
prevBase *src.PosBase
prevLine int64
prevColumn int64
// curfn is the current function we're importing into.
curfn *ir.Func
// Slice of all dcls for function, including any interior closures
allDcls []*ir.Name
allClosureVars []*ir.Name
autotmpgen int
}
func (p *iimporter) newReader(off uint64, pkg *types.Pkg) *importReader {
r := &importReader{
p: p,
currPkg: pkg,
}
// (*strings.Reader).Reset wasn't added until Go 1.7, and we
// need to build with Go 1.4.
r.Reader = *strings.NewReader(p.declData[off:])
return r
}
func (r *importReader) string() string { return r.p.stringAt(r.uint64()) }
func (r *importReader) posBase() *src.PosBase { return r.p.posBaseAt(r.uint64()) }
func (r *importReader) pkg() *types.Pkg { return r.p.pkgAt(r.uint64()) }
func (r *importReader) setPkg() {
r.currPkg = r.pkg()
}
func (r *importReader) doDecl(sym *types.Sym) *ir.Name {
tag := r.byte()
pos := r.pos()
switch tag {
case 'A':
typ := r.typ()
return importalias(pos, sym, typ)
case 'C':
typ := r.typ()
val := r.value(typ)
n := importconst(pos, sym, typ, val)
r.constExt(n)
return n
case 'F', 'G':
var tparams []*types.Field
if tag == 'G' {
tparams = r.tparamList()
}
typ := r.signature(nil, tparams)
n := importfunc(pos, sym, typ)
r.funcExt(n)
return n
case 'T', 'U':
// Types can be recursive. We need to setup a stub
// declaration before recursing.
n := importtype(pos, sym)
t := n.Type()
// Because of recursion, we need to defer width calculations and
// instantiations on intermediate types until the top-level type is
// fully constructed. Note that we can have recursion via type
// constraints.
types.DeferCheckSize()
deferDoInst()
if tag == 'U' {
rparams := r.typeList()
t.SetRParams(rparams)
}
underlying := r.typ()
t.SetUnderlying(underlying)
if underlying.IsInterface() {
// Finish up all type instantiations and CheckSize calls
// now that a top-level type is fully constructed.
resumeDoInst()
types.ResumeCheckSize()
r.typeExt(t)
return n
}
ms := make([]*types.Field, r.uint64())
for i := range ms {
mpos := r.pos()
msym := r.selector()
recv := r.param()
mtyp := r.signature(recv, nil)
// MethodSym already marked m.Sym as a function.
m := ir.NewNameAt(mpos, ir.MethodSym(recv.Type, msym))
m.Class = ir.PFUNC
m.SetType(mtyp)
m.Func = ir.NewFunc(mpos)
m.Func.Nname = m
f := types.NewField(mpos, msym, mtyp)
f.Nname = m
ms[i] = f
}
t.Methods().Set(ms)
// Finish up all instantiations and CheckSize calls now
// that a top-level type is fully constructed.
resumeDoInst()
types.ResumeCheckSize()
r.typeExt(t)
for _, m := range ms {
r.methExt(m)
}
return n
case 'P':
if r.p.exportVersion < iexportVersionGenerics {
base.Fatalf("unexpected type param type")
}
if sym.Def != nil {
// Make sure we use the same type param type for the same
// name, whether it is created during types1-import or
// this types2-to-types1 translation.
return sym.Def.(*ir.Name)
}
n := importsym(pos, sym, ir.OTYPE, ir.PTYPEPARAM)
// The typeparam index is set at the point where the containing type
// param list is imported.
t := types.NewTypeParam(n, 0)
n.SetType(t)
implicit := false
if r.p.exportVersion >= iexportVersionGo1_18 {
implicit = r.bool()
}
bound := r.typ()
if implicit {
bound.MarkImplicit()
}
t.SetBound(bound)
return n
case 'V':
typ := r.typ()
n := importvar(pos, sym, typ)
r.varExt(n)
return n
default:
base.Fatalf("unexpected tag: %v", tag)
panic("unreachable")
}
}
func (r *importReader) value(typ *types.Type) constant.Value {
var kind constant.Kind
var valType *types.Type
if r.p.exportVersion >= iexportVersionGo1_18 {
// TODO: add support for using the kind in the non-typeparam case.
kind = constant.Kind(r.int64())
}
if typ.IsTypeParam() {
if r.p.exportVersion < iexportVersionGo1_18 {
// If a constant had a typeparam type, then we wrote out its
// actual constant kind as well.
kind = constant.Kind(r.int64())
}
switch kind {
case constant.Int:
valType = types.Types[types.TINT64]
case constant.Float:
valType = types.Types[types.TFLOAT64]
case constant.Complex:
valType = types.Types[types.TCOMPLEX128]
}
} else {
kind = constTypeOf(typ)
valType = typ
}
switch kind {
case constant.Bool:
return constant.MakeBool(r.bool())
case constant.String:
return constant.MakeString(r.string())
case constant.Int:
var i big.Int
r.mpint(&i, valType)
return constant.Make(&i)
case constant.Float:
return r.float(valType)
case constant.Complex:
return makeComplex(r.float(valType), r.float(valType))
}
base.Fatalf("unexpected value type: %v", typ)
panic("unreachable")
}
func (r *importReader) mpint(x *big.Int, typ *types.Type) {
signed, maxBytes := intSize(typ)
maxSmall := 256 - maxBytes
if signed {
maxSmall = 256 - 2*maxBytes
}
if maxBytes == 1 {
maxSmall = 256
}
n, _ := r.ReadByte()
if uint(n) < maxSmall {
v := int64(n)
if signed {
v >>= 1
if n&1 != 0 {
v = ^v
}
}
x.SetInt64(v)
return
}
v := -n
if signed {
v = -(n &^ 1) >> 1
}
if v < 1 || uint(v) > maxBytes {
base.Fatalf("weird decoding: %v, %v => %v", n, signed, v)
}
b := make([]byte, v)
r.Read(b)
x.SetBytes(b)
if signed && n&1 != 0 {
x.Neg(x)
}
}
func (r *importReader) float(typ *types.Type) constant.Value {
var mant big.Int
r.mpint(&mant, typ)
var f big.Float
f.SetInt(&mant)
if f.Sign() != 0 {
f.SetMantExp(&f, int(r.int64()))
}
return constant.Make(&f)
}
func (r *importReader) mprat(orig constant.Value) constant.Value {
if !r.bool() {
return orig
}
var rat big.Rat
rat.SetString(r.string())
return constant.Make(&rat)
}
func (r *importReader) ident(selector bool) *types.Sym {
name := r.string()
if name == "" {
return nil
}
pkg := r.currPkg
if selector {
if types.IsExported(name) {
pkg = types.LocalPkg
}
} else {
if name == "$autotmp" {
name = autotmpname(r.autotmpgen)
r.autotmpgen++
}
}
return pkg.Lookup(name)
}
func (r *importReader) localIdent() *types.Sym { return r.ident(false) }
func (r *importReader) selector() *types.Sym { return r.ident(true) }
func (r *importReader) qualifiedIdent() *ir.Ident {
name := r.string()
pkg := r.pkg()
sym := pkg.Lookup(name)
return ir.NewIdent(src.NoXPos, sym)
}
func (r *importReader) pos() src.XPos {
delta := r.int64()
r.prevColumn += delta >> 1
if delta&1 != 0 {
delta = r.int64()
r.prevLine += delta >> 1
if delta&1 != 0 {
r.prevBase = r.posBase()
}
}
if (r.prevBase == nil || r.prevBase.AbsFilename() == "") && r.prevLine == 0 && r.prevColumn == 0 {
// TODO(mdempsky): Remove once we reliably write
// position information for all nodes.
return src.NoXPos
}
if r.prevBase == nil {
base.Fatalf("missing posbase")
}
pos := src.MakePos(r.prevBase, uint(r.prevLine), uint(r.prevColumn))
return base.Ctxt.PosTable.XPos(pos)
}
func (r *importReader) typ() *types.Type {
// If this is a top-level type call, defer type instantiations until the
// type is fully constructed.
types.DeferCheckSize()
deferDoInst()
t := r.p.typAt(r.uint64())
resumeDoInst()
types.ResumeCheckSize()
return t
}
func (r *importReader) exoticType() *types.Type {
switch r.uint64() {
case exoticTypeNil:
return nil
case exoticTypeTuple:
funarg := types.Funarg(r.uint64())
n := r.uint64()
fs := make([]*types.Field, n)
for i := range fs {
pos := r.pos()
var sym *types.Sym
switch r.uint64() {
case exoticTypeSymNil:
sym = nil
case exoticTypeSymNoPkg:
sym = types.NoPkg.Lookup(r.string())
case exoticTypeSymWithPkg:
pkg := r.pkg()
sym = pkg.Lookup(r.string())
default:
base.Fatalf("unknown symbol kind")
}
typ := r.typ()
f := types.NewField(pos, sym, typ)
fs[i] = f
}
t := types.NewStruct(types.NoPkg, fs)
t.StructType().Funarg = funarg
return t
case exoticTypeRecv:
var rcvr *types.Field
if r.bool() { // isFakeRecv
rcvr = types.FakeRecv()
} else {
rcvr = r.exoticParam()
}
return r.exoticSignature(rcvr)
case exoticTypeRegular:
return r.typ()
default:
base.Fatalf("bad kind of call type")
return nil
}
}
func (r *importReader) exoticSelector() *types.Sym {
name := r.string()
if name == "" {
return nil
}
pkg := r.currPkg
if types.IsExported(name) {
pkg = types.LocalPkg
}
if r.uint64() != 0 {
pkg = r.pkg()
}
return pkg.Lookup(name)
}
func (r *importReader) exoticSignature(recv *types.Field) *types.Type {
var pkg *types.Pkg
if r.bool() { // hasPkg
pkg = r.pkg()
}
params := r.exoticParamList()
results := r.exoticParamList()
return types.NewSignature(pkg, recv, nil, params, results)
}
func (r *importReader) exoticParamList() []*types.Field {
n := r.uint64()
fs := make([]*types.Field, n)
for i := range fs {
fs[i] = r.exoticParam()
}
return fs
}
func (r *importReader) exoticParam() *types.Field {
pos := r.pos()
sym := r.exoticSym()
off := r.uint64()
typ := r.exoticType()
ddd := r.bool()
f := types.NewField(pos, sym, typ)
f.Offset = int64(off)
if sym != nil {
f.Nname = ir.NewNameAt(pos, sym)
}
f.SetIsDDD(ddd)
return f
}
func (r *importReader) exoticField() *types.Field {
pos := r.pos()
sym := r.exoticSym()
off := r.uint64()
typ := r.exoticType()
note := r.string()
f := types.NewField(pos, sym, typ)
f.Offset = int64(off)
if sym != nil {
f.Nname = ir.NewNameAt(pos, sym)
}
f.Note = note
return f
}
func (r *importReader) exoticSym() *types.Sym {
name := r.string()
if name == "" {
return nil
}
var pkg *types.Pkg
if types.IsExported(name) {
pkg = types.LocalPkg
} else {
pkg = r.pkg()
}
return pkg.Lookup(name)
}
func (p *iimporter) typAt(off uint64) *types.Type {
t, ok := p.typCache[off]
if !ok {
if off < predeclReserved {
base.Fatalf("predeclared type missing from cache: %d", off)
}
t = p.newReader(off-predeclReserved, nil).typ1()
// Ensure size is calculated for imported types. Since CL 283313, the compiler
// does not compile the function immediately when it sees them. Instead, functions
// are pushed to compile queue, then draining from the queue for compiling.
// During this process, the size calculation is disabled, so it is not safe for
// calculating size during SSA generation anymore. See issue #44732.
//
// No need to calc sizes for re-instantiated generic types, and
// they are not necessarily resolved until the top-level type is
// defined (because of recursive types).
if t.OrigType() == nil || !t.HasTParam() {
types.CheckSize(t)
}
p.typCache[off] = t
}
return t
}
func (r *importReader) typ1() *types.Type {
switch k := r.kind(); k {
default:
base.Fatalf("unexpected kind tag in %q: %v", r.p.ipkg.Path, k)
return nil
case definedType:
// We might be called from within doInline, in which
// case Sym.Def can point to declared parameters
// instead of the top-level types. Also, we don't
// support inlining functions with local defined
// types. Therefore, this must be a package-scope
// type.
n := expandDecl(r.qualifiedIdent())
if n.Op() != ir.OTYPE {
base.Fatalf("expected OTYPE, got %v: %v, %v", n.Op(), n.Sym(), n)
}
return n.Type()
case pointerType:
return types.NewPtr(r.typ())
case sliceType:
return types.NewSlice(r.typ())
case arrayType:
n := r.uint64()
return types.NewArray(r.typ(), int64(n))
case chanType:
dir := types.ChanDir(r.uint64())
return types.NewChan(r.typ(), dir)
case mapType:
return types.NewMap(r.typ(), r.typ())
case signatureType:
r.setPkg()
return r.signature(nil, nil)
case structType:
r.setPkg()
fs := make([]*types.Field, r.uint64())
for i := range fs {
pos := r.pos()
sym := r.selector()
typ := r.typ()
emb := r.bool()
note := r.string()
f := types.NewField(pos, sym, typ)
if emb {
f.Embedded = 1
}
f.Note = note
fs[i] = f
}
return types.NewStruct(r.currPkg, fs)
case interfaceType:
r.setPkg()
embeddeds := make([]*types.Field, r.uint64())
for i := range embeddeds {
pos := r.pos()
typ := r.typ()
embeddeds[i] = types.NewField(pos, nil, typ)
}
methods := make([]*types.Field, r.uint64())
for i := range methods {
pos := r.pos()
sym := r.selector()
typ := r.signature(types.FakeRecv(), nil)
methods[i] = types.NewField(pos, sym, typ)
}
if len(embeddeds)+len(methods) == 0 {
return types.Types[types.TINTER]
}
t := types.NewInterface(r.currPkg, append(embeddeds, methods...), false)
// Ensure we expand the interface in the frontend (#25055).
types.CheckSize(t)
return t
case typeParamType:
if r.p.exportVersion < iexportVersionGenerics {
base.Fatalf("unexpected type param type")
}
// Similar to code for defined types, since we "declared"
// typeparams to deal with recursion (typeparam is used within its
// own type bound).
ident := r.qualifiedIdent()
if ident.Sym().Def != nil {
return ident.Sym().Def.(*ir.Name).Type()
}
n := expandDecl(ident)
if n.Op() != ir.OTYPE {
base.Fatalf("expected OTYPE, got %v: %v, %v", n.Op(), n.Sym(), n)
}
return n.Type()
case instanceType:
if r.p.exportVersion < iexportVersionGenerics {
base.Fatalf("unexpected instantiation type")
}
pos := r.pos()
len := r.uint64()
targs := make([]*types.Type, len)
for i := range targs {
targs[i] = r.typ()
}
baseType := r.typ()
t := Instantiate(pos, baseType, targs)
return t
case unionType:
if r.p.exportVersion < iexportVersionGenerics {
base.Fatalf("unexpected instantiation type")
}
nt := int(r.uint64())
terms := make([]*types.Type, nt)
tildes := make([]bool, nt)
for i := range terms {
tildes[i] = r.bool()
terms[i] = r.typ()
}
return types.NewUnion(terms, tildes)
}
}
func (r *importReader) kind() itag {
return itag(r.uint64())
}
func (r *importReader) signature(recv *types.Field, tparams []*types.Field) *types.Type {
params := r.paramList()
results := r.paramList()
if n := len(params); n > 0 {
params[n-1].SetIsDDD(r.bool())
}
return types.NewSignature(r.currPkg, recv, tparams, params, results)
}
func (r *importReader) typeList() []*types.Type {
n := r.uint64()
if n == 0 {
return nil
}
ts := make([]*types.Type, n)
for i := range ts {
ts[i] = r.typ()
if ts[i].IsTypeParam() {
ts[i].SetIndex(i)
}
}
return ts
}
func (r *importReader) tparamList() []*types.Field {
n := r.uint64()
if n == 0 {
return nil
}
fs := make([]*types.Field, n)
for i := range fs {
typ := r.typ()
typ.SetIndex(i)
fs[i] = types.NewField(typ.Pos(), typ.Sym(), typ)
}
return fs
}
func (r *importReader) paramList() []*types.Field {
fs := make([]*types.Field, r.uint64())
for i := range fs {
fs[i] = r.param()
}
return fs
}
func (r *importReader) param() *types.Field {
return types.NewField(r.pos(), r.localIdent(), r.typ())
}
func (r *importReader) bool() bool {
return r.uint64() != 0
}
func (r *importReader) int64() int64 {
n, err := binary.ReadVarint(r)
if err != nil {
base.Fatalf("readVarint: %v", err)
}
return n
}
func (r *importReader) uint64() uint64 {
n, err := binary.ReadUvarint(r)
if err != nil {
base.Fatalf("readVarint: %v", err)
}
return n
}
func (r *importReader) byte() byte {
x, err := r.ReadByte()
if err != nil {
base.Fatalf("declReader.ReadByte: %v", err)
}
return x
}
// Compiler-specific extensions.
func (r *importReader) constExt(n *ir.Name) {
switch n.Type() {
case types.UntypedFloat:
n.SetVal(r.mprat(n.Val()))
case types.UntypedComplex:
v := n.Val()
re := r.mprat(constant.Real(v))
im := r.mprat(constant.Imag(v))
n.SetVal(makeComplex(re, im))
}
}
func (r *importReader) varExt(n *ir.Name) {
r.linkname(n.Sym())
r.symIdx(n.Sym())
}
func (r *importReader) funcExt(n *ir.Name) {
r.linkname(n.Sym())
r.symIdx(n.Sym())
n.Func.ABI = obj.ABI(r.uint64())
// Make sure //go:noinline pragma is imported (so stenciled functions have
// same noinline status as the corresponding generic function.)
n.Func.Pragma = ir.PragmaFlag(r.uint64())
// Escape analysis.
for _, fs := range &types.RecvsParams {
for _, f := range fs(n.Type()).FieldSlice() {
f.Note = r.string()
}
}
// Inline body.
if u := r.uint64(); u > 0 {
n.Func.Inl = &ir.Inline{
Cost: int32(u - 1),
CanDelayResults: r.bool(),
}
n.Func.Endlineno = r.pos()
}
}
func (r *importReader) methExt(m *types.Field) {
if r.bool() {
m.SetNointerface(true)
}
r.funcExt(m.Nname.(*ir.Name))
}
func (r *importReader) linkname(s *types.Sym) {
s.Linkname = r.string()
}
func (r *importReader) symIdx(s *types.Sym) {
lsym := s.Linksym()
idx := int32(r.int64())
if idx != -1 {
if s.Linkname != "" {
base.Fatalf("bad index for linknamed symbol: %v %d\n", lsym, idx)
}
lsym.SymIdx = idx
lsym.Set(obj.AttrIndexed, true)
}
}
func (r *importReader) typeExt(t *types.Type) {
t.SetNotInHeap(r.bool())
SetBaseTypeIndex(t, r.int64(), r.int64())
}
func SetBaseTypeIndex(t *types.Type, i, pi int64) {
if t.Obj() == nil {
base.Fatalf("SetBaseTypeIndex on non-defined type %v", t)
}
if i != -1 && pi != -1 {
typeSymIdx[t] = [2]int64{i, pi}
}
}
// Map imported type T to the index of type descriptor symbols of T and *T,
// so we can use index to reference the symbol.
// TODO(mdempsky): Store this information directly in the Type's Name.
var typeSymIdx = make(map[*types.Type][2]int64)
func BaseTypeIndex(t *types.Type) int64 {
tbase := t
if t.IsPtr() && t.Sym() == nil && t.Elem().Sym() != nil {
tbase = t.Elem()
}
i, ok := typeSymIdx[tbase]
if !ok {
return -1
}
if t != tbase {
return i[1]
}
return i[0]
}
func (r *importReader) doInline(fn *ir.Func) {
if len(fn.Inl.Body) != 0 {
base.Fatalf("%v already has inline body", fn)
}
//fmt.Printf("Importing %s\n", fn.Nname.Sym().Name)
r.funcBody(fn)
importlist = append(importlist, fn)
if base.Flag.E > 0 && base.Flag.LowerM > 2 {
if base.Flag.LowerM > 3 {
fmt.Printf("inl body for %v %v: %+v\n", fn, fn.Type(), ir.Nodes(fn.Inl.Body))
} else {
fmt.Printf("inl body for %v %v: %v\n", fn, fn.Type(), ir.Nodes(fn.Inl.Body))
}
}
}
// ----------------------------------------------------------------------------
// Inlined function bodies
// Approach: Read nodes and use them to create/declare the same data structures
// as done originally by the (hidden) parser by closely following the parser's
// original code. In other words, "parsing" the import data (which happens to
// be encoded in binary rather textual form) is the best way at the moment to
// re-establish the syntax tree's invariants. At some future point we might be
// able to avoid this round-about way and create the rewritten nodes directly,
// possibly avoiding a lot of duplicate work (name resolution, type checking).
//
// Refined nodes (e.g., ODOTPTR as a refinement of OXDOT) are exported as their
// unrefined nodes (since this is what the importer uses). The respective case
// entries are unreachable in the importer.
func (r *importReader) funcBody(fn *ir.Func) {
outerfn := r.curfn
r.curfn = fn
// Import local declarations.
fn.Inl.Dcl = r.readFuncDcls(fn)
// Import function body.
body := r.stmtList()
if body == nil {
// Make sure empty body is not interpreted as
// no inlineable body (see also parser.fnbody)
// (not doing so can cause significant performance
// degradation due to unnecessary calls to empty
// functions).
body = []ir.Node{}
}
ir.VisitList(body, func(n ir.Node) {
n.SetTypecheck(1)
})
fn.Inl.Body = body
r.curfn = outerfn
if base.Flag.W >= 3 {
fmt.Printf("Imported for %v", fn)
ir.DumpList("", fn.Inl.Body)
}
}
func (r *importReader) readNames(fn *ir.Func) []*ir.Name {
dcls := make([]*ir.Name, r.int64())
for i := range dcls {
n := ir.NewDeclNameAt(r.pos(), ir.ONAME, r.localIdent())
n.Class = ir.PAUTO // overwritten below for parameters/results
n.Curfn = fn
n.SetType(r.typ())
dcls[i] = n
}
r.allDcls = append(r.allDcls, dcls...)
return dcls
}
func (r *importReader) readFuncDcls(fn *ir.Func) []*ir.Name {
dcls := r.readNames(fn)
// Fixup parameter classes and associate with their
// signature's type fields.
i := 0
fix := func(f *types.Field, class ir.Class) {
if class == ir.PPARAM && (f.Sym == nil || f.Sym.Name == "_") {
return
}
n := dcls[i]
n.Class = class
f.Nname = n
i++
}
typ := fn.Type()
if recv := typ.Recv(); recv != nil {
fix(recv, ir.PPARAM)
}
for _, f := range typ.Params().FieldSlice() {
fix(f, ir.PPARAM)
}
for _, f := range typ.Results().FieldSlice() {
fix(f, ir.PPARAMOUT)
}
return dcls
}
func (r *importReader) localName() *ir.Name {
i := r.int64()
if i == -1 {
return ir.BlankNode.(*ir.Name)
}
if i < 0 {
return r.allClosureVars[-i-2]
}
return r.allDcls[i]
}
func (r *importReader) stmtList() []ir.Node {
var list []ir.Node
for {
n := r.node()
if n == nil {
break
}
// OBLOCK nodes are not written to the import data directly,
// but the handling of ODCL calls liststmt, which creates one.
// Inline them into the statement list.
if n.Op() == ir.OBLOCK {
n := n.(*ir.BlockStmt)
list = append(list, n.List...)
continue
}
if len(list) > 0 {
// check for an optional label that can only immediately
// precede a for/range/select/switch statement.
if last := list[len(list)-1]; last.Op() == ir.OLABEL {
label := last.(*ir.LabelStmt).Label
switch n.Op() {
case ir.OFOR:
n.(*ir.ForStmt).Label = label
case ir.ORANGE:
n.(*ir.RangeStmt).Label = label
case ir.OSELECT:
n.(*ir.SelectStmt).Label = label
case ir.OSWITCH:
n.(*ir.SwitchStmt).Label = label
}
}
}
list = append(list, n)
}
return list
}
func (r *importReader) caseList(switchExpr ir.Node) []*ir.CaseClause {
namedTypeSwitch := isNamedTypeSwitch(switchExpr)
cases := make([]*ir.CaseClause, r.uint64())
for i := range cases {
cas := ir.NewCaseStmt(r.pos(), nil, nil)
cas.List = r.stmtList()
if namedTypeSwitch {
cas.Var = r.localName()
cas.Var.Defn = switchExpr
}
cas.Body = r.stmtList()
cases[i] = cas
}
return cases
}
func (r *importReader) commList() []*ir.CommClause {
cases := make([]*ir.CommClause, r.uint64())
for i := range cases {
pos := r.pos()
defaultCase := r.bool()
var comm ir.Node
if !defaultCase {
comm = r.node()
}
cases[i] = ir.NewCommStmt(pos, comm, r.stmtList())
}
return cases
}
func (r *importReader) exprList() []ir.Node {
var list []ir.Node
for {
n := r.expr()
if n == nil {
break
}
list = append(list, n)
}
return list
}
func (r *importReader) expr() ir.Node {
n := r.node()
if n != nil && n.Op() == ir.OBLOCK {
n := n.(*ir.BlockStmt)
base.Fatalf("unexpected block node: %v", n)
}
return n
}
// TODO(gri) split into expr and stmt
func (r *importReader) node() ir.Node {
op := r.op()
switch op {
// expressions
// case OPAREN:
// unreachable - unpacked by exporter
case ir.ONIL:
pos := r.pos()
typ := r.typ()
n := ir.NewNilExpr(pos)
n.SetType(typ)
return n
case ir.OLITERAL:
pos := r.pos()
typ := r.typ()
n := ir.NewBasicLit(pos, r.value(typ))
n.SetType(typ)
return n
case ir.ONONAME:
isKey := r.bool()
var n ir.Node = r.qualifiedIdent()
// Key ONONAME entries should not be resolved - they should
// stay as identifiers.
if !isKey {
n = Resolve(n)
}
typ := r.typ()
if n.Type() == nil {
n.SetType(typ)
}
return n
case ir.ONAME:
isBuiltin := r.bool()
if isBuiltin {
pkg := types.BuiltinPkg
if r.bool() {
pkg = types.UnsafePkg
}
return pkg.Lookup(r.string()).Def.(*ir.Name)
}
return r.localName()
// case OPACK, ONONAME:
// unreachable - should have been resolved by typechecking
case ir.OTYPE:
return ir.TypeNode(r.typ())
case ir.ODYNAMICTYPE:
n := ir.NewDynamicType(r.pos(), r.expr())
if r.bool() {
n.ITab = r.expr()
}
n.SetType(r.typ())
return n
case ir.OTYPESW:
pos := r.pos()
var tag *ir.Ident
if s := r.localIdent(); s != nil {
tag = ir.NewIdent(pos, s)
}
return ir.NewTypeSwitchGuard(pos, tag, r.expr())
// case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC:
// unreachable - should have been resolved by typechecking
case ir.OCLOSURE:
//println("Importing CLOSURE")
pos := r.pos()
r.setPkg()
typ := r.signature(nil, nil)
r.setPkg()
// All the remaining code below is similar to (*noder).funcLit(), but
// with Dcls and ClosureVars lists already set up
fn := ir.NewClosureFunc(pos, true)
fn.Nname.SetType(typ)
cvars := make([]*ir.Name, r.int64())
for i := range cvars {
cvars[i] = ir.CaptureName(r.pos(), fn, r.localName().Canonical())
if cvars[i].Defn == nil {
base.Fatalf("bad import of closure variable")
}
}
fn.ClosureVars = cvars
r.allClosureVars = append(r.allClosureVars, cvars...)
fn.Inl = &ir.Inline{}
// Read in the Dcls and Body of the closure after temporarily
// setting r.curfn to fn.
r.funcBody(fn)
fn.Dcl = fn.Inl.Dcl
fn.Body = fn.Inl.Body
if len(fn.Body) == 0 {
// An empty closure must be represented as a single empty
// block statement, else it will be dropped.
fn.Body = []ir.Node{ir.NewBlockStmt(src.NoXPos, nil)}
}
fn.Inl = nil
ir.FinishCaptureNames(pos, r.curfn, fn)
clo := fn.OClosure
clo.SetType(typ)
return clo
case ir.OSTRUCTLIT:
pos := r.pos()
typ := r.typ()
list := r.fieldList()
return ir.NewCompLitExpr(pos, ir.OSTRUCTLIT, typ, list)
case ir.OCOMPLIT:
pos := r.pos()
t := r.typ()
return ir.NewCompLitExpr(pos, ir.OCOMPLIT, t, r.exprList())
case ir.OARRAYLIT, ir.OSLICELIT, ir.OMAPLIT:
pos := r.pos()
typ := r.typ()
list := r.exprList()
n := ir.NewCompLitExpr(pos, op, typ, list)
if op == ir.OSLICELIT {
n.Len = int64(r.uint64())
}
return n
case ir.OKEY:
return ir.NewKeyExpr(r.pos(), r.expr(), r.expr())
// case OSTRUCTKEY:
// unreachable - handled in case OSTRUCTLIT by elemList
case ir.OXDOT, ir.ODOT, ir.ODOTPTR, ir.ODOTINTER, ir.ODOTMETH, ir.OMETHVALUE, ir.OMETHEXPR:
pos := r.pos()
expr := r.expr()
sel := r.exoticSelector()
n := ir.NewSelectorExpr(pos, op, expr, sel)
n.SetType(r.exoticType())
switch op {
case ir.OXDOT:
hasSelection := r.bool()
// We reconstruct n.Selection for method calls on
// generic types and method calls due to type param
// bounds. Otherwise, n.Selection is nil.
if hasSelection {
n1 := ir.NewSelectorExpr(pos, op, expr, sel)
AddImplicitDots(n1)
var m *types.Field
if n1.X.Type().IsTypeParam() {
genType := n1.X.Type().Bound()
m = Lookdot1(n1, sel, genType, genType.AllMethods(), 1)
} else {
genType := types.ReceiverBaseType(n1.X.Type())
if genType.IsInstantiatedGeneric() {
genType = genType.OrigType()
}
m = Lookdot1(n1, sel, genType, genType.Methods(), 1)
}
assert(m != nil)
n.Selection = m
}
case ir.ODOT, ir.ODOTPTR, ir.ODOTINTER:
n.Selection = r.exoticField()
case ir.OMETHEXPR:
n = typecheckMethodExpr(n).(*ir.SelectorExpr)
case ir.ODOTMETH, ir.OMETHVALUE:
// These require a Lookup to link to the correct declaration.
rcvrType := expr.Type()
typ := n.Type()
n.Selection = Lookdot(n, rcvrType, 1)
if op == ir.OMETHVALUE {
// Lookdot clobbers the opcode and type, undo that.
n.SetOp(op)
n.SetType(typ)
}
}
return n
case ir.ODOTTYPE, ir.ODOTTYPE2:
n := ir.NewTypeAssertExpr(r.pos(), r.expr(), r.typ())
n.SetOp(op)
return n
case ir.ODYNAMICDOTTYPE, ir.ODYNAMICDOTTYPE2:
n := ir.NewDynamicTypeAssertExpr(r.pos(), op, r.expr(), r.expr())
if r.bool() {
n.ITab = r.expr()
}
n.SetType(r.typ())
return n
case ir.OINDEX, ir.OINDEXMAP:
n := ir.NewIndexExpr(r.pos(), r.expr(), r.expr())
n.SetOp(op)
n.SetType(r.exoticType())
if op == ir.OINDEXMAP {
n.Assigned = r.bool()
}
return n
case ir.OSLICE, ir.OSLICESTR, ir.OSLICEARR, ir.OSLICE3, ir.OSLICE3ARR:
pos, x := r.pos(), r.expr()
low, high := r.exprsOrNil()
var max ir.Node
if op.IsSlice3() {
max = r.expr()
}
n := ir.NewSliceExpr(pos, op, x, low, high, max)
n.SetType(r.typ())
return n
case ir.OCONV, ir.OCONVIFACE, ir.OCONVIDATA, ir.OCONVNOP, ir.OBYTES2STR, ir.ORUNES2STR, ir.OSTR2BYTES, ir.OSTR2RUNES, ir.ORUNESTR, ir.OSLICE2ARRPTR:
return ir.NewConvExpr(r.pos(), op, r.typ(), r.expr())
case ir.OCOPY, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCAP, ir.OCLOSE, ir.ODELETE, ir.OLEN, ir.OMAKE, ir.ONEW, ir.OPANIC, ir.ORECOVER, ir.OPRINT, ir.OPRINTN, ir.OUNSAFEADD, ir.OUNSAFESLICE:
pos := r.pos()
switch op {
case ir.OCOPY, ir.OCOMPLEX, ir.OUNSAFEADD, ir.OUNSAFESLICE:
init := r.stmtList()
n := ir.NewBinaryExpr(pos, op, r.expr(), r.expr())
n.SetInit(init)
n.SetType(r.typ())
return n
case ir.OREAL, ir.OIMAG, ir.OCAP, ir.OCLOSE, ir.OLEN, ir.ONEW, ir.OPANIC:
n := ir.NewUnaryExpr(pos, op, r.expr())
if op != ir.OPANIC {
n.SetType(r.typ())
}
return n
case ir.OAPPEND, ir.ODELETE, ir.ORECOVER, ir.OPRINT, ir.OPRINTN:
init := r.stmtList()
n := ir.NewCallExpr(pos, op, nil, r.exprList())
n.SetInit(init)
if op == ir.OAPPEND {
n.IsDDD = r.bool()
}
if op == ir.OAPPEND || op == ir.ORECOVER {
n.SetType(r.typ())
}
return n
}
// ir.OMAKE
goto error
case ir.OCALL, ir.OCALLFUNC, ir.OCALLMETH, ir.OCALLINTER, ir.OGETG:
pos := r.pos()
init := r.stmtList()
n := ir.NewCallExpr(pos, ir.OCALL, r.expr(), r.exprList())
n.SetOp(op)
n.SetInit(init)
n.IsDDD = r.bool()
n.SetType(r.exoticType())
return n
case ir.OMAKEMAP, ir.OMAKECHAN, ir.OMAKESLICE:
pos := r.pos()
typ := r.typ()
list := r.exprList()
var len_, cap_ ir.Node
if len(list) > 0 {
len_ = list[0]
}
if len(list) > 1 {
cap_ = list[1]
}
n := ir.NewMakeExpr(pos, op, len_, cap_)
n.SetType(typ)
return n
case ir.OLINKSYMOFFSET:
pos := r.pos()
name := r.string()
off := r.uint64()
typ := r.typ()
return ir.NewLinksymOffsetExpr(pos, Lookup(name).Linksym(), int64(off), typ)
// unary expressions
case ir.OPLUS, ir.ONEG, ir.OBITNOT, ir.ONOT, ir.ORECV, ir.OIDATA:
n := ir.NewUnaryExpr(r.pos(), op, r.expr())
n.SetType(r.typ())
return n
case ir.OADDR, ir.OPTRLIT:
pos := r.pos()
expr := r.expr()
expr.SetTypecheck(1) // we do this for all nodes after importing, but do it now so markAddrOf can see it.
n := NodAddrAt(pos, expr)
n.SetOp(op)
n.SetType(r.typ())
return n
case ir.ODEREF:
n := ir.NewStarExpr(r.pos(), r.expr())
n.SetType(r.typ())
return n
// binary expressions
case ir.OADD, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OEQ, ir.OGE, ir.OGT, ir.OLE, ir.OLT,
ir.OLSH, ir.OMOD, ir.OMUL, ir.ONE, ir.OOR, ir.ORSH, ir.OSUB, ir.OXOR, ir.OEFACE:
n := ir.NewBinaryExpr(r.pos(), op, r.expr(), r.expr())
n.SetType(r.typ())
return n
case ir.OANDAND, ir.OOROR:
n := ir.NewLogicalExpr(r.pos(), op, r.expr(), r.expr())
n.SetType(r.typ())
return n
case ir.OSEND:
return ir.NewSendStmt(r.pos(), r.expr(), r.expr())
case ir.OADDSTR:
pos := r.pos()
list := r.exprList()
n := ir.NewAddStringExpr(pos, list)
n.SetType(r.typ())
return n
// --------------------------------------------------------------------
// statements
case ir.ODCL:
var stmts ir.Nodes
n := r.localName()
stmts.Append(ir.NewDecl(n.Pos(), ir.ODCL, n))
stmts.Append(ir.NewAssignStmt(n.Pos(), n, nil))
return ir.NewBlockStmt(n.Pos(), stmts)
// case OASWB:
// unreachable - never exported
case ir.OAS:
pos := r.pos()
init := r.stmtList()
n := ir.NewAssignStmt(pos, r.expr(), r.expr())
n.SetInit(init)
n.Def = r.bool()
return n
case ir.OASOP:
n := ir.NewAssignOpStmt(r.pos(), r.op(), r.expr(), nil)
if !r.bool() {
n.Y = ir.NewInt(1)
n.IncDec = true
} else {
n.Y = r.expr()
}
return n
case ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV:
pos := r.pos()
init := r.stmtList()
n := ir.NewAssignListStmt(pos, op, r.exprList(), r.exprList())
n.SetInit(init)
n.Def = r.bool()
return n
case ir.ORETURN:
return ir.NewReturnStmt(r.pos(), r.exprList())
// case ORETJMP:
// unreachable - generated by compiler for trampolin routines (not exported)
case ir.OGO, ir.ODEFER:
return ir.NewGoDeferStmt(r.pos(), op, r.expr())
case ir.OIF:
pos, init := r.pos(), r.stmtList()
n := ir.NewIfStmt(pos, r.expr(), r.stmtList(), r.stmtList())
n.SetInit(init)
return n
case ir.OFOR:
pos, init := r.pos(), r.stmtList()
cond, post := r.exprsOrNil()
n := ir.NewForStmt(pos, nil, cond, post, r.stmtList())
n.SetInit(init)
return n
case ir.ORANGE:
pos, init := r.pos(), r.stmtList()
k, v := r.exprsOrNil()
n := ir.NewRangeStmt(pos, k, v, r.expr(), r.stmtList())
n.SetInit(init)
return n
case ir.OSELECT:
pos := r.pos()
init := r.stmtList()
n := ir.NewSelectStmt(pos, r.commList())
n.SetInit(init)
return n
case ir.OSWITCH:
pos := r.pos()
init := r.stmtList()
x, _ := r.exprsOrNil()
n := ir.NewSwitchStmt(pos, x, r.caseList(x))
n.SetInit(init)
return n
// case OCASE:
// handled by caseList
case ir.OFALL:
return ir.NewBranchStmt(r.pos(), ir.OFALL, nil)
// case OEMPTY:
// unreachable - not emitted by exporter
case ir.OBREAK, ir.OCONTINUE, ir.OGOTO:
pos := r.pos()
var sym *types.Sym
if label := r.string(); label != "" {
sym = Lookup(label)
}
return ir.NewBranchStmt(pos, op, sym)
case ir.OLABEL:
return ir.NewLabelStmt(r.pos(), Lookup(r.string()))
case ir.OEND:
return nil
case ir.OFUNCINST:
pos := r.pos()
x := r.expr()
targs := make([]ir.Ntype, r.uint64())
for i := range targs {
targs[i] = ir.TypeNode(r.typ())
}
n := ir.NewInstExpr(pos, ir.OFUNCINST, x, targs)
n.SetType(r.typ())
return n
case ir.OSELRECV2:
pos := r.pos()
init := r.stmtList()
n := ir.NewAssignListStmt(pos, ir.OSELRECV2, r.exprList(), r.exprList())
n.SetInit(init)
n.Def = r.bool()
return n
default:
base.Fatalf("cannot import %v (%d) node\n"+
"\t==> please file an issue and assign to gri@", op, int(op))
panic("unreachable") // satisfy compiler
}
error:
base.Fatalf("cannot import %v (%d) node\n"+
"\t==> please file an issue and assign to khr@", op, int(op))
panic("unreachable") // satisfy compiler
}
func (r *importReader) op() ir.Op {
if debug && r.uint64() != magic {
base.Fatalf("import stream has desynchronized")
}
return ir.Op(r.uint64())
}
func (r *importReader) fieldList() []ir.Node {
list := make([]ir.Node, r.uint64())
for i := range list {
list[i] = ir.NewStructKeyExpr(r.pos(), r.exoticField(), r.expr())
}
return list
}
func (r *importReader) exprsOrNil() (a, b ir.Node) {
ab := r.uint64()
if ab&1 != 0 {
a = r.expr()
}
if ab&2 != 0 {
b = r.node()
}
return
}
// NewIncompleteNamedType returns a TFORW type t with name specified by sym, such
// that t.nod and sym.Def are set correctly. If there are any RParams for the type,
// they should be set soon after creating the TFORW type, before creating the
// underlying type. That ensures that the HasTParam and HasShape flags will be set
// properly, in case this type is part of some mutually recursive type.
func NewIncompleteNamedType(pos src.XPos, sym *types.Sym) *types.Type {
name := ir.NewDeclNameAt(pos, ir.OTYPE, sym)
forw := types.NewNamed(name)
name.SetType(forw)
sym.Def = name
return forw
}
// Instantiate creates a new named type which is the instantiation of the base
// named generic type, with the specified type args.
func Instantiate(pos src.XPos, baseType *types.Type, targs []*types.Type) *types.Type {
baseSym := baseType.Sym()
if strings.Index(baseSym.Name, "[") >= 0 {
base.Fatalf("arg to Instantiate is not a base generic type")
}
name := InstTypeName(baseSym.Name, targs)
instSym := baseSym.Pkg.Lookup(name)
if instSym.Def != nil {
// May match existing type from previous import or
// types2-to-types1 conversion.
t := instSym.Def.Type()
if t.Kind() != types.TFORW {
return t
}
// Or, we have started creating this type in (*TSubster).Typ, but its
// underlying type was not completed yet, so we need to add this type
// to deferredInstStack, if not already there.
found := false
for _, t2 := range deferredInstStack {
if t2 == t {
found = true
break
}
}
if !found {
deferredInstStack = append(deferredInstStack, t)
}
return t
}
t := NewIncompleteNamedType(baseType.Pos(), instSym)
t.SetRParams(targs)
t.SetOrigType(baseType)
// baseType may still be TFORW or its methods may not be fully filled in
// (since we are in the middle of importing it). So, delay call to
// substInstType until we get back up to the top of the current top-most
// type import.
deferredInstStack = append(deferredInstStack, t)
return t
}
var deferredInstStack []*types.Type
var deferInst int
// deferDoInst defers substitution on instantiated types until we are at the
// top-most defined type, so the base types are fully defined.
func deferDoInst() {
deferInst++
}
func resumeDoInst() {
if deferInst == 1 {
for len(deferredInstStack) > 0 {
t := deferredInstStack[0]
deferredInstStack = deferredInstStack[1:]
substInstType(t, t.OrigType(), t.RParams())
}
}
deferInst--
}
// doInst creates a new instantiation type (which will be added to
// deferredInstStack for completion later) for an incomplete type encountered
// during a type substitution for an instantiation. This is needed for
// instantiations of mutually recursive types.
func doInst(t *types.Type) *types.Type {
assert(t.Kind() == types.TFORW)
return Instantiate(t.Pos(), t.OrigType(), t.RParams())
}
// substInstType completes the instantiation of a generic type by doing a
// substitution on the underlying type itself and any methods. t is the
// instantiation being created, baseType is the base generic type, and targs are
// the type arguments that baseType is being instantiated with.
func substInstType(t *types.Type, baseType *types.Type, targs []*types.Type) {
assert(t.Kind() == types.TFORW)
subst := Tsubster{
Tparams: baseType.RParams(),
Targs: targs,
SubstForwFunc: doInst,
}
t.SetUnderlying(subst.Typ(baseType.Underlying()))
newfields := make([]*types.Field, baseType.Methods().Len())
for i, f := range baseType.Methods().Slice() {
if !f.IsMethod() || types.IsInterfaceMethod(f.Type) {
// Do a normal substitution if this is a non-method (which
// means this must be an interface used as a constraint) or
// an interface method.
t2 := subst.Typ(f.Type)
newfields[i] = types.NewField(f.Pos, f.Sym, t2)
continue
}
recvType := f.Type.Recv().Type
if recvType.IsPtr() {
recvType = recvType.Elem()
}
// Substitute in the method using the type params used in the
// method (not the type params in the definition of the generic type).
msubst := Tsubster{
Tparams: recvType.RParams(),
Targs: targs,
SubstForwFunc: doInst,
}
t2 := msubst.Typ(f.Type)
oldsym := f.Nname.Sym()
newsym := MakeFuncInstSym(oldsym, targs, true, true)
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
}
t.Methods().Set(newfields)
if !t.HasTParam() && !t.HasShape() && t.Kind() != types.TINTER && t.Methods().Len() > 0 {
// Generate all the methods for a new fully-instantiated,
// non-interface, non-shape type.
NeedInstType(t)
}
}
相关信息
相关文章
0
赞
热门推荐
-
2、 - 优质文章
-
3、 gate.io
-
8、 golang
-
9、 openharmony
-
10、 Vue中input框自动聚焦