mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
Similar to the previous CL, the only two users of NodeQueue only needed it for tracking objects, not arbitrary AST nodes. So change it's signature to use *Name instead of Node. This does require a tweak to the nowritebarrierrec checker, because previously it was pushing the ODCLFUNC *Func pointers into the queue, whereas now we push the ONAME/PFUNC *Name pointers instead. However, it's trivial and safe to flip between them. Also, this changes a handful of export-related code from Node to *Name, to avoid introducing type assertions within iexport.go. Passes buildall w/ toolstash -cmp. Updates #42982. Change-Id: I867f9752121509fc3da753978c6a41d5015bc0ce Reviewed-on: https://go-review.googlesource.com/c/go/+/275753 Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com> Trust: Matthew Dempsky <mdempsky@google.com> Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Go Bot <gobot@golang.org>
1000 lines
25 KiB
Go
1000 lines
25 KiB
Go
// Copyright 2009 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 gc
|
|
|
|
import (
|
|
"bytes"
|
|
"cmd/compile/internal/base"
|
|
"cmd/compile/internal/ir"
|
|
"cmd/compile/internal/types"
|
|
"cmd/internal/obj"
|
|
"cmd/internal/src"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// Declaration stack & operations
|
|
|
|
var externdcl []ir.Node
|
|
|
|
func testdclstack() {
|
|
if !types.IsDclstackValid() {
|
|
base.Fatalf("mark left on the dclstack")
|
|
}
|
|
}
|
|
|
|
// redeclare emits a diagnostic about symbol s being redeclared at pos.
|
|
func redeclare(pos src.XPos, s *types.Sym, where string) {
|
|
if !s.Lastlineno.IsKnown() {
|
|
pkg := s.Origpkg
|
|
if pkg == nil {
|
|
pkg = s.Pkg
|
|
}
|
|
base.ErrorfAt(pos, "%v redeclared %s\n"+
|
|
"\tprevious declaration during import %q", s, where, pkg.Path)
|
|
} else {
|
|
prevPos := s.Lastlineno
|
|
|
|
// When an import and a declaration collide in separate files,
|
|
// present the import as the "redeclared", because the declaration
|
|
// is visible where the import is, but not vice versa.
|
|
// See issue 4510.
|
|
if s.Def == nil {
|
|
pos, prevPos = prevPos, pos
|
|
}
|
|
|
|
base.ErrorfAt(pos, "%v redeclared %s\n"+
|
|
"\tprevious declaration at %v", s, where, base.FmtPos(prevPos))
|
|
}
|
|
}
|
|
|
|
var vargen int
|
|
|
|
// declare individual names - var, typ, const
|
|
|
|
var declare_typegen int
|
|
|
|
// declare records that Node n declares symbol n.Sym in the specified
|
|
// declaration context.
|
|
func declare(n *ir.Name, ctxt ir.Class) {
|
|
if ir.IsBlank(n) {
|
|
return
|
|
}
|
|
|
|
s := n.Sym()
|
|
|
|
// kludgy: typecheckok means we're past parsing. Eg genwrapper may declare out of package names later.
|
|
if !inimport && !typecheckok && s.Pkg != ir.LocalPkg {
|
|
base.ErrorfAt(n.Pos(), "cannot declare name %v", s)
|
|
}
|
|
|
|
gen := 0
|
|
if ctxt == ir.PEXTERN {
|
|
if s.Name == "init" {
|
|
base.ErrorfAt(n.Pos(), "cannot declare init - must be func")
|
|
}
|
|
if s.Name == "main" && s.Pkg.Name == "main" {
|
|
base.ErrorfAt(n.Pos(), "cannot declare main - must be func")
|
|
}
|
|
externdcl = append(externdcl, n)
|
|
} else {
|
|
if Curfn == nil && ctxt == ir.PAUTO {
|
|
base.Pos = n.Pos()
|
|
base.Fatalf("automatic outside function")
|
|
}
|
|
if Curfn != nil && ctxt != ir.PFUNC && n.Op() == ir.ONAME {
|
|
Curfn.Dcl = append(Curfn.Dcl, n)
|
|
}
|
|
if n.Op() == ir.OTYPE {
|
|
declare_typegen++
|
|
gen = declare_typegen
|
|
} else if n.Op() == ir.ONAME && ctxt == ir.PAUTO && !strings.Contains(s.Name, "ยท") {
|
|
vargen++
|
|
gen = vargen
|
|
}
|
|
types.Pushdcl(s)
|
|
n.Curfn = Curfn
|
|
}
|
|
|
|
if ctxt == ir.PAUTO {
|
|
n.SetOffset(0)
|
|
}
|
|
|
|
if s.Block == types.Block {
|
|
// functype will print errors about duplicate function arguments.
|
|
// Don't repeat the error here.
|
|
if ctxt != ir.PPARAM && ctxt != ir.PPARAMOUT {
|
|
redeclare(n.Pos(), s, "in this block")
|
|
}
|
|
}
|
|
|
|
s.Block = types.Block
|
|
s.Lastlineno = base.Pos
|
|
s.Def = n
|
|
n.Vargen = int32(gen)
|
|
n.SetClass(ctxt)
|
|
if ctxt == ir.PFUNC {
|
|
n.Sym().SetFunc(true)
|
|
}
|
|
|
|
autoexport(n, ctxt)
|
|
}
|
|
|
|
// declare variables from grammar
|
|
// new_name_list (type | [type] = expr_list)
|
|
func variter(vl []ir.Node, t ir.Ntype, el []ir.Node) []ir.Node {
|
|
var init []ir.Node
|
|
doexpr := len(el) > 0
|
|
|
|
if len(el) == 1 && len(vl) > 1 {
|
|
e := el[0]
|
|
as2 := ir.Nod(ir.OAS2, nil, nil)
|
|
as2.PtrList().Set(vl)
|
|
as2.PtrRlist().Set1(e)
|
|
for _, v := range vl {
|
|
v := v.(*ir.Name)
|
|
v.SetOp(ir.ONAME)
|
|
declare(v, dclcontext)
|
|
v.Ntype = t
|
|
v.Defn = as2
|
|
if Curfn != nil {
|
|
init = append(init, ir.Nod(ir.ODCL, v, nil))
|
|
}
|
|
}
|
|
|
|
return append(init, as2)
|
|
}
|
|
|
|
nel := len(el)
|
|
for _, v := range vl {
|
|
v := v.(*ir.Name)
|
|
var e ir.Node
|
|
if doexpr {
|
|
if len(el) == 0 {
|
|
base.Errorf("assignment mismatch: %d variables but %d values", len(vl), nel)
|
|
break
|
|
}
|
|
e = el[0]
|
|
el = el[1:]
|
|
}
|
|
|
|
v.SetOp(ir.ONAME)
|
|
declare(v, dclcontext)
|
|
v.Ntype = t
|
|
|
|
if e != nil || Curfn != nil || ir.IsBlank(v) {
|
|
if Curfn != nil {
|
|
init = append(init, ir.Nod(ir.ODCL, v, nil))
|
|
}
|
|
e = ir.Nod(ir.OAS, v, e)
|
|
init = append(init, e)
|
|
if e.Right() != nil {
|
|
v.Defn = e
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(el) != 0 {
|
|
base.Errorf("assignment mismatch: %d variables but %d values", len(vl), nel)
|
|
}
|
|
return init
|
|
}
|
|
|
|
// newFuncNameAt generates a new name node for a function or method.
|
|
func newFuncNameAt(pos src.XPos, s *types.Sym, fn *ir.Func) *ir.Name {
|
|
if fn.Nname != nil {
|
|
base.Fatalf("newFuncName - already have name")
|
|
}
|
|
n := ir.NewNameAt(pos, s)
|
|
n.SetFunc(fn)
|
|
fn.Nname = n
|
|
return n
|
|
}
|
|
|
|
func anonfield(typ *types.Type) *ir.Field {
|
|
return symfield(nil, typ)
|
|
}
|
|
|
|
func namedfield(s string, typ *types.Type) *ir.Field {
|
|
return symfield(lookup(s), typ)
|
|
}
|
|
|
|
func symfield(s *types.Sym, typ *types.Type) *ir.Field {
|
|
return ir.NewField(base.Pos, s, nil, typ)
|
|
}
|
|
|
|
// oldname returns the Node that declares symbol s in the current scope.
|
|
// If no such Node currently exists, an ONONAME Node is returned instead.
|
|
// Automatically creates a new closure variable if the referenced symbol was
|
|
// declared in a different (containing) function.
|
|
func oldname(s *types.Sym) ir.Node {
|
|
n := ir.AsNode(s.Def)
|
|
if n == nil {
|
|
// Maybe a top-level declaration will come along later to
|
|
// define s. resolve will check s.Def again once all input
|
|
// source has been processed.
|
|
return ir.NewDeclNameAt(base.Pos, s)
|
|
}
|
|
|
|
if Curfn != nil && n.Op() == ir.ONAME && n.Name().Curfn != nil && n.Name().Curfn != Curfn {
|
|
// Inner func is referring to var in outer func.
|
|
//
|
|
// TODO(rsc): If there is an outer variable x and we
|
|
// are parsing x := 5 inside the closure, until we get to
|
|
// the := it looks like a reference to the outer x so we'll
|
|
// make x a closure variable unnecessarily.
|
|
c := n.Name().Innermost
|
|
if c == nil || c.Curfn != Curfn {
|
|
// Do not have a closure var for the active closure yet; make one.
|
|
c = NewName(s)
|
|
c.SetClass(ir.PAUTOHEAP)
|
|
c.SetIsClosureVar(true)
|
|
c.SetIsDDD(n.IsDDD())
|
|
c.Defn = n
|
|
|
|
// Link into list of active closure variables.
|
|
// Popped from list in func funcLit.
|
|
c.Outer = n.Name().Innermost
|
|
n.Name().Innermost = c
|
|
|
|
Curfn.ClosureVars = append(Curfn.ClosureVars, c)
|
|
}
|
|
|
|
// return ref to closure var, not original
|
|
return c
|
|
}
|
|
|
|
return n
|
|
}
|
|
|
|
// importName is like oldname,
|
|
// but it reports an error if sym is from another package and not exported.
|
|
func importName(sym *types.Sym) ir.Node {
|
|
n := oldname(sym)
|
|
if !types.IsExported(sym.Name) && sym.Pkg != ir.LocalPkg {
|
|
n.SetDiag(true)
|
|
base.Errorf("cannot refer to unexported name %s.%s", sym.Pkg.Name, sym.Name)
|
|
}
|
|
return n
|
|
}
|
|
|
|
// := declarations
|
|
func colasname(n ir.Node) bool {
|
|
switch n.Op() {
|
|
case ir.ONAME,
|
|
ir.ONONAME,
|
|
ir.OPACK,
|
|
ir.OTYPE,
|
|
ir.OLITERAL:
|
|
return n.Sym() != nil
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func colasdefn(left []ir.Node, defn ir.Node) {
|
|
for _, n := range left {
|
|
if n.Sym() != nil {
|
|
n.Sym().SetUniq(true)
|
|
}
|
|
}
|
|
|
|
var nnew, nerr int
|
|
for i, n := range left {
|
|
if ir.IsBlank(n) {
|
|
continue
|
|
}
|
|
if !colasname(n) {
|
|
base.ErrorfAt(defn.Pos(), "non-name %v on left side of :=", n)
|
|
nerr++
|
|
continue
|
|
}
|
|
|
|
if !n.Sym().Uniq() {
|
|
base.ErrorfAt(defn.Pos(), "%v repeated on left side of :=", n.Sym())
|
|
n.SetDiag(true)
|
|
nerr++
|
|
continue
|
|
}
|
|
|
|
n.Sym().SetUniq(false)
|
|
if n.Sym().Block == types.Block {
|
|
continue
|
|
}
|
|
|
|
nnew++
|
|
n := NewName(n.Sym())
|
|
declare(n, dclcontext)
|
|
n.Defn = defn
|
|
defn.PtrInit().Append(ir.Nod(ir.ODCL, n, nil))
|
|
left[i] = n
|
|
}
|
|
|
|
if nnew == 0 && nerr == 0 {
|
|
base.ErrorfAt(defn.Pos(), "no new variables on left side of :=")
|
|
}
|
|
}
|
|
|
|
// declare the arguments in an
|
|
// interface field declaration.
|
|
func ifacedcl(n *ir.Field) {
|
|
if n.Sym == nil {
|
|
base.Fatalf("ifacedcl")
|
|
}
|
|
|
|
if n.Sym.IsBlank() {
|
|
base.Errorf("methods must have a unique non-blank name")
|
|
}
|
|
}
|
|
|
|
// declare the function proper
|
|
// and declare the arguments.
|
|
// called in extern-declaration context
|
|
// returns in auto-declaration context.
|
|
func funchdr(fn *ir.Func) {
|
|
// change the declaration context from extern to auto
|
|
funcStack = append(funcStack, funcStackEnt{Curfn, dclcontext})
|
|
Curfn = fn
|
|
dclcontext = ir.PAUTO
|
|
|
|
types.Markdcl()
|
|
|
|
if fn.Nname.Ntype != nil {
|
|
funcargs(fn.Nname.Ntype.(*ir.FuncType))
|
|
} else {
|
|
funcargs2(fn.Type())
|
|
}
|
|
}
|
|
|
|
func funcargs(nt *ir.FuncType) {
|
|
if nt.Op() != ir.OTFUNC {
|
|
base.Fatalf("funcargs %v", nt.Op())
|
|
}
|
|
|
|
// re-start the variable generation number
|
|
// we want to use small numbers for the return variables,
|
|
// so let them have the chunk starting at 1.
|
|
//
|
|
// TODO(mdempsky): This is ugly, and only necessary because
|
|
// esc.go uses Vargen to figure out result parameters' index
|
|
// within the result tuple.
|
|
vargen = len(nt.Results)
|
|
|
|
// declare the receiver and in arguments.
|
|
if nt.Recv != nil {
|
|
funcarg(nt.Recv, ir.PPARAM)
|
|
}
|
|
for _, n := range nt.Params {
|
|
funcarg(n, ir.PPARAM)
|
|
}
|
|
|
|
oldvargen := vargen
|
|
vargen = 0
|
|
|
|
// declare the out arguments.
|
|
gen := len(nt.Params)
|
|
for _, n := range nt.Results {
|
|
if n.Sym == nil {
|
|
// Name so that escape analysis can track it. ~r stands for 'result'.
|
|
n.Sym = lookupN("~r", gen)
|
|
gen++
|
|
}
|
|
if n.Sym.IsBlank() {
|
|
// Give it a name so we can assign to it during return. ~b stands for 'blank'.
|
|
// The name must be different from ~r above because if you have
|
|
// func f() (_ int)
|
|
// func g() int
|
|
// f is allowed to use a plain 'return' with no arguments, while g is not.
|
|
// So the two cases must be distinguished.
|
|
n.Sym = lookupN("~b", gen)
|
|
gen++
|
|
}
|
|
|
|
funcarg(n, ir.PPARAMOUT)
|
|
}
|
|
|
|
vargen = oldvargen
|
|
}
|
|
|
|
func funcarg(n *ir.Field, ctxt ir.Class) {
|
|
if n.Sym == nil {
|
|
return
|
|
}
|
|
|
|
name := ir.NewNameAt(n.Pos, n.Sym)
|
|
n.Decl = name
|
|
name.Ntype = n.Ntype
|
|
name.SetIsDDD(n.IsDDD)
|
|
declare(name, ctxt)
|
|
|
|
vargen++
|
|
n.Decl.Vargen = int32(vargen)
|
|
}
|
|
|
|
// Same as funcargs, except run over an already constructed TFUNC.
|
|
// This happens during import, where the hidden_fndcl rule has
|
|
// used functype directly to parse the function's type.
|
|
func funcargs2(t *types.Type) {
|
|
if t.Kind() != types.TFUNC {
|
|
base.Fatalf("funcargs2 %v", t)
|
|
}
|
|
|
|
for _, f := range t.Recvs().Fields().Slice() {
|
|
funcarg2(f, ir.PPARAM)
|
|
}
|
|
for _, f := range t.Params().Fields().Slice() {
|
|
funcarg2(f, ir.PPARAM)
|
|
}
|
|
for _, f := range t.Results().Fields().Slice() {
|
|
funcarg2(f, ir.PPARAMOUT)
|
|
}
|
|
}
|
|
|
|
func funcarg2(f *types.Field, ctxt ir.Class) {
|
|
if f.Sym == nil {
|
|
return
|
|
}
|
|
n := ir.NewNameAt(f.Pos, f.Sym)
|
|
f.Nname = n
|
|
n.SetType(f.Type)
|
|
n.SetIsDDD(f.IsDDD())
|
|
declare(n, ctxt)
|
|
}
|
|
|
|
var funcStack []funcStackEnt // stack of previous values of Curfn/dclcontext
|
|
|
|
type funcStackEnt struct {
|
|
curfn *ir.Func
|
|
dclcontext ir.Class
|
|
}
|
|
|
|
// finish the body.
|
|
// called in auto-declaration context.
|
|
// returns in extern-declaration context.
|
|
func funcbody() {
|
|
// change the declaration context from auto to previous context
|
|
types.Popdcl()
|
|
var e funcStackEnt
|
|
funcStack, e = funcStack[:len(funcStack)-1], funcStack[len(funcStack)-1]
|
|
Curfn, dclcontext = e.curfn, e.dclcontext
|
|
}
|
|
|
|
// structs, functions, and methods.
|
|
// they don't belong here, but where do they belong?
|
|
func checkembeddedtype(t *types.Type) {
|
|
if t == nil {
|
|
return
|
|
}
|
|
|
|
if t.Sym() == nil && t.IsPtr() {
|
|
t = t.Elem()
|
|
if t.IsInterface() {
|
|
base.Errorf("embedded type cannot be a pointer to interface")
|
|
}
|
|
}
|
|
|
|
if t.IsPtr() || t.IsUnsafePtr() {
|
|
base.Errorf("embedded type cannot be a pointer")
|
|
} else if t.Kind() == types.TFORW && !t.ForwardType().Embedlineno.IsKnown() {
|
|
t.ForwardType().Embedlineno = base.Pos
|
|
}
|
|
}
|
|
|
|
// checkdupfields emits errors for duplicately named fields or methods in
|
|
// a list of struct or interface types.
|
|
func checkdupfields(what string, fss ...[]*types.Field) {
|
|
seen := make(map[*types.Sym]bool)
|
|
for _, fs := range fss {
|
|
for _, f := range fs {
|
|
if f.Sym == nil || f.Sym.IsBlank() {
|
|
continue
|
|
}
|
|
if seen[f.Sym] {
|
|
base.ErrorfAt(f.Pos, "duplicate %s %s", what, f.Sym.Name)
|
|
continue
|
|
}
|
|
seen[f.Sym] = true
|
|
}
|
|
}
|
|
}
|
|
|
|
// convert a parsed id/type list into
|
|
// a type for struct/interface/arglist
|
|
func tostruct(l []*ir.Field) *types.Type {
|
|
lno := base.Pos
|
|
|
|
fields := make([]*types.Field, len(l))
|
|
for i, n := range l {
|
|
base.Pos = n.Pos
|
|
|
|
if n.Ntype != nil {
|
|
n.Type = typecheckNtype(n.Ntype).Type()
|
|
n.Ntype = nil
|
|
}
|
|
f := types.NewField(n.Pos, n.Sym, n.Type)
|
|
if n.Embedded {
|
|
checkembeddedtype(n.Type)
|
|
f.Embedded = 1
|
|
}
|
|
f.Note = n.Note
|
|
fields[i] = f
|
|
}
|
|
checkdupfields("field", fields)
|
|
|
|
base.Pos = lno
|
|
return types.NewStruct(ir.LocalPkg, fields)
|
|
}
|
|
|
|
func tointerface(nmethods []*ir.Field) *types.Type {
|
|
if len(nmethods) == 0 {
|
|
return types.Types[types.TINTER]
|
|
}
|
|
|
|
lno := base.Pos
|
|
|
|
methods := make([]*types.Field, len(nmethods))
|
|
for i, n := range nmethods {
|
|
base.Pos = n.Pos
|
|
if n.Ntype != nil {
|
|
n.Type = typecheckNtype(n.Ntype).Type()
|
|
n.Ntype = nil
|
|
}
|
|
methods[i] = types.NewField(n.Pos, n.Sym, n.Type)
|
|
}
|
|
|
|
base.Pos = lno
|
|
return types.NewInterface(ir.LocalPkg, methods)
|
|
}
|
|
|
|
func fakeRecv() *ir.Field {
|
|
return anonfield(types.FakeRecvType())
|
|
}
|
|
|
|
func fakeRecvField() *types.Field {
|
|
return types.NewField(src.NoXPos, nil, types.FakeRecvType())
|
|
}
|
|
|
|
// isifacemethod reports whether (field) m is
|
|
// an interface method. Such methods have the
|
|
// special receiver type types.FakeRecvType().
|
|
func isifacemethod(f *types.Type) bool {
|
|
return f.Recv().Type == types.FakeRecvType()
|
|
}
|
|
|
|
// turn a parsed function declaration into a type
|
|
func functype(nrecv *ir.Field, nparams, nresults []*ir.Field) *types.Type {
|
|
funarg := func(n *ir.Field) *types.Field {
|
|
lno := base.Pos
|
|
base.Pos = n.Pos
|
|
|
|
if n.Ntype != nil {
|
|
n.Type = typecheckNtype(n.Ntype).Type()
|
|
n.Ntype = nil
|
|
}
|
|
|
|
f := types.NewField(n.Pos, n.Sym, n.Type)
|
|
f.SetIsDDD(n.IsDDD)
|
|
if n.Decl != nil {
|
|
n.Decl.SetType(f.Type)
|
|
f.Nname = n.Decl
|
|
}
|
|
|
|
base.Pos = lno
|
|
return f
|
|
}
|
|
funargs := func(nn []*ir.Field) []*types.Field {
|
|
res := make([]*types.Field, len(nn))
|
|
for i, n := range nn {
|
|
res[i] = funarg(n)
|
|
}
|
|
return res
|
|
}
|
|
|
|
var recv *types.Field
|
|
if nrecv != nil {
|
|
recv = funarg(nrecv)
|
|
}
|
|
|
|
t := types.NewSignature(ir.LocalPkg, recv, funargs(nparams), funargs(nresults))
|
|
checkdupfields("argument", t.Recvs().FieldSlice(), t.Params().FieldSlice(), t.Results().FieldSlice())
|
|
return t
|
|
}
|
|
|
|
func hasNamedResults(fn *ir.Func) bool {
|
|
typ := fn.Type()
|
|
return typ.NumResults() > 0 && ir.OrigSym(typ.Results().Field(0).Sym) != nil
|
|
}
|
|
|
|
// methodSym returns the method symbol representing a method name
|
|
// associated with a specific receiver type.
|
|
//
|
|
// Method symbols can be used to distinguish the same method appearing
|
|
// in different method sets. For example, T.M and (*T).M have distinct
|
|
// method symbols.
|
|
//
|
|
// The returned symbol will be marked as a function.
|
|
func methodSym(recv *types.Type, msym *types.Sym) *types.Sym {
|
|
sym := methodSymSuffix(recv, msym, "")
|
|
sym.SetFunc(true)
|
|
return sym
|
|
}
|
|
|
|
// methodSymSuffix is like methodsym, but allows attaching a
|
|
// distinguisher suffix. To avoid collisions, the suffix must not
|
|
// start with a letter, number, or period.
|
|
func methodSymSuffix(recv *types.Type, msym *types.Sym, suffix string) *types.Sym {
|
|
if msym.IsBlank() {
|
|
base.Fatalf("blank method name")
|
|
}
|
|
|
|
rsym := recv.Sym()
|
|
if recv.IsPtr() {
|
|
if rsym != nil {
|
|
base.Fatalf("declared pointer receiver type: %v", recv)
|
|
}
|
|
rsym = recv.Elem().Sym()
|
|
}
|
|
|
|
// Find the package the receiver type appeared in. For
|
|
// anonymous receiver types (i.e., anonymous structs with
|
|
// embedded fields), use the "go" pseudo-package instead.
|
|
rpkg := gopkg
|
|
if rsym != nil {
|
|
rpkg = rsym.Pkg
|
|
}
|
|
|
|
var b bytes.Buffer
|
|
if recv.IsPtr() {
|
|
// The parentheses aren't really necessary, but
|
|
// they're pretty traditional at this point.
|
|
fmt.Fprintf(&b, "(%-S)", recv)
|
|
} else {
|
|
fmt.Fprintf(&b, "%-S", recv)
|
|
}
|
|
|
|
// A particular receiver type may have multiple non-exported
|
|
// methods with the same name. To disambiguate them, include a
|
|
// package qualifier for names that came from a different
|
|
// package than the receiver type.
|
|
if !types.IsExported(msym.Name) && msym.Pkg != rpkg {
|
|
b.WriteString(".")
|
|
b.WriteString(msym.Pkg.Prefix)
|
|
}
|
|
|
|
b.WriteString(".")
|
|
b.WriteString(msym.Name)
|
|
b.WriteString(suffix)
|
|
|
|
return rpkg.LookupBytes(b.Bytes())
|
|
}
|
|
|
|
// Add a method, declared as a function.
|
|
// - msym is the method symbol
|
|
// - t is function type (with receiver)
|
|
// Returns a pointer to the existing or added Field; or nil if there's an error.
|
|
func addmethod(n *ir.Func, msym *types.Sym, t *types.Type, local, nointerface bool) *types.Field {
|
|
if msym == nil {
|
|
base.Fatalf("no method symbol")
|
|
}
|
|
|
|
// get parent type sym
|
|
rf := t.Recv() // ptr to this structure
|
|
if rf == nil {
|
|
base.Errorf("missing receiver")
|
|
return nil
|
|
}
|
|
|
|
mt := methtype(rf.Type)
|
|
if mt == nil || mt.Sym() == nil {
|
|
pa := rf.Type
|
|
t := pa
|
|
if t != nil && t.IsPtr() {
|
|
if t.Sym() != nil {
|
|
base.Errorf("invalid receiver type %v (%v is a pointer type)", pa, t)
|
|
return nil
|
|
}
|
|
t = t.Elem()
|
|
}
|
|
|
|
switch {
|
|
case t == nil || t.Broke():
|
|
// rely on typecheck having complained before
|
|
case t.Sym() == nil:
|
|
base.Errorf("invalid receiver type %v (%v is not a defined type)", pa, t)
|
|
case t.IsPtr():
|
|
base.Errorf("invalid receiver type %v (%v is a pointer type)", pa, t)
|
|
case t.IsInterface():
|
|
base.Errorf("invalid receiver type %v (%v is an interface type)", pa, t)
|
|
default:
|
|
// Should have picked off all the reasons above,
|
|
// but just in case, fall back to generic error.
|
|
base.Errorf("invalid receiver type %v (%L / %L)", pa, pa, t)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
if local && mt.Sym().Pkg != ir.LocalPkg {
|
|
base.Errorf("cannot define new methods on non-local type %v", mt)
|
|
return nil
|
|
}
|
|
|
|
if msym.IsBlank() {
|
|
return nil
|
|
}
|
|
|
|
if mt.IsStruct() {
|
|
for _, f := range mt.Fields().Slice() {
|
|
if f.Sym == msym {
|
|
base.Errorf("type %v has both field and method named %v", mt, msym)
|
|
f.SetBroke(true)
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, f := range mt.Methods().Slice() {
|
|
if msym.Name != f.Sym.Name {
|
|
continue
|
|
}
|
|
// types.Identical only checks that incoming and result parameters match,
|
|
// so explicitly check that the receiver parameters match too.
|
|
if !types.Identical(t, f.Type) || !types.Identical(t.Recv().Type, f.Type.Recv().Type) {
|
|
base.Errorf("method redeclared: %v.%v\n\t%v\n\t%v", mt, msym, f.Type, t)
|
|
}
|
|
return f
|
|
}
|
|
|
|
f := types.NewField(base.Pos, msym, t)
|
|
f.Nname = n.Nname
|
|
f.SetNointerface(nointerface)
|
|
|
|
mt.Methods().Append(f)
|
|
return f
|
|
}
|
|
|
|
func funcsymname(s *types.Sym) string {
|
|
return s.Name + "ยทf"
|
|
}
|
|
|
|
// funcsym returns sยทf.
|
|
func funcsym(s *types.Sym) *types.Sym {
|
|
// funcsymsmu here serves to protect not just mutations of funcsyms (below),
|
|
// but also the package lookup of the func sym name,
|
|
// since this function gets called concurrently from the backend.
|
|
// There are no other concurrent package lookups in the backend,
|
|
// except for the types package, which is protected separately.
|
|
// Reusing funcsymsmu to also cover this package lookup
|
|
// avoids a general, broader, expensive package lookup mutex.
|
|
// Note makefuncsym also does package look-up of func sym names,
|
|
// but that it is only called serially, from the front end.
|
|
funcsymsmu.Lock()
|
|
sf, existed := s.Pkg.LookupOK(funcsymname(s))
|
|
// Don't export sยทf when compiling for dynamic linking.
|
|
// When dynamically linking, the necessary function
|
|
// symbols will be created explicitly with makefuncsym.
|
|
// See the makefuncsym comment for details.
|
|
if !base.Ctxt.Flag_dynlink && !existed {
|
|
funcsyms = append(funcsyms, s)
|
|
}
|
|
funcsymsmu.Unlock()
|
|
return sf
|
|
}
|
|
|
|
// makefuncsym ensures that sยทf is exported.
|
|
// It is only used with -dynlink.
|
|
// When not compiling for dynamic linking,
|
|
// the funcsyms are created as needed by
|
|
// the packages that use them.
|
|
// Normally we emit the sยทf stubs as DUPOK syms,
|
|
// but DUPOK doesn't work across shared library boundaries.
|
|
// So instead, when dynamic linking, we only create
|
|
// the sยทf stubs in s's package.
|
|
func makefuncsym(s *types.Sym) {
|
|
if !base.Ctxt.Flag_dynlink {
|
|
base.Fatalf("makefuncsym dynlink")
|
|
}
|
|
if s.IsBlank() {
|
|
return
|
|
}
|
|
if base.Flag.CompilingRuntime && (s.Name == "getg" || s.Name == "getclosureptr" || s.Name == "getcallerpc" || s.Name == "getcallersp") {
|
|
// runtime.getg(), getclosureptr(), getcallerpc(), and
|
|
// getcallersp() are not real functions and so do not
|
|
// get funcsyms.
|
|
return
|
|
}
|
|
if _, existed := s.Pkg.LookupOK(funcsymname(s)); !existed {
|
|
funcsyms = append(funcsyms, s)
|
|
}
|
|
}
|
|
|
|
// setNodeNameFunc marks a node as a function.
|
|
func setNodeNameFunc(n ir.Node) {
|
|
if n.Op() != ir.ONAME || n.Class() != ir.Pxxx {
|
|
base.Fatalf("expected ONAME/Pxxx node, got %v", n)
|
|
}
|
|
|
|
n.SetClass(ir.PFUNC)
|
|
n.Sym().SetFunc(true)
|
|
}
|
|
|
|
func dclfunc(sym *types.Sym, tfn ir.Ntype) *ir.Func {
|
|
if tfn.Op() != ir.OTFUNC {
|
|
base.Fatalf("expected OTFUNC node, got %v", tfn)
|
|
}
|
|
|
|
fn := ir.NewFunc(base.Pos)
|
|
fn.Nname = newFuncNameAt(base.Pos, sym, fn)
|
|
fn.Nname.Defn = fn
|
|
fn.Nname.Ntype = tfn
|
|
setNodeNameFunc(fn.Nname)
|
|
funchdr(fn)
|
|
fn.Nname.Ntype = typecheckNtype(fn.Nname.Ntype)
|
|
return fn
|
|
}
|
|
|
|
type nowritebarrierrecChecker struct {
|
|
// extraCalls contains extra function calls that may not be
|
|
// visible during later analysis. It maps from the ODCLFUNC of
|
|
// the caller to a list of callees.
|
|
extraCalls map[ir.Node][]nowritebarrierrecCall
|
|
|
|
// curfn is the current function during AST walks.
|
|
curfn *ir.Func
|
|
}
|
|
|
|
type nowritebarrierrecCall struct {
|
|
target *ir.Func // caller or callee
|
|
lineno src.XPos // line of call
|
|
}
|
|
|
|
// newNowritebarrierrecChecker creates a nowritebarrierrecChecker. It
|
|
// must be called before transformclosure and walk.
|
|
func newNowritebarrierrecChecker() *nowritebarrierrecChecker {
|
|
c := &nowritebarrierrecChecker{
|
|
extraCalls: make(map[ir.Node][]nowritebarrierrecCall),
|
|
}
|
|
|
|
// Find all systemstack calls and record their targets. In
|
|
// general, flow analysis can't see into systemstack, but it's
|
|
// important to handle it for this check, so we model it
|
|
// directly. This has to happen before transformclosure since
|
|
// it's a lot harder to work out the argument after.
|
|
for _, n := range xtop {
|
|
if n.Op() != ir.ODCLFUNC {
|
|
continue
|
|
}
|
|
c.curfn = n.(*ir.Func)
|
|
ir.Inspect(n, c.findExtraCalls)
|
|
}
|
|
c.curfn = nil
|
|
return c
|
|
}
|
|
|
|
func (c *nowritebarrierrecChecker) findExtraCalls(n ir.Node) bool {
|
|
if n.Op() != ir.OCALLFUNC {
|
|
return true
|
|
}
|
|
fn := n.Left()
|
|
if fn == nil || fn.Op() != ir.ONAME || fn.Class() != ir.PFUNC || fn.Name().Defn == nil {
|
|
return true
|
|
}
|
|
if !isRuntimePkg(fn.Sym().Pkg) || fn.Sym().Name != "systemstack" {
|
|
return true
|
|
}
|
|
|
|
var callee *ir.Func
|
|
arg := n.List().First()
|
|
switch arg.Op() {
|
|
case ir.ONAME:
|
|
callee = arg.Name().Defn.(*ir.Func)
|
|
case ir.OCLOSURE:
|
|
callee = arg.Func()
|
|
default:
|
|
base.Fatalf("expected ONAME or OCLOSURE node, got %+v", arg)
|
|
}
|
|
if callee.Op() != ir.ODCLFUNC {
|
|
base.Fatalf("expected ODCLFUNC node, got %+v", callee)
|
|
}
|
|
c.extraCalls[c.curfn] = append(c.extraCalls[c.curfn], nowritebarrierrecCall{callee, n.Pos()})
|
|
return true
|
|
}
|
|
|
|
// recordCall records a call from ODCLFUNC node "from", to function
|
|
// symbol "to" at position pos.
|
|
//
|
|
// This should be done as late as possible during compilation to
|
|
// capture precise call graphs. The target of the call is an LSym
|
|
// because that's all we know after we start SSA.
|
|
//
|
|
// This can be called concurrently for different from Nodes.
|
|
func (c *nowritebarrierrecChecker) recordCall(fn *ir.Func, to *obj.LSym, pos src.XPos) {
|
|
// We record this information on the *Func so this is concurrent-safe.
|
|
if fn.NWBRCalls == nil {
|
|
fn.NWBRCalls = new([]ir.SymAndPos)
|
|
}
|
|
*fn.NWBRCalls = append(*fn.NWBRCalls, ir.SymAndPos{Sym: to, Pos: pos})
|
|
}
|
|
|
|
func (c *nowritebarrierrecChecker) check() {
|
|
// We walk the call graph as late as possible so we can
|
|
// capture all calls created by lowering, but this means we
|
|
// only get to see the obj.LSyms of calls. symToFunc lets us
|
|
// get back to the ODCLFUNCs.
|
|
symToFunc := make(map[*obj.LSym]*ir.Func)
|
|
// funcs records the back-edges of the BFS call graph walk. It
|
|
// maps from the ODCLFUNC of each function that must not have
|
|
// write barriers to the call that inhibits them. Functions
|
|
// that are directly marked go:nowritebarrierrec are in this
|
|
// map with a zero-valued nowritebarrierrecCall. This also
|
|
// acts as the set of marks for the BFS of the call graph.
|
|
funcs := make(map[ir.Node]nowritebarrierrecCall)
|
|
// q is the queue of ODCLFUNC Nodes to visit in BFS order.
|
|
var q ir.NameQueue
|
|
|
|
for _, n := range xtop {
|
|
if n.Op() != ir.ODCLFUNC {
|
|
continue
|
|
}
|
|
fn := n.(*ir.Func)
|
|
|
|
symToFunc[fn.LSym] = fn
|
|
|
|
// Make nowritebarrierrec functions BFS roots.
|
|
if fn.Pragma&ir.Nowritebarrierrec != 0 {
|
|
funcs[fn] = nowritebarrierrecCall{}
|
|
q.PushRight(fn.Nname)
|
|
}
|
|
// Check go:nowritebarrier functions.
|
|
if fn.Pragma&ir.Nowritebarrier != 0 && fn.WBPos.IsKnown() {
|
|
base.ErrorfAt(fn.WBPos, "write barrier prohibited")
|
|
}
|
|
}
|
|
|
|
// Perform a BFS of the call graph from all
|
|
// go:nowritebarrierrec functions.
|
|
enqueue := func(src, target *ir.Func, pos src.XPos) {
|
|
if target.Pragma&ir.Yeswritebarrierrec != 0 {
|
|
// Don't flow into this function.
|
|
return
|
|
}
|
|
if _, ok := funcs[target]; ok {
|
|
// Already found a path to target.
|
|
return
|
|
}
|
|
|
|
// Record the path.
|
|
funcs[target] = nowritebarrierrecCall{target: src, lineno: pos}
|
|
q.PushRight(target.Nname)
|
|
}
|
|
for !q.Empty() {
|
|
fn := q.PopLeft().Func()
|
|
|
|
// Check fn.
|
|
if fn.WBPos.IsKnown() {
|
|
var err bytes.Buffer
|
|
call := funcs[fn]
|
|
for call.target != nil {
|
|
fmt.Fprintf(&err, "\n\t%v: called by %v", base.FmtPos(call.lineno), call.target.Nname)
|
|
call = funcs[call.target]
|
|
}
|
|
base.ErrorfAt(fn.WBPos, "write barrier prohibited by caller; %v%s", fn.Nname, err.String())
|
|
continue
|
|
}
|
|
|
|
// Enqueue fn's calls.
|
|
for _, callee := range c.extraCalls[fn] {
|
|
enqueue(fn, callee.target, callee.lineno)
|
|
}
|
|
if fn.NWBRCalls == nil {
|
|
continue
|
|
}
|
|
for _, callee := range *fn.NWBRCalls {
|
|
target := symToFunc[callee.Sym]
|
|
if target != nil {
|
|
enqueue(fn, target, callee.Pos)
|
|
}
|
|
}
|
|
}
|
|
}
|