aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/types2/testdata/examples/types.go2
blob: 4ecc34dfa45beee8bcb44fb65c6200d8c0420593 (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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
// Copyright 2019 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.

// This file shows some examples of generic types.

package p

// List is just what it says - a slice of E elements.
type List[E any] []E

// A generic (parameterized) type must always be instantiated
// before it can be used to designate the type of a variable
// (including a struct field, or function parameter); though
// for the latter cases, the provided type may be another type
// parameter. So:
var _ List[byte] = []byte{}

// A generic binary tree might be declared as follows.
type Tree[E any] struct {
	left, right *Tree[E]
	payload E
}

// A simple instantiation of Tree:
var root1 Tree[int]

// The actual type parameter provided may be a generic type itself:
var root2 Tree[List[int]]

// A couple of more complex examples.
// We don't need extra parentheses around the element type of the slices on
// the right (unlike when we use ()'s rather than []'s for type parameters).
var _ List[List[int]] = []List[int]{}
var _ List[List[List[Tree[int]]]] = []List[List[Tree[int]]]{}

// Type parameters act like type aliases when used in generic types
// in the sense that we can "emulate" a specific type instantiation
// with type aliases.
type T1[P any] struct {
	f P
}

type T2[P any] struct {
	f struct {
		g P
	}
}

var x1 T1[struct{ g int }]
var x2 T2[int]

func _() {
	// This assignment is invalid because the types of x1, x2 are T1(...)
	// and T2(...) respectively, which are two different defined types.
	x1 = x2 // ERROR assignment

	// This assignment is valid because the types of x1.f and x2.f are
	// both struct { g int }; the type parameters act like type aliases
	// and their actual names don't come into play here.
	x1.f = x2.f
}

// We can verify this behavior using type aliases instead:
type T1a struct {
	f A1
}
type A1 = struct { g int }

type T2a struct {
	f struct {
		g A2
	}
}
type A2 = int

var x1a T1a
var x2a T2a

func _() {
	x1a = x2a // ERROR assignment
	x1a.f = x2a.f
}

// Another interesting corner case are generic types that don't use
// their type arguments. For instance:
type T[P any] struct{}

var xint T[int]
var xbool T[bool]

// Are these two variables of the same type? After all, their underlying
// types are identical. We consider them to be different because each type
// instantiation creates a new named type, in this case T<int> and T<bool>
// even if their underlying types are identical. This is sensible because
// we might still have methods that have different signatures or behave
// differently depending on the type arguments, and thus we can't possibly
// consider such types identical. Consequently:
func _() {
	xint = xbool // ERROR assignment
}

// Generic types cannot be used without instantiation.
var _ T // ERROR cannot use generic type T

// In type context, generic (parameterized) types cannot be parenthesized before
// being instantiated. See also NOTES entry from 12/4/2019.
var _ (T /* ERROR cannot use generic type T */ )[ /* ERROR unexpected \[ */ int]

// All types may be parameterized, including interfaces.
type I1[T any] interface{
	m1(T)
}

// There is no such thing as a variadic generic type.
type _[T ... /* ERROR invalid use of ... */ interface{}] struct{}

// Generic interfaces may be embedded as one would expect.
type I2 interface {
	I1(int)     // method!
	I1[string]  // embedded I1
}

func _() {
	var x I2
	x.I1(0)
	x.m1("foo")
}

type I0 interface {
	m0()
}

type I3 interface {
	I0
	I1[bool]
	m(string)
}

func _() {
	var x I3
	x.m0()
	x.m1(true)
	x.m("foo")
}

type _ struct {
	( /* ERROR cannot parenthesize */ int8)
	( /* ERROR cannot parenthesize */ *int16)
	*( /* ERROR cannot parenthesize */ int32)
	List[int]

	int8 /* ERROR int8 redeclared */
	* /* ERROR int16 redeclared */ int16
	List /* ERROR List redeclared */ [int]
}

// Issue #45639: We don't allow this anymore. Keep this code
//               in case we decide to revisit this decision.
//
// It's possible to declare local types whose underlying types
// are type parameters. As with ordinary type definitions, the
// types underlying properties are "inherited" but the methods
// are not.
// func _[T interface{ m(); ~int }]() {
// 	type L T
// 	var x L
// 
// 	// m is not defined on L (it is not "inherited" from
// 	// its underlying type).
// 	x.m /* ERROR x.m undefined */ ()
// 
// 	// But the properties of T, such that as that it supports
// 	// the operations of the types given by its type bound,
// 	// are also the properties of L.
// 	x++
// 	_ = x - x
// 
// 	// On the other hand, if we define a local alias for T,
// 	// that alias stands for T as expected.
// 	type A = T
// 	var y A
// 	y.m()
// 	_ = y < 0
// }

// It is not permitted to declare a local type whose underlying
// type is a type parameters not declared by that type declaration.
func _[T any]() {
	type _ T         // ERROR cannot use function type parameter T as RHS in type declaration
	type _ [_ any] T // ERROR cannot use function type parameter T as RHS in type declaration
}

// As a special case, an explicit type argument may be omitted
// from a type parameter bound if the type bound expects exactly
// one type argument. In that case, the type argument is the
// respective type parameter to which the type bound applies.
// Note: We may not permit this syntactic sugar at first.
// Note: This is now disabled. All examples below are adjusted.
type Adder[T any] interface {
	Add(T) T
}

// We don't need to explicitly instantiate the Adder bound
// if we have exactly one type parameter.
func Sum[T Adder[T]](list []T) T {
	var sum T
	for _, x := range list {
		sum = sum.Add(x)
	}
	return sum
}

// Valid and invalid variations.
type B0 interface {}
type B1[_ any] interface{}
type B2[_, _ any] interface{}

func _[T1 B0]()
func _[T1 B1[T1]]()
func _[T1 B2 /* ERROR cannot use generic type .* without instantiation */ ]()

func _[T1, T2 B0]()
func _[T1 B1[T1], T2 B1[T2]]()
func _[T1, T2 B2 /* ERROR cannot use generic type .* without instantiation */ ]()

func _[T1 B0, T2 B1[T2]]() // here B1 applies to T2

// When the type argument is left away, the type bound is
// instantiated for each type parameter with that type
// parameter.
// Note: We may not permit this syntactic sugar at first.
func _[A Adder[A], B Adder[B], C Adder[A]]() {
	var a A // A's type bound is Adder[A]
	a = a.Add(a)
	var b B // B's type bound is Adder[B]
	b = b.Add(b)
	var c C // C's type bound is Adder[A]
	a = c.Add(a)
}

// The type of variables (incl. parameters and return values) cannot
// be an interface with type constraints or be/embed comparable.
type I interface {
	~int
}

var (
	_ interface /* ERROR contains type constraints */ {~int}
	_ I /* ERROR contains type constraints */
)

func _(I /* ERROR contains type constraints */ )
func _(x, y, z I /* ERROR contains type constraints */ )
func _() I /* ERROR contains type constraints */

func _() {
	var _ I /* ERROR contains type constraints */
}

type C interface {
	comparable
}

var _ comparable /* ERROR comparable */
var _ C /* ERROR comparable */

func _(_ comparable /* ERROR comparable */ , _ C /* ERROR comparable */ )

func _() {
	var _ comparable /* ERROR comparable */
	var _ C /* ERROR comparable */
}

// Type parameters are never const types, i.e., it's
// not possible to declare a constant of type parameter type.
// (If a type list contains just a single const type, we could
// allow it, but such type lists don't make much sense in the
// first place.)
func _[T interface{~int|~float64}]() {
	// not valid
	const _ = T /* ERROR not constant */ (0)
	const _ T /* ERROR invalid constant type T */ = 1

	// valid
	var _ = T(0)
	var _ T = 1
	_ = T(0)
}