]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: document deterministic method index order and add test
authorRobert Griesemer <gri@golang.org>
Wed, 7 Feb 2024 21:58:38 +0000 (13:58 -0800)
committerGopher Robot <gobot@golang.org>
Thu, 8 Feb 2024 17:31:55 +0000 (17:31 +0000)
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>

src/cmd/compile/internal/types2/named.go
src/cmd/compile/internal/types2/named_test.go
src/go/types/named.go
src/go/types/named_test.go

index 57caef123f97281df8b93224a6c31addff108f35..5d7bdc764f76805ec291b577ee05f45341102828 100644 (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()
 
index 705dcaee27dbf6d6bbd1688e86e267e76a091ce6..25aea267927b5f1f674297bae0842f78d7599c47 100644 (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)
+                               }
+                       }
+               }
+       }
+}
index e053fed76ba368209a61e42b8af71fc6aa450327..0800d83217aecf6d7fe64673e0d5b183bbae5ecc 100644 (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()
 
index 8e00f6e0f9798581661f6aa5709e184e1e113da4..d930874f1201d48e7a031ed45f8464f52fc8f8e8 100644 (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)
+                               }
+                       }
+               }
+       }
+}