From a49e9410276975d187a5cfda1a396194c45d4464 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Fri, 22 Jan 2021 16:59:45 -0800 Subject: [PATCH] [dev.typeparams] cmd/compile/internal/types2: remove MethodSet code - not used by types2 We can always re-introduce it if we decide to make use of it. Change-Id: Ia939fdae978568edc58e21d1af732c6137744aab Reviewed-on: https://go-review.googlesource.com/c/go/+/285678 Trust: Robert Griesemer Reviewed-by: Matthew Dempsky --- .../internal/importer/gcimporter_test.go | 5 +- src/cmd/compile/internal/types2/call.go | 45 --- .../compile/internal/types2/example_test.go | 55 ---- src/cmd/compile/internal/types2/lookup.go | 19 ++ src/cmd/compile/internal/types2/methodset.go | 262 ------------------ 5 files changed, 21 insertions(+), 365 deletions(-) delete mode 100644 src/cmd/compile/internal/types2/methodset.go diff --git a/src/cmd/compile/internal/importer/gcimporter_test.go b/src/cmd/compile/internal/importer/gcimporter_test.go index a275524484..7fb8fed59c 100644 --- a/src/cmd/compile/internal/importer/gcimporter_test.go +++ b/src/cmd/compile/internal/importer/gcimporter_test.go @@ -384,9 +384,8 @@ func TestCorrectMethodPackage(t *testing.T) { } mutex := imports["sync"].Scope().Lookup("Mutex").(*types2.TypeName).Type() - mset := types2.NewMethodSet(types2.NewPointer(mutex)) // methods of *sync.Mutex - sel := mset.Lookup(nil, "Lock") - lock := sel.Obj().(*types2.Func) + obj, _, _ := types2.LookupFieldOrMethod(types2.NewPointer(mutex), false, nil, "Lock") + lock := obj.(*types2.Func) if got, want := lock.Pkg().Path(), "sync"; got != want { t.Errorf("got package path %q; want %q", got, want) } diff --git a/src/cmd/compile/internal/types2/call.go b/src/cmd/compile/internal/types2/call.go index 5a7ae221e6..72a33b50b1 100644 --- a/src/cmd/compile/internal/types2/call.go +++ b/src/cmd/compile/internal/types2/call.go @@ -685,51 +685,6 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr) { // addressability, should we report the type &(x.typ) instead? check.recordSelection(e, MethodVal, x.typ, obj, index, indirect) - // TODO(gri) The verification pass below is disabled for now because - // method sets don't match method lookup in some cases. - // For instance, if we made a copy above when creating a - // custom method for a parameterized received type, the - // method set method doesn't match (no copy there). There - /// may be other situations. - disabled := true - if !disabled && debug { - // Verify that LookupFieldOrMethod and MethodSet.Lookup agree. - // TODO(gri) This only works because we call LookupFieldOrMethod - // _before_ calling NewMethodSet: LookupFieldOrMethod completes - // any incomplete interfaces so they are available to NewMethodSet - // (which assumes that interfaces have been completed already). - typ := x.typ - if x.mode == variable { - // If typ is not an (unnamed) pointer or an interface, - // use *typ instead, because the method set of *typ - // includes the methods of typ. - // Variables are addressable, so we can always take their - // address. - if _, ok := typ.(*Pointer); !ok && !IsInterface(typ) { - typ = &Pointer{base: typ} - } - } - // If we created a synthetic pointer type above, we will throw - // away the method set computed here after use. - // TODO(gri) Method set computation should probably always compute - // both, the value and the pointer receiver method set and represent - // them in a single structure. - // TODO(gri) Consider also using a method set cache for the lifetime - // of checker once we rely on MethodSet lookup instead of individual - // lookup. - mset := NewMethodSet(typ) - if m := mset.Lookup(check.pkg, sel); m == nil || m.obj != obj { - check.dump("%v: (%s).%v -> %s", posFor(e), typ, obj.name, m) - check.dump("%s\n", mset) - // Caution: MethodSets are supposed to be used externally - // only (after all interface types were completed). It's - // now possible that we get here incorrectly. Not urgent - // to fix since we only run this code in debug mode. - // TODO(gri) fix this eventually. - panic("method sets and lookup don't agree") - } - } - x.mode = value // remove receiver diff --git a/src/cmd/compile/internal/types2/example_test.go b/src/cmd/compile/internal/types2/example_test.go index dcdeaca0c0..ffd54fe459 100644 --- a/src/cmd/compile/internal/types2/example_test.go +++ b/src/cmd/compile/internal/types2/example_test.go @@ -107,61 +107,6 @@ func Unused() { {}; {{ var x int; _ = x }} } // make sure empty block scopes get // } } -// ExampleMethodSet prints the method sets of various types. -func ExampleMethodSet() { - // Parse a single source file. - const input = ` -package temperature -import "fmt" -type Celsius float64 -func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) } -func (c *Celsius) SetF(f float64) { *c = Celsius(f - 32 / 9 * 5) } - -type S struct { I; m int } -type I interface { m() byte } -` - f, err := parseSrc("celsius.go", input) - if err != nil { - log.Fatal(err) - } - - // Type-check a package consisting of this file. - // Type information for the imported packages - // comes from $GOROOT/pkg/$GOOS_$GOOARCH/fmt.a. - conf := types2.Config{Importer: defaultImporter()} - pkg, err := conf.Check("temperature", []*syntax.File{f}, nil) - if err != nil { - log.Fatal(err) - } - - // Print the method sets of Celsius and *Celsius. - celsius := pkg.Scope().Lookup("Celsius").Type() - for _, t := range []types2.Type{celsius, types2.NewPointer(celsius)} { - fmt.Printf("Method set of %s:\n", t) - mset := types2.NewMethodSet(t) - for i := 0; i < mset.Len(); i++ { - fmt.Println(mset.At(i)) - } - fmt.Println() - } - - // Print the method set of S. - styp := pkg.Scope().Lookup("S").Type() - fmt.Printf("Method set of %s:\n", styp) - fmt.Println(types2.NewMethodSet(styp)) - - // Output: - // Method set of temperature.Celsius: - // method (temperature.Celsius) String() string - // - // Method set of *temperature.Celsius: - // method (*temperature.Celsius) SetF(f float64) - // method (*temperature.Celsius) String() string - // - // Method set of temperature.S: - // MethodSet {} -} - // ExampleInfo prints various facts recorded by the type checker in a // types2.Info struct: definitions of and references to each named object, // and the type, value, and mode of every expression in the package. diff --git a/src/cmd/compile/internal/types2/lookup.go b/src/cmd/compile/internal/types2/lookup.go index e1e7b5814d..5dfb8bfee7 100644 --- a/src/cmd/compile/internal/types2/lookup.go +++ b/src/cmd/compile/internal/types2/lookup.go @@ -491,3 +491,22 @@ func lookupMethod(methods []*Func, pkg *Package, name string) (int, *Func) { } return -1, nil } + +// ptrRecv reports whether the receiver is of the form *T. +func ptrRecv(f *Func) bool { + // If a method's receiver type is set, use that as the source of truth for the receiver. + // Caution: Checker.funcDecl (decl.go) marks a function by setting its type to an empty + // signature. We may reach here before the signature is fully set up: we must explicitly + // check if the receiver is set (we cannot just look for non-nil f.typ). + if sig, _ := f.typ.(*Signature); sig != nil && sig.recv != nil { + _, isPtr := deref(sig.recv.typ) + return isPtr + } + + // If a method's type is not set it may be a method/function that is: + // 1) client-supplied (via NewFunc with no signature), or + // 2) internally created but not yet type-checked. + // For case 1) we can't do anything; the client must know what they are doing. + // For case 2) we can use the information gathered by the resolver. + return f.hasPtrRecv +} diff --git a/src/cmd/compile/internal/types2/methodset.go b/src/cmd/compile/internal/types2/methodset.go deleted file mode 100644 index eb8f1221cc..0000000000 --- a/src/cmd/compile/internal/types2/methodset.go +++ /dev/null @@ -1,262 +0,0 @@ -// UNREVIEWED -// Copyright 2013 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. - -// This file implements method sets. - -package types2 - -import ( - "bytes" - "fmt" - "sort" -) - -// A MethodSet is an ordered set of concrete or abstract (interface) methods; -// a method is a MethodVal selection, and they are ordered by ascending m.Obj().Id(). -// The zero value for a MethodSet is a ready-to-use empty method set. -type MethodSet struct { - list []*Selection -} - -func (s *MethodSet) String() string { - if s.Len() == 0 { - return "MethodSet {}" - } - - // Would like to use strings.Builder but it's not available in Go 1.4. - var buf bytes.Buffer - fmt.Fprintln(&buf, "MethodSet {") - for _, f := range s.list { - fmt.Fprintf(&buf, "\t%s\n", f) - } - fmt.Fprintln(&buf, "}") - return buf.String() -} - -// Len returns the number of methods in s. -func (s *MethodSet) Len() int { return len(s.list) } - -// At returns the i'th method in s for 0 <= i < s.Len(). -func (s *MethodSet) At(i int) *Selection { return s.list[i] } - -// Lookup returns the method with matching package and name, or nil if not found. -func (s *MethodSet) Lookup(pkg *Package, name string) *Selection { - if s.Len() == 0 { - return nil - } - - key := Id(pkg, name) - i := sort.Search(len(s.list), func(i int) bool { - m := s.list[i] - return m.obj.Id() >= key - }) - if i < len(s.list) { - m := s.list[i] - if m.obj.Id() == key { - return m - } - } - return nil -} - -// Shared empty method set. -var emptyMethodSet MethodSet - -// Note: NewMethodSet is intended for external use only as it -// requires interfaces to be complete. If may be used -// internally if LookupFieldOrMethod completed the same -// interfaces beforehand. - -// NewMethodSet returns the method set for the given type T. -// It always returns a non-nil method set, even if it is empty. -func NewMethodSet(T Type) *MethodSet { - // WARNING: The code in this function is extremely subtle - do not modify casually! - // This function and lookupFieldOrMethod should be kept in sync. - - // method set up to the current depth, allocated lazily - var base methodSet - - typ, isPtr := deref(T) - - // *typ where typ is an interface has no methods. - if isPtr && IsInterface(typ) { - return &emptyMethodSet - } - - // Start with typ as single entry at shallowest depth. - current := []embeddedType{{typ, nil, isPtr, false}} - - // Named types that we have seen already, allocated lazily. - // Used to avoid endless searches in case of recursive types. - // Since only Named types can be used for recursive types, we - // only need to track those. - // (If we ever allow type aliases to construct recursive types, - // we must use type identity rather than pointer equality for - // the map key comparison, as we do in consolidateMultiples.) - var seen map[*Named]bool - - // collect methods at current depth - for len(current) > 0 { - var next []embeddedType // embedded types found at current depth - - // field and method sets at current depth, indexed by names (Id's), and allocated lazily - var fset map[string]bool // we only care about the field names - var mset methodSet - - for _, e := range current { - typ := e.typ - - // If we have a named type, we may have associated methods. - // Look for those first. - if named := typ.Named(); named != nil { - if seen[named] { - // We have seen this type before, at a more shallow depth - // (note that multiples of this type at the current depth - // were consolidated before). The type at that depth shadows - // this same type at the current depth, so we can ignore - // this one. - continue - } - if seen == nil { - seen = make(map[*Named]bool) - } - seen[named] = true - - mset = mset.add(named.methods, e.index, e.indirect, e.multiples) - - // continue with underlying type - typ = named.underlying - } - - switch t := typ.(type) { - case *Struct: - for i, f := range t.fields { - if fset == nil { - fset = make(map[string]bool) - } - fset[f.Id()] = true - - // Embedded fields are always of the form T or *T where - // T is a type name. If typ appeared multiple times at - // this depth, f.Type appears multiple times at the next - // depth. - if f.embedded { - typ, isPtr := deref(f.typ) - // TODO(gri) optimization: ignore types that can't - // have fields or methods (only Named, Struct, and - // Interface types need to be considered). - next = append(next, embeddedType{typ, concat(e.index, i), e.indirect || isPtr, e.multiples}) - } - } - - case *Interface: - mset = mset.add(t.allMethods, e.index, true, e.multiples) - } - } - - // Add methods and collisions at this depth to base if no entries with matching - // names exist already. - for k, m := range mset { - if _, found := base[k]; !found { - // Fields collide with methods of the same name at this depth. - if fset[k] { - m = nil // collision - } - if base == nil { - base = make(methodSet) - } - base[k] = m - } - } - - // Add all (remaining) fields at this depth as collisions (since they will - // hide any method further down) if no entries with matching names exist already. - for k := range fset { - if _, found := base[k]; !found { - if base == nil { - base = make(methodSet) - } - base[k] = nil // collision - } - } - - // It's ok to call consolidateMultiples with a nil *Checker because - // MethodSets are not used internally (outside debug mode). When used - // externally, interfaces are expected to be completed and then we do - // not need a *Checker to complete them when (indirectly) calling - // Checker.identical via consolidateMultiples. - current = (*Checker)(nil).consolidateMultiples(next) - } - - if len(base) == 0 { - return &emptyMethodSet - } - - // collect methods - var list []*Selection - for _, m := range base { - if m != nil { - m.recv = T - list = append(list, m) - } - } - // sort by unique name - sort.Slice(list, func(i, j int) bool { - return list[i].obj.Id() < list[j].obj.Id() - }) - return &MethodSet{list} -} - -// A methodSet is a set of methods and name collisions. -// A collision indicates that multiple methods with the -// same unique id, or a field with that id appeared. -type methodSet map[string]*Selection // a nil entry indicates a name collision - -// Add adds all functions in list to the method set s. -// If multiples is set, every function in list appears multiple times -// and is treated as a collision. -func (s methodSet) add(list []*Func, index []int, indirect bool, multiples bool) methodSet { - if len(list) == 0 { - return s - } - if s == nil { - s = make(methodSet) - } - for i, f := range list { - key := f.Id() - // if f is not in the set, add it - if !multiples { - // TODO(gri) A found method may not be added because it's not in the method set - // (!indirect && ptrRecv(f)). A 2nd method on the same level may be in the method - // set and may not collide with the first one, thus leading to a false positive. - // Is that possible? Investigate. - if _, found := s[key]; !found && (indirect || !ptrRecv(f)) { - s[key] = &Selection{MethodVal, nil, f, concat(index, i), indirect} - continue - } - } - s[key] = nil // collision - } - return s -} - -// ptrRecv reports whether the receiver is of the form *T. -func ptrRecv(f *Func) bool { - // If a method's receiver type is set, use that as the source of truth for the receiver. - // Caution: Checker.funcDecl (decl.go) marks a function by setting its type to an empty - // signature. We may reach here before the signature is fully set up: we must explicitly - // check if the receiver is set (we cannot just look for non-nil f.typ). - if sig, _ := f.typ.(*Signature); sig != nil && sig.recv != nil { - _, isPtr := deref(sig.recv.typ) - return isPtr - } - - // If a method's type is not set it may be a method/function that is: - // 1) client-supplied (via NewFunc with no signature), or - // 2) internally created but not yet type-checked. - // For case 1) we can't do anything; the client must know what they are doing. - // For case 2) we can use the information gathered by the resolver. - return f.hasPtrRecv -}