// 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 generic constraint interfaces.

package p

type MyInt int

type (
	// Arbitrary types may be embedded like interfaces.
	_ interface{int}
	_ interface{~int}

	// Types may be combined into a union.
	union interface{int|~string}

	// Union terms must describe disjoint (non-overlapping) type sets.
	_ interface{int|int /* ERROR "overlapping terms int" */ }
	_ interface{int|~ /* ERROR "overlapping terms ~int" */ int }
	_ interface{~int|~ /* ERROR "overlapping terms ~int" */ int }
	_ interface{~int|MyInt /* ERROR "overlapping terms p.MyInt and ~int" */ }
	_ interface{int|any}
	_ interface{int|~string|union}
	_ interface{int|~string|interface{int}}
	_ interface{union|int}   // interfaces (here: union) are ignored when checking for overlap
	_ interface{union|union} // ditto

	// For now we do not permit interfaces with methods in unions.
	_ interface{~ /* ERROR "invalid use of ~" */ any}
	_ interface{int|interface /* ERRORx `cannot use .* in union` */ { m() }}
)

type (
	// Tilde is not permitted on defined types or interfaces.
	foo int
	bar any
	_ interface{foo}
	_ interface{~ /* ERROR "invalid use of ~" */ foo }
	_ interface{~ /* ERROR "invalid use of ~" */ bar }
)

// Stand-alone type parameters are not permitted as elements or terms in unions.
type (
	_[T interface{ *T } ] struct{}        // ok
	_[T interface{ int | *T } ] struct{}  // ok
	_[T interface{ T /* ERROR "term cannot be a type parameter" */ } ] struct{}
	_[T interface{ ~T /* ERROR "type in term ~T cannot be a type parameter" */ } ] struct{}
	_[T interface{ int|T /* ERROR "term cannot be a type parameter" */ }] struct{}
)

// Multiple embedded union elements are intersected. The order in which they
// appear in the interface doesn't matter since intersection is a symmetric
// operation.

type myInt1 int
type myInt2 int

func _[T interface{ myInt1|myInt2; ~int }]() T { return T(0) }
func _[T interface{ ~int; myInt1|myInt2 }]() T { return T(0) }

// Here the intersections are empty - there's no type that's in the type set of T.
func _[T interface{ myInt1|myInt2; int }]() T { return T(0 /* ERROR "cannot convert" */ ) }
func _[T interface{ int; myInt1|myInt2 }]() T { return T(0 /* ERROR "cannot convert" */ ) }

// Union elements may be interfaces as long as they don't define
// any methods or embed comparable.

type (
	Integer interface{ ~int|~int8|~int16|~int32|~int64 }
	Unsigned interface{ ~uint|~uint8|~uint16|~uint32|~uint64 }
	Floats interface{ ~float32|~float64 }
	Complex interface{ ~complex64|~complex128 }
	Number interface{ Integer|Unsigned|Floats|Complex }
	Ordered interface{ Integer|Unsigned|Floats|~string }

	_ interface{ Number | error /* ERROR "cannot use error in union" */ }
	_ interface{ Ordered | comparable /* ERROR "cannot use comparable in union" */ }
)
