blob: 760930609948078569a97fb86179c2e7b38e888a [file] [log] [blame]
// Copyright 2022 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.
package types2_test
import (
"testing"
"cmd/compile/internal/syntax"
. "cmd/compile/internal/types2"
)
func BenchmarkNamed(b *testing.B) {
const src = `
package p
type T struct {
P int
}
func (T) M(int) {}
func (T) N() (i int) { return }
type G[P any] struct {
F P
}
func (G[P]) M(P) {}
func (G[P]) N() (p P) { return }
type Inst = G[int]
`
pkg := mustTypecheck(src, nil, nil)
var (
T = pkg.Scope().Lookup("T").Type()
G = pkg.Scope().Lookup("G").Type()
SrcInst = pkg.Scope().Lookup("Inst").Type()
UserInst = mustInstantiate(b, G, Typ[Int])
)
tests := []struct {
name string
typ Type
}{
{"nongeneric", T},
{"generic", G},
{"src instance", SrcInst},
{"user instance", UserInst},
}
b.Run("Underlying", func(b *testing.B) {
for _, test := range tests {
b.Run(test.name, func(b *testing.B) {
// Access underlying once, to trigger any lazy calculation.
_ = test.typ.Underlying()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = test.typ.Underlying()
}
})
}
})
}
func mustInstantiate(tb testing.TB, orig Type, targs ...Type) Type {
inst, err := Instantiate(nil, orig, targs, true)
if err != nil {
tb.Fatal(err)
}
return inst
}
// Test that types do not expand infinitely, as in go.dev/issue/52715.
func TestFiniteTypeExpansion(t *testing.T) {
const src = `
package p
type Tree[T any] struct {
*Node[T]
}
func (*Tree[R]) N(r R) R { return r }
type Node[T any] struct {
*Tree[T]
}
func (Node[Q]) M(Q) {}
type Inst = *Tree[int]
`
f := mustParse(src)
pkg := NewPackage("p", f.PkgName.Value)
if err := NewChecker(nil, pkg, nil).Files([]*syntax.File{f}); err != nil {
t.Fatal(err)
}
firstFieldType := func(n *Named) *Named {
return n.Underlying().(*Struct).Field(0).Type().(*Pointer).Elem().(*Named)
}
Inst := Unalias(pkg.Scope().Lookup("Inst").Type()).(*Pointer).Elem().(*Named)
Node := firstFieldType(Inst)
Tree := firstFieldType(Node)
if !Identical(Inst, Tree) {
t.Fatalf("Not a cycle: got %v, want %v", Tree, Inst)
}
if Inst != Tree {
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)
}
}
}
}
}