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

View as plain text