whatcanGOwrong
This commit is contained in:
+50
@@ -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
|
||||
}
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"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 "golang.org/x/exp/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()
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// This package is golang.org/x/exp/constraints, so the root of the x/exp
|
||||
// module is the parent directory of the directory in which this test runs.
|
||||
expModDir := filepath.Dir(cwd)
|
||||
|
||||
modFile := fmt.Sprintf(`module constraintest
|
||||
|
||||
go 1.18
|
||||
|
||||
replace golang.org/x/exp => %s
|
||||
`, expModDir)
|
||||
if err := os.WriteFile(filepath.Join(tmpdir, "go.mod"), []byte(modFile), 0666); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Write the prolog as its own file so that 'go mod tidy' has something to inspect.
|
||||
// This will ensure that the go.mod and go.sum files include any dependencies
|
||||
// needed by the constraints package (which should just be some version of
|
||||
// x/exp itself).
|
||||
if err := os.WriteFile(filepath.Join(tmpdir, "prolog.go"), []byte(prolog), 0666); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tidyCmd := exec.Command(gocmd, "mod", "tidy")
|
||||
tidyCmd.Dir = tmpdir
|
||||
tidyCmd.Env = append(os.Environ(), "PWD="+tmpdir)
|
||||
if out, err := tidyCmd.CombinedOutput(); err != nil {
|
||||
t.Fatalf("%v: %v\n%s", tidyCmd, err, out)
|
||||
} else {
|
||||
t.Logf("%v:\n%s", tidyCmd, out)
|
||||
}
|
||||
|
||||
// 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)
|
||||
if !wantRx.Match(out) {
|
||||
t.Errorf("output does not match %q", wantRx)
|
||||
}
|
||||
} else {
|
||||
t.Error("no error output, expected something")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var wantRx = regexp.MustCompile("does not (implement|satisfy)")
|
||||
Reference in New Issue
Block a user