Skip to content

Commit 4dd5f09

Browse files
constraints: new package
The constraint packages defined a set of useful constraints to be used with type parameters. Fixes #45458 Change-Id: Id4f4e6c55debb90e6b10ea0dbe2319be1e888746 Reviewed-on: https://go-review.googlesource.com/c/go/+/349709 Trust: Ian Lance Taylor <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Robert Griesemer <[email protected]>
1 parent c90ead9 commit 4dd5f09

File tree

3 files changed

+221
-1
lines changed

3 files changed

+221
-1
lines changed

src/constraints/constraints.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright 2021 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// Package constraints defines a set of useful constraints to be used
6+
// with type parameters.
7+
package constraints
8+
9+
// Signed is a constraint that permits any signed integer type.
10+
// If future releases of Go add new predeclared signed integer types,
11+
// this constraint will be modified to include them.
12+
type Signed interface {
13+
~int | ~int8 | ~int16 | ~int32 | ~int64
14+
}
15+
16+
// Unsigned is a constraint that permits any unsigned integer type.
17+
// If future releases of Go add new predeclared unsigned integer types,
18+
// this constraint will be modified to include them.
19+
type Unsigned interface {
20+
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
21+
}
22+
23+
// Integer is a constraint that permits any integer type.
24+
// If future releases of Go add new predeclared integer types,
25+
// this constraint will be modified to include them.
26+
type Integer interface {
27+
Signed | Unsigned
28+
}
29+
30+
// Float is a constraint that permits any floating-point type.
31+
// If future releases of Go add new predeclared floating-point types,
32+
// this constraint will be modified to include them.
33+
type Float interface {
34+
~float32 | ~float64
35+
}
36+
37+
// Complex is a constraint that permits any complex numeric type.
38+
// If future releases of Go add new predeclared complex numeric types,
39+
// this constraint will be modified to include them.
40+
type Complex interface {
41+
~complex64 | ~complex128
42+
}
43+
44+
// Ordered is a constraint that permits any ordered type: any type
45+
// that supports the operators < <= >= >.
46+
// If future releases of Go add new ordered types,
47+
// this constraint will be modified to include them.
48+
type Ordered interface {
49+
Integer | Float | ~string
50+
}
51+
52+
// Slice is a constraint that matches slices of any element type.
53+
type Slice[Elem any] interface {
54+
~[]Elem
55+
}
56+
57+
// Map is a constraint that matches maps of any element and value type.
58+
type Map[Key comparable, Val any] interface {
59+
~map[Key]Val
60+
}
61+
62+
// Chan is a constraint that matches channels of any element type.
63+
type Chan[Elem any] interface {
64+
~chan Elem
65+
}

