go/types, types2: document deterministic method index order and add test

Fixes #61298.

Change-Id: Ie2f930752867710884ace3990447866e785ebf1c
Reviewed-on: https://go-review.googlesource.com/c/go/+/562347
Reviewed-by: Robert Griesemer <gri@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Griesemer <gri@google.com>
Auto-Submit: Robert Griesemer <gri@google.com>
This commit is contained in:
Robert Griesemer 2024-02-07 13:58:38 -08:00 committed by Gopher Robot
parent 1400b26852
commit 210f051d6b
4 changed files with 108 additions and 0 deletions

View File

@ -335,6 +335,12 @@ func (t *Named) NumMethods() int {
// For an ordinary or instantiated type t, the receiver base type of this
// method is the named type t. For an uninstantiated generic type t, each
// method receiver is instantiated with its receiver type parameters.
//
// Methods are numbered deterministically: given the same list of source files
// presented to the type checker, or the same sequence of NewMethod and AddMethod
// calls, the mapping from method index to corresponding method remains the same.
// But the specific ordering is not specified and must not be relied on as it may
// change in the future.
func (t *Named) Method(i int) *Func {
t.resolve()

View File

@ -112,3 +112,51 @@ type Inst = *Tree[int]
t.Errorf("Duplicate instances in cycle: %s (%p) -> %s (%p) -> %s (%p)", Inst, Inst, Node, Node, Tree, Tree)
}
}
// TestMethodOrdering is a simple test verifying that the indices of methods of
// a named type remain the same as long as the same source and AddMethod calls
// are presented to the type checker in the same order (go.dev/issue/61298).
func TestMethodOrdering(t *testing.T) {
const src = `
package p
type T struct{}
func (T) a() {}
func (T) c() {}
func (T) b() {}
`
// should get the same method order each time
var methods []string
for i := 0; i < 5; i++ {
// collect T methods as provided in src
pkg := mustTypecheck(src, nil, nil)
T := pkg.Scope().Lookup("T").Type().(*Named)
// add a few more methods manually
for _, name := range []string{"foo", "bar", "bal"} {
m := NewFunc(nopos, pkg, name, nil /* don't care about signature */)
T.AddMethod(m)
}
// check method order
if i == 0 {
// first round: collect methods in given order
methods = make([]string, T.NumMethods())
for j := range methods {
methods[j] = T.Method(j).Name()
}
} else {
// successive rounds: methods must appear in the same order
if got := T.NumMethods(); got != len(methods) {
t.Errorf("got %d methods, want %d", got, len(methods))
continue
}
for j, m := range methods {
if got := T.Method(j).Name(); got != m {
t.Errorf("got method %s, want %s", got, m)
}
}
}
}
}

View File

@ -337,6 +337,12 @@ func (t *Named) NumMethods() int {
// For an ordinary or instantiated type t, the receiver base type of this
// method is the named type t. For an uninstantiated generic type t, each
// method receiver is instantiated with its receiver type parameters.
//
// Methods are numbered deterministically: given the same list of source files
// presented to the type checker, or the same sequence of NewMethod and AddMethod
// calls, the mapping from method index to corresponding method remains the same.
// But the specific ordering is not specified and must not be relied on as it may
// change in the future.
func (t *Named) Method(i int) *Func {
t.resolve()

View File

@ -127,3 +127,51 @@ type Inst = *Tree[int]
t.Errorf("Duplicate instances in cycle: %s (%p) -> %s (%p) -> %s (%p)", Inst, Inst, Node, Node, Tree, Tree)
}
}
// TestMethodOrdering is a simple test verifying that the indices of methods of
// a named type remain the same as long as the same source and AddMethod calls
// are presented to the type checker in the same order (go.dev/issue/61298).
func TestMethodOrdering(t *testing.T) {
const src = `
package p
type T struct{}
func (T) a() {}
func (T) c() {}
func (T) b() {}
`
// should get the same method order each time
var methods []string
for i := 0; i < 5; i++ {
// collect T methods as provided in src
pkg := mustTypecheck(src, nil, nil)
T := pkg.Scope().Lookup("T").Type().(*Named)
// add a few more methods manually
for _, name := range []string{"foo", "bar", "bal"} {
m := NewFunc(nopos, pkg, name, nil /* don't care about signature */)
T.AddMethod(m)
}
// check method order
if i == 0 {
// first round: collect methods in given order
methods = make([]string, T.NumMethods())
for j := range methods {
methods[j] = T.Method(j).Name()
}
} else {
// successive rounds: methods must appear in the same order
if got := T.NumMethods(); got != len(methods) {
t.Errorf("got %d methods, want %d", got, len(methods))
continue
}
for j, m := range methods {
if got := T.Method(j).Name(); got != m {
t.Errorf("got method %s, want %s", got, m)
}
}
}
}
}