mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
The code for generating method value wrappers is weird that it sets sym.Def to the generated ir.Func, whereas normally sym.Def points to ir.Name. While here, change methodValueWrapper to return the ir.Name too, since that's what the caller wants. Change-Id: I3da5320ca0bf4d32d7b420345454f19075d19a26 Reviewed-on: https://go-review.googlesource.com/c/go/+/339410 Trust: Matthew Dempsky <mdempsky@google.com> Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com> Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2363 lines
53 KiB
Go
2363 lines
53 KiB
Go
// 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
|
|
|
|
fn := r.string()
|
|
absfn := r.string()
|
|
|
|
if r.bool() {
|
|
b = src.NewFileBase(fn, absfn)
|
|
} else {
|
|
pos := r.pos0()
|
|
line := r.uint()
|
|
col := r.uint()
|
|
b = src.NewLinePragmaBase(pos, fn, absfn, 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 : [<T> I <T>]
|
|
// - 0 -> 1 -> 2 : [func(I) I <T>]
|
|
// - 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
|
|
|
|
func (r *reader) needWrapper(typ *types.Type) *types.Type {
|
|
// TODO(mdempsky): Be more judicious about generating wrappers.
|
|
// For now, generating all possible wrappers is simple and correct,
|
|
// but potentially wastes a lot of time/space.
|
|
|
|
if typ.IsPtr() {
|
|
base.Fatalf("bad pointer type: %v", typ)
|
|
}
|
|
|
|
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)
|
|
for _, typ := range needWrapperTypes {
|
|
if typ.Sym() == nil {
|
|
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)
|
|
}
|
|
continue
|
|
}
|
|
seen[key] = 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): Fix typecheck to not depend on creation of
|
|
// imported method value wrappers.
|
|
if false && !reflectdata.NeedEmit(tbase) {
|
|
return
|
|
}
|
|
|
|
// 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)
|
|
|
|
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)
|
|
}
|