Commit 33eaf75a authored by Matthew Dempsky's avatar Matthew Dempsky

cmd/compile: cleanup method expression type checking

Passes toolstash-check.

Change-Id: I804e73447b6fdbb75af6235c193c4ee7cbcf8d3a
Reviewed-on: https://go-review.googlesource.com/105045
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
parent 950a5689
...@@ -406,12 +406,7 @@ func methods(t *types.Type) []*Sig { ...@@ -406,12 +406,7 @@ func methods(t *types.Type) []*Sig {
// if pointer receiver but non-pointer t and // if pointer receiver but non-pointer t and
// this is not an embedded pointer inside a struct, // this is not an embedded pointer inside a struct,
// method does not apply. // method does not apply.
this := f.Type.Recv().Type if !isMethodApplicable(t, f) {
if this.IsPtr() && this.Elem() == t {
continue
}
if this.IsPtr() && !t.IsPtr() && f.Embedded != 2 && !isifacemethod(f.Type) {
continue continue
} }
...@@ -431,6 +426,8 @@ func methods(t *types.Type) []*Sig { ...@@ -431,6 +426,8 @@ func methods(t *types.Type) []*Sig {
sig.type_ = methodfunc(f.Type, t) sig.type_ = methodfunc(f.Type, t)
sig.mtype = methodfunc(f.Type, nil) sig.mtype = methodfunc(f.Type, nil)
this := f.Type.Recv().Type
if !sig.isym.Siggen() { if !sig.isym.Siggen() {
sig.isym.SetSiggen(true) sig.isym.SetSiggen(true)
if !eqtype(this, it) { if !eqtype(this, it) {
......
...@@ -847,30 +847,10 @@ func typecheck1(n *Node, top int) *Node { ...@@ -847,30 +847,10 @@ func typecheck1(n *Node, top int) *Node {
s := n.Sym s := n.Sym
if n.Left.Op == OTYPE { if n.Left.Op == OTYPE {
if !looktypedot(n, t, 0) { n = typecheckMethodExpr(n)
if looktypedot(n, t, 1) { if n.Type == nil {
yyerror("%v undefined (cannot refer to unexported method %v)", n, n.Sym)
} else {
yyerror("%v undefined (type %v has no method %v)", n, t, n.Sym)
}
n.Type = nil
return n
}
if n.Type.Etype != TFUNC || !n.IsMethod() {
yyerror("type %v has no method %S", n.Left.Type, n.Sym)
n.Type = nil
return n return n
} }
n.Op = ONAME
if n.Name == nil {
n.Name = new(Name)
}
n.Right = newname(n.Sym)
n.Type = methodfunc(n.Type, n.Left.Type)
n.Xoffset = 0
n.SetClass(PFUNC)
ok = Erv ok = Erv
break break
} }
...@@ -2343,56 +2323,73 @@ func lookdot1(errnode *Node, s *types.Sym, t *types.Type, fs *types.Fields, dost ...@@ -2343,56 +2323,73 @@ func lookdot1(errnode *Node, s *types.Sym, t *types.Type, fs *types.Fields, dost
return r return r
} }
func looktypedot(n *Node, t *types.Type, dostrcmp int) bool { // typecheckMethodExpr checks selector expressions (ODOT) where the
s := n.Sym // base expression is a type expression (OTYPE).
func typecheckMethodExpr(n *Node) *Node {
t := n.Left.Type
// Compute the method set for t.
var ms *types.Fields
if t.IsInterface() { if t.IsInterface() {
f1 := lookdot1(n, s, t, t.Fields(), dostrcmp) ms = t.Fields()
if f1 == nil { } else {
return false mt := methtype(t)
if mt == nil {
yyerror("%v undefined (type %v has no method %v)", n, t, n.Sym)
n.Type = nil
return n
} }
expandmeth(mt)
ms = mt.AllMethods()
n.Sym = methodSym(t, n.Sym) // The method expression T.m requires a wrapper when T
n.Xoffset = f1.Offset // is different from m's declared receiver type. We
n.Type = f1.Type // normally generate these wrappers while writing out
n.Op = ODOTINTER // runtime type descriptors, which is always done for
return true // types declared at package scope. However, we need
} // to make sure to generate wrappers for anonymous
// receiver types too.
// Find the base type: methtype will fail if t if mt.Sym == nil {
// is not of the form T or *T. addsignat(t)
mt := methtype(t) }
if mt == nil {
return false
} }
expandmeth(mt) s := n.Sym
f2 := lookdot1(n, s, mt, mt.AllMethods(), dostrcmp) m := lookdot1(n, s, t, ms, 0)
if f2 == nil { if m == nil {
return false if lookdot1(n, s, t, ms, 1) != nil {
yyerror("%v undefined (cannot refer to unexported method %v)", n, s)
} else {
yyerror("%v undefined (type %v has no method %v)", n, t, s)
}
n.Type = nil
return n
} }
// disallow T.m if m requires *T receiver if !isMethodApplicable(t, m) {
if f2.Type.Recv().Type.IsPtr() && !t.IsPtr() && f2.Embedded != 2 && !isifacemethod(f2.Type) { yyerror("invalid method expression %v (needs pointer receiver: (*%v).%S)", n, t, s)
yyerror("invalid method expression %v (needs pointer receiver: (*%v).%S)", n, t, f2.Sym) n.Type = nil
return false return n
} }
// The method expression T.m requires a wrapper when T is n.Op = ONAME
// different from m's declared receiver type. We normally if n.Name == nil {
// generate these wrappers while writing out runtime type n.Name = new(Name)
// descriptors, which is always done for types declared at
// package scope. However, we need to make sure to generate
// wrappers for anonymous receiver types too.
if mt.Sym == nil {
addsignat(t)
} }
n.Right = newname(n.Sym)
n.Sym = methodSym(t, n.Sym) n.Sym = methodSym(t, n.Sym)
n.Xoffset = f2.Offset n.Type = methodfunc(m.Type, n.Left.Type)
n.Type = f2.Type n.Xoffset = 0
n.Op = ODOTMETH n.SetClass(PFUNC)
return true return n
}
// isMethodApplicable reports whether method m can be called on a
// value of type t. This is necessary because we compute a single
// method set for both T and *T, but some *T methods are not
// applicable to T receivers.
func isMethodApplicable(t *types.Type, m *types.Field) bool {
return t.IsPtr() || !m.Type.Recv().Type.IsPtr() || isifacemethod(m.Type) || m.Embedded == 2
} }
func derefall(t *types.Type) *types.Type { func derefall(t *types.Type) *types.Type {
......
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