constraints: new package

Move to exp from the pre-1.18 standard library.

For golang/go#45458
For golang/go#50792

Change-Id: I70f054c9d68c20e98e750f6501f3eabd04a70f17
Reviewed-on: https://go-review.googlesource.com/c/exp/+/382535
Trust: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/constraints/constraints.go b/constraints/constraints.go
new file mode 100644
index 0000000..2c033df
--- /dev/null
+++ b/constraints/constraints.go
@@ -0,0 +1,50 @@
+// 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.
+
+// Package constraints defines a set of useful constraints to be used
+// with type parameters.
+package constraints
+
+// Signed is a constraint that permits any signed integer type.
+// If future releases of Go add new predeclared signed integer types,
+// this constraint will be modified to include them.
+type Signed interface {
+	~int | ~int8 | ~int16 | ~int32 | ~int64
+}
+
+// Unsigned is a constraint that permits any unsigned integer type.
+// If future releases of Go add new predeclared unsigned integer types,
+// this constraint will be modified to include them.
+type Unsigned interface {
+	~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
+}
+
+// Integer is a constraint that permits any integer type.
+// If future releases of Go add new predeclared integer types,
+// this constraint will be modified to include them.
+type Integer interface {
+	Signed | Unsigned
+}
+
+// Float is a constraint that permits any floating-point type.
+// If future releases of Go add new predeclared floating-point types,
+// this constraint will be modified to include them.
+type Float interface {
+	~float32 | ~float64
+}
+
+// Complex is a constraint that permits any complex numeric type.
+// If future releases of Go add new predeclared complex numeric types,
+// this constraint will be modified to include them.
+type Complex interface {
+	~complex64 | ~complex128
+}
+
+// Ordered is a constraint that permits any ordered type: any type
+// that supports the operators < <= >= >.
+// If future releases of Go add new ordered types,
+// this constraint will be modified to include them.
+type Ordered interface {
+	Integer | Float | ~string
+}
diff --git a/constraints/constraints_test.go b/constraints/constraints_test.go
new file mode 100644
index 0000000..a285ee6
--- /dev/null
+++ b/constraints/constraints_test.go
@@ -0,0 +1,129 @@
+// 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.
+
+package constraints
+
+import (
+	"bytes"
+	"fmt"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"runtime"
+	"testing"
+)
+
+type (
+	testSigned[T Signed]     struct{ f T }
+	testUnsigned[T Unsigned] struct{ f T }
+	testInteger[T Integer]   struct{ f T }
+	testFloat[T Float]       struct{ f T }
+	testComplex[T Complex]   struct{ f T }
+	testOrdered[T Ordered]   struct{ f T }
+)
+
+// TestTypes passes if it compiles.
+type TestTypes struct {
+	_ testSigned[int]
+	_ testSigned[int64]
+	_ testUnsigned[uint]
+	_ testUnsigned[uintptr]
+	_ testInteger[int8]
+	_ testInteger[uint8]
+	_ testInteger[uintptr]
+	_ testFloat[float32]
+	_ testComplex[complex64]
+	_ testOrdered[int]
+	_ testOrdered[float64]
+	_ testOrdered[string]
+}
+
+var prolog = []byte(`
+package constrainttest
+
+import "constraints"
+
+type (
+	testSigned[T constraints.Signed]     struct{ f T }
+	testUnsigned[T constraints.Unsigned] struct{ f T }
+	testInteger[T constraints.Integer]   struct{ f T }
+	testFloat[T constraints.Float]       struct{ f T }
+	testComplex[T constraints.Complex]   struct{ f T }
+	testOrdered[T constraints.Ordered]   struct{ f T }
+)
+`)
+
+func TestFailure(t *testing.T) {
+	switch runtime.GOOS {
+	case "android", "js", "ios":
+		t.Skipf("can't run go tool on %s", runtime.GOOS)
+	}
+
+	var exeSuffix string
+	if runtime.GOOS == "windows" {
+		exeSuffix = ".exe"
+	}
+	gocmd := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix)
+	if _, err := os.Stat(gocmd); err != nil {
+		t.Skipf("skipping because can't stat %s: %v", gocmd, err)
+	}
+
+	tmpdir := t.TempDir()
+
+	if err := os.WriteFile(filepath.Join(tmpdir, "go.mod"), []byte("module constraintest"), 0666); err != nil {
+		t.Fatal(err)
+	}
+
+	// Test for types that should not satisfy a constraint.
+	// For each pair of constraint and type, write a Go file
+	//     var V constraint[type]
+	// For example,
+	//     var V testSigned[uint]
+	// This should not compile, as testSigned (above) uses
+	// constraints.Signed, and uint does not satisfy that constraint.
+	// Therefore, the build of that code should fail.
+	for i, test := range []struct {
+		constraint, typ string
+	}{
+		{"testSigned", "uint"},
+		{"testUnsigned", "int"},
+		{"testInteger", "float32"},
+		{"testFloat", "int8"},
+		{"testComplex", "float64"},
+		{"testOrdered", "bool"},
+	} {
+		i := i
+		test := test
+		t.Run(fmt.Sprintf("%s %d", test.constraint, i), func(t *testing.T) {
+			t.Parallel()
+			name := fmt.Sprintf("go%d.go", i)
+			f, err := os.Create(filepath.Join(tmpdir, name))
+			if err != nil {
+				t.Fatal(err)
+			}
+			if _, err := f.Write(prolog); err != nil {
+				t.Fatal(err)
+			}
+			if _, err := fmt.Fprintf(f, "var V %s[%s]\n", test.constraint, test.typ); err != nil {
+				t.Fatal(err)
+			}
+			if err := f.Close(); err != nil {
+				t.Fatal(err)
+			}
+			cmd := exec.Command(gocmd, "build", name)
+			cmd.Dir = tmpdir
+			if out, err := cmd.CombinedOutput(); err == nil {
+				t.Error("build succeeded, but expected to fail")
+			} else if len(out) > 0 {
+				t.Logf("%s", out)
+				const want = "does not implement"
+				if !bytes.Contains(out, []byte(want)) {
+					t.Errorf("output does not include %q", want)
+				}
+			} else {
+				t.Error("no error output, expected something")
+			}
+		})
+	}
+}
diff --git a/slices/slices.go b/slices/slices.go
index 7fe3412..55f39e7 100644
--- a/slices/slices.go
+++ b/slices/slices.go
@@ -7,7 +7,7 @@
 // of a slice at index 0 <= i < len(s).
 package slices
 
