Michael Pratt 342b495301 cmd/compile: add //go:uintptrkeepalive
This CL exports the existing ir.UintptrKeepAlive via the new directive
//go:uintptrkeepalive. This makes the compiler insert KeepAlives for
pointers converted to uintptr in calls, keeping them alive for the
duration of the call.

//go:uintptrkeepalive requires //go:nosplit, as stack growth can't
handle these arguments (it cannot know which are pointers). We currently
check this on the immediate function, but the actual restriction applies
to all transitive calls.

The existing //go:uintptrescapes is an extension of
//go:uintptrkeepalive which forces pointers to escape to the heap, thus
eliminating the stack growth issue.

This pragma is limited to the standard library.

For #51087

Change-Id: If9a19d484d3561b4219e5539b70c11a3cc09391e
Reviewed-on: https://go-review.googlesource.com/c/go/+/388095
Run-TryBot: Michael Pratt <mpratt@google.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2022-04-21 18:06:38 +00:00

1879 lines
40 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 (
"fmt"
"internal/pkgbits"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/syntax"
"cmd/compile/internal/types2"
)
type pkgWriter struct {
pkgbits.PkgEncoder
m posMap
curpkg *types2.Package
info *types2.Info
posBasesIdx map[*syntax.PosBase]int
pkgsIdx map[*types2.Package]int
typsIdx map[types2.Type]int
globalsIdx map[types2.Object]int
funDecls map[*types2.Func]*syntax.FuncDecl
typDecls map[*types2.TypeName]typeDeclGen
linknames map[types2.Object]string
cgoPragmas [][]string
}
func newPkgWriter(m posMap, pkg *types2.Package, info *types2.Info) *pkgWriter {
return &pkgWriter{
PkgEncoder: pkgbits.NewPkgEncoder(base.Debug.SyncFrames),
m: m,
curpkg: pkg,
info: info,
pkgsIdx: make(map[*types2.Package]int),
globalsIdx: make(map[types2.Object]int),
typsIdx: make(map[types2.Type]int),
posBasesIdx: make(map[*syntax.PosBase]int),
funDecls: make(map[*types2.Func]*syntax.FuncDecl),
typDecls: make(map[*types2.TypeName]typeDeclGen),
linknames: make(map[types2.Object]string),
}
}
func (pw *pkgWriter) errorf(p poser, msg string, args ...interface{}) {
base.ErrorfAt(pw.m.pos(p), msg, args...)
}
func (pw *pkgWriter) fatalf(p poser, msg string, args ...interface{}) {
base.FatalfAt(pw.m.pos(p), msg, args...)
}
func (pw *pkgWriter) unexpected(what string, p poser) {
pw.fatalf(p, "unexpected %s: %v (%T)", what, p, p)
}
type writer struct {
p *pkgWriter
pkgbits.Encoder
// TODO(mdempsky): We should be able to prune localsIdx whenever a
// scope closes, and then maybe we can just use the same map for
// storing the TypeParams too (as their TypeName instead).
// variables declared within this function
localsIdx map[*types2.Var]int
closureVars []posObj
closureVarsIdx map[*types2.Var]int
dict *writerDict
derived bool
}
// A writerDict tracks types and objects that are used by a declaration.
type writerDict struct {
implicits []*types2.TypeName
// derived is a slice of type indices for computing derived types
// (i.e., types that depend on the declaration's type parameters).
derived []derivedInfo
// derivedIdx maps a Type to its corresponding index within the
// derived slice, if present.
derivedIdx map[types2.Type]int
// funcs lists references to generic functions that were
// instantiated with derived types (i.e., that require
// sub-dictionaries when called at run time).
funcs []objInfo
// itabs lists itabs that are needed for dynamic type assertions
// (including type switches).
itabs []itabInfo
}
type derivedInfo struct {
idx int
needed bool
}
type typeInfo struct {
idx int
derived bool
}
type objInfo struct {
idx int // index for the generic function declaration
explicits []typeInfo // info for the type arguments
}
type itabInfo struct {
typIdx int // always a derived type index
iface typeInfo // always a non-empty interface type
}
func (info objInfo) anyDerived() bool {
for _, explicit := range info.explicits {
if explicit.derived {
return true
}
}
return false
}
func (info objInfo) equals(other objInfo) bool {
if info.idx != other.idx {
return false
}
assert(len(info.explicits) == len(other.explicits))
for i, targ := range info.explicits {
if targ != other.explicits[i] {
return false
}
}
return true
}
func (pw *pkgWriter) newWriter(k pkgbits.RelocKind, marker pkgbits.SyncMarker) *writer {
return &writer{
Encoder: pw.NewEncoder(k, marker),
p: pw,
}
}
// @@@ Positions
func (w *writer) pos(p poser) {
w.Sync(pkgbits.SyncPos)
pos := p.Pos()
// TODO(mdempsky): Track down the remaining cases here and fix them.
if !w.Bool(pos.IsKnown()) {
return
}
// TODO(mdempsky): Delta encoding. Also, if there's a b-side, update
// its position base too (but not vice versa!).
w.posBase(pos.Base())
w.Uint(pos.Line())
w.Uint(pos.Col())
}
func (w *writer) posBase(b *syntax.PosBase) {
w.Reloc(pkgbits.RelocPosBase, w.p.posBaseIdx(b))
}
func (pw *pkgWriter) posBaseIdx(b *syntax.PosBase) int {
if idx, ok := pw.posBasesIdx[b]; ok {
return idx
}
w := pw.newWriter(pkgbits.RelocPosBase, pkgbits.SyncPosBase)
w.p.posBasesIdx[b] = w.Idx
w.String(trimFilename(b))
if !w.Bool(b.IsFileBase()) {
w.pos(b)
w.Uint(b.Line())
w.Uint(b.Col())
}
return w.Flush()
}
// @@@ Packages
func (w *writer) pkg(pkg *types2.Package) {
w.Sync(pkgbits.SyncPkg)
w.Reloc(pkgbits.RelocPkg, w.p.pkgIdx(pkg))
}
func (pw *pkgWriter) pkgIdx(pkg *types2.Package) int {
if idx, ok := pw.pkgsIdx[pkg]; ok {
return idx
}
w := pw.newWriter(pkgbits.RelocPkg, pkgbits.SyncPkgDef)
pw.pkgsIdx[pkg] = w.Idx
if pkg == nil {
w.String("builtin")
} else {
var path string
if pkg != w.p.curpkg {
path = pkg.Path()
}
w.String(path)
w.String(pkg.Name())
w.Len(pkg.Height())
w.Len(len(pkg.Imports()))
for _, imp := range pkg.Imports() {
w.pkg(imp)
}
}
return w.Flush()
}
// @@@ Types
var anyTypeName = types2.Universe.Lookup("any").(*types2.TypeName)
func (w *writer) typ(typ types2.Type) {
w.typInfo(w.p.typIdx(typ, w.dict))
}
func (w *writer) typInfo(info typeInfo) {
w.Sync(pkgbits.SyncType)
if w.Bool(info.derived) {
w.Len(info.idx)
w.derived = true
} else {
w.Reloc(pkgbits.RelocType, info.idx)
}
}
// typIdx returns the index where the export data description of type
// can be read back in. If no such index exists yet, it's created.
//
// typIdx also reports whether typ is a derived type; that is, whether
// its identity depends on type parameters.
func (pw *pkgWriter) typIdx(typ types2.Type, dict *writerDict) typeInfo {
if idx, ok := pw.typsIdx[typ]; ok {
return typeInfo{idx: idx, derived: false}
}
if dict != nil {
if idx, ok := dict.derivedIdx[typ]; ok {
return typeInfo{idx: idx, derived: true}
}
}
w := pw.newWriter(pkgbits.RelocType, pkgbits.SyncTypeIdx)
w.dict = dict
switch typ := typ.(type) {
default:
base.Fatalf("unexpected type: %v (%T)", typ, typ)
case *types2.Basic:
switch kind := typ.Kind(); {
case kind == types2.Invalid:
base.Fatalf("unexpected types2.Invalid")
case types2.Typ[kind] == typ:
w.Code(pkgbits.TypeBasic)
w.Len(int(kind))
default:
// Handle "byte" and "rune" as references to their TypeName.
obj := types2.Universe.Lookup(typ.Name())
assert(obj.Type() == typ)
w.Code(pkgbits.TypeNamed)
w.obj(obj, nil)
}
case *types2.Named:
assert(typ.TypeParams().Len() == typ.TypeArgs().Len())
// TODO(mdempsky): Why do we need to loop here?
orig := typ
for orig.TypeArgs() != nil {
orig = orig.Origin()
}
w.Code(pkgbits.TypeNamed)
w.obj(orig.Obj(), typ.TypeArgs())
case *types2.TypeParam:
index := func() int {
for idx, name := range w.dict.implicits {
if name.Type().(*types2.TypeParam) == typ {
return idx
}
}
return len(w.dict.implicits) + typ.Index()
}()
w.derived = true
w.Code(pkgbits.TypeTypeParam)
w.Len(index)
case *types2.Array:
w.Code(pkgbits.TypeArray)
w.Uint64(uint64(typ.Len()))
w.typ(typ.Elem())
case *types2.Chan:
w.Code(pkgbits.TypeChan)
w.Len(int(typ.Dir()))
w.typ(typ.Elem())
case *types2.Map:
w.Code(pkgbits.TypeMap)
w.typ(typ.Key())
w.typ(typ.Elem())
case *types2.Pointer:
w.Code(pkgbits.TypePointer)
w.typ(typ.Elem())
case *types2.Signature:
base.Assertf(typ.TypeParams() == nil, "unexpected type params: %v", typ)
w.Code(pkgbits.TypeSignature)
w.signature(typ)
case *types2.Slice:
w.Code(pkgbits.TypeSlice)
w.typ(typ.Elem())
case *types2.Struct:
w.Code(pkgbits.TypeStruct)
w.structType(typ)
case *types2.Interface:
if typ == anyTypeName.Type() {
w.Code(pkgbits.TypeNamed)
w.obj(anyTypeName, nil)
break
}
w.Code(pkgbits.TypeInterface)
w.interfaceType(typ)
case *types2.Union:
w.Code(pkgbits.TypeUnion)
w.unionType(typ)
}
if w.derived {
idx := len(dict.derived)
dict.derived = append(dict.derived, derivedInfo{idx: w.Flush()})
dict.derivedIdx[typ] = idx
return typeInfo{idx: idx, derived: true}
}
pw.typsIdx[typ] = w.Idx
return typeInfo{idx: w.Flush(), derived: false}
}
func (w *writer) structType(typ *types2.Struct) {
w.Len(typ.NumFields())
for i := 0; i < typ.NumFields(); i++ {
f := typ.Field(i)
w.pos(f)
w.selector(f)
w.typ(f.Type())
w.String(typ.Tag(i))
w.Bool(f.Embedded())
}
}
func (w *writer) unionType(typ *types2.Union) {
w.Len(typ.Len())
for i := 0; i < typ.Len(); i++ {
t := typ.Term(i)
w.Bool(t.Tilde())
w.typ(t.Type())
}
}
func (w *writer) interfaceType(typ *types2.Interface) {
w.Len(typ.NumExplicitMethods())
w.Len(typ.NumEmbeddeds())
if typ.NumExplicitMethods() == 0 && typ.NumEmbeddeds() == 1 {
w.Bool(typ.IsImplicit())
} else {
// Implicit interfaces always have 0 explicit methods and 1
// embedded type, so we skip writing out the implicit flag
// otherwise as a space optimization.
assert(!typ.IsImplicit())
}
for i := 0; i < typ.NumExplicitMethods(); i++ {
m := typ.ExplicitMethod(i)
sig := m.Type().(*types2.Signature)
assert(sig.TypeParams() == nil)
w.pos(m)
w.selector(m)
w.signature(sig)
}
for i := 0; i < typ.NumEmbeddeds(); i++ {
w.typ(typ.EmbeddedType(i))
}
}
func (w *writer) signature(sig *types2.Signature) {
w.Sync(pkgbits.SyncSignature)
w.params(sig.Params())
w.params(sig.Results())
w.Bool(sig.Variadic())
}
func (w *writer) params(typ *types2.Tuple) {
w.Sync(pkgbits.SyncParams)
w.Len(typ.Len())
for i := 0; i < typ.Len(); i++ {
w.param(typ.At(i))
}
}
func (w *writer) param(param *types2.Var) {
w.Sync(pkgbits.SyncParam)
w.pos(param)
w.localIdent(param)
w.typ(param.Type())
}
// @@@ Objects
func (w *writer) obj(obj types2.Object, explicits *types2.TypeList) {
explicitInfos := make([]typeInfo, explicits.Len())
for i := range explicitInfos {
explicitInfos[i] = w.p.typIdx(explicits.At(i), w.dict)
}
info := objInfo{idx: w.p.objIdx(obj), explicits: explicitInfos}
if _, ok := obj.(*types2.Func); ok && info.anyDerived() {
idx := -1
for i, prev := range w.dict.funcs {
if prev.equals(info) {
idx = i
}
}
if idx < 0 {
idx = len(w.dict.funcs)
w.dict.funcs = append(w.dict.funcs, info)
}
// TODO(mdempsky): Push up into expr; this shouldn't appear
// outside of expression context.
w.Sync(pkgbits.SyncObject)
w.Bool(true)
w.Len(idx)
return
}
// TODO(mdempsky): Push up into typIdx; this shouldn't be needed
// except while writing out types.
if isDefinedType(obj) && obj.Pkg() == w.p.curpkg {
decl, ok := w.p.typDecls[obj.(*types2.TypeName)]
assert(ok)
if len(decl.implicits) != 0 {
w.derived = true
}
}
w.Sync(pkgbits.SyncObject)
w.Bool(false)
w.Reloc(pkgbits.RelocObj, info.idx)
w.Len(len(info.explicits))
for _, info := range info.explicits {
w.typInfo(info)
}
}
func (pw *pkgWriter) objIdx(obj types2.Object) int {
if idx, ok := pw.globalsIdx[obj]; ok {
return idx
}
dict := &writerDict{
derivedIdx: make(map[types2.Type]int),
}
if isDefinedType(obj) && obj.Pkg() == pw.curpkg {
decl, ok := pw.typDecls[obj.(*types2.TypeName)]
assert(ok)
dict.implicits = decl.implicits
}
w := pw.newWriter(pkgbits.RelocObj, pkgbits.SyncObject1)
wext := pw.newWriter(pkgbits.RelocObjExt, pkgbits.SyncObject1)
wname := pw.newWriter(pkgbits.RelocName, pkgbits.SyncObject1)
wdict := pw.newWriter(pkgbits.RelocObjDict, pkgbits.SyncObject1)
pw.globalsIdx[obj] = w.Idx // break cycles
assert(wext.Idx == w.Idx)
assert(wname.Idx == w.Idx)
assert(wdict.Idx == w.Idx)
w.dict = dict
wext.dict = dict
code := w.doObj(wext, obj)
w.Flush()
wext.Flush()
wname.qualifiedIdent(obj)
wname.Code(code)
wname.Flush()
wdict.objDict(obj, w.dict)
wdict.Flush()
return w.Idx
}
func (w *writer) doObj(wext *writer, obj types2.Object) pkgbits.CodeObj {
if obj.Pkg() != w.p.curpkg {
return pkgbits.ObjStub
}
switch obj := obj.(type) {
default:
w.p.unexpected("object", obj)
panic("unreachable")
case *types2.Const:
w.pos(obj)
w.typ(obj.Type())
w.Value(obj.Val())
return pkgbits.ObjConst
case *types2.Func:
decl, ok := w.p.funDecls[obj]
assert(ok)
sig := obj.Type().(*types2.Signature)
w.pos(obj)
w.typeParamNames(sig.TypeParams())
w.signature(sig)
w.pos(decl)
wext.funcExt(obj)
return pkgbits.ObjFunc
case *types2.TypeName:
decl, ok := w.p.typDecls[obj]
assert(ok)
if obj.IsAlias() {
w.pos(obj)
w.typ(obj.Type())
return pkgbits.ObjAlias
}
named := obj.Type().(*types2.Named)
assert(named.TypeArgs() == nil)
w.pos(obj)
w.typeParamNames(named.TypeParams())
wext.typeExt(obj)
w.typExpr(decl.Type)
w.Len(named.NumMethods())
for i := 0; i < named.NumMethods(); i++ {
w.method(wext, named.Method(i))
}
return pkgbits.ObjType
case *types2.Var:
w.pos(obj)
w.typ(obj.Type())
wext.varExt(obj)
return pkgbits.ObjVar
}
}
// typExpr writes the type represented by the given expression.
func (w *writer) typExpr(expr syntax.Expr) {
tv, ok := w.p.info.Types[expr]
assert(ok)
assert(tv.IsType())
w.typ(tv.Type)
}
// objDict writes the dictionary needed for reading the given object.
func (w *writer) objDict(obj types2.Object, dict *writerDict) {
// TODO(mdempsky): Split objDict into multiple entries? reader.go
// doesn't care about the type parameter bounds, and reader2.go
// doesn't care about referenced functions.
w.dict = dict // TODO(mdempsky): This is a bit sketchy.
w.Len(len(dict.implicits))
tparams := objTypeParams(obj)
ntparams := tparams.Len()
w.Len(ntparams)
for i := 0; i < ntparams; i++ {
w.typ(tparams.At(i).Constraint())
}
nderived := len(dict.derived)
w.Len(nderived)
for _, typ := range dict.derived {
w.Reloc(pkgbits.RelocType, typ.idx)
w.Bool(typ.needed)
}
nfuncs := len(dict.funcs)
w.Len(nfuncs)
for _, fn := range dict.funcs {
w.Reloc(pkgbits.RelocObj, fn.idx)
w.Len(len(fn.explicits))
for _, targ := range fn.explicits {
w.typInfo(targ)
}
}
nitabs := len(dict.itabs)
w.Len(nitabs)
for _, itab := range dict.itabs {
w.Len(itab.typIdx)
w.typInfo(itab.iface)
}
assert(len(dict.derived) == nderived)
assert(len(dict.funcs) == nfuncs)
}
func (w *writer) typeParamNames(tparams *types2.TypeParamList) {
w.Sync(pkgbits.SyncTypeParamNames)
ntparams := tparams.Len()
for i := 0; i < ntparams; i++ {
tparam := tparams.At(i).Obj()
w.pos(tparam)
w.localIdent(tparam)
}
}
func (w *writer) method(wext *writer, meth *types2.Func) {
decl, ok := w.p.funDecls[meth]
assert(ok)
sig := meth.Type().(*types2.Signature)
w.Sync(pkgbits.SyncMethod)
w.pos(meth)
w.selector(meth)
w.typeParamNames(sig.RecvTypeParams())
w.param(sig.Recv())
w.signature(sig)
w.pos(decl) // XXX: Hack to workaround linker limitations.
wext.funcExt(meth)
}
// qualifiedIdent writes out the name of an object declared at package
// scope. (For now, it's also used to refer to local defined types.)
func (w *writer) qualifiedIdent(obj types2.Object) {
w.Sync(pkgbits.SyncSym)
name := obj.Name()
if isDefinedType(obj) && obj.Pkg() == w.p.curpkg {
decl, ok := w.p.typDecls[obj.(*types2.TypeName)]
assert(ok)
if decl.gen != 0 {
// TODO(mdempsky): Find a better solution than embedding middle
// dot in the symbol name; this is terrible.
name = fmt.Sprintf("%s·%v", name, decl.gen)
}
}
w.pkg(obj.Pkg())
w.String(name)
}
// TODO(mdempsky): We should be able to omit pkg from both localIdent
// and selector, because they should always be known from context.
// However, past frustrations with this optimization in iexport make
// me a little nervous to try it again.
// localIdent writes the name of a locally declared object (i.e.,
// objects that can only be accessed by name, within the context of a
// particular function).
func (w *writer) localIdent(obj types2.Object) {
assert(!isGlobal(obj))
w.Sync(pkgbits.SyncLocalIdent)
w.pkg(obj.Pkg())
w.String(obj.Name())
}
// selector writes the name of a field or method (i.e., objects that
// can only be accessed using selector expressions).
func (w *writer) selector(obj types2.Object) {
w.Sync(pkgbits.SyncSelector)
w.pkg(obj.Pkg())
w.String(obj.Name())
}
// @@@ Compiler extensions
func (w *writer) funcExt(obj *types2.Func) {
decl, ok := w.p.funDecls[obj]
assert(ok)
// TODO(mdempsky): Extend these pragma validation flags to account
// for generics. E.g., linkname probably doesn't make sense at
// least.
pragma := asPragmaFlag(decl.Pragma)
if pragma&ir.Systemstack != 0 && pragma&ir.Nosplit != 0 {
w.p.errorf(decl, "go:nosplit and go:systemstack cannot be combined")
}
if decl.Body != nil {
if pragma&ir.Noescape != 0 {
w.p.errorf(decl, "can only use //go:noescape with external func implementations")
}
if (pragma&ir.UintptrKeepAlive != 0 && pragma&ir.UintptrEscapes == 0) && pragma&ir.Nosplit == 0 {
// Stack growth can't handle uintptr arguments that may
// be pointers (as we don't know which are pointers
// when creating the stack map). Thus uintptrkeepalive
// functions (and all transitive callees) must be
// nosplit.
//
// N.B. uintptrescapes implies uintptrkeepalive but it
// is OK since the arguments must escape to the heap.
//
// TODO(prattmic): Add recursive nosplit check of callees.
// TODO(prattmic): Functions with no body (i.e.,
// assembly) must also be nosplit, but we can't check
// that here.
w.p.errorf(decl, "go:uintptrkeepalive requires go:nosplit")
}
} else {
if base.Flag.Complete || decl.Name.Value == "init" {
// Linknamed functions are allowed to have no body. Hopefully
// the linkname target has a body. See issue 23311.
if _, ok := w.p.linknames[obj]; !ok {
w.p.errorf(decl, "missing function body")
}
}
}
sig, block := obj.Type().(*types2.Signature), decl.Body
body, closureVars := w.p.bodyIdx(w.p.curpkg, sig, block, w.dict)
assert(len(closureVars) == 0)
w.Sync(pkgbits.SyncFuncExt)
w.pragmaFlag(pragma)
w.linkname(obj)
w.Bool(false) // stub extension
w.Reloc(pkgbits.RelocBody, body)
w.Sync(pkgbits.SyncEOF)
}
func (w *writer) typeExt(obj *types2.TypeName) {
decl, ok := w.p.typDecls[obj]
assert(ok)
w.Sync(pkgbits.SyncTypeExt)
w.pragmaFlag(asPragmaFlag(decl.Pragma))
// No LSym.SymIdx info yet.
w.Int64(-1)
w.Int64(-1)
}
func (w *writer) varExt(obj *types2.Var) {
w.Sync(pkgbits.SyncVarExt)
w.linkname(obj)
}
func (w *writer) linkname(obj types2.Object) {
w.Sync(pkgbits.SyncLinkname)
w.Int64(-1)
w.String(w.p.linknames[obj])
}
func (w *writer) pragmaFlag(p ir.PragmaFlag) {
w.Sync(pkgbits.SyncPragma)
w.Int(int(p))
}
// @@@ Function bodies
func (pw *pkgWriter) bodyIdx(pkg *types2.Package, sig *types2.Signature, block *syntax.BlockStmt, dict *writerDict) (idx int, closureVars []posObj) {
w := pw.newWriter(pkgbits.RelocBody, pkgbits.SyncFuncBody)
w.dict = dict
w.funcargs(sig)
if w.Bool(block != nil) {
w.stmts(block.List)
w.pos(block.Rbrace)
}
return w.Flush(), w.closureVars
}
func (w *writer) funcargs(sig *types2.Signature) {
do := func(params *types2.Tuple, result bool) {
for i := 0; i < params.Len(); i++ {
w.funcarg(params.At(i), result)
}
}
if recv := sig.Recv(); recv != nil {
w.funcarg(recv, false)
}
do(sig.Params(), false)
do(sig.Results(), true)
}
func (w *writer) funcarg(param *types2.Var, result bool) {
if param.Name() != "" || result {
w.addLocal(param)
}
}
func (w *writer) addLocal(obj *types2.Var) {
w.Sync(pkgbits.SyncAddLocal)
idx := len(w.localsIdx)
if pkgbits.EnableSync {
w.Int(idx)
}
if w.localsIdx == nil {
w.localsIdx = make(map[*types2.Var]int)
}
w.localsIdx[obj] = idx
}
func (w *writer) useLocal(pos syntax.Pos, obj *types2.Var) {
w.Sync(pkgbits.SyncUseObjLocal)
if idx, ok := w.localsIdx[obj]; w.Bool(ok) {
w.Len(idx)
return
}
idx, ok := w.closureVarsIdx[obj]
if !ok {
if w.closureVarsIdx == nil {
w.closureVarsIdx = make(map[*types2.Var]int)
}
idx = len(w.closureVars)
w.closureVars = append(w.closureVars, posObj{pos, obj})
w.closureVarsIdx[obj] = idx
}
w.Len(idx)
}
func (w *writer) openScope(pos syntax.Pos) {
w.Sync(pkgbits.SyncOpenScope)
w.pos(pos)
}
func (w *writer) closeScope(pos syntax.Pos) {
w.Sync(pkgbits.SyncCloseScope)
w.pos(pos)
w.closeAnotherScope()
}
func (w *writer) closeAnotherScope() {
w.Sync(pkgbits.SyncCloseAnotherScope)
}
// @@@ Statements
func (w *writer) stmt(stmt syntax.Stmt) {
var stmts []syntax.Stmt
if stmt != nil {
stmts = []syntax.Stmt{stmt}
}
w.stmts(stmts)
}
func (w *writer) stmts(stmts []syntax.Stmt) {
w.Sync(pkgbits.SyncStmts)
for _, stmt := range stmts {
w.stmt1(stmt)
}
w.Code(stmtEnd)
w.Sync(pkgbits.SyncStmtsEnd)
}
func (w *writer) stmt1(stmt syntax.Stmt) {
switch stmt := stmt.(type) {
default:
w.p.unexpected("statement", stmt)
case nil, *syntax.EmptyStmt:
return
case *syntax.AssignStmt:
switch {
case stmt.Rhs == nil:
w.Code(stmtIncDec)
w.op(binOps[stmt.Op])
w.expr(stmt.Lhs)
w.pos(stmt)
case stmt.Op != 0 && stmt.Op != syntax.Def:
w.Code(stmtAssignOp)
w.op(binOps[stmt.Op])
w.expr(stmt.Lhs)
w.pos(stmt)
w.expr(stmt.Rhs)
default:
w.Code(stmtAssign)
w.pos(stmt)
w.exprList(stmt.Rhs)
w.assignList(stmt.Lhs)
}
case *syntax.BlockStmt:
w.Code(stmtBlock)
w.blockStmt(stmt)
case *syntax.BranchStmt:
w.Code(stmtBranch)
w.pos(stmt)
w.op(branchOps[stmt.Tok])
w.optLabel(stmt.Label)
case *syntax.CallStmt:
w.Code(stmtCall)
w.pos(stmt)
w.op(callOps[stmt.Tok])
w.expr(stmt.Call)
case *syntax.DeclStmt:
for _, decl := range stmt.DeclList {
w.declStmt(decl)
}
case *syntax.ExprStmt:
w.Code(stmtExpr)
w.expr(stmt.X)
case *syntax.ForStmt:
w.Code(stmtFor)
w.forStmt(stmt)
case *syntax.IfStmt:
w.Code(stmtIf)
w.ifStmt(stmt)
case *syntax.LabeledStmt:
w.Code(stmtLabel)
w.pos(stmt)
w.label(stmt.Label)
w.stmt1(stmt.Stmt)
case *syntax.ReturnStmt:
w.Code(stmtReturn)
w.pos(stmt)
w.exprList(stmt.Results)
case *syntax.SelectStmt:
w.Code(stmtSelect)
w.selectStmt(stmt)
case *syntax.SendStmt:
w.Code(stmtSend)
w.pos(stmt)
w.expr(stmt.Chan)
w.expr(stmt.Value)
case *syntax.SwitchStmt:
w.Code(stmtSwitch)
w.switchStmt(stmt)
}
}
func (w *writer) assignList(expr syntax.Expr) {
exprs := unpackListExpr(expr)
w.Len(len(exprs))
for _, expr := range exprs {
if name, ok := expr.(*syntax.Name); ok && name.Value != "_" {
if obj, ok := w.p.info.Defs[name]; ok {
obj := obj.(*types2.Var)
w.Bool(true)
w.pos(obj)
w.localIdent(obj)
w.typ(obj.Type())
// TODO(mdempsky): Minimize locals index size by deferring
// this until the variables actually come into scope.
w.addLocal(obj)
continue
}
}
w.Bool(false)
w.expr(expr)
}
}
func (w *writer) declStmt(decl syntax.Decl) {
switch decl := decl.(type) {
default:
w.p.unexpected("declaration", decl)
case *syntax.ConstDecl, *syntax.TypeDecl:
case *syntax.VarDecl:
w.Code(stmtAssign)
w.pos(decl)
w.exprList(decl.Values)
w.assignList(namesAsExpr(decl.NameList))
}
}
func (w *writer) blockStmt(stmt *syntax.BlockStmt) {
w.Sync(pkgbits.SyncBlockStmt)
w.openScope(stmt.Pos())
w.stmts(stmt.List)
w.closeScope(stmt.Rbrace)
}
func (w *writer) forStmt(stmt *syntax.ForStmt) {
w.Sync(pkgbits.SyncForStmt)
w.openScope(stmt.Pos())
if rang, ok := stmt.Init.(*syntax.RangeClause); w.Bool(ok) {
w.pos(rang)
w.expr(rang.X)
w.assignList(rang.Lhs)
} else {
w.pos(stmt)
w.stmt(stmt.Init)
w.expr(stmt.Cond)
w.stmt(stmt.Post)
}
w.blockStmt(stmt.Body)
w.closeAnotherScope()
}
func (w *writer) ifStmt(stmt *syntax.IfStmt) {
w.Sync(pkgbits.SyncIfStmt)
w.openScope(stmt.Pos())
w.pos(stmt)
w.stmt(stmt.Init)
w.expr(stmt.Cond)
w.blockStmt(stmt.Then)
w.stmt(stmt.Else)
w.closeAnotherScope()
}
func (w *writer) selectStmt(stmt *syntax.SelectStmt) {
w.Sync(pkgbits.SyncSelectStmt)
w.pos(stmt)
w.Len(len(stmt.Body))
for i, clause := range stmt.Body {
if i > 0 {
w.closeScope(clause.Pos())
}
w.openScope(clause.Pos())
w.pos(clause)
w.stmt(clause.Comm)
w.stmts(clause.Body)
}
if len(stmt.Body) > 0 {
w.closeScope(stmt.Rbrace)
}
}
func (w *writer) switchStmt(stmt *syntax.SwitchStmt) {
w.Sync(pkgbits.SyncSwitchStmt)
w.openScope(stmt.Pos())
w.pos(stmt)
w.stmt(stmt.Init)
var iface types2.Type
if guard, ok := stmt.Tag.(*syntax.TypeSwitchGuard); w.Bool(ok) {
tv, ok := w.p.info.Types[guard.X]
assert(ok && tv.IsValue())
iface = tv.Type
w.pos(guard)
if tag := guard.Lhs; w.Bool(tag != nil) {
w.pos(tag)
w.String(tag.Value)
}
w.expr(guard.X)
} else {
w.expr(stmt.Tag)
}
w.Len(len(stmt.Body))
for i, clause := range stmt.Body {
if i > 0 {
w.closeScope(clause.Pos())
}
w.openScope(clause.Pos())
w.pos(clause)
if iface != nil {
cases := unpackListExpr(clause.Cases)
w.Len(len(cases))
for _, cas := range cases {
w.exprType(iface, cas, true)
}
} else {
w.exprList(clause.Cases)
}
if obj, ok := w.p.info.Implicits[clause]; ok {
// TODO(mdempsky): These pos details are quirkish, but also
// necessary so the variable's position is correct for DWARF
// scope assignment later. It would probably be better for us to
// instead just set the variable's DWARF scoping info earlier so
// we can give it the correct position information.
pos := clause.Pos()
if typs := unpackListExpr(clause.Cases); len(typs) != 0 {
pos = typeExprEndPos(typs[len(typs)-1])
}
w.pos(pos)
obj := obj.(*types2.Var)
w.typ(obj.Type())
w.addLocal(obj)
}
w.stmts(clause.Body)
}
if len(stmt.Body) > 0 {
w.closeScope(stmt.Rbrace)
}
w.closeScope(stmt.Rbrace)
}
func (w *writer) label(label *syntax.Name) {
w.Sync(pkgbits.SyncLabel)
// TODO(mdempsky): Replace label strings with dense indices.
w.String(label.Value)
}
func (w *writer) optLabel(label *syntax.Name) {
w.Sync(pkgbits.SyncOptLabel)
if w.Bool(label != nil) {
w.label(label)
}
}
// @@@ Expressions
func (w *writer) expr(expr syntax.Expr) {
expr = unparen(expr) // skip parens; unneeded after typecheck
obj, inst := lookupObj(w.p.info, expr)
targs := inst.TypeArgs
if tv, ok := w.p.info.Types[expr]; ok {
// TODO(mdempsky): Be more judicious about which types are marked as "needed".
if inst.Type != nil {
w.needType(inst.Type)
} else {
w.needType(tv.Type)
}
if tv.IsType() {
w.Code(exprType)
w.exprType(nil, expr, false)
return
}
if tv.Value != nil {
w.Code(exprConst)
w.pos(expr)
w.typ(tv.Type)
w.Value(tv.Value)
// TODO(mdempsky): These details are only important for backend
// diagnostics. Explore writing them out separately.
w.op(constExprOp(expr))
w.String(syntax.String(expr))
return
}
}
if obj != nil {
if isGlobal(obj) {
w.Code(exprName)
w.obj(obj, targs)
return
}
obj := obj.(*types2.Var)
assert(!obj.IsField())
assert(targs.Len() == 0)
w.Code(exprLocal)
w.useLocal(expr.Pos(), obj)
return
}
switch expr := expr.(type) {
default:
w.p.unexpected("expression", expr)
case nil: // absent slice index, for condition, or switch tag
w.Code(exprNone)
case *syntax.Name:
assert(expr.Value == "_")
w.Code(exprBlank)
case *syntax.CompositeLit:
w.Code(exprCompLit)
w.compLit(expr)
case *syntax.FuncLit:
w.Code(exprFuncLit)
w.funcLit(expr)
case *syntax.SelectorExpr:
sel, ok := w.p.info.Selections[expr]
assert(ok)
w.Code(exprSelector)
w.expr(expr.X)
w.pos(expr)
w.selector(sel.Obj())
case *syntax.IndexExpr:
tv, ok := w.p.info.Types[expr.Index]
assert(ok && tv.IsValue())
w.Code(exprIndex)
w.expr(expr.X)
w.pos(expr)
w.expr(expr.Index)
case *syntax.SliceExpr:
w.Code(exprSlice)
w.expr(expr.X)
w.pos(expr)
for _, n := range &expr.Index {
w.expr(n)
}
case *syntax.AssertExpr:
tv, ok := w.p.info.Types[expr.X]
assert(ok && tv.IsValue())
w.Code(exprAssert)
w.expr(expr.X)
w.pos(expr)
w.exprType(tv.Type, expr.Type, false)
case *syntax.Operation:
if expr.Y == nil {
w.Code(exprUnaryOp)
w.op(unOps[expr.Op])
w.pos(expr)
w.expr(expr.X)
break
}
w.Code(exprBinaryOp)
w.op(binOps[expr.Op])
w.expr(expr.X)
w.pos(expr)
w.expr(expr.Y)
case *syntax.CallExpr:
tv, ok := w.p.info.Types[expr.Fun]
assert(ok)
if tv.IsType() {
assert(len(expr.ArgList) == 1)
assert(!expr.HasDots)
w.Code(exprConvert)
w.typ(tv.Type)
w.pos(expr)
w.expr(expr.ArgList[0])
break
}
writeFunExpr := func() {
if selector, ok := unparen(expr.Fun).(*syntax.SelectorExpr); ok {
if sel, ok := w.p.info.Selections[selector]; ok && sel.Kind() == types2.MethodVal {
w.expr(selector.X)
w.Bool(true) // method call
w.pos(selector)
w.selector(sel.Obj())
return
}
}
w.expr(expr.Fun)
w.Bool(false) // not a method call (i.e., normal function call)
}
w.Code(exprCall)
writeFunExpr()
w.pos(expr)
w.exprs(expr.ArgList)
w.Bool(expr.HasDots)
}
}
func (w *writer) compLit(lit *syntax.CompositeLit) {
tv, ok := w.p.info.Types[lit]
assert(ok)
w.Sync(pkgbits.SyncCompLit)
w.pos(lit)
w.typ(tv.Type)
typ := tv.Type
if ptr, ok := types2.CoreType(typ).(*types2.Pointer); ok {
typ = ptr.Elem()
}
str, isStruct := types2.CoreType(typ).(*types2.Struct)
w.Len(len(lit.ElemList))
for i, elem := range lit.ElemList {
if isStruct {
if kv, ok := elem.(*syntax.KeyValueExpr); ok {
// use position of expr.Key rather than of elem (which has position of ':')
w.pos(kv.Key)
w.Len(fieldIndex(w.p.info, str, kv.Key.(*syntax.Name)))
elem = kv.Value
} else {
w.pos(elem)
w.Len(i)
}
} else {
if kv, ok := elem.(*syntax.KeyValueExpr); w.Bool(ok) {
// use position of expr.Key rather than of elem (which has position of ':')
w.pos(kv.Key)
w.expr(kv.Key)
elem = kv.Value
}
}
w.pos(elem)
w.expr(elem)
}
}
func (w *writer) funcLit(expr *syntax.FuncLit) {
tv, ok := w.p.info.Types[expr]
assert(ok)
sig := tv.Type.(*types2.Signature)
body, closureVars := w.p.bodyIdx(w.p.curpkg, sig, expr.Body, w.dict)
w.Sync(pkgbits.SyncFuncLit)
w.pos(expr)
w.signature(sig)
w.Len(len(closureVars))
for _, cv := range closureVars {
w.pos(cv.pos)
w.useLocal(cv.pos, cv.obj)
}
w.Reloc(pkgbits.RelocBody, body)
}
type posObj struct {
pos syntax.Pos
obj *types2.Var
}
func (w *writer) exprList(expr syntax.Expr) {
w.Sync(pkgbits.SyncExprList)
w.exprs(unpackListExpr(expr))
}
func (w *writer) exprs(exprs []syntax.Expr) {
if len(exprs) == 0 {
assert(exprs == nil)
}
w.Sync(pkgbits.SyncExprs)
w.Len(len(exprs))
for _, expr := range exprs {
w.expr(expr)
}
}
func (w *writer) exprType(iface types2.Type, typ syntax.Expr, nilOK bool) {
base.Assertf(iface == nil || isInterface(iface), "%v must be nil or an interface type", iface)
tv, ok := w.p.info.Types[typ]
assert(ok)
w.Sync(pkgbits.SyncExprType)
if nilOK && w.Bool(tv.IsNil()) {
return
}
assert(tv.IsType())
info := w.p.typIdx(tv.Type, w.dict)
w.pos(typ)
if w.Bool(info.derived && iface != nil && !iface.Underlying().(*types2.Interface).Empty()) {
ifaceInfo := w.p.typIdx(iface, w.dict)
idx := -1
for i, itab := range w.dict.itabs {
if itab.typIdx == info.idx && itab.iface == ifaceInfo {
idx = i
}
}
if idx < 0 {
idx = len(w.dict.itabs)
w.dict.itabs = append(w.dict.itabs, itabInfo{typIdx: info.idx, iface: ifaceInfo})
}
w.Len(idx)
return
}
w.typInfo(info)
}
func isInterface(typ types2.Type) bool {
if _, ok := typ.(*types2.TypeParam); ok {
// typ is a type parameter and may be instantiated as either a
// concrete or interface type, so the writer can't depend on
// knowing this.
base.Fatalf("%v is a type parameter", typ)
}
_, ok := typ.Underlying().(*types2.Interface)
return ok
}
func (w *writer) op(op ir.Op) {
// TODO(mdempsky): Remove in favor of explicit codes? Would make
// export data more stable against internal refactorings, but low
// priority at the moment.
assert(op != 0)
w.Sync(pkgbits.SyncOp)
w.Len(int(op))
}
func (w *writer) needType(typ types2.Type) {
// Decompose tuple into component element types.
if typ, ok := typ.(*types2.Tuple); ok {
for i := 0; i < typ.Len(); i++ {
w.needType(typ.At(i).Type())
}
return
}
if info := w.p.typIdx(typ, w.dict); info.derived {
w.dict.derived[info.idx].needed = true
}
}
// @@@ Package initialization
// Caution: This code is still clumsy, because toolstash -cmp is
// particularly sensitive to it.
type typeDeclGen struct {
*syntax.TypeDecl
gen int
// Implicit type parameters in scope at this type declaration.
implicits []*types2.TypeName
}
type fileImports struct {
importedEmbed, importedUnsafe bool
}
type declCollector struct {
pw *pkgWriter
typegen *int
file *fileImports
withinFunc bool
implicits []*types2.TypeName
}
func (c *declCollector) withTParams(obj types2.Object) *declCollector {
tparams := objTypeParams(obj)
n := tparams.Len()
if n == 0 {
return c
}
copy := *c
copy.implicits = copy.implicits[:len(copy.implicits):len(copy.implicits)]
for i := 0; i < n; i++ {
copy.implicits = append(copy.implicits, tparams.At(i).Obj())
}
return &copy
}
func (c *declCollector) Visit(n syntax.Node) syntax.Visitor {
pw := c.pw
switch n := n.(type) {
case *syntax.File:
pw.checkPragmas(n.Pragma, ir.GoBuildPragma, false)
case *syntax.ImportDecl:
pw.checkPragmas(n.Pragma, 0, false)
switch pkgNameOf(pw.info, n).Imported().Path() {
case "embed":
c.file.importedEmbed = true
case "unsafe":
c.file.importedUnsafe = true
}
case *syntax.ConstDecl:
pw.checkPragmas(n.Pragma, 0, false)
case *syntax.FuncDecl:
pw.checkPragmas(n.Pragma, funcPragmas, false)
obj := pw.info.Defs[n.Name].(*types2.Func)
pw.funDecls[obj] = n
return c.withTParams(obj)
case *syntax.TypeDecl:
obj := pw.info.Defs[n.Name].(*types2.TypeName)
d := typeDeclGen{TypeDecl: n, implicits: c.implicits}
if n.Alias {
pw.checkPragmas(n.Pragma, 0, false)
} else {
pw.checkPragmas(n.Pragma, typePragmas, false)
// Assign a unique ID to function-scoped defined types.
if c.withinFunc {
*c.typegen++
d.gen = *c.typegen
}
}
pw.typDecls[obj] = d
// TODO(mdempsky): Omit? Not strictly necessary; only matters for
// type declarations within function literals within parameterized
// type declarations, but types2 the function literals will be
// constant folded away.
return c.withTParams(obj)
case *syntax.VarDecl:
pw.checkPragmas(n.Pragma, 0, true)
if p, ok := n.Pragma.(*pragmas); ok && len(p.Embeds) > 0 {
if err := checkEmbed(n, c.file.importedEmbed, c.withinFunc); err != nil {
pw.errorf(p.Embeds[0].Pos, "%s", err)
}
}
case *syntax.BlockStmt:
if !c.withinFunc {
copy := *c
copy.withinFunc = true
return &copy
}
}
return c
}
func (pw *pkgWriter) collectDecls(noders []*noder) {
var typegen int
for _, p := range noders {
var file fileImports
syntax.Walk(p.file, &declCollector{
pw: pw,
typegen: &typegen,
file: &file,
})
pw.cgoPragmas = append(pw.cgoPragmas, p.pragcgobuf...)
for _, l := range p.linknames {
if !file.importedUnsafe {
pw.errorf(l.pos, "//go:linkname only allowed in Go files that import \"unsafe\"")
continue
}
switch obj := pw.curpkg.Scope().Lookup(l.local).(type) {
case *types2.Func, *types2.Var:
if _, ok := pw.linknames[obj]; !ok {
pw.linknames[obj] = l.remote
} else {
pw.errorf(l.pos, "duplicate //go:linkname for %s", l.local)
}
default:
// TODO(mdempsky): Enable after #42938 is fixed.
if false {
pw.errorf(l.pos, "//go:linkname must refer to declared function or variable")
}
}
}
}
}
func (pw *pkgWriter) checkPragmas(p syntax.Pragma, allowed ir.PragmaFlag, embedOK bool) {
if p == nil {
return
}
pragma := p.(*pragmas)
for _, pos := range pragma.Pos {
if pos.Flag&^allowed != 0 {
pw.errorf(pos.Pos, "misplaced compiler directive")
}
}
if !embedOK {
for _, e := range pragma.Embeds {
pw.errorf(e.Pos, "misplaced go:embed directive")
}
}
}
func (w *writer) pkgInit(noders []*noder) {
w.Len(len(w.p.cgoPragmas))
for _, cgoPragma := range w.p.cgoPragmas {
w.Strings(cgoPragma)
}
w.Sync(pkgbits.SyncDecls)
for _, p := range noders {
for _, decl := range p.file.DeclList {
w.pkgDecl(decl)
}
}
w.Code(declEnd)
w.Sync(pkgbits.SyncEOF)
}
func (w *writer) pkgDecl(decl syntax.Decl) {
switch decl := decl.(type) {
default:
w.p.unexpected("declaration", decl)
case *syntax.ImportDecl:
case *syntax.ConstDecl:
w.Code(declOther)
w.pkgObjs(decl.NameList...)
case *syntax.FuncDecl:
if decl.Name.Value == "_" {
break // skip blank functions
}
obj := w.p.info.Defs[decl.Name].(*types2.Func)
sig := obj.Type().(*types2.Signature)
if sig.RecvTypeParams() != nil || sig.TypeParams() != nil {
break // skip generic functions
}
if recv := sig.Recv(); recv != nil {
w.Code(declMethod)
w.typ(recvBase(recv))
w.selector(obj)
break
}
w.Code(declFunc)
w.pkgObjs(decl.Name)
case *syntax.TypeDecl:
if len(decl.TParamList) != 0 {
break // skip generic type decls
}
if decl.Name.Value == "_" {
break // skip blank type decls
}
name := w.p.info.Defs[decl.Name].(*types2.TypeName)
// Skip type declarations for interfaces that are only usable as
// type parameter bounds.
if iface, ok := name.Type().Underlying().(*types2.Interface); ok && !iface.IsMethodSet() {
break
}
w.Code(declOther)
w.pkgObjs(decl.Name)
case *syntax.VarDecl:
w.Code(declVar)
w.pos(decl)
w.pkgObjs(decl.NameList...)
w.exprList(decl.Values)
var embeds []pragmaEmbed
if p, ok := decl.Pragma.(*pragmas); ok {
embeds = p.Embeds
}
w.Len(len(embeds))
for _, embed := range embeds {
w.pos(embed.Pos)
w.Strings(embed.Patterns)
}
}
}
func (w *writer) pkgObjs(names ...*syntax.Name) {
w.Sync(pkgbits.SyncDeclNames)
w.Len(len(names))
for _, name := range names {
obj, ok := w.p.info.Defs[name]
assert(ok)
w.Sync(pkgbits.SyncDeclName)
w.obj(obj, nil)
}
}
// @@@ Helpers
// isDefinedType reports whether obj is a defined type.
func isDefinedType(obj types2.Object) bool {
if obj, ok := obj.(*types2.TypeName); ok {
return !obj.IsAlias()
}
return false
}
// isGlobal reports whether obj was declared at package scope.
//
// Caveat: blank objects are not declared.
func isGlobal(obj types2.Object) bool {
return obj.Parent() == obj.Pkg().Scope()
}
// lookupObj returns the object that expr refers to, if any. If expr
// is an explicit instantiation of a generic object, then the instance
// object is returned as well.
func lookupObj(info *types2.Info, expr syntax.Expr) (obj types2.Object, inst types2.Instance) {
if index, ok := expr.(*syntax.IndexExpr); ok {
args := unpackListExpr(index.Index)
if len(args) == 1 {
tv, ok := info.Types[args[0]]
assert(ok)
if tv.IsValue() {
return // normal index expression
}
}
expr = index.X
}
// Strip package qualifier, if present.
if sel, ok := expr.(*syntax.SelectorExpr); ok {
if !isPkgQual(info, sel) {
return // normal selector expression
}
expr = sel.Sel
}
if name, ok := expr.(*syntax.Name); ok {
obj = info.Uses[name]
inst = info.Instances[name]
}
return
}
// isPkgQual reports whether the given selector expression is a
// package-qualified identifier.
func isPkgQual(info *types2.Info, sel *syntax.SelectorExpr) bool {
if name, ok := sel.X.(*syntax.Name); ok {
_, isPkgName := info.Uses[name].(*types2.PkgName)
return isPkgName
}
return false
}
// recvBase returns the base type for the given receiver parameter.
func recvBase(recv *types2.Var) *types2.Named {
typ := recv.Type()
if ptr, ok := typ.(*types2.Pointer); ok {
typ = ptr.Elem()
}
return typ.(*types2.Named)
}
// namesAsExpr returns a list of names as a syntax.Expr.
func namesAsExpr(names []*syntax.Name) syntax.Expr {
if len(names) == 1 {
return names[0]
}
exprs := make([]syntax.Expr, len(names))
for i, name := range names {
exprs[i] = name
}
return &syntax.ListExpr{ElemList: exprs}
}
// fieldIndex returns the index of the struct field named by key.
func fieldIndex(info *types2.Info, str *types2.Struct, key *syntax.Name) int {
field := info.Uses[key].(*types2.Var)
for i := 0; i < str.NumFields(); i++ {
if str.Field(i) == field {
return i
}
}
panic(fmt.Sprintf("%s: %v is not a field of %v", key.Pos(), field, str))
}
// objTypeParams returns the type parameters on the given object.
func objTypeParams(obj types2.Object) *types2.TypeParamList {
switch obj := obj.(type) {
case *types2.Func:
sig := obj.Type().(*types2.Signature)
if sig.Recv() != nil {
return sig.RecvTypeParams()
}
return sig.TypeParams()
case *types2.TypeName:
if !obj.IsAlias() {
return obj.Type().(*types2.Named).TypeParams()
}
}
return nil
}
func asPragmaFlag(p syntax.Pragma) ir.PragmaFlag {
if p == nil {
return 0
}
return p.(*pragmas).Flag
}