// UNREVIEWED // Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package noder import ( "bytes" "fmt" "go/constant" "strings" "cmd/compile/internal/base" "cmd/compile/internal/deadcode" "cmd/compile/internal/dwarfgen" "cmd/compile/internal/inline" "cmd/compile/internal/ir" "cmd/compile/internal/reflectdata" "cmd/compile/internal/typecheck" "cmd/compile/internal/types" "cmd/internal/obj" "cmd/internal/src" ) // TODO(mdempsky): Suppress duplicate type/const errors that can arise // during typecheck due to naive type substitution (e.g., see #42758). // I anticipate these will be handled as a consequence of adding // dictionaries support, so it's probably not important to focus on // this until after that's done. type pkgReader struct { pkgDecoder posBases []*src.PosBase pkgs []*types.Pkg typs []*types.Type // offset for rewriting the given index into the output, // but bitwise inverted so we can detect if we're missing the entry or not. newindex []int } func newPkgReader(pr pkgDecoder) *pkgReader { return &pkgReader{ pkgDecoder: pr, posBases: make([]*src.PosBase, pr.numElems(relocPosBase)), pkgs: make([]*types.Pkg, pr.numElems(relocPkg)), typs: make([]*types.Type, pr.numElems(relocType)), newindex: make([]int, pr.totalElems()), } } type pkgReaderIndex struct { pr *pkgReader idx int dict *readerDict } func (pri pkgReaderIndex) asReader(k reloc, marker syncMarker) *reader { r := pri.pr.newReader(k, pri.idx, marker) r.dict = pri.dict return r } func (pr *pkgReader) newReader(k reloc, idx int, marker syncMarker) *reader { return &reader{ decoder: pr.newDecoder(k, idx, marker), p: pr, } } type reader struct { decoder p *pkgReader ext *reader dict *readerDict // TODO(mdempsky): The state below is all specific to reading // function bodies. It probably makes sense to split it out // separately so that it doesn't take up space in every reader // instance. curfn *ir.Func locals []*ir.Name closureVars []*ir.Name funarghack bool // scopeVars is a stack tracking the number of variables declared in // the current function at the moment each open scope was opened. scopeVars []int marker dwarfgen.ScopeMarker lastCloseScopePos src.XPos // === details for handling inline body expansion === // If we're reading in a function body because of inlining, this is // the call that we're inlining for. inlCaller *ir.Func inlCall *ir.CallExpr inlFunc *ir.Func inlTreeIndex int inlPosBases map[*src.PosBase]*src.PosBase delayResults bool // Label to return to. retlabel *types.Sym inlvars, retvars ir.Nodes } type readerDict struct { // targs holds the implicit and explicit type arguments in use for // reading the current object. For example: // // func F[T any]() { // type X[U any] struct { t T; u U } // var _ X[string] // } // // var _ = F[int] // // While instantiating F[int], we need to in turn instantiate // X[string]. [int] and [string] are explicit type arguments for F // and X, respectively; but [int] is also the implicit type // arguments for X. // // (As an analogy to function literals, explicits are the function // literal's formal parameters, while implicits are variables // captured by the function literal.) targs []*types.Type // implicits counts how many of types within targs are implicit type // arguments; the rest are explicit. implicits int derived []derivedInfo // reloc index of the derived type's descriptor derivedTypes []*types.Type // slice of previously computed derived types funcs []objInfo funcsObj []ir.Node } func (r *reader) setType(n ir.Node, typ *types.Type) { n.SetType(typ) n.SetTypecheck(1) if name, ok := n.(*ir.Name); ok { name.SetWalkdef(1) name.Ntype = ir.TypeNode(name.Type()) } } func (r *reader) setValue(name *ir.Name, val constant.Value) { name.SetVal(val) name.Defn = nil } // @@@ Positions func (r *reader) pos() src.XPos { return base.Ctxt.PosTable.XPos(r.pos0()) } func (r *reader) pos0() src.Pos { r.sync(syncPos) if !r.bool() { return src.NoPos } posBase := r.posBase() line := r.uint() col := r.uint() return src.MakePos(posBase, line, col) } func (r *reader) posBase() *src.PosBase { return r.inlPosBase(r.p.posBaseIdx(r.reloc(relocPosBase))) } func (pr *pkgReader) posBaseIdx(idx int) *src.PosBase { if b := pr.posBases[idx]; b != nil { return b } r := pr.newReader(relocPosBase, idx, syncPosBase) var b *src.PosBase filename := r.string() if r.bool() { b = src.NewFileBase(filename, filename) } else { pos := r.pos0() line := r.uint() col := r.uint() b = src.NewLinePragmaBase(pos, filename, filename, line, col) } pr.posBases[idx] = b return b } func (r *reader) inlPosBase(oldBase *src.PosBase) *src.PosBase { if r.inlCall == nil { return oldBase } if newBase, ok := r.inlPosBases[oldBase]; ok { return newBase } newBase := src.NewInliningBase(oldBase, r.inlTreeIndex) r.inlPosBases[oldBase] = newBase return newBase } func (r *reader) updatePos(xpos src.XPos) src.XPos { pos := base.Ctxt.PosTable.Pos(xpos) pos.SetBase(r.inlPosBase(pos.Base())) return base.Ctxt.PosTable.XPos(pos) } func (r *reader) origPos(xpos src.XPos) src.XPos { if r.inlCall == nil { return xpos } pos := base.Ctxt.PosTable.Pos(xpos) for old, new := range r.inlPosBases { if pos.Base() == new { pos.SetBase(old) return base.Ctxt.PosTable.XPos(pos) } } base.FatalfAt(xpos, "pos base missing from inlPosBases") panic("unreachable") } // @@@ Packages func (r *reader) pkg() *types.Pkg { r.sync(syncPkg) return r.p.pkgIdx(r.reloc(relocPkg)) } func (pr *pkgReader) pkgIdx(idx int) *types.Pkg { if pkg := pr.pkgs[idx]; pkg != nil { return pkg } pkg := pr.newReader(relocPkg, idx, syncPkgDef).doPkg() pr.pkgs[idx] = pkg return pkg } func (r *reader) doPkg() *types.Pkg { path := r.string() if path == "builtin" { return types.BuiltinPkg } if path == "" { path = r.p.pkgPath } name := r.string() height := r.len() pkg := types.NewPkg(path, "") if pkg.Name == "" { pkg.Name = name } else { assert(pkg.Name == name) } if pkg.Height == 0 { pkg.Height = height } else { assert(pkg.Height == height) } return pkg } // @@@ Types func (r *reader) typ() *types.Type { return r.p.typIdx(r.typInfo(), r.dict) } func (r *reader) typInfo() typeInfo { r.sync(syncType) if r.bool() { return typeInfo{idx: r.len(), derived: true} } return typeInfo{idx: r.reloc(relocType), derived: false} } func (pr *pkgReader) typIdx(info typeInfo, dict *readerDict) *types.Type { idx := info.idx var where **types.Type if info.derived { where = &dict.derivedTypes[idx] idx = dict.derived[idx].idx } else { where = &pr.typs[idx] } if typ := *where; typ != nil { return typ } r := pr.newReader(relocType, idx, syncTypeIdx) r.dict = dict typ := r.doTyp() assert(typ != nil) // For recursive type declarations involving interfaces and aliases, // above r.doTyp() call may have already set pr.typs[idx], so just // double check and return the type. // // Example: // // type F = func(I) // // type I interface { // m(F) // } // // The writer writes data types in following index order: // // 0: func(I) // 1: I // 2: interface{m(func(I))} // // The reader resolves it in following index order: // // 0 -> 1 -> 2 -> 0 -> 1 // // and can divide in logically 2 steps: // // - 0 -> 1 : first time the reader reach type I, // it creates new named type with symbol I. // // - 2 -> 0 -> 1: the reader ends up reaching symbol I again, // now the symbol I was setup in above step, so // the reader just return the named type. // // Now, the functions called return, the pr.typs looks like below: // // - 0 -> 1 -> 2 -> 0 : [ I ] // - 0 -> 1 -> 2 : [func(I) I ] // - 0 -> 1 : [func(I) I interface { "".m(func("".I)) }] // // The idx 1, corresponding with type I was resolved successfully // after r.doTyp() call. if prev := *where; prev != nil { return prev } *where = typ if !typ.IsUntyped() { types.CheckSize(typ) } return typ } func (r *reader) doTyp() *types.Type { switch tag := codeType(r.code(syncType)); tag { default: panic(fmt.Sprintf("unexpected type: %v", tag)) case typeBasic: return *basics[r.len()] case typeNamed: obj := r.obj() assert(obj.Op() == ir.OTYPE) return obj.Type() case typeTypeParam: return r.dict.targs[r.len()] case typeArray: len := int64(r.uint64()) return types.NewArray(r.typ(), len) case typeChan: dir := dirs[r.len()] return types.NewChan(r.typ(), dir) case typeMap: return types.NewMap(r.typ(), r.typ()) case typePointer: return types.NewPtr(r.typ()) case typeSignature: return r.signature(types.LocalPkg, nil) case typeSlice: return types.NewSlice(r.typ()) case typeStruct: return r.structType() case typeInterface: return r.interfaceType() } } func (r *reader) interfaceType() *types.Type { tpkg := types.LocalPkg // TODO(mdempsky): Remove after iexport is gone. nmethods, nembeddeds := r.len(), r.len() fields := make([]*types.Field, nmethods+nembeddeds) methods, embeddeds := fields[:nmethods], fields[nmethods:] for i := range methods { pos := r.pos() pkg, sym := r.selector() tpkg = pkg mtyp := r.signature(pkg, typecheck.FakeRecv()) methods[i] = types.NewField(pos, sym, mtyp) } for i := range embeddeds { embeddeds[i] = types.NewField(src.NoXPos, nil, r.typ()) } if len(fields) == 0 { return types.Types[types.TINTER] // empty interface } return r.needWrapper(types.NewInterface(tpkg, fields)) } func (r *reader) structType() *types.Type { tpkg := types.LocalPkg // TODO(mdempsky): Remove after iexport is gone. fields := make([]*types.Field, r.len()) for i := range fields { pos := r.pos() pkg, sym := r.selector() tpkg = pkg ftyp := r.typ() tag := r.string() embedded := r.bool() f := types.NewField(pos, sym, ftyp) f.Note = tag if embedded { f.Embedded = 1 } fields[i] = f } return r.needWrapper(types.NewStruct(tpkg, fields)) } func (r *reader) signature(tpkg *types.Pkg, recv *types.Field) *types.Type { r.sync(syncSignature) params := r.params(&tpkg) results := r.params(&tpkg) if r.bool() { // variadic params[len(params)-1].SetIsDDD(true) } return types.NewSignature(tpkg, recv, nil, params, results) } func (r *reader) params(tpkg **types.Pkg) []*types.Field { r.sync(syncParams) fields := make([]*types.Field, r.len()) for i := range fields { *tpkg, fields[i] = r.param() } return fields } func (r *reader) param() (*types.Pkg, *types.Field) { r.sync(syncParam) pos := r.pos() pkg, sym := r.localIdent() typ := r.typ() return pkg, types.NewField(pos, sym, typ) } // @@@ Objects var objReader = map[*types.Sym]pkgReaderIndex{} func (r *reader) obj() ir.Node { r.sync(syncObject) if r.bool() { idx := r.len() obj := r.dict.funcsObj[idx] if obj == nil { fn := r.dict.funcs[idx] targs := make([]*types.Type, len(fn.explicits)) for i, targ := range fn.explicits { targs[i] = r.p.typIdx(targ, r.dict) } obj = r.p.objIdx(fn.idx, nil, targs) assert(r.dict.funcsObj[idx] == nil) r.dict.funcsObj[idx] = obj } return obj } idx := r.reloc(relocObj) explicits := make([]*types.Type, r.len()) for i := range explicits { explicits[i] = r.typ() } var implicits []*types.Type if r.dict != nil { implicits = r.dict.targs } return r.p.objIdx(idx, implicits, explicits) } func (pr *pkgReader) objIdx(idx int, implicits, explicits []*types.Type) ir.Node { rname := pr.newReader(relocName, idx, syncObject1) _, sym := rname.qualifiedIdent() tag := codeObj(rname.code(syncCodeObj)) if tag == objStub { assert(!sym.IsBlank()) switch sym.Pkg { case types.BuiltinPkg, ir.Pkgs.Unsafe: return sym.Def.(ir.Node) } if pri, ok := objReader[sym]; ok { return pri.pr.objIdx(pri.idx, nil, explicits) } if haveLegacyImports { assert(len(explicits) == 0) return typecheck.Resolve(ir.NewIdent(src.NoXPos, sym)) } base.Fatalf("unresolved stub: %v", sym) } dict := pr.objDictIdx(sym, idx, implicits, explicits) r := pr.newReader(relocObj, idx, syncObject1) r.ext = pr.newReader(relocObjExt, idx, syncObject1) r.dict = dict r.ext.dict = dict sym = r.mangle(sym) if !sym.IsBlank() && sym.Def != nil { return sym.Def.(*ir.Name) } do := func(op ir.Op, hasTParams bool) *ir.Name { pos := r.pos() if hasTParams { r.typeParamNames() } name := ir.NewDeclNameAt(pos, op, sym) name.Class = ir.PEXTERN // may be overridden later if !sym.IsBlank() { if sym.Def != nil { base.FatalfAt(name.Pos(), "already have a definition for %v", name) } assert(sym.Def == nil) sym.Def = name } return name } switch tag { default: panic("unexpected object") case objAlias: name := do(ir.OTYPE, false) r.setType(name, r.typ()) name.SetAlias(true) return name case objConst: name := do(ir.OLITERAL, false) typ, val := r.value() r.setType(name, typ) r.setValue(name, val) return name case objFunc: if sym.Name == "init" { sym = renameinit() } name := do(ir.ONAME, true) r.setType(name, r.signature(sym.Pkg, nil)) name.Func = ir.NewFunc(r.pos()) name.Func.Nname = name r.ext.funcExt(name) return name case objType: name := do(ir.OTYPE, true) typ := types.NewNamed(name) r.setType(name, typ) // Important: We need to do this before SetUnderlying. r.ext.typeExt(name) // We need to defer CheckSize until we've called SetUnderlying to // handle recursive types. types.DeferCheckSize() typ.SetUnderlying(r.typ()) types.ResumeCheckSize() methods := make([]*types.Field, r.len()) for i := range methods { methods[i] = r.method() } if len(methods) != 0 { typ.Methods().Set(methods) } if !typ.IsPtr() { r.needWrapper(typ) } return name case objVar: name := do(ir.ONAME, false) r.setType(name, r.typ()) r.ext.varExt(name) return name } } func (r *reader) mangle(sym *types.Sym) *types.Sym { if !r.hasTypeParams() { return sym } var buf bytes.Buffer buf.WriteString(sym.Name) buf.WriteByte('[') for i, targ := range r.dict.targs { if i > 0 { if i == r.dict.implicits { buf.WriteByte(';') } else { buf.WriteByte(',') } } buf.WriteString(targ.LinkString()) } buf.WriteByte(']') return sym.Pkg.Lookup(buf.String()) } func (pr *pkgReader) objDictIdx(sym *types.Sym, idx int, implicits, explicits []*types.Type) *readerDict { r := pr.newReader(relocObjDict, idx, syncObject1) var dict readerDict nimplicits := r.len() nexplicits := r.len() if nimplicits > len(implicits) || nexplicits != len(explicits) { base.Fatalf("%v has %v+%v params, but instantiated with %v+%v args", sym, nimplicits, nexplicits, len(implicits), len(explicits)) } dict.targs = append(implicits[:nimplicits:nimplicits], explicits...) dict.implicits = nimplicits // For stenciling, we can just skip over the type parameters. for range dict.targs[dict.implicits:] { // Skip past bounds without actually evaluating them. r.sync(syncType) if r.bool() { r.len() } else { r.reloc(relocType) } } dict.derived = make([]derivedInfo, r.len()) dict.derivedTypes = make([]*types.Type, len(dict.derived)) for i := range dict.derived { dict.derived[i] = derivedInfo{r.reloc(relocType), r.bool()} } dict.funcs = make([]objInfo, r.len()) dict.funcsObj = make([]ir.Node, len(dict.funcs)) for i := range dict.funcs { objIdx := r.reloc(relocObj) targs := make([]typeInfo, r.len()) for j := range targs { targs[j] = r.typInfo() } dict.funcs[i] = objInfo{idx: objIdx, explicits: targs} } return &dict } func (r *reader) typeParamNames() { r.sync(syncTypeParamNames) for range r.dict.targs[r.dict.implicits:] { r.pos() r.localIdent() } } func (r *reader) value() (*types.Type, constant.Value) { r.sync(syncValue) typ := r.typ() return typ, FixValue(typ, r.rawValue()) } func (r *reader) method() *types.Field { r.sync(syncMethod) pos := r.pos() pkg, sym := r.selector() r.typeParamNames() _, recv := r.param() typ := r.signature(pkg, recv) fnsym := sym fnsym = ir.MethodSym(recv.Type, fnsym) name := ir.NewNameAt(pos, fnsym) r.setType(name, typ) name.Func = ir.NewFunc(r.pos()) name.Func.Nname = name r.ext.funcExt(name) meth := types.NewField(name.Func.Pos(), sym, typ) meth.Nname = name meth.SetNointerface(name.Func.Pragma&ir.Nointerface != 0) return meth } func (r *reader) qualifiedIdent() (pkg *types.Pkg, sym *types.Sym) { r.sync(syncSym) pkg = r.pkg() if name := r.string(); name != "" { sym = pkg.Lookup(name) } return } func (r *reader) localIdent() (pkg *types.Pkg, sym *types.Sym) { r.sync(syncLocalIdent) pkg = r.pkg() if name := r.string(); name != "" { sym = pkg.Lookup(name) } return } func (r *reader) selector() (origPkg *types.Pkg, sym *types.Sym) { r.sync(syncSelector) origPkg = r.pkg() name := r.string() pkg := origPkg if types.IsExported(name) { pkg = types.LocalPkg } sym = pkg.Lookup(name) return } func (r *reader) hasTypeParams() bool { return r.dict.hasTypeParams() } func (dict *readerDict) hasTypeParams() bool { return dict != nil && len(dict.targs) != 0 } // @@@ Compiler extensions func (r *reader) funcExt(name *ir.Name) { r.sync(syncFuncExt) name.Class = 0 // so MarkFunc doesn't complain ir.MarkFunc(name) fn := name.Func // XXX: Workaround because linker doesn't know how to copy Pos. if !fn.Pos().IsKnown() { fn.SetPos(name.Pos()) } // Normally, we only compile local functions, which saves redundant compilation work. // n.Defn is not nil for local functions, and is nil for imported function. But for // generic functions, we might have an instantiation that no other package has seen before. // So we need to be conservative and compile it again. // // That's why name.Defn is set here, so ir.VisitFuncsBottomUp can analyze function. // TODO(mdempsky,cuonglm): find a cleaner way to handle this. if name.Sym().Pkg == types.LocalPkg || r.hasTypeParams() { name.Defn = fn } fn.Pragma = r.pragmaFlag() r.linkname(name) typecheck.Func(fn) if r.bool() { fn.ABI = obj.ABI(r.uint64()) // Escape analysis. for _, fs := range &types.RecvsParams { for _, f := range fs(name.Type()).FieldSlice() { f.Note = r.string() } } if r.bool() { fn.Inl = &ir.Inline{ Cost: int32(r.len()), CanDelayResults: r.bool(), } r.addBody(name.Func) } } else { r.addBody(name.Func) } r.sync(syncEOF) } func (r *reader) typeExt(name *ir.Name) { r.sync(syncTypeExt) typ := name.Type() if r.hasTypeParams() { // Set "RParams" (really type arguments here, not parameters) so // this type is treated as "fully instantiated". This ensures the // type descriptor is written out as DUPOK and method wrappers are // generated even for imported types. var targs []*types.Type targs = append(targs, r.dict.targs...) typ.SetRParams(targs) } name.SetPragma(r.pragmaFlag()) if name.Pragma()&ir.NotInHeap != 0 { typ.SetNotInHeap(true) } typecheck.SetBaseTypeIndex(typ, r.int64(), r.int64()) } func (r *reader) varExt(name *ir.Name) { r.sync(syncVarExt) r.linkname(name) } func (r *reader) linkname(name *ir.Name) { assert(name.Op() == ir.ONAME) r.sync(syncLinkname) if idx := r.int64(); idx >= 0 { lsym := name.Linksym() lsym.SymIdx = int32(idx) lsym.Set(obj.AttrIndexed, true) } else { name.Sym().Linkname = r.string() } } func (r *reader) pragmaFlag() ir.PragmaFlag { r.sync(syncPragma) return ir.PragmaFlag(r.int()) } // @@@ Function bodies // bodyReader tracks where the serialized IR for a function's body can // be found. var bodyReader = map[*ir.Func]pkgReaderIndex{} // todoBodies holds the list of function bodies that still need to be // constructed. var todoBodies []*ir.Func func (r *reader) addBody(fn *ir.Func) { pri := pkgReaderIndex{r.p, r.reloc(relocBody), r.dict} bodyReader[fn] = pri if r.curfn == nil { todoBodies = append(todoBodies, fn) return } pri.funcBody(fn) } func (pri pkgReaderIndex) funcBody(fn *ir.Func) { r := pri.asReader(relocBody, syncFuncBody) r.funcBody(fn) } func (r *reader) funcBody(fn *ir.Func) { r.curfn = fn r.closureVars = fn.ClosureVars ir.WithFunc(fn, func() { r.funcargs(fn) if !r.bool() { return } body := r.stmts() if body == nil { pos := src.NoXPos if quirksMode() { pos = funcParamsEndPos(fn) } body = []ir.Node{typecheck.Stmt(ir.NewBlockStmt(pos, nil))} } fn.Body = body fn.Endlineno = r.pos() }) r.marker.WriteTo(fn) } func (r *reader) funcargs(fn *ir.Func) { sig := fn.Nname.Type() if recv := sig.Recv(); recv != nil { r.funcarg(recv, recv.Sym, ir.PPARAM) } for _, param := range sig.Params().FieldSlice() { r.funcarg(param, param.Sym, ir.PPARAM) } for i, param := range sig.Results().FieldSlice() { sym := types.OrigSym(param.Sym) if sym == nil || sym.IsBlank() { prefix := "~r" if r.inlCall != nil { prefix = "~R" } else if sym != nil { prefix = "~b" } sym = typecheck.LookupNum(prefix, i) } r.funcarg(param, sym, ir.PPARAMOUT) } } func (r *reader) funcarg(param *types.Field, sym *types.Sym, ctxt ir.Class) { if sym == nil { assert(ctxt == ir.PPARAM) if r.inlCall != nil { r.inlvars.Append(ir.BlankNode) } return } name := ir.NewNameAt(r.updatePos(param.Pos), sym) r.setType(name, param.Type) r.addLocal(name, ctxt) if r.inlCall == nil { if !r.funarghack { param.Sym = sym param.Nname = name } } else { if ctxt == ir.PPARAMOUT { r.retvars.Append(name) } else { r.inlvars.Append(name) } } } func (r *reader) addLocal(name *ir.Name, ctxt ir.Class) { assert(ctxt == ir.PAUTO || ctxt == ir.PPARAM || ctxt == ir.PPARAMOUT) r.sync(syncAddLocal) if enableSync { want := r.int() if have := len(r.locals); have != want { base.FatalfAt(name.Pos(), "locals table has desynced") } } name.SetUsed(true) r.locals = append(r.locals, name) // TODO(mdempsky): Move earlier. if ir.IsBlank(name) { return } if r.inlCall != nil { if ctxt == ir.PAUTO { name.SetInlLocal(true) } else { name.SetInlFormal(true) ctxt = ir.PAUTO } // TODO(mdempsky): Rethink this hack. if strings.HasPrefix(name.Sym().Name, "~") || base.Flag.GenDwarfInl == 0 { name.SetPos(r.inlCall.Pos()) name.SetInlFormal(false) name.SetInlLocal(false) } } name.Class = ctxt name.Curfn = r.curfn r.curfn.Dcl = append(r.curfn.Dcl, name) if ctxt == ir.PAUTO { name.SetFrameOffset(0) } } func (r *reader) useLocal() *ir.Name { r.sync(syncUseObjLocal) if r.bool() { return r.locals[r.len()] } return r.closureVars[r.len()] } func (r *reader) openScope() { r.sync(syncOpenScope) pos := r.pos() if base.Flag.Dwarf { r.scopeVars = append(r.scopeVars, len(r.curfn.Dcl)) r.marker.Push(pos) } } func (r *reader) closeScope() { r.sync(syncCloseScope) r.lastCloseScopePos = r.pos() r.closeAnotherScope() } // closeAnotherScope is like closeScope, but it reuses the same mark // position as the last closeScope call. This is useful for "for" and // "if" statements, as their implicit blocks always end at the same // position as an explicit block. func (r *reader) closeAnotherScope() { r.sync(syncCloseAnotherScope) if base.Flag.Dwarf { scopeVars := r.scopeVars[len(r.scopeVars)-1] r.scopeVars = r.scopeVars[:len(r.scopeVars)-1] // Quirkish: noder decides which scopes to keep before // typechecking, whereas incremental typechecking during IR // construction can result in new autotemps being allocated. To // produce identical output, we ignore autotemps here for the // purpose of deciding whether to retract the scope. // // This is important for net/http/fcgi, because it contains: // // var body io.ReadCloser // if len(content) > 0 { // body, req.pw = io.Pipe() // } else { … } // // Notably, io.Pipe is inlinable, and inlining it introduces a ~R0 // variable at the call site. // // Noder does not preserve the scope where the io.Pipe() call // resides, because it doesn't contain any declared variables in // source. So the ~R0 variable ends up being assigned to the // enclosing scope instead. // // However, typechecking this assignment also introduces // autotemps, because io.Pipe's results need conversion before // they can be assigned to their respective destination variables. // // TODO(mdempsky): We should probably just keep all scopes, and // let dwarfgen take care of pruning them instead. retract := true for _, n := range r.curfn.Dcl[scopeVars:] { if !n.AutoTemp() { retract = false break } } if retract { // no variables were declared in this scope, so we can retract it. r.marker.Unpush() } else { r.marker.Pop(r.lastCloseScopePos) } } } // @@@ Statements func (r *reader) stmt() ir.Node { switch stmts := r.stmts(); len(stmts) { case 0: return nil case 1: return stmts[0] default: return ir.NewBlockStmt(stmts[0].Pos(), stmts) } } func (r *reader) stmts() []ir.Node { assert(ir.CurFunc == r.curfn) var res ir.Nodes r.sync(syncStmts) for { tag := codeStmt(r.code(syncStmt1)) if tag == stmtEnd { r.sync(syncStmtsEnd) return res } if n := r.stmt1(tag, &res); n != nil { res.Append(typecheck.Stmt(n)) } } } func (r *reader) stmt1(tag codeStmt, out *ir.Nodes) ir.Node { var label *types.Sym if n := len(*out); n > 0 { if ls, ok := (*out)[n-1].(*ir.LabelStmt); ok { label = ls.Label } } switch tag { default: panic("unexpected statement") case stmtAssign: pos := r.pos() // TODO(mdempsky): After quirks mode is gone, swap these // statements so we visit LHS before RHS again. rhs := r.exprList() names, lhs := r.assignList() if len(rhs) == 0 { for _, name := range names { as := ir.NewAssignStmt(pos, name, nil) as.PtrInit().Append(ir.NewDecl(pos, ir.ODCL, name)) out.Append(typecheck.Stmt(as)) } return nil } if len(lhs) == 1 && len(rhs) == 1 { n := ir.NewAssignStmt(pos, lhs[0], rhs[0]) n.Def = r.initDefn(n, names) return n } n := ir.NewAssignListStmt(pos, ir.OAS2, lhs, rhs) n.Def = r.initDefn(n, names) return n case stmtAssignOp: op := r.op() lhs := r.expr() pos := r.pos() rhs := r.expr() return ir.NewAssignOpStmt(pos, op, lhs, rhs) case stmtIncDec: op := r.op() lhs := r.expr() pos := r.pos() n := ir.NewAssignOpStmt(pos, op, lhs, ir.NewBasicLit(pos, one)) n.IncDec = true return n case stmtBlock: out.Append(r.blockStmt()...) return nil case stmtBranch: pos := r.pos() op := r.op() sym := r.optLabel() return ir.NewBranchStmt(pos, op, sym) case stmtCall: pos := r.pos() op := r.op() call := r.expr() return ir.NewGoDeferStmt(pos, op, call) case stmtExpr: return r.expr() case stmtFor: return r.forStmt(label) case stmtIf: return r.ifStmt() case stmtLabel: pos := r.pos() sym := r.label() return ir.NewLabelStmt(pos, sym) case stmtReturn: pos := r.pos() results := r.exprList() return ir.NewReturnStmt(pos, results) case stmtSelect: return r.selectStmt(label) case stmtSend: pos := r.pos() ch := r.expr() value := r.expr() return ir.NewSendStmt(pos, ch, value) case stmtSwitch: return r.switchStmt(label) case stmtTypeDeclHack: // fake "type _ = int" declaration to prevent inlining in quirks mode. assert(quirksMode()) name := ir.NewDeclNameAt(src.NoXPos, ir.OTYPE, ir.BlankNode.Sym()) name.SetAlias(true) r.setType(name, types.Types[types.TINT]) n := ir.NewDecl(src.NoXPos, ir.ODCLTYPE, name) n.SetTypecheck(1) return n } } func (r *reader) assignList() ([]*ir.Name, []ir.Node) { lhs := make([]ir.Node, r.len()) var names []*ir.Name for i := range lhs { if r.bool() { pos := r.pos() _, sym := r.localIdent() typ := r.typ() name := ir.NewNameAt(pos, sym) lhs[i] = name names = append(names, name) r.setType(name, typ) r.addLocal(name, ir.PAUTO) continue } lhs[i] = r.expr() } return names, lhs } func (r *reader) blockStmt() []ir.Node { r.sync(syncBlockStmt) r.openScope() stmts := r.stmts() r.closeScope() return stmts } func (r *reader) forStmt(label *types.Sym) ir.Node { r.sync(syncForStmt) r.openScope() if r.bool() { pos := r.pos() // TODO(mdempsky): After quirks mode is gone, swap these // statements so we read LHS before X again. x := r.expr() names, lhs := r.assignList() body := r.blockStmt() r.closeAnotherScope() rang := ir.NewRangeStmt(pos, nil, nil, x, body) if len(lhs) >= 1 { rang.Key = lhs[0] if len(lhs) >= 2 { rang.Value = lhs[1] } } rang.Def = r.initDefn(rang, names) rang.Label = label return rang } pos := r.pos() init := r.stmt() cond := r.expr() post := r.stmt() body := r.blockStmt() r.closeAnotherScope() stmt := ir.NewForStmt(pos, init, cond, post, body) stmt.Label = label return stmt } func (r *reader) ifStmt() ir.Node { r.sync(syncIfStmt) r.openScope() pos := r.pos() init := r.stmts() cond := r.expr() then := r.blockStmt() els := r.stmts() n := ir.NewIfStmt(pos, cond, then, els) n.SetInit(init) r.closeAnotherScope() return n } func (r *reader) selectStmt(label *types.Sym) ir.Node { r.sync(syncSelectStmt) pos := r.pos() clauses := make([]*ir.CommClause, r.len()) for i := range clauses { if i > 0 { r.closeScope() } r.openScope() pos := r.pos() comm := r.stmt() body := r.stmts() clauses[i] = ir.NewCommStmt(pos, comm, body) } if len(clauses) > 0 { r.closeScope() } n := ir.NewSelectStmt(pos, clauses) n.Label = label return n } func (r *reader) switchStmt(label *types.Sym) ir.Node { r.sync(syncSwitchStmt) r.openScope() pos := r.pos() init := r.stmt() var tag ir.Node if r.bool() { pos := r.pos() var ident *ir.Ident if r.bool() { pos := r.pos() sym := typecheck.Lookup(r.string()) ident = ir.NewIdent(pos, sym) } x := r.expr() tag = ir.NewTypeSwitchGuard(pos, ident, x) } else { tag = r.expr() } tswitch, ok := tag.(*ir.TypeSwitchGuard) if ok && tswitch.Tag == nil { tswitch = nil } clauses := make([]*ir.CaseClause, r.len()) for i := range clauses { if i > 0 { r.closeScope() } r.openScope() pos := r.pos() cases := r.exprList() clause := ir.NewCaseStmt(pos, cases, nil) if tswitch != nil { pos := r.pos() typ := r.typ() name := ir.NewNameAt(pos, tswitch.Tag.Sym()) r.setType(name, typ) r.addLocal(name, ir.PAUTO) clause.Var = name name.Defn = tswitch } clause.Body = r.stmts() clauses[i] = clause } if len(clauses) > 0 { r.closeScope() } r.closeScope() n := ir.NewSwitchStmt(pos, tag, clauses) n.Label = label if init != nil { n.SetInit([]ir.Node{init}) } return n } func (r *reader) label() *types.Sym { r.sync(syncLabel) name := r.string() if r.inlCall != nil { name = fmt.Sprintf("~%s·%d", name, inlgen) } return typecheck.Lookup(name) } func (r *reader) optLabel() *types.Sym { r.sync(syncOptLabel) if r.bool() { return r.label() } return nil } // initDefn marks the given names as declared by defn and populates // its Init field with ODCL nodes. It then reports whether any names // were so declared, which can be used to initialize defn.Def. func (r *reader) initDefn(defn ir.InitNode, names []*ir.Name) bool { if len(names) == 0 { return false } init := make([]ir.Node, len(names)) for i, name := range names { name.Defn = defn init[i] = ir.NewDecl(name.Pos(), ir.ODCL, name) } defn.SetInit(init) return true } // @@@ Expressions // expr reads and returns a typechecked expression. func (r *reader) expr() (res ir.Node) { defer func() { if res != nil && res.Typecheck() == 0 { base.FatalfAt(res.Pos(), "%v missed typecheck", res) } }() switch tag := codeExpr(r.code(syncExpr)); tag { default: panic("unhandled expression") case exprNone: return nil case exprBlank: // blank only allowed in LHS of assignments // TODO(mdempsky): Handle directly in assignList instead? return typecheck.AssignExpr(ir.BlankNode) case exprLocal: return typecheck.Expr(r.useLocal()) case exprName: // Callee instead of Expr allows builtins // TODO(mdempsky): Handle builtins directly in exprCall, like method calls? return typecheck.Callee(r.obj()) case exprType: // TODO(mdempsky): ir.TypeNode should probably return a typecheck'd node. n := ir.TypeNode(r.typ()) n.SetTypecheck(1) return n case exprConst: pos := r.pos() typ, val := r.value() op := r.op() orig := r.string() return typecheck.Expr(OrigConst(pos, typ, val, op, orig)) case exprCompLit: return r.compLit() case exprFuncLit: return r.funcLit() case exprSelector: x := r.expr() pos := r.pos() _, sym := r.selector() return typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, x, sym)) case exprIndex: x := r.expr() pos := r.pos() index := r.expr() return typecheck.Expr(ir.NewIndexExpr(pos, x, index)) case exprSlice: x := r.expr() pos := r.pos() var index [3]ir.Node for i := range index { index[i] = r.expr() } op := ir.OSLICE if index[2] != nil { op = ir.OSLICE3 } return typecheck.Expr(ir.NewSliceExpr(pos, op, x, index[0], index[1], index[2])) case exprAssert: x := r.expr() pos := r.pos() typ := r.expr().(ir.Ntype) return typecheck.Expr(ir.NewTypeAssertExpr(pos, x, typ)) case exprUnaryOp: op := r.op() pos := r.pos() x := r.expr() switch op { case ir.OADDR: return typecheck.Expr(typecheck.NodAddrAt(pos, x)) case ir.ODEREF: return typecheck.Expr(ir.NewStarExpr(pos, x)) } return typecheck.Expr(ir.NewUnaryExpr(pos, op, x)) case exprBinaryOp: op := r.op() x := r.expr() pos := r.pos() y := r.expr() switch op { case ir.OANDAND, ir.OOROR: return typecheck.Expr(ir.NewLogicalExpr(pos, op, x, y)) } return typecheck.Expr(ir.NewBinaryExpr(pos, op, x, y)) case exprCall: fun := r.expr() if r.bool() { // method call pos := r.pos() _, sym := r.selector() fun = typecheck.Callee(ir.NewSelectorExpr(pos, ir.OXDOT, fun, sym)) } pos := r.pos() args := r.exprs() dots := r.bool() return typecheck.Call(pos, fun, args, dots) case exprConvert: typ := r.typ() pos := r.pos() x := r.expr() return typecheck.Expr(ir.NewConvExpr(pos, ir.OCONV, typ, x)) } } func (r *reader) compLit() ir.Node { r.sync(syncCompLit) pos := r.pos() typ0 := r.typ() typ := typ0 if typ.IsPtr() { typ = typ.Elem() } if typ.Kind() == types.TFORW { base.FatalfAt(pos, "unresolved composite literal type: %v", typ) } isStruct := typ.Kind() == types.TSTRUCT elems := make([]ir.Node, r.len()) for i := range elems { elemp := &elems[i] if isStruct { sk := ir.NewStructKeyExpr(r.pos(), typ.Field(r.len()), nil) *elemp, elemp = sk, &sk.Value } else if r.bool() { kv := ir.NewKeyExpr(r.pos(), r.expr(), nil) *elemp, elemp = kv, &kv.Value } *elemp = wrapName(r.pos(), r.expr()) } lit := typecheck.Expr(ir.NewCompLitExpr(pos, ir.OCOMPLIT, ir.TypeNode(typ), elems)) if typ0.IsPtr() { lit = typecheck.Expr(typecheck.NodAddrAt(pos, lit)) lit.SetType(typ0) } return lit } func wrapName(pos src.XPos, x ir.Node) ir.Node { // These nodes do not carry line numbers. // Introduce a wrapper node to give them the correct line. switch ir.Orig(x).Op() { case ir.OTYPE, ir.OLITERAL: if x.Sym() == nil { break } fallthrough case ir.ONAME, ir.ONONAME, ir.OPACK, ir.ONIL: p := ir.NewParenExpr(pos, x) p.SetImplicit(true) return p } return x } func (r *reader) funcLit() ir.Node { r.sync(syncFuncLit) pos := r.pos() typPos := r.pos() xtype2 := r.signature(types.LocalPkg, nil) opos := pos if quirksMode() { opos = r.origPos(pos) } fn := ir.NewClosureFunc(opos, r.curfn != nil) clo := fn.OClosure ir.NameClosure(clo, r.curfn) r.setType(fn.Nname, xtype2) if quirksMode() { fn.Nname.Ntype = ir.TypeNodeAt(typPos, xtype2) } typecheck.Func(fn) r.setType(clo, fn.Type()) fn.ClosureVars = make([]*ir.Name, 0, r.len()) for len(fn.ClosureVars) < cap(fn.ClosureVars) { ir.NewClosureVar(r.pos(), fn, r.useLocal()) } r.addBody(fn) // TODO(mdempsky): Remove hard-coding of typecheck.Target. return ir.UseClosure(clo, typecheck.Target) } func (r *reader) exprList() []ir.Node { r.sync(syncExprList) return r.exprs() } func (r *reader) exprs() []ir.Node { r.sync(syncExprs) nodes := make([]ir.Node, r.len()) if len(nodes) == 0 { return nil // TODO(mdempsky): Unclear if this matters. } for i := range nodes { nodes[i] = r.expr() } return nodes } func (r *reader) op() ir.Op { r.sync(syncOp) return ir.Op(r.len()) } // @@@ Package initialization func (r *reader) pkgInit(self *types.Pkg, target *ir.Package) { if quirksMode() { for i, n := 0, r.len(); i < n; i++ { // Eagerly register position bases, so their filenames are // assigned stable indices. posBase := r.posBase() _ = base.Ctxt.PosTable.XPos(src.MakePos(posBase, 0, 0)) } for i, n := 0, r.len(); i < n; i++ { // Eagerly resolve imported objects, so any filenames registered // in the process are assigned stable indices too. _, sym := r.qualifiedIdent() typecheck.Resolve(ir.NewIdent(src.NoXPos, sym)) assert(sym.Def != nil) } } cgoPragmas := make([][]string, r.len()) for i := range cgoPragmas { cgoPragmas[i] = r.strings() } target.CgoPragmas = cgoPragmas r.pkgDecls(target) r.sync(syncEOF) } func (r *reader) pkgDecls(target *ir.Package) { r.sync(syncDecls) for { switch code := codeDecl(r.code(syncDecl)); code { default: panic(fmt.Sprintf("unhandled decl: %v", code)) case declEnd: return case declFunc: names := r.pkgObjs(target) assert(len(names) == 1) target.Decls = append(target.Decls, names[0].Func) case declMethod: typ := r.typ() _, sym := r.selector() method := typecheck.Lookdot1(nil, sym, typ, typ.Methods(), 0) target.Decls = append(target.Decls, method.Nname.(*ir.Name).Func) case declVar: pos := r.pos() names := r.pkgObjs(target) values := r.exprList() if len(names) > 1 && len(values) == 1 { as := ir.NewAssignListStmt(pos, ir.OAS2, nil, values) for _, name := range names { as.Lhs.Append(name) name.Defn = as } target.Decls = append(target.Decls, as) } else { for i, name := range names { as := ir.NewAssignStmt(pos, name, nil) if i < len(values) { as.Y = values[i] } name.Defn = as target.Decls = append(target.Decls, as) } } if n := r.len(); n > 0 { assert(len(names) == 1) embeds := make([]ir.Embed, n) for i := range embeds { embeds[i] = ir.Embed{Pos: r.pos(), Patterns: r.strings()} } names[0].Embed = &embeds target.Embeds = append(target.Embeds, names[0]) } case declOther: r.pkgObjs(target) } } } func (r *reader) pkgObjs(target *ir.Package) []*ir.Name { r.sync(syncDeclNames) nodes := make([]*ir.Name, r.len()) for i := range nodes { r.sync(syncDeclName) name := r.obj().(*ir.Name) nodes[i] = name sym := name.Sym() if sym.IsBlank() { continue } switch name.Class { default: base.FatalfAt(name.Pos(), "unexpected class: %v", name.Class) case ir.PEXTERN: target.Externs = append(target.Externs, name) case ir.PFUNC: assert(name.Type().Recv() == nil) // TODO(mdempsky): Cleaner way to recognize init? if strings.HasPrefix(sym.Name, "init.") { target.Inits = append(target.Inits, name.Func) } } if types.IsExported(sym.Name) { assert(!sym.OnExportList()) target.Exports = append(target.Exports, name) sym.SetOnExportList(true) } if base.Flag.AsmHdr != "" { assert(!sym.Asm()) target.Asms = append(target.Asms, name) sym.SetAsm(true) } } return nodes } // @@@ Inlining var inlgen = 0 func InlineCall(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExpr { // TODO(mdempsky): Turn callerfn into an explicit parameter. callerfn := ir.CurFunc pri, ok := bodyReader[fn] if !ok { // Assume it's an imported function or something that we don't // have access to in quirks mode. if haveLegacyImports { return nil } base.FatalfAt(call.Pos(), "missing function body for call to %v", fn) } if fn.Inl.Body == nil { expandInline(fn, pri) } r := pri.asReader(relocBody, syncFuncBody) // TODO(mdempsky): This still feels clumsy. Can we do better? tmpfn := ir.NewFunc(fn.Pos()) tmpfn.Nname = ir.NewNameAt(fn.Nname.Pos(), callerfn.Sym()) tmpfn.Closgen = callerfn.Closgen defer func() { callerfn.Closgen = tmpfn.Closgen }() r.setType(tmpfn.Nname, fn.Type()) r.curfn = tmpfn r.inlCaller = callerfn r.inlCall = call r.inlFunc = fn r.inlTreeIndex = inlIndex r.inlPosBases = make(map[*src.PosBase]*src.PosBase) r.closureVars = make([]*ir.Name, len(r.inlFunc.ClosureVars)) for i, cv := range r.inlFunc.ClosureVars { r.closureVars[i] = cv.Outer } r.funcargs(fn) assert(r.bool()) // have body r.delayResults = fn.Inl.CanDelayResults r.retlabel = typecheck.AutoLabel(".i") inlgen++ init := ir.TakeInit(call) // For normal function calls, the function callee expression // may contain side effects. Make sure to preserve these, // if necessary (#42703). if call.Op() == ir.OCALLFUNC { inline.CalleeEffects(&init, call.X) } var args ir.Nodes if call.Op() == ir.OCALLMETH { base.FatalfAt(call.Pos(), "OCALLMETH missed by typecheck") } args.Append(call.Args...) // Create assignment to declare and initialize inlvars. as2 := ir.NewAssignListStmt(call.Pos(), ir.OAS2, r.inlvars, args) as2.Def = true var as2init ir.Nodes for _, name := range r.inlvars { if ir.IsBlank(name) { continue } // TODO(mdempsky): Use inlined position of name.Pos() instead? name := name.(*ir.Name) as2init.Append(ir.NewDecl(call.Pos(), ir.ODCL, name)) name.Defn = as2 } as2.SetInit(as2init) init.Append(typecheck.Stmt(as2)) if !r.delayResults { // If not delaying retvars, declare and zero initialize the // result variables now. for _, name := range r.retvars { // TODO(mdempsky): Use inlined position of name.Pos() instead? name := name.(*ir.Name) init.Append(ir.NewDecl(call.Pos(), ir.ODCL, name)) ras := ir.NewAssignStmt(call.Pos(), name, nil) init.Append(typecheck.Stmt(ras)) } } // Add an inline mark just before the inlined body. // This mark is inline in the code so that it's a reasonable spot // to put a breakpoint. Not sure if that's really necessary or not // (in which case it could go at the end of the function instead). // Note issue 28603. init.Append(ir.NewInlineMarkStmt(call.Pos().WithIsStmt(), int64(r.inlTreeIndex))) nparams := len(r.curfn.Dcl) ir.WithFunc(r.curfn, func() { r.curfn.Body = r.stmts() r.curfn.Endlineno = r.pos() deadcode.Func(r.curfn) // Replace any "return" statements within the function body. var edit func(ir.Node) ir.Node edit = func(n ir.Node) ir.Node { if ret, ok := n.(*ir.ReturnStmt); ok { n = typecheck.Stmt(r.inlReturn(ret)) } ir.EditChildren(n, edit) return n } edit(r.curfn) }) body := ir.Nodes(r.curfn.Body) // Quirk: If deadcode elimination turned a non-empty function into // an empty one, we need to set the position for the empty block // left behind to the the inlined position for src.NoXPos, so that // an empty string gets added into the DWARF file name listing at // the appropriate index. if quirksMode() && len(body) == 1 { if block, ok := body[0].(*ir.BlockStmt); ok && len(block.List) == 0 { block.SetPos(r.updatePos(src.NoXPos)) } } // Quirkish: We need to eagerly prune variables added during // inlining, but removed by deadcode.FuncBody above. Unused // variables will get removed during stack frame layout anyway, but // len(fn.Dcl) ends up influencing things like autotmp naming. used := usedLocals(body) for i, name := range r.curfn.Dcl { if i < nparams || used.Has(name) { name.Curfn = callerfn callerfn.Dcl = append(callerfn.Dcl, name) // Quirkish. TODO(mdempsky): Document why. if name.AutoTemp() { name.SetEsc(ir.EscUnknown) if base.Flag.GenDwarfInl != 0 { name.SetInlLocal(true) } else { name.SetPos(r.inlCall.Pos()) } } } } body.Append(ir.NewLabelStmt(call.Pos(), r.retlabel)) res := ir.NewInlinedCallExpr(call.Pos(), body, append([]ir.Node(nil), r.retvars...)) res.SetInit(init) res.SetType(call.Type()) res.SetTypecheck(1) // Inlining shouldn't add any functions to todoBodies. assert(len(todoBodies) == 0) return res } // inlReturn returns a statement that can substitute for the given // return statement when inlining. func (r *reader) inlReturn(ret *ir.ReturnStmt) *ir.BlockStmt { pos := r.inlCall.Pos() block := ir.TakeInit(ret) if results := ret.Results; len(results) != 0 { assert(len(r.retvars) == len(results)) as2 := ir.NewAssignListStmt(pos, ir.OAS2, append([]ir.Node(nil), r.retvars...), ret.Results) if r.delayResults { for _, name := range r.retvars { // TODO(mdempsky): Use inlined position of name.Pos() instead? name := name.(*ir.Name) block.Append(ir.NewDecl(pos, ir.ODCL, name)) name.Defn = as2 } } block.Append(as2) } block.Append(ir.NewBranchStmt(pos, ir.OGOTO, r.retlabel)) return ir.NewBlockStmt(pos, block) } // expandInline reads in an extra copy of IR to populate // fn.Inl.{Dcl,Body}. func expandInline(fn *ir.Func, pri pkgReaderIndex) { // TODO(mdempsky): Remove this function. It's currently needed by // dwarfgen/dwarf.go:preInliningDcls, which requires fn.Inl.Dcl to // create abstract function DIEs. But we should be able to provide it // with the same information some other way. fndcls := len(fn.Dcl) topdcls := len(typecheck.Target.Decls) tmpfn := ir.NewFunc(fn.Pos()) tmpfn.Nname = ir.NewNameAt(fn.Nname.Pos(), fn.Sym()) tmpfn.ClosureVars = fn.ClosureVars { r := pri.asReader(relocBody, syncFuncBody) r.setType(tmpfn.Nname, fn.Type()) // Don't change parameter's Sym/Nname fields. r.funarghack = true r.funcBody(tmpfn) ir.WithFunc(tmpfn, func() { deadcode.Func(tmpfn) }) } used := usedLocals(tmpfn.Body) for _, name := range tmpfn.Dcl { if name.Class != ir.PAUTO || used.Has(name) { name.Curfn = fn fn.Inl.Dcl = append(fn.Inl.Dcl, name) } } fn.Inl.Body = tmpfn.Body // Double check that we didn't change fn.Dcl by accident. assert(fndcls == len(fn.Dcl)) // typecheck.Stmts may have added function literals to // typecheck.Target.Decls. Remove them again so we don't risk trying // to compile them multiple times. typecheck.Target.Decls = typecheck.Target.Decls[:topdcls] } // usedLocals returns a set of local variables that are used within body. func usedLocals(body []ir.Node) ir.NameSet { var used ir.NameSet ir.VisitList(body, func(n ir.Node) { if n, ok := n.(*ir.Name); ok && n.Op() == ir.ONAME && n.Class == ir.PAUTO { used.Add(n) } }) return used } // @@@ Method wrappers // needWrapperTypes lists types for which we may need to generate // method wrappers. var needWrapperTypes []*types.Type // haveWrapperTypes lists types for which we know we already have // method wrappers, because we found the type in an imported package. var haveWrapperTypes []*types.Type func (r *reader) needWrapper(typ *types.Type) *types.Type { if typ.IsPtr() { base.Fatalf("bad pointer type: %v", typ) } // If a type was found in an imported package, then we can assume // that package (or one of its transitive dependencies) already // generated method wrappers for it. // // Exception: If we're instantiating an imported generic type or // function, we might be instantiating it with type arguments not // previously seen before. // // TODO(mdempsky): Distinguish when a generic function or type was // instantiated in an imported package so that we can add types to // haveWrapperTypes instead. if r.p != localPkgReader && !r.hasTypeParams() { haveWrapperTypes = append(haveWrapperTypes, typ) } else { needWrapperTypes = append(needWrapperTypes, typ) } return typ } func (r *reader) wrapTypes(target *ir.Package) { // always generate a wrapper for error.Error (#29304) r.needWrapper(types.ErrorType) seen := make(map[string]*types.Type) addType := func(typ *types.Type) bool { if typ.Sym() != nil { return true } key := typ.LinkString() if prev := seen[key]; prev != nil { if !types.Identical(typ, prev) { base.Fatalf("collision: types %v and %v have short string %q", typ, prev, key) } return false } seen[key] = typ return true } for _, typ := range haveWrapperTypes { addType(typ) } haveWrapperTypes = nil for _, typ := range needWrapperTypes { if addType(typ) { r.wrapType(typ, target) } } needWrapperTypes = nil } func (r *reader) wrapType(typ *types.Type, target *ir.Package) { if !typ.IsInterface() { typecheck.CalcMethods(typ) } for _, meth := range typ.AllMethods().Slice() { if meth.Sym.IsBlank() || !meth.IsMethod() { base.FatalfAt(meth.Pos, "invalid method: %v", meth) } r.methodValueWrapper(typ, meth, target) r.methodWrapper(0, typ, meth, target) // For non-interface types, we also want *T wrappers. if !typ.IsInterface() { r.methodWrapper(1, typ, meth, target) // For not-in-heap types, *T is a scalar, not pointer shaped, // so the interface wrappers use **T. if typ.NotInHeap() { r.methodWrapper(2, typ, meth, target) } } } } func (r *reader) methodWrapper(derefs int, tbase *types.Type, method *types.Field, target *ir.Package) { wrapper := tbase for i := 0; i < derefs; i++ { wrapper = types.NewPtr(wrapper) } sym := ir.MethodSym(wrapper, method.Sym) assert(!sym.Siggen()) sym.SetSiggen(true) wrappee := method.Type.Recv().Type if types.Identical(wrapper, wrappee) || !types.IsMethodApplicable(wrapper, method) || !reflectdata.NeedEmit(tbase) { return } // TODO(mdempsky): Use method.Pos instead? pos := base.AutogeneratedPos fn := r.newWrapperFunc(pos, sym, wrapper, method) var recv ir.Node = fn.Nname.Type().Recv().Nname.(*ir.Name) // For simple *T wrappers around T methods, panicwrap produces a // nicer panic message. if wrapper.IsPtr() && types.Identical(wrapper.Elem(), wrappee) { cond := ir.NewBinaryExpr(pos, ir.OEQ, recv, types.BuiltinPkg.Lookup("nil").Def.(ir.Node)) then := []ir.Node{ir.NewCallExpr(pos, ir.OCALL, typecheck.LookupRuntime("panicwrap"), nil)} fn.Body.Append(ir.NewIfStmt(pos, cond, then, nil)) } // typecheck will add one implicit deref, if necessary, // but not-in-heap types require more for their **T wrappers. for i := 1; i < derefs; i++ { recv = Implicit(ir.NewStarExpr(pos, recv)) } addTailCall(pos, fn, recv, method) r.finishWrapperFunc(fn, target) } func (r *reader) methodValueWrapper(tbase *types.Type, method *types.Field, target *ir.Package) { recvType := tbase if !tbase.IsInterface() { recvType = method.Type.Recv().Type if !types.Identical(tbase, types.ReceiverBaseType(recvType)) { return } } sym := ir.MethodSymSuffix(recvType, method.Sym, "-fm") assert(!sym.Uniq()) sym.SetUniq(true) // TODO(mdempsky): Use method.Pos instead? pos := base.AutogeneratedPos fn := r.newWrapperFunc(pos, sym, nil, method) sym.Def = fn.Nname // Declare and initialize variable holding receiver. recv := ir.NewHiddenParam(pos, fn, typecheck.Lookup(".this"), recvType) if !reflectdata.NeedEmit(tbase) { typecheck.Func(fn) return } addTailCall(pos, fn, recv, method) r.finishWrapperFunc(fn, target) } func (r *reader) newWrapperFunc(pos src.XPos, sym *types.Sym, wrapper *types.Type, method *types.Field) *ir.Func { fn := ir.NewFunc(pos) fn.SetDupok(true) // TODO(mdempsky): Leave unset for local, non-generic wrappers? name := ir.NewNameAt(pos, sym) ir.MarkFunc(name) name.Func = fn name.Defn = fn fn.Nname = name sig := newWrapperType(wrapper, method) r.setType(name, sig) // TODO(mdempsky): De-duplicate with similar logic in funcargs. defParams := func(class ir.Class, params *types.Type) { for _, param := range params.FieldSlice() { name := ir.NewNameAt(param.Pos, param.Sym) name.Class = class r.setType(name, param.Type) name.Curfn = fn fn.Dcl = append(fn.Dcl, name) param.Nname = name } } defParams(ir.PPARAM, sig.Recvs()) defParams(ir.PPARAM, sig.Params()) defParams(ir.PPARAMOUT, sig.Results()) return fn } func (r *reader) finishWrapperFunc(fn *ir.Func, target *ir.Package) { typecheck.Func(fn) ir.WithFunc(fn, func() { typecheck.Stmts(fn.Body) }) target.Decls = append(target.Decls, fn) } // newWrapperType returns a copy of the given signature type, but with // the receiver parameter type substituted with recvType. // If recvType is nil, newWrapperType returns a signature // without a receiver parameter. func newWrapperType(recvType *types.Type, method *types.Field) *types.Type { clone := func(params []*types.Field) []*types.Field { res := make([]*types.Field, len(params)) for i, param := range params { sym := param.Sym if sym == nil || sym.Name == "_" { sym = typecheck.LookupNum(".anon", i) } res[i] = types.NewField(param.Pos, sym, param.Type) res[i].SetIsDDD(param.IsDDD()) } return res } sig := method.Type var recv *types.Field if recvType != nil { recv = types.NewField(sig.Recv().Pos, typecheck.Lookup(".this"), recvType) } params := clone(sig.Params().FieldSlice()) results := clone(sig.Results().FieldSlice()) return types.NewSignature(types.NoPkg, recv, nil, params, results) } func addTailCall(pos src.XPos, fn *ir.Func, recv ir.Node, method *types.Field) { sig := fn.Nname.Type() args := make([]ir.Node, sig.NumParams()) for i, param := range sig.Params().FieldSlice() { args[i] = param.Nname.(*ir.Name) } // TODO(mdempsky): Support creating OTAILCALL, when possible. See reflectdata.methodWrapper. // Not urgent though, because tail calls are currently incompatible with regabi anyway. fn.SetWrapper(true) // TODO(mdempsky): Leave unset for tail calls? dot := ir.NewSelectorExpr(pos, ir.OXDOT, recv, method.Sym) call := typecheck.Call(pos, dot, args, method.Type.IsVariadic()).(*ir.CallExpr) if method.Type.NumResults() == 0 { fn.Body.Append(call) return } ret := ir.NewReturnStmt(pos, nil) ret.Results = []ir.Node{call} fn.Body.Append(ret) }