-import "constraints"
+import "golang.org/x/exp/constraints"
 
 // Equal reports whether two slices are equal: the same length and all
 // elements equal. If the lengths are different, Equal returns false.
diff --git a/slices/slices_test.go b/slices/slices_test.go
index d511a88..14ae6b8 100644
--- a/slices/slices_test.go
+++ b/slices/slices_test.go
@@ -5,10 +5,11 @@
 package slices
 
 import (
-	"constraints"
 	"math"
 	"strings"
 	"testing"
+
+	"golang.org/x/exp/constraints"
 )
 
 var equalIntTests = []struct {
diff --git a/slices/sort.go b/slices/sort.go
index 64f334f..ed9f41a 100644
--- a/slices/sort.go
+++ b/slices/sort.go
@@ -4,7 +4,7 @@
 
 package slices
 
-import "constraints"
+import "golang.org/x/exp/constraints"
 
 // Sort sorts a slice of any ordered type in ascending order.
 func Sort[Elem constraints.Ordered](x []Elem) {
diff --git a/slices/zsortordered.go b/slices/zsortordered.go
index 1667de0..6fa64a2 100644
--- a/slices/zsortordered.go
+++ b/slices/zsortordered.go
@@ -6,7 +6,7 @@
 
 package slices
 
-import "constraints"
+import "golang.org/x/exp/constraints"
 
 // insertionSortOrdered sorts data[a:b] using insertion sort.
 func insertionSortOrdered[Elem constraints.Ordered](data []Elem, a, b int) {