Commit 638f112d authored by Matthew Dempsky's avatar Matthew Dempsky

cmd/compile: cleanup method symbol creation

There were multiple ad hoc ways to create method symbols, with subtle
and confusing differences between them. This CL unifies them into a
single well-documented encoding and implementation.

This introduces some inconsequential changes to symbol format for the
sake of simplicity and consistency. Two notable changes:

1) Symbol construction is now insensitive to the package currently
being compiled. Previously, non-exported methods on anonymous types
received different method symbols depending on whether the method was
local or imported.

2) Symbols for method values parenthesized non-pointer receiver types
and non-exported method names, and also always package-qualified
non-exported method names. Now they use the same rules as normal
method symbols.

The methodSym function is also now stricter about rejecting
non-sensical method/receiver combinations. Notably, this means that
typecheckfunc needs to call addmethod to validate the method before
calling declare, which also means we no longer emit errors about
redeclaring bogus methods.

Change-Id: I9501c7a53dd70ef60e5c74603974e5ecc06e2003
Reviewed-on: https://go-review.googlesource.com/104876Reviewed-by: default avatarRobert Griesemer <gri@golang.org>
parent 2e7e5777
...@@ -581,7 +581,6 @@ var knownFormats = map[string]string{ ...@@ -581,7 +581,6 @@ var knownFormats = map[string]string{
"*cmd/compile/internal/types.Field %p": "", "*cmd/compile/internal/types.Field %p": "",
"*cmd/compile/internal/types.Field %v": "", "*cmd/compile/internal/types.Field %v": "",
"*cmd/compile/internal/types.Sym %+v": "", "*cmd/compile/internal/types.Sym %+v": "",
"*cmd/compile/internal/types.Sym %-v": "",
"*cmd/compile/internal/types.Sym %0S": "", "*cmd/compile/internal/types.Sym %0S": "",
"*cmd/compile/internal/types.Sym %S": "", "*cmd/compile/internal/types.Sym %S": "",
"*cmd/compile/internal/types.Sym %p": "", "*cmd/compile/internal/types.Sym %p": "",
......
...@@ -529,7 +529,7 @@ func (p *importer) typ() *types.Type { ...@@ -529,7 +529,7 @@ func (p *importer) typ() *types.Type {
continue continue
} }
n := newfuncnamel(mpos, methodname(sym, recv[0].Type)) n := newfuncnamel(mpos, methodSym(recv[0].Type, sym))
n.Type = mt n.Type = mt
n.SetClass(PFUNC) n.SetClass(PFUNC)
checkwidth(n.Type) checkwidth(n.Type)
......
...@@ -421,37 +421,9 @@ func typecheckpartialcall(fn *Node, sym *types.Sym) { ...@@ -421,37 +421,9 @@ func typecheckpartialcall(fn *Node, sym *types.Sym) {
fn.Type = xfunc.Type fn.Type = xfunc.Type
} }
var makepartialcall_gopkg *types.Pkg
func makepartialcall(fn *Node, t0 *types.Type, meth *types.Sym) *Node { func makepartialcall(fn *Node, t0 *types.Type, meth *types.Sym) *Node {
var p string
rcvrtype := fn.Left.Type rcvrtype := fn.Left.Type
if exportname(meth.Name) { sym := methodSymSuffix(rcvrtype, meth, "-fm")
p = fmt.Sprintf("(%-S).%s-fm", rcvrtype, meth.Name)
} else {
p = fmt.Sprintf("(%-S).(%-v)-fm", rcvrtype, meth)
}
basetype := rcvrtype
if rcvrtype.IsPtr() {
basetype = basetype.Elem()
}
if !basetype.IsInterface() && basetype.Sym == nil {
Fatalf("missing base type for %v", rcvrtype)
}
var spkg *types.Pkg
if basetype.Sym != nil {
spkg = basetype.Sym.Pkg
}
if spkg == nil {
if makepartialcall_gopkg == nil {
makepartialcall_gopkg = types.NewPkg("go", "")
}
spkg = makepartialcall_gopkg
}
sym := spkg.Lookup(p)
if sym.Uniq() { if sym.Uniq() {
return asNode(sym.Def) return asNode(sym.Def)
......
...@@ -827,79 +827,63 @@ func functypefield0(t *types.Type, this *types.Field, in, out []*types.Field) { ...@@ -827,79 +827,63 @@ func functypefield0(t *types.Type, this *types.Field, in, out []*types.Field) {
} }
} }
var methodsym_toppkg *types.Pkg // 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, "")
}
func methodsym(nsym *types.Sym, t0 *types.Type) *types.Sym { // methodSymSuffix is like methodsym, but allows attaching a
if t0 == nil { // distinguisher suffix. To avoid collisions, the suffix must not
Fatalf("methodsym: nil receiver type") // 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")
} }
t := t0 rsym := recv.Sym
s := t.Sym if recv.IsPtr() {
if s == nil && t.IsPtr() { if rsym != nil {
t = t.Elem() Fatalf("declared pointer receiver type: %v", recv)
if t == nil {
Fatalf("methodsym: ptrto nil")
} }
s = t.Sym rsym = recv.Elem().Sym
} }
// if t0 == *t and t0 has a sym, // Find the package the receiver type appeared in. For
// we want to see *t, not t0, in the method name. // anonymous receiver types (i.e., anonymous structs with
if t != t0 && t0.Sym != nil { // embedded fields), use the "go" pseudo-package instead.
t0 = types.NewPtr(t) rpkg := gopkg
} if rsym != nil {
rpkg = rsym.Pkg
var spkg *types.Pkg
if s != nil {
spkg = s.Pkg
}
pkgprefix := ""
if (spkg == nil || nsym.Pkg != spkg) && !exportname(nsym.Name) && nsym.Pkg.Prefix != `""` {
pkgprefix = "." + nsym.Pkg.Prefix
}
var p string
if t0.Sym == nil && t0.IsPtr() {
p = fmt.Sprintf("(%-S)%s.%s", t0, pkgprefix, nsym.Name)
} else {
p = fmt.Sprintf("%-S%s.%s", t0, pkgprefix, nsym.Name)
} }
if spkg == nil { var b bytes.Buffer
if methodsym_toppkg == nil {
methodsym_toppkg = types.NewPkg("go", "")
}
spkg = methodsym_toppkg
}
return spkg.Lookup(p)
}
// methodname is a misnomer because this now returns a Sym, rather
// than an ONAME.
// TODO(mdempsky): Reconcile with methodsym.
func methodname(s *types.Sym, recv *types.Type) *types.Sym {
star := false
if recv.IsPtr() { if recv.IsPtr() {
star = true // The parentheses aren't really necessary, but
recv = recv.Elem() // they're pretty traditional at this point.
} fmt.Fprintf(&b, "(%-S)", recv)
} else {
tsym := recv.Sym fmt.Fprintf(&b, "%-S", recv)
if tsym == nil || s.IsBlank() {
return s
} }
var p string // A particular receiver type may have multiple non-exported
if star { // methods with the same name. To disambiguate them, include a
p = fmt.Sprintf("(*%v).%v", tsym.Name, s) // package qualifier for names that came from a different
} else { // package than the receiver type.
p = fmt.Sprintf("%v.%v", tsym, s) if !exportname(msym.Name) && msym.Pkg != rpkg {
b.WriteString(".")
b.WriteString(msym.Pkg.Prefix)
} }
s = tsym.Pkg.Lookup(p) b.WriteString(".")
b.WriteString(msym.Name)
b.WriteString(suffix)
return s return rpkg.LookupBytes(b.Bytes())
} }
// Add a method, declared as a function. // Add a method, declared as a function.
......
...@@ -126,6 +126,9 @@ var unsafepkg *types.Pkg // package unsafe ...@@ -126,6 +126,9 @@ var unsafepkg *types.Pkg // package unsafe
var trackpkg *types.Pkg // fake package for field tracking var trackpkg *types.Pkg // fake package for field tracking
var mappkg *types.Pkg // fake package for map zero value var mappkg *types.Pkg // fake package for map zero value
var gopkg *types.Pkg // pseudo-package for method symbols on anonymous receiver types
var zerosize int64 var zerosize int64
var myimportpath string var myimportpath string
......
...@@ -170,6 +170,9 @@ func Main(archInit func(*Arch)) { ...@@ -170,6 +170,9 @@ func Main(archInit func(*Arch)) {
mappkg = types.NewPkg("go.map", "go.map") mappkg = types.NewPkg("go.map", "go.map")
mappkg.Prefix = "go.map" mappkg.Prefix = "go.map"
// pseudo-package used for methods with anonymous receivers
gopkg = types.NewPkg("go", "")
Nacl = objabi.GOOS == "nacl" Nacl = objabi.GOOS == "nacl"
flag.BoolVar(&compiling_runtime, "+", false, "compiling runtime") flag.BoolVar(&compiling_runtime, "+", false, "compiling runtime")
......
...@@ -427,8 +427,8 @@ func methods(t *types.Type) []*Sig { ...@@ -427,8 +427,8 @@ func methods(t *types.Type) []*Sig {
sig.pkg = method.Pkg sig.pkg = method.Pkg
} }
sig.isym = methodsym(method, it) sig.isym = methodSym(it, method)
sig.tsym = methodsym(method, t) sig.tsym = methodSym(t, method)
sig.type_ = methodfunc(f.Type, t) sig.type_ = methodfunc(f.Type, t)
sig.mtype = methodfunc(f.Type, nil) sig.mtype = methodfunc(f.Type, nil)
...@@ -493,7 +493,7 @@ func imethods(t *types.Type) []*Sig { ...@@ -493,7 +493,7 @@ func imethods(t *types.Type) []*Sig {
// IfaceType.Method is not in the reflect data. // IfaceType.Method is not in the reflect data.
// Generate the method body, so that compiled // Generate the method body, so that compiled
// code can refer to it. // code can refer to it.
isym := methodsym(method, t) isym := methodSym(t, method)
if !isym.Siggen() { if !isym.Siggen() {
isym.SetSiggen(true) isym.SetSiggen(true)
genwrapper(t, f, isym) genwrapper(t, f, isym)
......
...@@ -1723,7 +1723,7 @@ func genwrapper(rcvr *types.Type, method *types.Field, newnam *types.Sym) { ...@@ -1723,7 +1723,7 @@ func genwrapper(rcvr *types.Type, method *types.Field, newnam *types.Sym) {
as := nod(OAS, this.Left, nod(OCONVNOP, dot, nil)) as := nod(OAS, this.Left, nod(OCONVNOP, dot, nil))
as.Right.Type = rcvr as.Right.Type = rcvr
fn.Nbody.Append(as) fn.Nbody.Append(as)
fn.Nbody.Append(nodSym(ORETJMP, nil, methodsym(method.Sym, methodrcvr))) fn.Nbody.Append(nodSym(ORETJMP, nil, methodSym(methodrcvr, method.Sym)))
} else { } else {
fn.Func.SetWrapper(true) // ignore frame for panic+recover matching fn.Func.SetWrapper(true) // ignore frame for panic+recover matching
call := nod(OCALL, dot, nil) call := nod(OCALL, dot, nil)
......
...@@ -2352,7 +2352,7 @@ func looktypedot(n *Node, t *types.Type, dostrcmp int) bool { ...@@ -2352,7 +2352,7 @@ func looktypedot(n *Node, t *types.Type, dostrcmp int) bool {
return false return false
} }
n.Sym = methodsym(n.Sym, t) n.Sym = methodSym(t, n.Sym)
n.Xoffset = f1.Offset n.Xoffset = f1.Offset
n.Type = f1.Type n.Type = f1.Type
n.Op = ODOTINTER n.Op = ODOTINTER
...@@ -2378,7 +2378,7 @@ func looktypedot(n *Node, t *types.Type, dostrcmp int) bool { ...@@ -2378,7 +2378,7 @@ func looktypedot(n *Node, t *types.Type, dostrcmp int) bool {
return false return false
} }
n.Sym = methodsym(n.Sym, t) n.Sym = methodSym(t, n.Sym)
n.Xoffset = f2.Offset n.Xoffset = f2.Offset
n.Type = f2.Type n.Type = f2.Type
n.Op = ODOTMETH n.Op = ODOTMETH
...@@ -2495,10 +2495,9 @@ func lookdot(n *Node, t *types.Type, dostrcmp int) *types.Field { ...@@ -2495,10 +2495,9 @@ func lookdot(n *Node, t *types.Type, dostrcmp int) *types.Field {
return nil return nil
} }
n.Sym = methodsym(n.Sym, n.Left.Type) n.Sym = methodSym(n.Left.Type, f2.Sym)
n.Xoffset = f2.Offset n.Xoffset = f2.Offset
n.Type = f2.Type n.Type = f2.Type
n.Op = ODOTMETH n.Op = ODOTMETH
return f2 return f2
...@@ -3449,10 +3448,13 @@ func typecheckfunc(n *Node) { ...@@ -3449,10 +3448,13 @@ func typecheckfunc(n *Node) {
t.FuncType().Nname = asTypesNode(n.Func.Nname) t.FuncType().Nname = asTypesNode(n.Func.Nname)
rcvr := t.Recv() rcvr := t.Recv()
if rcvr != nil && n.Func.Shortname != nil { if rcvr != nil && n.Func.Shortname != nil {
n.Func.Nname.Sym = methodname(n.Func.Shortname, rcvr.Type) m := addmethod(n.Func.Shortname, t, true, n.Func.Pragma&Nointerface != 0)
declare(n.Func.Nname, PFUNC) if m == nil {
return
}
addmethod(n.Func.Shortname, t, true, n.Func.Pragma&Nointerface != 0) n.Func.Nname.Sym = methodSym(rcvr.Type, n.Func.Shortname)
declare(n.Func.Nname, PFUNC)
} }
if Ctxt.Flag_dynlink && !inimport && n.Func.Nname != nil { if Ctxt.Flag_dynlink && !inimport && n.Func.Nname != nil {
......
...@@ -95,10 +95,10 @@ type _ = reflect.ValueOf // ERROR "reflect.ValueOf is not a type|expected type" ...@@ -95,10 +95,10 @@ type _ = reflect.ValueOf // ERROR "reflect.ValueOf is not a type|expected type"
func (A1) m() {} // ERROR "cannot define new methods on non-local type int|may not define methods on non-local type" func (A1) m() {} // ERROR "cannot define new methods on non-local type int|may not define methods on non-local type"
func (A2) m() {} // ERROR "invalid receiver type" func (A2) m() {} // ERROR "invalid receiver type"
func (A3) m() {} // ERROR "cannot define new methods on non-local type reflect.Value|may not define methods on non-local type" func (A3) m() {} // ERROR "cannot define new methods on non-local type reflect.Value|may not define methods on non-local type"
func (A4) m() {} // ERROR "reflect.Value.m redeclared in this block" "cannot define new methods on non-local type reflect.Value|may not define methods on non-local type" func (A4) m() {} // ERROR "cannot define new methods on non-local type reflect.Value|may not define methods on non-local type"
type B1 = struct{} type B1 = struct{}
func (B1) m() {} // ERROR "m redeclared in this block" "invalid receiver type" func (B1) m() {} // ERROR "invalid receiver type"
// TODO(gri) expand // TODO(gri) expand
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment