aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/types2/testdata/examples
diff options
context:
space:
mode:
authorRobert Griesemer <gri@golang.org>2021-04-28 15:08:47 -0700
committerRobert Griesemer <gri@golang.org>2021-04-29 15:54:27 +0000
commit948a262455346c6035df6bd5dc36c5d4b6aca409 (patch)
treea9facf2b7f8fabf909e759fd43df250e57979212 /src/cmd/compile/internal/types2/testdata/examples
parent12eaefead46d7ba10fce622f093d4c9b2989a5c0 (diff)
downloadgo-948a262455346c6035df6bd5dc36c5d4b6aca409.tar.gz
go-948a262455346c6035df6bd5dc36c5d4b6aca409.zip
cmd/compile/internal/types2: nest all test data under the testdata directory
This matches https://golang.org/cl/314829 for go/types. Change-Id: If3d127af0557bb13d504581920ea03e39db0eb07 Reviewed-on: https://go-review.googlesource.com/c/go/+/314772 Trust: Robert Griesemer <gri@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
Diffstat (limited to 'src/cmd/compile/internal/types2/testdata/examples')
-rw-r--r--src/cmd/compile/internal/types2/testdata/examples/functions.go2215
-rw-r--r--src/cmd/compile/internal/types2/testdata/examples/inference.go2101
-rw-r--r--src/cmd/compile/internal/types2/testdata/examples/methods.go296
-rw-r--r--src/cmd/compile/internal/types2/testdata/examples/types.go2279
4 files changed, 691 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/types2/testdata/examples/functions.go2 b/src/cmd/compile/internal/types2/testdata/examples/functions.go2
new file mode 100644
index 0000000000..0c2a408f02
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/examples/functions.go2
@@ -0,0 +1,215 @@
+// 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 type-parameterized functions.
+
+package p
+
+// Reverse is a generic function that takes a []T argument and
+// reverses that slice in place.
+func Reverse[T any](list []T) {
+ i := 0
+ j := len(list)-1
+ for i < j {
+ list[i], list[j] = list[j], list[i]
+ i++
+ j--
+ }
+}
+
+func _() {
+ // Reverse can be called with an explicit type argument.
+ Reverse[int](nil)
+ Reverse[string]([]string{"foo", "bar"})
+ Reverse[struct{x, y int}]([]struct{x, y int}{{1, 2}, {2, 3}, {3, 4}})
+
+ // Since the type parameter is used for an incoming argument,
+ // it can be inferred from the provided argument's type.
+ Reverse([]string{"foo", "bar"})
+ Reverse([]struct{x, y int}{{1, 2}, {2, 3}, {3, 4}})
+
+ // But the incoming argument must have a type, even if it's a
+ // default type. An untyped nil won't work.
+ // Reverse(nil) // this won't type-check
+
+ // A typed nil will work, though.
+ Reverse([]int(nil))
+}
+
+// Certain functions, such as the built-in `new` could be written using
+// type parameters.
+func new[T any]() *T {
+ var x T
+ return &x
+}
+
+// When calling our own `new`, we need to pass the type parameter
+// explicitly since there is no (value) argument from which the
+// result type could be inferred. We don't try to infer the
+// result type from the assignment to keep things simple and
+// easy to understand.
+var _ = new[int]()
+var _ *float64 = new[float64]() // the result type is indeed *float64
+
+// A function may have multiple type parameters, of course.
+func foo[A, B, C any](a A, b []B, c *C) B {
+ // do something here
+ return b[0]
+}
+
+// As before, we can pass type parameters explicitly.
+var s = foo[int, string, float64](1, []string{"first"}, new[float64]())
+
+// Or we can use type inference.
+var _ float64 = foo(42, []float64{1.0}, &s)
+
+// Type inference works in a straight-forward manner even
+// for variadic functions.
+func variadic[A, B any](A, B, ...B) int
+
+// var _ = variadic(1) // ERROR not enough arguments
+var _ = variadic(1, 2.3)
+var _ = variadic(1, 2.3, 3.4, 4.5)
+var _ = variadic[int, float64](1, 2.3, 3.4, 4)
+
+// Type inference also works in recursive function calls where
+// the inferred type is the type parameter of the caller.
+func f1[T any](x T) {
+ f1(x)
+}
+
+func f2a[T any](x, y T) {
+ f2a(x, y)
+}
+
+func f2b[T any](x, y T) {
+ f2b(y, x)
+}
+
+func g2a[P, Q any](x P, y Q) {
+ g2a(x, y)
+}
+
+func g2b[P, Q any](x P, y Q) {
+ g2b(y, x)
+}
+
+// Here's an example of a recursive function call with variadic
+// arguments and type inference inferring the type parameter of
+// the caller (i.e., itself).
+func max[T interface{ type int }](x ...T) T {
+ var x0 T
+ if len(x) > 0 {
+ x0 = x[0]
+ }
+ if len(x) > 1 {
+ x1 := max(x[1:]...)
+ if x1 > x0 {
+ return x1
+ }
+ }
+ return x0
+}
+
+// When inferring channel types, the channel direction is ignored
+// for the purpose of type inference. Once the type has been in-
+// fered, the usual parameter passing rules are applied.
+// Thus even if a type can be inferred successfully, the function
+// call may not be valid.
+
+func fboth[T any](chan T)
+func frecv[T any](<-chan T)
+func fsend[T any](chan<- T)
+
+func _() {
+ var both chan int
+ var recv <-chan int
+ var send chan<-int
+
+ fboth(both)
+ fboth(recv /* ERROR cannot use */ )
+ fboth(send /* ERROR cannot use */ )
+
+ frecv(both)
+ frecv(recv)
+ frecv(send /* ERROR cannot use */ )
+
+ fsend(both)
+ fsend(recv /* ERROR cannot use */)
+ fsend(send)
+}
+
+func ffboth[T any](func(chan T))
+func ffrecv[T any](func(<-chan T))
+func ffsend[T any](func(chan<- T))
+
+func _() {
+ var both func(chan int)
+ var recv func(<-chan int)
+ var send func(chan<- int)
+
+ ffboth(both)
+ ffboth(recv /* ERROR cannot use */ )
+ ffboth(send /* ERROR cannot use */ )
+
+ ffrecv(both /* ERROR cannot use */ )
+ ffrecv(recv)
+ ffrecv(send /* ERROR cannot use */ )
+
+ ffsend(both /* ERROR cannot use */ )
+ ffsend(recv /* ERROR cannot use */ )
+ ffsend(send)
+}
+
+// When inferring elements of unnamed composite parameter types,
+// if the arguments are defined types, use their underlying types.
+// Even though the matching types are not exactly structurally the
+// same (one is a type literal, the other a named type), because
+// assignment is permitted, parameter passing is permitted as well,
+// so type inference should be able to handle these cases well.
+
+func g1[T any]([]T)
+func g2[T any]([]T, T)
+func g3[T any](*T, ...T)
+
+func _() {
+ type intSlize []int
+ g1([]int{})
+ g1(intSlize{})
+ g2(nil, 0)
+
+ type myString string
+ var s1 string
+ g3(nil, "1", myString("2"), "3")
+ g3(&s1, "1", myString /* ERROR does not match */ ("2"), "3")
+ _ = s1
+
+ type myStruct struct{x int}
+ var s2 myStruct
+ g3(nil, struct{x int}{}, myStruct{})
+ g3(&s2, struct{x int}{}, myStruct{})
+ g3(nil, myStruct{}, struct{x int}{})
+ g3(&s2, myStruct{}, struct{x int}{})
+}
+
+// Here's a realistic example.
+
+func append[T any](s []T, t ...T) []T
+
+func _() {
+ var f func()
+ type Funcs []func()
+ var funcs Funcs
+ _ = append(funcs, f)
+}
+
+// Generic type declarations cannot have empty type parameter lists
+// (that would indicate a slice type). Thus, generic functions cannot
+// have empty type parameter lists, either. This is a syntax error.
+
+func h[] /* ERROR empty type parameter list */ ()
+
+func _() {
+ h[] /* ERROR operand */ ()
+}
diff --git a/src/cmd/compile/internal/types2/testdata/examples/inference.go2 b/src/cmd/compile/internal/types2/testdata/examples/inference.go2
new file mode 100644
index 0000000000..b47ce75805
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/examples/inference.go2
@@ -0,0 +1,101 @@
+// Copyright 2021 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 type inference.
+
+package p
+
+type Ordered interface {
+ type int, float64, string
+}
+
+func min[T Ordered](x, y T) T
+
+func _() {
+ // min can be called with explicit instantiation.
+ _ = min[int](1, 2)
+
+ // Alternatively, the type argument can be inferred from
+ // one of the arguments. Untyped arguments will be considered
+ // last.
+ var x int
+ _ = min(x, x)
+ _ = min(x, 1)
+ _ = min(x, 1.0)
+ _ = min(1, 2)
+ _ = min(1, 2.3 /* ERROR default type float64 .* does not match */ )
+
+ var y float64
+ _ = min(1, y)
+ _ = min(1.2, y)
+ _ = min(1.2, 3.4)
+ _ = min(1.2, 3 /* ERROR default type int .* does not match */ )
+
+ var s string
+ _ = min(s, "foo")
+ _ = min("foo", "bar")
+}
+
+func mixed[T1, T2, T3 any](T1, T2, T3)
+
+func _() {
+ // mixed can be called with explicit instantiation.
+ mixed[int, string, bool](0, "", false)
+
+ // Alternatively, partial type arguments may be provided
+ // (from left to right), and the other may be inferred.
+ mixed[int, string](0, "", false)
+ mixed[int](0, "", false)
+ mixed(0, "", false)
+
+ // Provided type arguments always take precedence over
+ // inferred types.
+ mixed[int, string](1.1 /* ERROR cannot use 1.1 */ , "", false)
+}
+
+func related1[Slice interface{type []Elem}, Elem any](s Slice, e Elem)
+
+func _() {
+ // related1 can be called with explicit instantiation.
+ var si []int
+ related1[[]int, int](si, 0)
+
+ // Alternatively, the 2nd type argument can be inferred
+ // from the first one through constraint type inference.
+ var ss []string
+ _ = related1[[]string]
+ related1[[]string](ss, "foo")
+
+ // A type argument inferred from another explicitly provided
+ // type argument overrides whatever value argument type is given.
+ related1[[]string](ss, 0 /* ERROR cannot use 0 */ )
+
+ // A type argument may be inferred from a value argument
+ // and then help infer another type argument via constraint
+ // type inference.
+ related1(si, 0)
+ related1(si, "foo" /* ERROR cannot use "foo" */ )
+}
+
+func related2[Elem any, Slice interface{type []Elem}](e Elem, s Slice)
+
+func _() {
+ // related2 can be called with explicit instantiation.
+ var si []int
+ related2[int, []int](0, si)
+
+ // Alternatively, the 2nd type argument can be inferred
+ // from the first one through constraint type inference.
+ var ss []string
+ _ = related2[string]
+ related2[string]("foo", ss)
+
+ // A type argument may be inferred from a value argument
+ // and then help infer another type argument via constraint
+ // type inference. Untyped arguments are always considered
+ // last.
+ related2(1.2, []float64{})
+ related2(1.0, []int{})
+ related2( /* ERROR does not satisfy */ float64(1.0), []int{}) // TODO(gri) fix error position
+}
diff --git a/src/cmd/compile/internal/types2/testdata/examples/methods.go2 b/src/cmd/compile/internal/types2/testdata/examples/methods.go2
new file mode 100644
index 0000000000..76c6539e1b
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/examples/methods.go2
@@ -0,0 +1,96 @@
+// 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 methods on type-parameterized types.
+
+package p
+
+// Parameterized types may have methods.
+type T1[A any] struct{ a A }
+
+// When declaring a method for a parameterized type, the "instantiated"
+// receiver type acts as an implicit declaration of the type parameters
+// for the receiver type. In the example below, method m1 on type T1 has
+// the receiver type T1[A] which declares the type parameter A for use
+// with this method. That is, within the method m1, A stands for the
+// actual type argument provided to an instantiated T1.
+func (t T1[A]) m1() A { return t.a }
+
+// For instance, if T1 is instantiated with the type int, the type
+// parameter A in m1 assumes that type (int) as well and we can write
+// code like this:
+var x T1[int]
+var _ int = x.m1()
+
+// Because the type parameter provided to a parameterized receiver type
+// is declared through that receiver declaration, it must be an identifier.
+// It cannot possibly be some other type because the receiver type is not
+// instantiated with concrete types, it is standing for the parameterized
+// receiver type.
+func (t T1[[ /* ERROR must be an identifier */ ]int]) m2() {}
+
+// Note that using what looks like a predeclared identifier, say int,
+// as type parameter in this situation is deceptive and considered bad
+// style. In m3 below, int is the name of the local receiver type parameter
+// and it shadows the predeclared identifier int which then cannot be used
+// anymore as expected.
+// This is no different from locally redelaring a predeclared identifier
+// and usually should be avoided. There are some notable exceptions; e.g.,
+// sometimes it makes sense to use the identifier "copy" which happens to
+// also be the name of a predeclared built-in function.
+func (t T1[int]) m3() { var _ int = 42 /* ERROR cannot use 42 .* as int */ }
+
+// The names of the type parameters used in a parameterized receiver
+// type don't have to match the type parameter names in the declaration
+// of the type used for the receiver. In our example, even though T1 is
+// declared with type parameter named A, methods using that receiver type
+// are free to use their own name for that type parameter. That is, the
+// name of type parameters is always local to the declaration where they
+// are introduced. In our example we can write a method m2 and use the
+// name X instead of A for the type parameter w/o any difference.
+func (t T1[X]) m4() X { return t.a }
+
+// If the receiver type is parameterized, type parameters must always be
+// provided: this simply follows from the general rule that a parameterized
+// type must be instantiated before it can be used. A method receiver
+// declaration using a parameterized receiver type is no exception. It is
+// simply that such receiver type expressions perform two tasks simultaneously:
+// they declare the (local) type parameters and then use them to instantiate
+// the receiver type. Forgetting to provide a type parameter leads to an error.
+func (t T1 /* ERROR generic type .* without instantiation */ ) m5() {}
+
+// However, sometimes we don't need the type parameter, and thus it is
+// inconvenient to have to choose a name. Since the receiver type expression
+// serves as a declaration for its type parameters, we are free to choose the
+// blank identifier:
+func (t T1[_]) m6() {}
+
+// Naturally, these rules apply to any number of type parameters on the receiver
+// type. Here are some more complex examples.
+type T2[A, B, C any] struct {
+ a A
+ b B
+ c C
+}
+
+// Naming of the type parameters is local and has no semantic impact:
+func (t T2[A, B, C]) m1() (A, B, C) { return t.a, t.b, t.c }
+func (t T2[C, B, A]) m2() (C, B, A) { return t.a, t.b, t.c }
+func (t T2[X, Y, Z]) m3() (X, Y, Z) { return t.a, t.b, t.c }
+
+// Type parameters may be left blank if they are not needed:
+func (t T2[A, _, C]) m4() (A, C) { return t.a, t.c }
+func (t T2[_, _, X]) m5() X { return t.c }
+func (t T2[_, _, _]) m6() {}
+
+// As usual, blank names may be used for any object which we don't care about
+// using later. For instance, we may write an unnamed method with a receiver
+// that cannot be accessed:
+func (_ T2[_, _, _]) _() int { return 42 }
+
+// Because a receiver parameter list is simply a parameter list, we can
+// leave the receiver argument away for receiver types.
+type T0 struct{}
+func (T0) _() {}
+func (T1[A]) _() {}
diff --git a/src/cmd/compile/internal/types2/testdata/examples/types.go2 b/src/cmd/compile/internal/types2/testdata/examples/types.go2
new file mode 100644
index 0000000000..a7825ed2d9
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/examples/types.go2
@@ -0,0 +1,279 @@
+// 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]
+}
+
+// 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(); type 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
+}
+
+// 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 {
+ type int
+}
+
+var (
+ _ interface /* ERROR contains type constraints */ {type 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 { type 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)
+}