| // 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.nameAt(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.nameAt(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] |
| } |
| |
| // nameAt is the same as stringAt, except it replaces instances |
| // of "" with the path of the package being imported. |
| func (p *iimporter) nameAt(off uint64) string { |
| s := p.stringAt(off) |
| // Names of objects (functions, methods, globals) may include "" |
| // to represent the path name of the imported package. |
| // Replace "" with the imported package prefix. This occurs |
| // specifically for generics where the names of instantiations |
| // and dictionaries contain package-qualified type names. |
| // Include the dot to avoid matching with struct tags ending in '"'. |
| if strings.Contains(s, "\"\".") { |
| s = strings.Replace(s, "\"\".", p.ipkg.Prefix+".", -1) |
| } |
| return s |
| } |
| |
| 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) name() string { return r.p.nameAt(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) |
| } |
| // The typeparam index is set at the point where the containing type |
| // param list is imported. |
| t := types.NewTypeParam(sym, 0) |
| // Nname needed to save the pos. |
| nname := ir.NewDeclNameAt(pos, ir.OTYPE, sym) |
| sym.Def = nname |
| nname.SetType(t) |
| t.SetNod(nname) |
| implicit := false |
| if r.p.exportVersion >= iexportVersionGo1_18 { |
| implicit = r.bool() |
| } |
| bound := r.typ() |
| if implicit { |
| bound.MarkImplicit() |
| } |
| t.SetBound(bound) |
| return nname |
| |
| 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.name() |
| 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, funtions |
| // 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.OrigSym() == 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{} |
| } |
| if go117ExportTypes { |
| 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() |
| n := r.qualifiedIdent() |
| if go117ExportTypes { |
| var n2 ir.Node = n |
| // Key ONONAME entries should not be resolved - they should |
| // stay as identifiers. |
| if !isKey { |
| n2 = Resolve(n) |
| } |
| typ := r.typ() |
| if n2.Type() == nil { |
| n2.SetType(typ) |
| } |
| return n2 |
| } |
| 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 go117ExportTypes && 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 |
| if go117ExportTypes { |
| clo.SetType(typ) |
| } |
| return clo |
| |
| case ir.OSTRUCTLIT: |
| if go117ExportTypes { |
| pos := r.pos() |
| typ := r.typ() |
| list := r.fieldList() |
| n := ir.NewCompLitExpr(pos, ir.OSTRUCTLIT, nil, list) |
| n.SetType(typ) |
| return n |
| } |
| return ir.NewCompLitExpr(r.pos(), ir.OCOMPLIT, ir.TypeNode(r.typ()), r.fieldList()) |
| |
| case ir.OCOMPLIT: |
| pos := r.pos() |
| t := r.typ() |
| n := ir.NewCompLitExpr(pos, ir.OCOMPLIT, ir.TypeNode(t), r.exprList()) |
| n.SetType(t) |
| return n |
| |
| case ir.OARRAYLIT, ir.OSLICELIT, ir.OMAPLIT: |
| if !go117ExportTypes { |
| // unreachable - mapped to OCOMPLIT by exporter |
| goto error |
| } |
| pos := r.pos() |
| typ := r.typ() |
| list := r.exprList() |
| n := ir.NewCompLitExpr(pos, op, ir.TypeNode(typ), list) |
| n.SetType(typ) |
| 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: |
| // For !go117ExportTypes, we should only see OXDOT. |
| // For go117ExportTypes, we usually see all the other ops, but can see |
| // OXDOT for generic functions. |
| if op != ir.OXDOT && !go117ExportTypes { |
| goto error |
| } |
| pos := r.pos() |
| expr := r.expr() |
| sel := r.exoticSelector() |
| n := ir.NewSelectorExpr(pos, op, expr, sel) |
| if go117ExportTypes { |
| 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.OrigSym().Def.Type() |
| } |
| 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(), nil) |
| n.SetType(r.typ()) |
| if go117ExportTypes { |
| n.SetOp(op) |
| } |
| return n |
| |
| case ir.ODYNAMICDOTTYPE, ir.ODYNAMICDOTTYPE2: |
| n := ir.NewDynamicTypeAssertExpr(r.pos(), op, r.expr(), r.expr()) |
| n.SetType(r.typ()) |
| return n |
| |
| case ir.OINDEX, ir.OINDEXMAP: |
| n := ir.NewIndexExpr(r.pos(), r.expr(), r.expr()) |
| if go117ExportTypes { |
| 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) |
| if go117ExportTypes { |
| 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: |
| if !go117ExportTypes && op != ir.OCONV { |
| // unreachable - mapped to OCONV case by exporter |
| goto error |
| } |
| 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: |
| if go117ExportTypes { |
| switch op { |
| case ir.OCOPY, ir.OCOMPLEX, ir.OUNSAFEADD, ir.OUNSAFESLICE: |
| n := ir.NewBinaryExpr(r.pos(), op, r.expr(), r.expr()) |
| n.SetType(r.typ()) |
| return n |
| case ir.OREAL, ir.OIMAG, ir.OCAP, ir.OCLOSE, ir.OLEN, ir.ONEW, ir.OPANIC: |
| n := ir.NewUnaryExpr(r.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: |
| n := ir.NewCallExpr(r.pos(), op, nil, r.exprList()) |
| 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 |
| } |
| n := builtinCall(r.pos(), op) |
| n.Args = r.exprList() |
| if op == ir.OAPPEND { |
| n.IsDDD = r.bool() |
| } |
| return n |
| |
| 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()) |
| if go117ExportTypes { |
| n.SetOp(op) |
| } |
| n.SetInit(init) |
| n.IsDDD = r.bool() |
| if go117ExportTypes { |
| n.SetType(r.exoticType()) |
| } |
| return n |
| |
| case ir.OMAKEMAP, ir.OMAKECHAN, ir.OMAKESLICE: |
| if go117ExportTypes { |
| 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 |
| } |
| n := builtinCall(r.pos(), ir.OMAKE) |
| n.Args.Append(ir.TypeNode(r.typ())) |
| n.Args.Append(r.exprList()...) |
| 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()) |
| if go117ExportTypes { |
| n.SetType(r.typ()) |
| } |
| return n |
| |
| case ir.OADDR, ir.OPTRLIT: |
| if go117ExportTypes { |
| 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 |
| } |
| n := NodAddrAt(r.pos(), r.expr()) |
| return n |
| |
| case ir.ODEREF: |
| n := ir.NewStarExpr(r.pos(), r.expr()) |
| if go117ExportTypes { |
| 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()) |
| if go117ExportTypes { |
| n.SetType(r.typ()) |
| } |
| return n |
| |
| case ir.OANDAND, ir.OOROR: |
| n := ir.NewLogicalExpr(r.pos(), op, r.expr(), r.expr()) |
| if go117ExportTypes { |
| 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() |
| if go117ExportTypes { |
| n := ir.NewAddStringExpr(pos, list) |
| n.SetType(r.typ()) |
| return n |
| } |
| x := list[0] |
| for _, y := range list[1:] { |
| x = ir.NewBinaryExpr(pos, ir.OADD, x, y) |
| } |
| return x |
| |
| // -------------------------------------------------------------------- |
| // 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: |
| if !go117ExportTypes && op != ir.OAS2 { |
| // unreachable - mapped to case OAS2 by exporter |
| goto error |
| } |
| 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() |
| ntargs := r.uint64() |
| var targs []ir.Node |
| if ntargs > 0 { |
| targs = make([]ir.Node, ntargs) |
| for i := range targs { |
| targs[i] = ir.TypeNode(r.typ()) |
| } |
| } |
| n := ir.NewInstExpr(pos, ir.OFUNCINST, x, targs) |
| if go117ExportTypes { |
| 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 |
| } |
| |
| func builtinCall(pos src.XPos, op ir.Op) *ir.CallExpr { |
| if go117ExportTypes { |
| // These should all be encoded as direct ops, not OCALL. |
| base.Fatalf("builtinCall should not be invoked when types are included in import/export") |
| } |
| return ir.NewCallExpr(pos, ir.OCALL, ir.NewIdent(base.Pos, types.BuiltinPkg.Lookup(ir.OpNames[op])), nil) |
| } |
| |
| // 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.SetOrigSym(baseSym) |
| |
| // 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.OrigSym().Def.(*ir.Name).Type(), 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.OrigSym().Def.(*ir.Name).Type(), 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) |
| } |
| } |