src/constraints/constraints_test.go

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
// Copyright 2021 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package constraints
6+
7+
import (
8+
"bytes"
9+
"fmt"
10+
"internal/testenv"
11+
"os"
12+
"os/exec"
13+
"path/filepath"
14+
"testing"
15+
)
16+
17+
type (
18+
testSigned[T Signed] struct{ f T }
19+
testUnsigned[T Unsigned] struct{ f T }
20+
testInteger[T Integer] struct{ f T }
21+
testFloat[T Float] struct{ f T }
22+
testComplex[T Complex] struct{ f T }
23+
testOrdered[T Ordered] struct{ f T }
24+
testSlice[T Slice[E], E any] struct{ f T }
25+
testMap[T Map[K, V], K comparable, V any] struct{ f T }
26+
testChan[T Chan[E], E any] struct{ f T }
27+
)
28+
29+
// TestTypes passes if it compiles.
30+
type TestTypes struct {
31+
_ testSigned[int]
32+
_ testSigned[int64]
33+
_ testUnsigned[uint]
34+
_ testUnsigned[uintptr]
35+
_ testInteger[int8]
36+
_ testInteger[uint8]
37+
_ testInteger[uintptr]
38+
_ testFloat[float32]
39+
_ testComplex[complex64]
40+
_ testOrdered[int]
41+
_ testOrdered[float64]
42+
_ testOrdered[string]
43+
_ testSlice[[]int, int]
44+
_ testMap[map[int]bool, int, bool]
45+
_ testChan[chan int, int]
46+
}
47+
48+
func infer1[S Slice[E], E any](s S, v E) S { return s }
49+
func infer2[M Map[K, V], K comparable, V any](m M, k K, v V) M { return m }
50+
func infer3[C Chan[E], E any](c C, v E) C { return c }
51+
52+
func TestInference(t *testing.T) {
53+
var empty interface{}
54+
55+
type S []int
56+
empty = infer1(S{}, 0)
57+
if _, ok := empty.(S); !ok {
58+
t.Errorf("infer1(S) returned %T, expected S", empty)
59+
}
60+
61+
type M map[int]bool
62+
empty = infer2(M{}, 0, false)
63+
if _, ok := empty.(M); !ok {
64+
t.Errorf("infer2(M) returned %T, expected M", empty)
65+
}
66+
67+
type C chan bool
68+
empty = infer3(make(C), true)
69+
if _, ok := empty.(C); !ok {
70+
t.Errorf("infer3(C) returned %T, expected C", empty)
71+
}
72+
}
73+
74+
var prolog = []byte(`
75+
package constrainttest
76+
77+
import "constraints"
78+
79+
type (
80+
testSigned[T constraints.Signed] struct{ f T }
81+
testUnsigned[T constraints.Unsigned] struct{ f T }
82+
testInteger[T constraints.Integer] struct{ f T }
83+
testFloat[T constraints.Float] struct{ f T }
84+
testComplex[T constraints.Complex] struct{ f T }
85+
testOrdered[T constraints.Ordered] struct{ f T }
86+
testSlice[T constraints.Slice[E], E any] struct{ f T }
87+
testMap[T constraints.Map[K, V], K comparable, V any] struct{ f T }
88+
testChan[T constraints.Chan[E], E any] struct{ f T }
89+
)
90+
`)
91+
92+
func TestFailure(t *testing.T) {
93+
testenv.MustHaveGoBuild(t)
94+
gocmd := testenv.GoToolPath(t)
95+
tmpdir := t.TempDir()
96+
97+
if err := os.WriteFile(filepath.Join(tmpdir, "go.mod"), []byte("module constraintest"), 0666); err != nil {
98+
t.Fatal(err)
99+
}
100+
101+
// Test for types that should not satisfy a constraint.
102+
// For each pair of constraint and type, write a Go file
103+
// var V constraint[type]
104+
// For example,
105+
// var V testSigned[uint]
106+
// This should not compile, as testSigned (above) uses
107+
// constraints.Signed, and uint does not satisfy that constraint.
108+
// Therefore, the build of that code should fail.
109+
for i, test := range []struct {
110+
constraint, typ string
111+
}{
112+
{"testSigned", "uint"},
113+
{"testUnsigned", "int"},
114+
{"testInteger", "float32"},
115+
{"testFloat", "int8"},
116+
{"testComplex", "float64"},
117+
{"testOrdered", "bool"},
118+
{"testSlice", "int, int"},
119+
{"testMap", "string, string, string"},
120+
{"testChan", "[]int, int"},
121+
} {
122+
i := i
123+
test := test
124+
t.Run(fmt.Sprintf("%s %d", test.constraint, i), func(t *testing.T) {
125+
t.Parallel()
126+
name := fmt.Sprintf("go%d.go", i)
127+
f, err := os.Create(filepath.Join(tmpdir, name))
128+
if err != nil {
129+
t.Fatal(err)
130+
}
131+
if _, err := f.Write(prolog); err != nil {
132+
t.Fatal(err)
133+
}
134+
if _, err := fmt.Fprintf(f, "var V %s[%s]\n", test.constraint, test.typ); err != nil {
135+
t.Fatal(err)
136+
}
137+
if err := f.Close(); err != nil {
138+
t.Fatal(err)
139+
}
140+
cmd := exec.Command(gocmd, "build", name)
141+
cmd.Dir = tmpdir
142+
if out, err := cmd.CombinedOutput(); err == nil {
143+
t.Error("build succeeded, but expected to fail")
144+
} else if len(out) > 0 {
145+
t.Logf("%s", out)
146+
const want = "does not satisfy"
147+
if !bytes.Contains(out, []byte(want)) {
148+
t.Errorf("output does not include %q", want)
149+
}
150+
} else {
151+
t.Error("no error output, expected something")
152+
}
153+
})
154+
}
155+
}

src/go/build/deps_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ import (
7070
var depsRules = `
7171
# No dependencies allowed for any of these packages.
7272
NONE
73-
< container/list, container/ring,
73+
< constraints, container/list, container/ring,
7474
internal/cfg, internal/cpu, internal/goarch,
7575
internal/goexperiment, internal/goos,
7676
internal/goversion, internal/nettrace,

0 commit comments

Comments
 (0)