go/src/cmd/compile/internal/gc/dcl.go
Robert Griesemer 9be4f312bf cmd/compile: revert internal parameter rename (from ".anonX" to "") before export
In the old binary export format, parameter names for parameter lists
which contained only types where never written, so this problem didn't
come up.

Fixes #25101.

Change-Id: Ia8b817f7f467570b05f88d584e86b6ef4acdccc6
Reviewed-on: https://go-review.googlesource.com/116376
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2018-06-05 20:22:07 +00:00

1196 lines
28 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/types"
"cmd/internal/obj"
"cmd/internal/src"
"fmt"
"strings"
)
// Declaration stack & operations
var externdcl []*Node
func testdclstack() {
if !types.IsDclstackValid() {
if nerrors != 0 {
errorexit()
}
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
}
yyerrorl(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
}
yyerrorl(pos, "%v redeclared %s\n"+
"\tprevious declaration at %v", s, where, linestr(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 *Node, ctxt Class) {
if ctxt == PDISCARD {
return
}
if n.isBlank() {
return
}
if n.Name == nil {
// named OLITERAL needs Name; most OLITERALs don't.
n.Name = new(Name)
}
s := n.Sym
// kludgy: typecheckok means we're past parsing. Eg genwrapper may declare out of package names later.
if !inimport && !typecheckok && s.Pkg != localpkg {
yyerrorl(n.Pos, "cannot declare name %v", s)
}
gen := 0
if ctxt == PEXTERN {
if s.Name == "init" {
yyerrorl(n.Pos, "cannot declare init - must be func")
}
if s.Name == "main" && s.Pkg.Name == "main" {
yyerrorl(n.Pos, "cannot declare main - must be func")
}
externdcl = append(externdcl, n)
} else {
if Curfn == nil && ctxt == PAUTO {
lineno = n.Pos
Fatalf("automatic outside function")
}
if Curfn != nil {
Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
}
if n.Op == OTYPE {
declare_typegen++
gen = declare_typegen
} else if n.Op == ONAME && ctxt == PAUTO && !strings.Contains(s.Name, "ยท") {
vargen++
gen = vargen
}
types.Pushdcl(s)
n.Name.Curfn = Curfn
}
if ctxt == PAUTO {
n.Xoffset = 0
}
if s.Block == types.Block {
// functype will print errors about duplicate function arguments.
// Don't repeat the error here.
if ctxt != PPARAM && ctxt != PPARAMOUT {
redeclare(n.Pos, s, "in this block")
}
}
s.Block = types.Block
s.Lastlineno = lineno
s.Def = asTypesNode(n)
n.Name.Vargen = int32(gen)
n.SetClass(ctxt)
autoexport(n, ctxt)
}
func addvar(n *Node, t *types.Type, ctxt Class) {
if n == nil || n.Sym == nil || (n.Op != ONAME && n.Op != ONONAME) || t == nil {
Fatalf("addvar: n=%v t=%v nil", n, t)
}
n.Op = ONAME
declare(n, ctxt)
n.Type = t
}
// declare variables from grammar
// new_name_list (type | [type] = expr_list)
func variter(vl []*Node, t *Node, el []*Node) []*Node {
var init []*Node
doexpr := len(el) > 0
if len(el) == 1 && len(vl) > 1 {
e := el[0]
as2 := nod(OAS2, nil, nil)
as2.List.Set(vl)
as2.Rlist.Set1(e)
for _, v := range vl {
v.Op = ONAME
declare(v, dclcontext)
v.Name.Param.Ntype = t
v.Name.Defn = as2
if Curfn != nil {
init = append(init, nod(ODCL, v, nil))
}
}
return append(init, as2)
}
for _, v := range vl {
var e *Node
if doexpr {
if len(el) == 0 {
yyerror("missing expression in var declaration")
break
}
e = el[0]
el = el[1:]
}
v.Op = ONAME
declare(v, dclcontext)
v.Name.Param.Ntype = t
if e != nil || Curfn != nil || v.isBlank() {
if Curfn != nil {
init = append(init, nod(ODCL, v, nil))
}
e = nod(OAS, v, e)
init = append(init, e)
if e.Right != nil {
v.Name.Defn = e
}
}
}
if len(el) != 0 {
yyerror("extra expression in var declaration")
}
return init
}
// newnoname returns a new ONONAME Node associated with symbol s.
func newnoname(s *types.Sym) *Node {
if s == nil {
Fatalf("newnoname nil")
}
n := nod(ONONAME, nil, nil)
n.Sym = s
n.SetAddable(true)
n.Xoffset = 0
return n
}
// newfuncname generates a new name node for a function or method.
// TODO(rsc): Use an ODCLFUNC node instead. See comment in CL 7360.
func newfuncname(s *types.Sym) *Node {
return newfuncnamel(lineno, s)
}
// newfuncnamel generates a new name node for a function or method.
// TODO(rsc): Use an ODCLFUNC node instead. See comment in CL 7360.
func newfuncnamel(pos src.XPos, s *types.Sym) *Node {
n := newnamel(pos, s)
n.Func = new(Func)
n.Func.SetIsHiddenClosure(Curfn != nil)
return n
}
// this generates a new name node for a name
// being declared.
func dclname(s *types.Sym) *Node {
n := newname(s)
n.Op = ONONAME // caller will correct it
return n
}
func typenod(t *types.Type) *Node {
return typenodl(src.NoXPos, t)
}
func typenodl(pos src.XPos, t *types.Type) *Node {
// if we copied another type with *t = *u
// then t->nod might be out of date, so
// check t->nod->type too
if asNode(t.Nod) == nil || asNode(t.Nod).Type != t {
t.Nod = asTypesNode(nodl(pos, OTYPE, nil, nil))
asNode(t.Nod).Type = t
asNode(t.Nod).Sym = t.Sym
}
return asNode(t.Nod)
}
func anonfield(typ *types.Type) *Node {
return symfield(nil, typ)
}
func namedfield(s string, typ *types.Type) *Node {
return symfield(lookup(s), typ)
}
func symfield(s *types.Sym, typ *types.Type) *Node {
n := nodSym(ODCLFIELD, nil, s)
n.Type = typ
return n
}
// oldname returns the Node that declares symbol s in the current scope.
// If no such Node currently exists, an ONONAME Node is returned instead.
func oldname(s *types.Sym) *Node {
n := 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 newnoname(s)
}
if Curfn != nil && n.Op == 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.Param.Innermost
if c == nil || c.Name.Curfn != Curfn {
// Do not have a closure var for the active closure yet; make one.
c = newname(s)
c.SetClass(PAUTOHEAP)
c.SetIsClosureVar(true)
c.SetIsddd(n.Isddd())
c.Name.Defn = n
c.SetAddable(false)
// Link into list of active closure variables.
// Popped from list in func closurebody.
c.Name.Param.Outer = n.Name.Param.Innermost
n.Name.Param.Innermost = c
Curfn.Func.Cvars.Append(c)
}
// return ref to closure var, not original
return c
}
return n
}
// := declarations
func colasname(n *Node) bool {
switch n.Op {
case ONAME,
ONONAME,
OPACK,
OTYPE,
OLITERAL:
return n.Sym != nil
}
return false
}
func colasdefn(left []*Node, defn *Node) {
for _, n := range left {
if n.Sym != nil {
n.Sym.SetUniq(true)
}
}
var nnew, nerr int
for i, n := range left {
if n.isBlank() {
continue
}
if !colasname(n) {
yyerrorl(defn.Pos, "non-name %v on left side of :=", n)
nerr++
continue
}
if !n.Sym.Uniq() {
yyerrorl(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.Name.Defn = defn
defn.Ninit.Append(nod(ODCL, n, nil))
left[i] = n
}
if nnew == 0 && nerr == 0 {
yyerrorl(defn.Pos, "no new variables on left side of :=")
}
}
// declare the arguments in an
// interface field declaration.
func ifacedcl(n *Node) {
if n.Op != ODCLFIELD || n.Left == nil {
Fatalf("ifacedcl")
}
if n.Sym.IsBlank() {
yyerror("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(n *Node) {
// change the declaration context from extern to auto
if Curfn == nil && dclcontext != PEXTERN {
Fatalf("funchdr: dclcontext = %d", dclcontext)
}
dclcontext = PAUTO
types.Markdcl()
funcstack = append(funcstack, Curfn)
Curfn = n
if n.Func.Nname != nil {
funcargs(n.Func.Nname.Name.Param.Ntype)
} else if n.Func.Ntype != nil {
funcargs(n.Func.Ntype)
} else {
funcargs2(n.Type)
}
}
func funcargs(nt *Node) {
if nt.Op != OTFUNC {
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 = nt.Rlist.Len()
// declare the receiver and in arguments.
if nt.Left != nil {
funcarg(nt.Left, PPARAM)
}
for _, n := range nt.List.Slice() {
funcarg(n, PPARAM)
}
oldvargen := vargen
vargen = 0
// declare the out arguments.
gen := nt.List.Len()
for _, n := range nt.Rlist.Slice() {
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, PPARAMOUT)
}
vargen = oldvargen
}
func funcarg(n *Node, ctxt Class) {
if n.Op != ODCLFIELD {
Fatalf("funcarg %v", n.Op)
}
if n.Sym == nil {
return
}
n.Right = newnamel(n.Pos, n.Sym)
n.Right.Name.Param.Ntype = n.Left
n.Right.SetIsddd(n.Isddd())
declare(n.Right, ctxt)
vargen++
n.Right.Name.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.Etype != TFUNC {
Fatalf("funcargs2 %v", t)
}
for _, f := range t.Recvs().Fields().Slice() {
funcarg2(f, PPARAM)
}
for _, f := range t.Params().Fields().Slice() {
funcarg2(f, PPARAM)
}
for _, f := range t.Results().Fields().Slice() {
funcarg2(f, PPARAMOUT)
}
}
func funcarg2(f *types.Field, ctxt Class) {
if f.Sym == nil {
return
}
n := newnamel(f.Pos, f.Sym)
f.Nname = asTypesNode(n)
n.Type = f.Type
n.SetIsddd(f.Isddd())
declare(n, ctxt)
}
var funcstack []*Node // stack of previous values of Curfn
// finish the body.
// called in auto-declaration context.
// returns in extern-declaration context.
func funcbody() {
// change the declaration context from auto to extern
if dclcontext != PAUTO {
Fatalf("funcbody: unexpected dclcontext %d", dclcontext)
}
types.Popdcl()
funcstack, Curfn = funcstack[:len(funcstack)-1], funcstack[len(funcstack)-1]
if Curfn == nil {
dclcontext = PEXTERN
}
}
// 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() {
yyerror("embedded type cannot be a pointer to interface")
}
}
if t.IsPtr() || t.IsUnsafePtr() {
yyerror("embedded type cannot be a pointer")
} else if t.Etype == TFORW && !t.ForwardType().Embedlineno.IsKnown() {
t.ForwardType().Embedlineno = lineno
}
}
func structfield(n *Node) *types.Field {
lno := lineno
lineno = n.Pos
if n.Op != ODCLFIELD {
Fatalf("structfield: oops %v\n", n)
}
f := types.NewField()
f.Pos = n.Pos
f.Sym = n.Sym
if n.Left != nil {
n.Left = typecheck(n.Left, Etype)
n.Type = n.Left.Type
n.Left = nil
}
f.Type = n.Type
if f.Type == nil {
f.SetBroke(true)
}
if n.Embedded() {
checkembeddedtype(n.Type)
f.Embedded = 1
} else {
f.Embedded = 0
}
switch u := n.Val().U.(type) {
case string:
f.Note = u
default:
yyerror("field tag must be a string")
case nil:
// no-op
}
lineno = lno
return f
}
// checkdupfields emits errors for duplicately named fields or methods in
// a list of struct or interface types.
func checkdupfields(what string, ts ...*types.Type) {
seen := make(map[*types.Sym]bool)
for _, t := range ts {
for _, f := range t.Fields().Slice() {
if f.Sym == nil || f.Sym.IsBlank() {
continue
}
if seen[f.Sym] {
yyerrorl(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 []*Node) *types.Type {
t := types.New(TSTRUCT)
tostruct0(t, l)
return t
}
func tostruct0(t *types.Type, l []*Node) {
if t == nil || !t.IsStruct() {
Fatalf("struct expected")
}
fields := make([]*types.Field, len(l))
for i, n := range l {
f := structfield(n)
if f.Broke() {
t.SetBroke(true)
}
fields[i] = f
}
t.SetFields(fields)
checkdupfields("field", t)
if !t.Broke() {
checkwidth(t)
}
}
func tofunargs(l []*Node, funarg types.Funarg) *types.Type {
t := types.New(TSTRUCT)
t.StructType().Funarg = funarg
fields := make([]*types.Field, len(l))
for i, n := range l {
f := structfield(n)
f.SetIsddd(n.Isddd())
if n.Right != nil {
n.Right.Type = f.Type
f.Nname = asTypesNode(n.Right)
}
if f.Broke() {
t.SetBroke(true)
}
fields[i] = f
}
t.SetFields(fields)
return t
}
func tofunargsfield(fields []*types.Field, funarg types.Funarg) *types.Type {
t := types.New(TSTRUCT)
t.StructType().Funarg = funarg
t.SetFields(fields)
return t
}
func interfacefield(n *Node) *types.Field {
lno := lineno
lineno = n.Pos
if n.Op != ODCLFIELD {
Fatalf("interfacefield: oops %v\n", n)
}
if n.Val().Ctype() != CTxxx {
yyerror("interface method cannot have annotation")
}
// MethodSpec = MethodName Signature | InterfaceTypeName .
//
// If Sym != nil, then Sym is MethodName and Left is Signature.
// Otherwise, Left is InterfaceTypeName.
if n.Left != nil {
n.Left = typecheck(n.Left, Etype)
n.Type = n.Left.Type
n.Left = nil
}
f := types.NewField()
f.Pos = n.Pos
f.Sym = n.Sym
f.Type = n.Type
if f.Type == nil {
f.SetBroke(true)
}
lineno = lno
return f
}
func tointerface(l []*Node) *types.Type {
if len(l) == 0 {
return types.Types[TINTER]
}
t := types.New(TINTER)
tointerface0(t, l)
return t
}
func tointerface0(t *types.Type, l []*Node) {
if t == nil || !t.IsInterface() {
Fatalf("interface expected")
}
var fields []*types.Field
for _, n := range l {
f := interfacefield(n)
if f.Broke() {
t.SetBroke(true)
}
fields = append(fields, f)
}
t.SetInterface(fields)
}
func fakeRecv() *Node {
return anonfield(types.FakeRecvType())
}
func fakeRecvField() *types.Field {
f := types.NewField()
f.Type = types.FakeRecvType()
return f
}
// 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(this *Node, in, out []*Node) *types.Type {
t := types.New(TFUNC)
functype0(t, this, in, out)
return t
}
func functype0(t *types.Type, this *Node, in, out []*Node) {
if t == nil || t.Etype != TFUNC {
Fatalf("function type expected")
}
var rcvr []*Node
if this != nil {
rcvr = []*Node{this}
}
t.FuncType().Receiver = tofunargs(rcvr, types.FunargRcvr)
t.FuncType().Params = tofunargs(in, types.FunargParams)
t.FuncType().Results = tofunargs(out, types.FunargResults)
checkdupfields("argument", t.Recvs(), t.Params(), t.Results())
if t.Recvs().Broke() || t.Results().Broke() || t.Params().Broke() {
t.SetBroke(true)
}
t.FuncType().Outnamed = t.NumResults() > 0 && origSym(t.Results().Field(0).Sym) != nil
}
func functypefield(this *types.Field, in, out []*types.Field) *types.Type {
t := types.New(TFUNC)
functypefield0(t, this, in, out)
return t
}
func functypefield0(t *types.Type, this *types.Field, in, out []*types.Field) {
var rcvr []*types.Field
if this != nil {
rcvr = []*types.Field{this}
}
t.FuncType().Receiver = tofunargsfield(rcvr, types.FunargRcvr)
t.FuncType().Params = tofunargsfield(in, types.FunargParams)
t.FuncType().Results = tofunargsfield(out, types.FunargResults)
t.FuncType().Outnamed = t.NumResults() > 0 && origSym(t.Results().Field(0).Sym) != nil
}
// origSym returns the original symbol written by the user.
func origSym(s *types.Sym) *types.Sym {
if s == nil {
return nil
}
if len(s.Name) > 1 && s.Name[0] == '~' {
switch s.Name[1] {
case 'r': // originally an unnamed result
return nil
case 'b': // originally the blank identifier _
// TODO(mdempsky): Does s.Pkg matter here?
return nblank.Sym
}
return s
}
if strings.HasPrefix(s.Name, ".anon") {
// originally an unnamed or _ name (see subr.go: structargs)
return nil
}
return s
}
// 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.
func methodSym(recv *types.Type, msym *types.Sym) *types.Sym {
return methodSymSuffix(recv, msym, "")
}
// 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() {
Fatalf("blank method name")
}
rsym := recv.Sym
if recv.IsPtr() {
if rsym != nil {
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.
func addmethod(msym *types.Sym, t *types.Type, local, nointerface bool) *types.Field {
if msym == nil {
Fatalf("no method symbol")
}
// get parent type sym
rf := t.Recv() // ptr to this structure
if rf == nil {
yyerror("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 {
yyerror("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:
yyerror("invalid receiver type %v (%v is not a defined type)", pa, t)
case t.IsPtr():
yyerror("invalid receiver type %v (%v is a pointer type)", pa, t)
case t.IsInterface():
yyerror("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.
yyerror("invalid receiver type %v (%L / %L)", pa, pa, t)
}
return nil
}
if local && mt.Sym.Pkg != localpkg {
yyerror("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 {
yyerror("type %v has both field and method named %v", mt, msym)
return nil
}
}
}
for _, f := range mt.Methods().Slice() {
if msym.Name != f.Sym.Name {
continue
}
// eqtype only checks that incoming and result parameters match,
// so explicitly check that the receiver parameters match too.
if !eqtype(t, f.Type) || !eqtype(t.Recv().Type, f.Type.Recv().Type) {
yyerror("method redeclared: %v.%v\n\t%v\n\t%v", mt, msym, f.Type, t)
}
return f
}
f := types.NewField()
f.Pos = lineno
f.Sym = msym
f.Type = t
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 !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 !Ctxt.Flag_dynlink {
Fatalf("makefuncsym dynlink")
}
if s.IsBlank() {
return
}
if compiling_runtime && (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)
}
}
// disableExport prevents sym from being included in package export
// data. To be effectual, it must be called before declare.
func disableExport(sym *types.Sym) {
sym.SetOnExportList(true)
}
func dclfunc(sym *types.Sym, tfn *Node) *Node {
if tfn.Op != OTFUNC {
Fatalf("expected OTFUNC node, got %v", tfn)
}
fn := nod(ODCLFUNC, nil, nil)
fn.Func.Nname = newfuncname(sym)
fn.Func.Nname.Name.Defn = fn
fn.Func.Nname.Name.Param.Ntype = tfn
declare(fn.Func.Nname, PFUNC)
funchdr(fn)
fn.Func.Nname.Name.Param.Ntype = typecheck(fn.Func.Nname.Name.Param.Ntype, Etype)
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[*Node][]nowritebarrierrecCall
// curfn is the current function during AST walks.
curfn *Node
}
type nowritebarrierrecCall struct {
target *Node // ODCLFUNC of caller or callee
lineno src.XPos // line of call
}
type nowritebarrierrecCallSym struct {
target *obj.LSym // LSym of 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[*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 != ODCLFUNC {
continue
}
c.curfn = n
inspect(n, c.findExtraCalls)
}
c.curfn = nil
return c
}
func (c *nowritebarrierrecChecker) findExtraCalls(n *Node) bool {
if n.Op != OCALLFUNC {
return true
}
fn := n.Left
if fn == nil || fn.Op != ONAME || fn.Class() != PFUNC || fn.Name.Defn == nil {
return true
}
if !isRuntimePkg(fn.Sym.Pkg) || fn.Sym.Name != "systemstack" {
return true
}
var callee *Node
arg := n.List.First()
switch arg.Op {
case ONAME:
callee = arg.Name.Defn
case OCLOSURE:
callee = arg.Func.Closure
default:
Fatalf("expected ONAME or OCLOSURE node, got %+v", arg)
}
if callee.Op != ODCLFUNC {
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(from *Node, to *obj.LSym, pos src.XPos) {
if from.Op != ODCLFUNC {
Fatalf("expected ODCLFUNC, got %v", from)
}
// We record this information on the *Func so this is
// concurrent-safe.
fn := from.Func
if fn.nwbrCalls == nil {
fn.nwbrCalls = new([]nowritebarrierrecCallSym)
}
*fn.nwbrCalls = append(*fn.nwbrCalls, nowritebarrierrecCallSym{to, 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]*Node)
// 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[*Node]nowritebarrierrecCall)
// q is the queue of ODCLFUNC Nodes to visit in BFS order.
var q nodeQueue
for _, n := range xtop {
if n.Op != ODCLFUNC {
continue
}
symToFunc[n.Func.lsym] = n
// Make nowritebarrierrec functions BFS roots.
if n.Func.Pragma&Nowritebarrierrec != 0 {
funcs[n] = nowritebarrierrecCall{}
q.pushRight(n)
}
// Check go:nowritebarrier functions.
if n.Func.Pragma&Nowritebarrier != 0 && n.Func.WBPos.IsKnown() {
yyerrorl(n.Func.WBPos, "write barrier prohibited")
}
}
// Perform a BFS of the call graph from all
// go:nowritebarrierrec functions.
enqueue := func(src, target *Node, pos src.XPos) {
if target.Func.Pragma&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)
}
for !q.empty() {
fn := q.popLeft()
// Check fn.
if fn.Func.WBPos.IsKnown() {
var err bytes.Buffer
call := funcs[fn]
for call.target != nil {
fmt.Fprintf(&err, "\n\t%v: called by %v", linestr(call.lineno), call.target.Func.Nname)
call = funcs[call.target]
}
yyerrorl(fn.Func.WBPos, "write barrier prohibited by caller; %v%s", fn.Func.Nname, err.String())
continue
}
// Enqueue fn's calls.
for _, callee := range c.extraCalls[fn] {
enqueue(fn, callee.target, callee.lineno)
}
if fn.Func.nwbrCalls == nil {
continue
}
for _, callee := range *fn.Func.nwbrCalls {
target := symToFunc[callee.target]
if target != nil {
enqueue(fn, target, callee.lineno)
}
}
}
}