summaryrefslogtreecommitdiffstats
path: root/src/go/types/named_test.go
blob: 92f17e5455e735d6e600c7d68f5fe1aaf38585d9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// 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 types_test

import (
	"go/ast"
	"go/token"
	"testing"

	. "go/types"
)

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("p", src, 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()
				}
			})
		}
	})

	b.Run("NewMethodSet", func(b *testing.B) {
		for _, test := range tests {
			b.Run(test.name, func(b *testing.B) {
				// Access underlying once, to trigger any lazy calculation.
				_ = NewMethodSet(test.typ)
				b.ResetTimer()
				for i := 0; i < b.N; i++ {
					_ = NewMethodSet(test.typ)
				}
			})
		}
	})
}

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 golang/go#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]
`

	fset := token.NewFileSet()
	f := mustParse(fset, "foo.go", src)
	pkg := NewPackage("p", f.Name.Name)
	if err := NewChecker(nil, fset, pkg, nil).Files([]*ast.File{f}); err != nil {
		t.Fatal(err)
	}

	firstFieldType := func(n *Named) *Named {
		return n.Underlying().(*Struct).Field(0).Type().(*Pointer).Elem().(*Named)
	}

	Inst := 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)
	}
}