From: Matthew Dempsky Date: Fri, 6 Apr 2018 07:54:20 +0000 (-0700) Subject: cmd/compile: cleanup method expression type checking X-Git-Tag: go1.11beta1~941 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=33eaf75a031694e11981218963f395174baca6e1;p=gostls13.git 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 TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go index 053e3cb031..f350d514e5 100644 --- a/src/cmd/compile/internal/gc/reflect.go +++ b/src/cmd/compile/internal/gc/reflect.go @@ -406,12 +406,7 @@ func methods(t *types.Type) []*Sig { // if pointer receiver but non-pointer t and // this is not an embedded pointer inside a struct, // method does not apply. - this := f.Type.Recv().Type - - if this.IsPtr() && this.Elem() == t { - continue - } - if this.IsPtr() && !t.IsPtr() && f.Embedded != 2 && !isifacemethod(f.Type) { + if !isMethodApplicable(t, f) { continue } @@ -431,6 +426,8 @@ func methods(t *types.Type) []*Sig { sig.type_ = methodfunc(f.Type, t) sig.mtype = methodfunc(f.Type, nil) + this := f.Type.Recv().Type + if !sig.isym.Siggen() { sig.isym.SetSiggen(true) if !eqtype(this, it) { diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 4254d5655d..a30c9ca4ff 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -847,30 +847,10 @@ func typecheck1(n *Node, top int) *Node { s := n.Sym if n.Left.Op == OTYPE { - if !looktypedot(n, t, 0) { - if looktypedot(n, t, 1) { - 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 + n = typecheckMethodExpr(n) + if n.Type == nil { 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 break } @@ -2343,56 +2323,73 @@ func lookdot1(errnode *Node, s *types.Sym, t *types.Type, fs *types.Fields, dost return r } -func looktypedot(n *Node, t *types.Type, dostrcmp int) bool { - s := n.Sym +// typecheckMethodExpr checks selector expressions (ODOT) where the +// 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() { - f1 := lookdot1(n, s, t, t.Fields(), dostrcmp) - if f1 == nil { - return false + ms = t.Fields() + } else { + 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) - n.Xoffset = f1.Offset - n.Type = f1.Type - n.Op = ODOTINTER - return true - } - - // Find the base type: methtype will fail if t - // is not of the form T or *T. - mt := methtype(t) - if mt == nil { - return false + // The method expression T.m requires a wrapper when T + // is different from m's declared receiver type. We + // normally generate these wrappers while writing out + // runtime type 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) + } } - expandmeth(mt) - f2 := lookdot1(n, s, mt, mt.AllMethods(), dostrcmp) - if f2 == nil { - return false + s := n.Sym + m := lookdot1(n, s, t, ms, 0) + if m == nil { + 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 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, f2.Sym) - return false + if !isMethodApplicable(t, m) { + yyerror("invalid method expression %v (needs pointer receiver: (*%v).%S)", n, t, s) + n.Type = nil + return n } - // The method expression T.m requires a wrapper when T is - // different from m's declared receiver type. We normally - // generate these wrappers while writing out runtime type - // 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.Op = ONAME + if n.Name == nil { + n.Name = new(Name) } - + n.Right = newname(n.Sym) n.Sym = methodSym(t, n.Sym) - n.Xoffset = f2.Offset - n.Type = f2.Type - n.Op = ODOTMETH - return true + n.Type = methodfunc(m.Type, n.Left.Type) + n.Xoffset = 0 + n.SetClass(PFUNC) + 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 {