Source file src/go/types/named_test.go

     1  // Copyright 2022 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package types_test
     6  
     7  import (
     8  	"go/ast"
     9  	"go/token"
    10  	"testing"
    11  
    12  	. "go/types"
    13  )
    14  
    15  func BenchmarkNamed(b *testing.B) {
    16  	const src = `
    17  package p
    18  
    19  type T struct {
    20  	P int
    21  }
    22  
    23  func (T) M(int) {}
    24  func (T) N() (i int) { return }
    25  
    26  type G[P any] struct {
    27  	F P
    28  }
    29  
    30  func (G[P]) M(P) {}
    31  func (G[P]) N() (p P) { return }
    32  
    33  type Inst = G[int]
    34  	`
    35  	pkg := mustTypecheck(src, nil, nil)
    36  
    37  	var (
    38  		T        = pkg.Scope().Lookup("T").Type()
    39  		G        = pkg.Scope().Lookup("G").Type()
    40  		SrcInst  = pkg.Scope().Lookup("Inst").Type()
    41  		UserInst = mustInstantiate(b, G, Typ[Int])
    42  	)
    43  
    44  	tests := []struct {
    45  		name string
    46  		typ  Type
    47  	}{
    48  		{"nongeneric", T},
    49  		{"generic", G},
    50  		{"src instance", SrcInst},
    51  		{"user instance", UserInst},
    52  	}
    53  
    54  	b.Run("Underlying", func(b *testing.B) {
    55  		for _, test := range tests {
    56  			b.Run(test.name, func(b *testing.B) {
    57  				// Access underlying once, to trigger any lazy calculation.
    58  				_ = test.typ.Underlying()
    59  				b.ResetTimer()
    60  				for i := 0; i < b.N; i++ {
    61  					_ = test.typ.Underlying()
    62  				}
    63  			})
    64  		}
    65  	})
    66  
    67  	b.Run("NewMethodSet", func(b *testing.B) {
    68  		for _, test := range tests {
    69  			b.Run(test.name, func(b *testing.B) {
    70  				// Access underlying once, to trigger any lazy calculation.
    71  				_ = NewMethodSet(test.typ)
    72  				b.ResetTimer()
    73  				for i := 0; i < b.N; i++ {
    74  					_ = NewMethodSet(test.typ)
    75  				}
    76  			})
    77  		}
    78  	})
    79  }
    80  
    81  func mustInstantiate(tb testing.TB, orig Type, targs ...Type) Type {
    82  	inst, err := Instantiate(nil, orig, targs, true)
    83  	if err != nil {
    84  		tb.Fatal(err)
    85  	}
    86  	return inst
    87  }
    88  
    89  // Test that types do not expand infinitely, as in go.dev/issue/52715.
    90  func TestFiniteTypeExpansion(t *testing.T) {
    91  	const src = `
    92  package p
    93  
    94  type Tree[T any] struct {
    95  	*Node[T]
    96  }
    97  
    98  func (*Tree[R]) N(r R) R { return r }
    99  
   100  type Node[T any] struct {
   101  	*Tree[T]
   102  }
   103  
   104  func (Node[Q]) M(Q) {}
   105  
   106  type Inst = *Tree[int]
   107  `
   108  
   109  	fset := token.NewFileSet()
   110  	f := mustParse(fset, src)
   111  	pkg := NewPackage("p", f.Name.Name)
   112  	if err := NewChecker(nil, fset, pkg, nil).Files([]*ast.File{f}); err != nil {
   113  		t.Fatal(err)
   114  	}
   115  
   116  	firstFieldType := func(n *Named) *Named {
   117  		return n.Underlying().(*Struct).Field(0).Type().(*Pointer).Elem().(*Named)
   118  	}
   119  
   120  	Inst := Unalias(pkg.Scope().Lookup("Inst").Type()).(*Pointer).Elem().(*Named)
   121  	Node := firstFieldType(Inst)
   122  	Tree := firstFieldType(Node)
   123  	if !Identical(Inst, Tree) {
   124  		t.Fatalf("Not a cycle: got %v, want %v", Tree, Inst)
   125  	}
   126  	if Inst != Tree {
   127  		t.Errorf("Duplicate instances in cycle: %s (%p) -> %s (%p) -> %s (%p)", Inst, Inst, Node, Node, Tree, Tree)
   128  	}
   129  }
   130  
   131  // TestMethodOrdering is a simple test verifying that the indices of methods of
   132  // a named type remain the same as long as the same source and AddMethod calls
   133  // are presented to the type checker in the same order (go.dev/issue/61298).
   134  func TestMethodOrdering(t *testing.T) {
   135  	const src = `
   136  package p
   137  
   138  type T struct{}
   139  
   140  func (T) a() {}
   141  func (T) c() {}
   142  func (T) b() {}
   143  `
   144  	// should get the same method order each time
   145  	var methods []string
   146  	for i := 0; i < 5; i++ {
   147  		// collect T methods as provided in src
   148  		pkg := mustTypecheck(src, nil, nil)
   149  		T := pkg.Scope().Lookup("T").Type().(*Named)
   150  
   151  		// add a few more methods manually
   152  		for _, name := range []string{"foo", "bar", "bal"} {
   153  			m := NewFunc(nopos, pkg, name, nil /* don't care about signature */)
   154  			T.AddMethod(m)
   155  		}
   156  
   157  		// check method order
   158  		if i == 0 {
   159  			// first round: collect methods in given order
   160  			methods = make([]string, T.NumMethods())
   161  			for j := range methods {
   162  				methods[j] = T.Method(j).Name()
   163  			}
   164  		} else {
   165  			// successive rounds: methods must appear in the same order
   166  			if got := T.NumMethods(); got != len(methods) {
   167  				t.Errorf("got %d methods, want %d", got, len(methods))
   168  				continue
   169  			}
   170  			for j, m := range methods {
   171  				if got := T.Method(j).Name(); got != m {
   172  					t.Errorf("got method %s, want %s", got, m)
   173  				}
   174  			}
   175  		}
   176  	}
   177  }
   178  

View as plain text