libgo: update to final Go 1.18 release
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/393377
This commit is contained in:
parent
7fd6e36ea9
commit
69921f4a7e
92 changed files with 2020 additions and 641 deletions
|
@ -1,4 +1,4 @@
|
|||
5042f7efbdb2d64537dfef53a19e96ee5ec4db2d
|
||||
7f33baa09a8172bb2c5f1ca0435d9efe3e194c9b
|
||||
|
||||
The first line of this file holds the git revision number of the last
|
||||
merge done from the gofrontend repository.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
cb5a598d7f2ebd276686403d141a97c026d33458
|
||||
4aa1efed4853ea067d665a952eee77c52faac774
|
||||
|
||||
The first line of this file holds the git revision number of the
|
||||
last merge done from the master library sources.
|
||||
|
|
|
@ -1 +1 @@
|
|||
go1.18rc1
|
||||
go1.18
|
||||
|
|
|
@ -1356,7 +1356,7 @@
|
|||
//
|
||||
// Workspace maintenance
|
||||
//
|
||||
// Go workspace provides access to operations on workspaces.
|
||||
// Work provides access to operations on workspaces.
|
||||
//
|
||||
// Note that support for workspaces is built into many other commands, not
|
||||
// just 'go work'.
|
||||
|
@ -1364,6 +1364,12 @@
|
|||
// See 'go help modules' for information about Go's module system of which
|
||||
// workspaces are a part.
|
||||
//
|
||||
// See https://go.dev/ref/mod#workspaces for an in-depth reference on
|
||||
// workspaces.
|
||||
//
|
||||
// See https://go.dev/doc/tutorial/workspaces for an introductory
|
||||
// tutorial on workspaces.
|
||||
//
|
||||
// A workspace is specified by a go.work file that specifies a set of
|
||||
// module directories with the "use" directive. These modules are used as
|
||||
// root modules by the go command for builds and related operations. A
|
||||
|
@ -1485,9 +1491,8 @@
|
|||
// Version string
|
||||
// }
|
||||
//
|
||||
// See the workspaces design proposal at
|
||||
// https://go.googlesource.com/proposal/+/master/design/45713-workspace.md for
|
||||
// more information.
|
||||
// See the workspaces reference at https://go.dev/ref/mod#workspaces
|
||||
// for more information.
|
||||
//
|
||||
//
|
||||
// Initialize workspace file
|
||||
|
@ -1507,6 +1512,9 @@
|
|||
// Each argument path is added to a use directive in the go.work file. The
|
||||
// current go version will also be listed in the go.work file.
|
||||
//
|
||||
// See the workspaces reference at https://go.dev/ref/mod#workspaces
|
||||
// for more information.
|
||||
//
|
||||
//
|
||||
// Sync workspace build list to modules
|
||||
//
|
||||
|
@ -1530,12 +1538,15 @@
|
|||
// build list's version of each module is always the same or higher than
|
||||
// that in each workspace module.
|
||||
//
|
||||
// See the workspaces reference at https://go.dev/ref/mod#workspaces
|
||||
// for more information.
|
||||
//
|
||||
//
|
||||
// Add modules to workspace file
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// go work use [-r] [moddirs]
|
||||
// go work use [-r] moddirs
|
||||
//
|
||||
// Use provides a command-line interface for adding
|
||||
// directories, optionally recursively, to a go.work file.
|
||||
|
@ -1549,6 +1560,9 @@
|
|||
// were specified as arguments: namely, use directives will be added for
|
||||
// directories that exist, and removed for directories that do not exist.
|
||||
//
|
||||
// See the workspaces reference at https://go.dev/ref/mod#workspaces
|
||||
// for more information.
|
||||
//
|
||||
//
|
||||
// Compile and run Go program
|
||||
//
|
||||
|
|
|
@ -305,17 +305,46 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
|
|||
//
|
||||
// (If the version is +incompatible, then the go.mod file must not exist:
|
||||
// +incompatible is not an ongoing opt-out from semantic import versioning.)
|
||||
var canUseIncompatible func() bool
|
||||
canUseIncompatible = func() bool {
|
||||
var ok bool
|
||||
if r.codeDir == "" && r.pathMajor == "" {
|
||||
incompatibleOk := map[string]bool{}
|
||||
canUseIncompatible := func(v string) bool {
|
||||
if r.codeDir != "" || r.pathMajor != "" {
|
||||
// A non-empty codeDir indicates a module within a subdirectory,
|
||||
// which necessarily has a go.mod file indicating the module boundary.
|
||||
// A non-empty pathMajor indicates a module path with a major-version
|
||||
// suffix, which must match.
|
||||
return false
|
||||
}
|
||||
|
||||
ok, seen := incompatibleOk[""]
|
||||
if !seen {
|
||||
_, errGoMod := r.code.ReadFile(info.Name, "go.mod", codehost.MaxGoMod)
|
||||
if errGoMod != nil {
|
||||
ok = true
|
||||
ok = (errGoMod != nil)
|
||||
incompatibleOk[""] = ok
|
||||
}
|
||||
if !ok {
|
||||
// A go.mod file exists at the repo root.
|
||||
return false
|
||||
}
|
||||
|
||||
// Per https://go.dev/issue/51324, previous versions of the 'go' command
|
||||
// didn't always check for go.mod files in subdirectories, so if the user
|
||||
// requests a +incompatible version explicitly, we should continue to allow
|
||||
// it. Otherwise, if vN/go.mod exists, expect that release tags for that
|
||||
// major version are intended for the vN module.
|
||||
if v != "" && !strings.HasSuffix(statVers, "+incompatible") {
|
||||
major := semver.Major(v)
|
||||
ok, seen = incompatibleOk[major]
|
||||
if !seen {
|
||||
_, errGoModSub := r.code.ReadFile(info.Name, path.Join(major, "go.mod"), codehost.MaxGoMod)
|
||||
ok = (errGoModSub != nil)
|
||||
incompatibleOk[major] = ok
|
||||
}
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
canUseIncompatible = func() bool { return ok }
|
||||
return ok
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// checkCanonical verifies that the canonical version v is compatible with the
|
||||
|
@ -367,7 +396,7 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
|
|||
base := strings.TrimSuffix(v, "+incompatible")
|
||||
var errIncompatible error
|
||||
if !module.MatchPathMajor(base, r.pathMajor) {
|
||||
if canUseIncompatible() {
|
||||
if canUseIncompatible(base) {
|
||||
v = base + "+incompatible"
|
||||
} else {
|
||||
if r.pathMajor != "" {
|
||||
|
@ -495,7 +524,7 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
|
|||
// Save the highest non-retracted canonical tag for the revision.
|
||||
// If we don't find a better match, we'll use it as the canonical version.
|
||||
if tagIsCanonical && semver.Compare(highestCanonical, v) < 0 && !isRetracted(v) {
|
||||
if module.MatchPathMajor(v, r.pathMajor) || canUseIncompatible() {
|
||||
if module.MatchPathMajor(v, r.pathMajor) || canUseIncompatible(v) {
|
||||
highestCanonical = v
|
||||
}
|
||||
}
|
||||
|
@ -513,12 +542,12 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
|
|||
// retracted versions.
|
||||
allowedMajor := func(major string) func(v string) bool {
|
||||
return func(v string) bool {
|
||||
return (major == "" || semver.Major(v) == major) && !isRetracted(v)
|
||||
return ((major == "" && canUseIncompatible(v)) || semver.Major(v) == major) && !isRetracted(v)
|
||||
}
|
||||
}
|
||||
if pseudoBase == "" {
|
||||
var tag string
|
||||
if r.pseudoMajor != "" || canUseIncompatible() {
|
||||
if r.pseudoMajor != "" || canUseIncompatible("") {
|
||||
tag, _ = r.code.RecentTag(info.Name, tagPrefix, allowedMajor(r.pseudoMajor))
|
||||
} else {
|
||||
// Allow either v1 or v0, but not incompatible higher versions.
|
||||
|
|
|
@ -458,6 +458,54 @@ var codeRepoTests = []codeRepoTest{
|
|||
rev: "v3.0.0-devel",
|
||||
err: `resolves to version v0.1.1-0.20220203155313-d59622f6e4d7 (v3.0.0-devel is not a tag)`,
|
||||
},
|
||||
|
||||
// If v2/go.mod exists, then we should prefer to match the "v2"
|
||||
// pseudo-versions to the nested module, and resolve the module in the parent
|
||||
// directory to only compatible versions.
|
||||
//
|
||||
// However (https://go.dev/issue/51324), previous versions of the 'go' command
|
||||
// didn't always do so, so if the user explicitly requests a +incompatible
|
||||
// version (as would be present in an existing go.mod file), we should
|
||||
// continue to allow it.
|
||||
{
|
||||
vcs: "git",
|
||||
path: "vcs-test.golang.org/git/v2sub.git",
|
||||
rev: "80beb17a1603",
|
||||
version: "v0.0.0-20220222205507-80beb17a1603",
|
||||
name: "80beb17a16036f17a5aedd1bb5bd6d407b3c6dc5",
|
||||
short: "80beb17a1603",
|
||||
time: time.Date(2022, 2, 22, 20, 55, 7, 0, time.UTC),
|
||||
},
|
||||
{
|
||||
vcs: "git",
|
||||
path: "vcs-test.golang.org/git/v2sub.git",
|
||||
rev: "v2.0.0",
|
||||
err: `module contains a go.mod file, so module path must match major version ("vcs-test.golang.org/git/v2sub.git/v2")`,
|
||||
},
|
||||
{
|
||||
vcs: "git",
|
||||
path: "vcs-test.golang.org/git/v2sub.git",
|
||||
rev: "v2.0.1-0.20220222205507-80beb17a1603",
|
||||
err: `module contains a go.mod file, so module path must match major version ("vcs-test.golang.org/git/v2sub.git/v2")`,
|
||||
},
|
||||
{
|
||||
vcs: "git",
|
||||
path: "vcs-test.golang.org/git/v2sub.git",
|
||||
rev: "v2.0.0+incompatible",
|
||||
version: "v2.0.0+incompatible",
|
||||
name: "5fcd3eaeeb391d399f562fd45a50dac9fc34ae8b",
|
||||
short: "5fcd3eaeeb39",
|
||||
time: time.Date(2022, 2, 22, 20, 53, 33, 0, time.UTC),
|
||||
},
|
||||
{
|
||||
vcs: "git",
|
||||
path: "vcs-test.golang.org/git/v2sub.git",
|
||||
rev: "v2.0.1-0.20220222205507-80beb17a1603+incompatible",
|
||||
version: "v2.0.1-0.20220222205507-80beb17a1603+incompatible",
|
||||
name: "80beb17a16036f17a5aedd1bb5bd6d407b3c6dc5",
|
||||
short: "80beb17a1603",
|
||||
time: time.Date(2022, 2, 22, 20, 55, 7, 0, time.UTC),
|
||||
},
|
||||
}
|
||||
|
||||
func TestCodeRepo(t *testing.T) {
|
||||
|
|
|
@ -288,6 +288,11 @@ func BinDir() string {
|
|||
// operate in workspace mode. It should not be called by other commands,
|
||||
// for example 'go mod tidy', that don't operate in workspace mode.
|
||||
func InitWorkfile() {
|
||||
if RootMode == NoRoot {
|
||||
workFilePath = ""
|
||||
return
|
||||
}
|
||||
|
||||
switch gowork := cfg.Getenv("GOWORK"); gowork {
|
||||
case "off":
|
||||
workFilePath = ""
|
||||
|
|
|
@ -802,7 +802,7 @@ var latestVersionIgnoringRetractionsCache par.Cache // path → queryLatestVersi
|
|||
// an absolute path or a relative path starting with a '.' or '..'
|
||||
// path component.
|
||||
func ToDirectoryPath(path string) string {
|
||||
if modfile.IsDirectoryPath(path) {
|
||||
if path == "." || modfile.IsDirectoryPath(path) {
|
||||
return path
|
||||
}
|
||||
// The path is not a relative path or an absolute path, so make it relative
|
||||
|
|
|
@ -73,8 +73,6 @@ func printStderr(args ...any) (int, error) {
|
|||
}
|
||||
|
||||
func runRun(ctx context.Context, cmd *base.Command, args []string) {
|
||||
modload.InitWorkfile()
|
||||
|
||||
if shouldUseOutsideModuleMode(args) {
|
||||
// Set global module flags for 'go run cmd@version'.
|
||||
// This must be done before modload.Init, but we need to call work.BuildInit
|
||||
|
@ -84,7 +82,10 @@ func runRun(ctx context.Context, cmd *base.Command, args []string) {
|
|||
modload.RootMode = modload.NoRoot
|
||||
modload.AllowMissingModuleImports()
|
||||
modload.Init()
|
||||
} else {
|
||||
modload.InitWorkfile()
|
||||
}
|
||||
|
||||
work.BuildInit()
|
||||
var b work.Builder
|
||||
b.Init()
|
||||
|
|
|
@ -312,7 +312,7 @@ func gitStatus(vcsGit *Cmd, rootDir string) (Status, error) {
|
|||
// uncommitted files and skip tagging revision / committime.
|
||||
var rev string
|
||||
var commitTime time.Time
|
||||
out, err = vcsGit.runOutputVerboseOnly(rootDir, "show -s --no-show-signature --format=%H:%ct")
|
||||
out, err = vcsGit.runOutputVerboseOnly(rootDir, "-c log.showsignature=false show -s --format=%H:%ct")
|
||||
if err != nil && !uncommitted {
|
||||
return Status{}, err
|
||||
} else if err == nil {
|
||||
|
|
|
@ -84,9 +84,8 @@ writing it back to go.mod. The JSON output corresponds to these Go types:
|
|||
Version string
|
||||
}
|
||||
|
||||
See the workspaces design proposal at
|
||||
https://go.googlesource.com/proposal/+/master/design/45713-workspace.md for
|
||||
more information.
|
||||
See the workspaces reference at https://go.dev/ref/mod#workspaces
|
||||
for more information.
|
||||
`,
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,8 @@ modules will be created.
|
|||
Each argument path is added to a use directive in the go.work file. The
|
||||
current go version will also be listed in the go.work file.
|
||||
|
||||
See the workspaces reference at https://go.dev/ref/mod#workspaces
|
||||
for more information.
|
||||
`,
|
||||
Run: runInit,
|
||||
}
|
||||
|
|
|
@ -33,6 +33,9 @@ if the dependency module's version is not already the same as the build
|
|||
list's version. Note that Minimal Version Selection guarantees that the
|
||||
build list's version of each module is always the same or higher than
|
||||
that in each workspace module.
|
||||
|
||||
See the workspaces reference at https://go.dev/ref/mod#workspaces
|
||||
for more information.
|
||||
`,
|
||||
Run: runSync,
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ import (
|
|||
)
|
||||
|
||||
var cmdUse = &base.Command{
|
||||
UsageLine: "go work use [-r] [moddirs]",
|
||||
UsageLine: "go work use [-r] moddirs",
|
||||
Short: "add modules to workspace file",
|
||||
Long: `Use provides a command-line interface for adding
|
||||
directories, optionally recursively, to a go.work file.
|
||||
|
@ -33,6 +33,9 @@ The -r flag searches recursively for modules in the argument
|
|||
directories, and the use command operates as if each of the directories
|
||||
were specified as arguments: namely, use directives will be added for
|
||||
directories that exist, and removed for directories that do not exist.
|
||||
|
||||
See the workspaces reference at https://go.dev/ref/mod#workspaces
|
||||
for more information.
|
||||
`,
|
||||
}
|
||||
|
||||
|
@ -101,6 +104,9 @@ func runUse(ctx context.Context, cmd *base.Command, args []string) {
|
|||
keepDirs[absDir] = dir
|
||||
}
|
||||
|
||||
if len(args) == 0 {
|
||||
base.Fatalf("go: 'go work use' requires one or more directory arguments")
|
||||
}
|
||||
for _, useDir := range args {
|
||||
if !*useR {
|
||||
lookDir(useDir)
|
||||
|
@ -186,5 +192,5 @@ func pathRel(workDir, dir string) (abs, canonical string) {
|
|||
|
||||
// Normalize relative paths to use slashes, so that checked-in go.work
|
||||
// files with relative paths within the repo are platform-independent.
|
||||
return abs, filepath.ToSlash(rel)
|
||||
return abs, modload.ToDirectoryPath(rel)
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
var CmdWork = &base.Command{
|
||||
UsageLine: "go work",
|
||||
Short: "workspace maintenance",
|
||||
Long: `Go workspace provides access to operations on workspaces.
|
||||
Long: `Work provides access to operations on workspaces.
|
||||
|
||||
Note that support for workspaces is built into many other commands, not
|
||||
just 'go work'.
|
||||
|
@ -20,6 +20,12 @@ just 'go work'.
|
|||
See 'go help modules' for information about Go's module system of which
|
||||
workspaces are a part.
|
||||
|
||||
See https://go.dev/ref/mod#workspaces for an in-depth reference on
|
||||
workspaces.
|
||||
|
||||
See https://go.dev/doc/tutorial/workspaces for an introductory
|
||||
tutorial on workspaces.
|
||||
|
||||
A workspace is specified by a go.work file that specifies a set of
|
||||
module directories with the "use" directive. These modules are used as
|
||||
root modules by the go command for builds and related operations. A
|
||||
|
|
16
libgo/go/cmd/go/testdata/script/run_work_versioned.txt
vendored
Normal file
16
libgo/go/cmd/go/testdata/script/run_work_versioned.txt
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
[short] skip
|
||||
go run example.com/printversion@v0.1.0
|
||||
stdout '^main is example.com/printversion v0.1.0$'
|
||||
|
||||
-- go.work --
|
||||
go 1.18
|
||||
|
||||
use (
|
||||
.
|
||||
)
|
||||
-- go.mod --
|
||||
module example
|
||||
|
||||
go 1.18
|
||||
|
||||
require example.com/printversion v1.0.0
|
84
libgo/go/cmd/go/testdata/script/test_fuzz_minimize_dirty_cov.txt
vendored
Normal file
84
libgo/go/cmd/go/testdata/script/test_fuzz_minimize_dirty_cov.txt
vendored
Normal file
|
@ -0,0 +1,84 @@
|
|||
# Test that minimization doesn't use dirty coverage snapshots when it
|
||||
# is unable to actually minimize the input. We do this by checking that
|
||||
# a expected value appears in the cache. If a dirty coverage map is used
|
||||
# (i.e. the coverage map generated during the last minimization step,
|
||||
# rather than the map provided with the initial input) then this value
|
||||
# is unlikely to appear in the cache, since the map generated during
|
||||
# the last minimization step should not increase the coverage.
|
||||
|
||||
[short] skip
|
||||
[!fuzz-instrumented] skip
|
||||
|
||||
env GOCACHE=$WORK/gocache
|
||||
go test -fuzz=FuzzCovMin -fuzztime=25s -test.fuzzcachedir=$GOCACHE/fuzz
|
||||
go run check_file/main.go $GOCACHE/fuzz/FuzzCovMin abcd
|
||||
|
||||
-- go.mod --
|
||||
module test
|
||||
|
||||
-- covmin_test.go --
|
||||
package covmin
|
||||
|
||||
import "testing"
|
||||
|
||||
func FuzzCovMin(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
if len(data) >= 4 && data[0] == 'a' && data[1] == 'b' && data[2] == 'c' && data[3] == 'd' {
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
-- check_file/main.go --
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func checkFile(name, expected string) (bool, error) {
|
||||
data, err := os.ReadFile(name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, line := range bytes.Split(data, []byte("\n")) {
|
||||
m := valRe.FindSubmatch(line)
|
||||
if m == nil {
|
||||
continue
|
||||
}
|
||||
fmt.Println(strconv.Unquote(string(m[1])))
|
||||
if s, err := strconv.Unquote(string(m[1])); err != nil {
|
||||
return false, err
|
||||
} else if s == expected {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
var valRe = regexp.MustCompile(`^\[\]byte\(([^)]+)\)$`)
|
||||
|
||||
func main() {
|
||||
dir, expected := os.Args[1], os.Args[2]
|
||||
ents, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
for _, ent := range ents {
|
||||
name := filepath.Join(dir, ent.Name())
|
||||
if good, err := checkFile(name, expected); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
} else if good {
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "input over minimized")
|
||||
os.Exit(1)
|
||||
}
|
|
@ -127,19 +127,8 @@ func FuzzMinCache(f *testing.F) {
|
|||
if bytes.Equal(buf, seed) {
|
||||
return
|
||||
}
|
||||
if n := sum(buf); n < 0 {
|
||||
t.Error("sum cannot be negative")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func sum(buf []byte) int {
|
||||
n := 0
|
||||
for _, b := range buf {
|
||||
n += int(b)
|
||||
}
|
||||
return n
|
||||
}
|
||||
-- check_testdata/check_testdata.go --
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
|
|
@ -111,7 +111,7 @@ rm $GOBIN/d$GOEXE
|
|||
go list -x ./...
|
||||
stdout -count=3 '^example.com'
|
||||
stderr -count=1 '^git status'
|
||||
stderr -count=1 '^git show'
|
||||
stderr -count=1 '^git -c log.showsignature=false show'
|
||||
|
||||
-- $WORK/fakebin/git --
|
||||
#!/bin/sh
|
||||
|
|
4
libgo/go/cmd/go/testdata/script/work.txt
vendored
4
libgo/go/cmd/go/testdata/script/work.txt
vendored
|
@ -4,7 +4,7 @@ go env GOWORK
|
|||
! stdout .
|
||||
|
||||
go work init ./a ./b
|
||||
cmp go.work go.work.want
|
||||
cmpenv go.work go.work.want
|
||||
go env GOWORK
|
||||
stdout '^'$WORK'(\\|/)gopath(\\|/)src(\\|/)go.work$'
|
||||
|
||||
|
@ -69,7 +69,7 @@ use (
|
|||
../src/a
|
||||
)
|
||||
-- go.work.want --
|
||||
go 1.18
|
||||
go $goversion
|
||||
|
||||
use (
|
||||
./a
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
# Test editing go.work files.
|
||||
|
||||
go work init m
|
||||
cmp go.work go.work.want_initial
|
||||
cmpenv go.work go.work.want_initial
|
||||
|
||||
go work edit -use n
|
||||
cmp go.work go.work.want_use_n
|
||||
cmpenv go.work go.work.want_use_n
|
||||
|
||||
go work edit -go 1.18
|
||||
cmp go.work go.work.want_go_118
|
||||
|
@ -39,11 +39,11 @@ module m
|
|||
|
||||
go 1.18
|
||||
-- go.work.want_initial --
|
||||
go 1.18
|
||||
go $goversion
|
||||
|
||||
use ./m
|
||||
-- go.work.want_use_n --
|
||||
go 1.18
|
||||
go $goversion
|
||||
|
||||
use (
|
||||
./m
|
||||
|
|
17
libgo/go/cmd/go/testdata/script/work_init_path.txt
vendored
Normal file
17
libgo/go/cmd/go/testdata/script/work_init_path.txt
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Regression test for https://go.dev/issue/51448.
|
||||
# 'go work init . foo/bar' should produce a go.work file
|
||||
# with the same paths as 'go work init; go work use -r .'.
|
||||
|
||||
go work init . foo/bar
|
||||
mv go.work go.work.init
|
||||
|
||||
go work init
|
||||
go work use -r .
|
||||
cmp go.work go.work.init
|
||||
|
||||
-- go.mod --
|
||||
module example
|
||||
go 1.18
|
||||
-- foo/bar/go.mod --
|
||||
module example
|
||||
go 1.18
|
10
libgo/go/cmd/go/testdata/script/work_use.txt
vendored
10
libgo/go/cmd/go/testdata/script/work_use.txt
vendored
|
@ -14,16 +14,16 @@ use (
|
|||
go 1.18
|
||||
|
||||
use (
|
||||
foo
|
||||
foo/bar/baz
|
||||
./foo
|
||||
./foo/bar/baz
|
||||
)
|
||||
-- go.want_work_other --
|
||||
go 1.18
|
||||
|
||||
use (
|
||||
foo
|
||||
foo/bar/baz
|
||||
other
|
||||
./foo
|
||||
./foo/bar/baz
|
||||
./other
|
||||
)
|
||||
-- foo/go.mod --
|
||||
module foo
|
||||
|
|
|
@ -6,13 +6,13 @@ go 1.18
|
|||
|
||||
use (
|
||||
.
|
||||
sub
|
||||
sub/dir/deleted
|
||||
./sub
|
||||
./sub/dir/deleted
|
||||
)
|
||||
-- go.work.want --
|
||||
go 1.18
|
||||
|
||||
use sub/dir
|
||||
use ./sub/dir
|
||||
-- sub/README.txt --
|
||||
A go.mod file has been deleted from this directory.
|
||||
In addition, the entire subdirectory sub/dir/deleted
|
||||
|
|
|
@ -31,7 +31,7 @@ grep '^use ["]?'$PWD'["]?$' ../../go.work
|
|||
# resulting workspace would contain a duplicate module.
|
||||
cp ../../go.work.orig ../../go.work
|
||||
! go work use $PWD .
|
||||
stderr '^go: already added "bar/baz" as "'$PWD'"$'
|
||||
stderr '^go: already added "\./bar/baz" as "'$PWD'"$'
|
||||
cmp ../../go.work ../../go.work.orig
|
||||
|
||||
|
||||
|
@ -43,7 +43,7 @@ go 1.18
|
|||
-- go.work.rel --
|
||||
go 1.18
|
||||
|
||||
use bar/baz
|
||||
use ./bar/baz
|
||||
-- bar/baz/go.mod --
|
||||
module example/bar/baz
|
||||
go 1.18
|
||||
|
|
11
libgo/go/cmd/go/testdata/script/work_use_noargs.txt
vendored
Normal file
11
libgo/go/cmd/go/testdata/script/work_use_noargs.txt
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
# For now, 'go work use' requires arguments.
|
||||
# (Eventually, we may may it implicitly behave like 'go work use .'.
|
||||
|
||||
! go work use
|
||||
stderr '^go: ''go work use'' requires one or more directory arguments'
|
||||
|
||||
! go work use -r
|
||||
stderr '^go: ''go work use'' requires one or more directory arguments'
|
||||
|
||||
-- go.work --
|
||||
go 1.18
|
|
@ -512,7 +512,7 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
|
|||
}
|
||||
fv := finfo.value(val, dontInitNilPointers)
|
||||
|
||||
if finfo.flags&fOmitEmpty != 0 && isEmptyValue(fv) {
|
||||
if finfo.flags&fOmitEmpty != 0 && (!fv.IsValid() || isEmptyValue(fv)) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -2499,3 +2499,39 @@ func TestInvalidXMLName(t *testing.T) {
|
|||
t.Errorf("error %q does not contain %q", err, want)
|
||||
}
|
||||
}
|
||||
|
||||
// Issue 50164. Crash on zero value XML attribute.
|
||||
type LayerOne struct {
|
||||
XMLName Name `xml:"l1"`
|
||||
|
||||
Value *float64 `xml:"value,omitempty"`
|
||||
*LayerTwo `xml:",omitempty"`
|
||||
}
|
||||
|
||||
type LayerTwo struct {
|
||||
ValueTwo *int `xml:"value_two,attr,omitempty"`
|
||||
}
|
||||
|
||||
func TestMarshalZeroValue(t *testing.T) {
|
||||
proofXml := `<l1><value>1.2345</value></l1>`
|
||||
var l1 LayerOne
|
||||
err := Unmarshal([]byte(proofXml), &l1)
|
||||
if err != nil {
|
||||
t.Fatalf("unmarshal XML error: %v", err)
|
||||
}
|
||||
want := float64(1.2345)
|
||||
got := *l1.Value
|
||||
if got != want {
|
||||
t.Fatalf("unexpected unmarshal result, want %f but got %f", want, got)
|
||||
}
|
||||
|
||||
// Marshal again (or Encode again)
|
||||
// In issue 50164, here `Marshal(l1)` will panic because of the zero value of xml attribute ValueTwo `value_two`.
|
||||
anotherXML, err := Marshal(l1)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal XML error: %v", err)
|
||||
}
|
||||
if string(anotherXML) != proofXml {
|
||||
t.Fatalf("unexpected unmarshal result, want %q but got %q", proofXml, anotherXML)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -181,6 +181,15 @@ func iImportData(fset *token.FileSet, imports map[string]*types.Package, dataRea
|
|||
p.doDecl(localpkg, name)
|
||||
}
|
||||
|
||||
// SetConstraint can't be called if the constraint type is not yet complete.
|
||||
// When type params are created in the 'P' case of (*importReader).obj(),
|
||||
// the associated constraint type may not be complete due to recursion.
|
||||
// Therefore, we defer calling SetConstraint there, and call it here instead
|
||||
// after all types are complete.
|
||||
for _, d := range p.later {
|
||||
d.t.SetConstraint(d.constraint)
|
||||
}
|
||||
|
||||
for _, typ := range p.interfaceList {
|
||||
typ.Complete()
|
||||
}
|
||||
|
@ -195,6 +204,11 @@ func iImportData(fset *token.FileSet, imports map[string]*types.Package, dataRea
|
|||
return localpkg, nil
|
||||
}
|
||||
|
||||
type setConstraintArgs struct {
|
||||
t *types.TypeParam
|
||||
constraint types.Type
|
||||
}
|
||||
|
||||
type iimporter struct {
|
||||
exportVersion int64
|
||||
ipath string
|
||||
|
@ -211,6 +225,9 @@ type iimporter struct {
|
|||
|
||||
fake fakeFileSet
|
||||
interfaceList []*types.Interface
|
||||
|
||||
// Arguments for calls to SetConstraint that are deferred due to recursive types
|
||||
later []setConstraintArgs
|
||||
}
|
||||
|
||||
func (p *iimporter) doDecl(pkg *types.Package, name string) {
|
||||
|
@ -391,7 +408,11 @@ func (r *importReader) obj(name string) {
|
|||
}
|
||||
iface.MarkImplicit()
|
||||
}
|
||||
t.SetConstraint(constraint)
|
||||
// The constraint type may not be complete, if we
|
||||
// are in the middle of a type recursion involving type
|
||||
// constraints. So, we defer SetConstraint until we have
|
||||
// completely set up all types in ImportData.
|
||||
r.p.later = append(r.p.later, setConstraintArgs{t: t, constraint: constraint})
|
||||
|
||||
case 'V':
|
||||
typ := r.typ()
|
||||
|
|
|
@ -319,9 +319,17 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
|
|||
}
|
||||
}
|
||||
|
||||
func (p *printer) parameters(fields *ast.FieldList, isTypeParam bool) {
|
||||
type paramMode int
|
||||
|
||||
const (
|
||||
funcParam paramMode = iota
|
||||
funcTParam
|
||||
typeTParam
|
||||
)
|
||||
|
||||
func (p *printer) parameters(fields *ast.FieldList, mode paramMode) {
|
||||
openTok, closeTok := token.LPAREN, token.RPAREN
|
||||
if isTypeParam {
|
||||
if mode != funcParam {
|
||||
openTok, closeTok = token.LBRACK, token.RBRACK
|
||||
}
|
||||
p.print(fields.Opening, openTok)
|
||||
|
@ -373,7 +381,7 @@ func (p *printer) parameters(fields *ast.FieldList, isTypeParam bool) {
|
|||
if closing := p.lineFor(fields.Closing); 0 < prevLine && prevLine < closing {
|
||||
p.print(token.COMMA)
|
||||
p.linebreak(closing, 0, ignore, true)
|
||||
} else if isTypeParam && fields.NumFields() == 1 {
|
||||
} else if mode == typeTParam && fields.NumFields() == 1 {
|
||||
// Otherwise, if we are in a type parameter list that could be confused
|
||||
// with the constant array length expression [P*C], print a comma so that
|
||||
// parsing is unambiguous.
|
||||
|
@ -411,10 +419,10 @@ func isTypeLit(x ast.Expr) bool {
|
|||
|
||||
func (p *printer) signature(sig *ast.FuncType) {
|
||||
if sig.TypeParams != nil {
|
||||
p.parameters(sig.TypeParams, true)
|
||||
p.parameters(sig.TypeParams, funcTParam)
|
||||
}
|
||||
if sig.Params != nil {
|
||||
p.parameters(sig.Params, false)
|
||||
p.parameters(sig.Params, funcParam)
|
||||
} else {
|
||||
p.print(token.LPAREN, token.RPAREN)
|
||||
}
|
||||
|
@ -428,7 +436,7 @@ func (p *printer) signature(sig *ast.FuncType) {
|
|||
p.expr(stripParensAlways(res.List[0].Type))
|
||||
return
|
||||
}
|
||||
p.parameters(res, false)
|
||||
p.parameters(res, funcParam)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1639,7 +1647,7 @@ func (p *printer) spec(spec ast.Spec, n int, doIndent bool) {
|
|||
p.setComment(s.Doc)
|
||||
p.expr(s.Name)
|
||||
if s.TypeParams != nil {
|
||||
p.parameters(s.TypeParams, true)
|
||||
p.parameters(s.TypeParams, typeTParam)
|
||||
}
|
||||
if n == 1 {
|
||||
p.print(blank)
|
||||
|
@ -1829,7 +1837,7 @@ func (p *printer) funcDecl(d *ast.FuncDecl) {
|
|||
// FUNC is emitted).
|
||||
startCol := p.out.Column - len("func ")
|
||||
if d.Recv != nil {
|
||||
p.parameters(d.Recv, false) // method: print receiver
|
||||
p.parameters(d.Recv, funcParam) // method: print receiver
|
||||
p.print(blank)
|
||||
}
|
||||
p.expr(d.Name)
|
||||
|
|
26
libgo/go/go/printer/testdata/generics.golden
vendored
26
libgo/go/go/printer/testdata/generics.golden
vendored
|
@ -64,3 +64,29 @@ type _ [P*T - T]struct{}
|
|||
type _[
|
||||
P *T,
|
||||
] struct{}
|
||||
|
||||
// equivalent test cases for potentially ambiguous type parameter lists, except
|
||||
// for function declarations there is no ambiguity (issue #51548)
|
||||
func _[P *T]() {}
|
||||
func _[P *T, _ any]() {}
|
||||
func _[P *T]() {}
|
||||
func _[P *T, _ any]() {}
|
||||
func _[P T]() {}
|
||||
func _[P T, _ any]() {}
|
||||
|
||||
func _[P *struct{}]() {}
|
||||
func _[P *struct{}]() {}
|
||||
func _[P []int]() {}
|
||||
|
||||
func _[P T]() {}
|
||||
func _[P T]() {}
|
||||
func _[P **T]() {}
|
||||
func _[P *T]() {}
|
||||
func _[P *T]() {}
|
||||
func _[P **T]() {}
|
||||
func _[P *T]() {}
|
||||
|
||||
func _[
|
||||
P *T,
|
||||
]() {
|
||||
}
|
||||
|
|
25
libgo/go/go/printer/testdata/generics.input
vendored
25
libgo/go/go/printer/testdata/generics.input
vendored
|
@ -61,3 +61,28 @@ type _ [P * T - T]struct{}
|
|||
type _[
|
||||
P *T,
|
||||
] struct{}
|
||||
|
||||
// equivalent test cases for potentially ambiguous type parameter lists, except
|
||||
// for function declarations there is no ambiguity (issue #51548)
|
||||
func _[P *T,]() {}
|
||||
func _[P *T, _ any]() {}
|
||||
func _[P (*T),]() {}
|
||||
func _[P (*T), _ any]() {}
|
||||
func _[P (T),]() {}
|
||||
func _[P (T), _ any]() {}
|
||||
|
||||
func _[P *struct{}] () {}
|
||||
func _[P (*struct{})] () {}
|
||||
func _[P ([]int)] () {}
|
||||
|
||||
func _ [P(T)]() {}
|
||||
func _ [P((T))]() {}
|
||||
func _ [P * *T]() {}
|
||||
func _ [P * T]() {}
|
||||
func _ [P(*T)]() {}
|
||||
func _ [P(**T)]() {}
|
||||
func _ [P * T]() {}
|
||||
|
||||
func _[
|
||||
P *T,
|
||||
]() {}
|
||||
|
|
|
@ -201,12 +201,12 @@ type Info struct {
|
|||
// qualified identifiers are collected in the Uses map.
|
||||
Types map[ast.Expr]TypeAndValue
|
||||
|
||||
// Instances maps identifiers denoting parameterized types or functions to
|
||||
// their type arguments and instantiated type.
|
||||
// Instances maps identifiers denoting generic types or functions to their
|
||||
// type arguments and instantiated type.
|
||||
//
|
||||
// For example, Instances will map the identifier for 'T' in the type
|
||||
// instantiation T[int, string] to the type arguments [int, string] and
|
||||
// resulting instantiated *Named type. Given a parameterized function
|
||||
// resulting instantiated *Named type. Given a generic function
|
||||
// func F[A any](A), Instances will map the identifier for 'F' in the call
|
||||
// expression F(int(1)) to the inferred type arguments [int], and resulting
|
||||
// instantiated *Signature.
|
||||
|
@ -419,8 +419,11 @@ func (conf *Config) Check(path string, fset *token.FileSet, files []*ast.File, i
|
|||
}
|
||||
|
||||
// AssertableTo reports whether a value of type V can be asserted to have type T.
|
||||
// The behavior of AssertableTo is undefined if V is a generalized interface; i.e.,
|
||||
// an interface that may only be used as a type constraint in Go code.
|
||||
//
|
||||
// The behavior of AssertableTo is undefined in two cases:
|
||||
// - if V is a generalized interface; i.e., an interface that may only be used
|
||||
// as a type constraint in Go code
|
||||
// - if T is an uninstantiated generic type
|
||||
func AssertableTo(V *Interface, T Type) bool {
|
||||
// Checker.newAssertableTo suppresses errors for invalid types, so we need special
|
||||
// handling here.
|
||||
|
@ -430,20 +433,31 @@ func AssertableTo(V *Interface, T Type) bool {
|
|||
return (*Checker)(nil).newAssertableTo(V, T) == nil
|
||||
}
|
||||
|
||||
// AssignableTo reports whether a value of type V is assignable to a variable of type T.
|
||||
// AssignableTo reports whether a value of type V is assignable to a variable
|
||||
// of type T.
|
||||
//
|
||||
// The behavior of AssignableTo is undefined if V or T is an uninstantiated
|
||||
// generic type.
|
||||
func AssignableTo(V, T Type) bool {
|
||||
x := operand{mode: value, typ: V}
|
||||
ok, _ := x.assignableTo(nil, T, nil) // check not needed for non-constant x
|
||||
return ok
|
||||
}
|
||||
|
||||
// ConvertibleTo reports whether a value of type V is convertible to a value of type T.
|
||||
// ConvertibleTo reports whether a value of type V is convertible to a value of
|
||||
// type T.
|
||||
//
|
||||
// The behavior of ConvertibleTo is undefined if V or T is an uninstantiated
|
||||
// generic type.
|
||||
func ConvertibleTo(V, T Type) bool {
|
||||
x := operand{mode: value, typ: V}
|
||||
return x.convertibleTo(nil, T, nil) // check not needed for non-constant x
|
||||
}
|
||||
|
||||
// Implements reports whether type V implements interface T.
|
||||
//
|
||||
// The behavior of Implements is undefined if V is an uninstantiated generic
|
||||
// type.
|
||||
func Implements(V Type, T *Interface) bool {
|
||||
if T.Empty() {
|
||||
// All types (even Typ[Invalid]) implement the empty interface.
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"internal/testenv"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -435,129 +436,146 @@ func TestTypesInfo(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestInstanceInfo(t *testing.T) {
|
||||
var tests = []struct {
|
||||
src string
|
||||
name string
|
||||
targs []string
|
||||
typ string
|
||||
}{
|
||||
{`package p0; func f[T any](T) {}; func _() { f(42) }`,
|
||||
`f`,
|
||||
[]string{`int`},
|
||||
`func(int)`,
|
||||
},
|
||||
{`package p1; func f[T any](T) T { panic(0) }; func _() { f('@') }`,
|
||||
`f`,
|
||||
[]string{`rune`},
|
||||
`func(rune) rune`,
|
||||
},
|
||||
{`package p2; func f[T any](...T) T { panic(0) }; func _() { f(0i) }`,
|
||||
`f`,
|
||||
[]string{`complex128`},
|
||||
`func(...complex128) complex128`,
|
||||
},
|
||||
{`package p3; func f[A, B, C any](A, *B, []C) {}; func _() { f(1.2, new(string), []byte{}) }`,
|
||||
`f`,
|
||||
[]string{`float64`, `string`, `byte`},
|
||||
`func(float64, *string, []byte)`,
|
||||
},
|
||||
{`package p4; func f[A, B any](A, *B, ...[]B) {}; func _() { f(1.2, new(byte)) }`,
|
||||
`f`,
|
||||
[]string{`float64`, `byte`},
|
||||
`func(float64, *byte, ...[]byte)`,
|
||||
},
|
||||
|
||||
{`package s1; func f[T any, P interface{~*T}](x T) {}; func _(x string) { f(x) }`,
|
||||
`f`,
|
||||
[]string{`string`, `*string`},
|
||||
`func(x string)`,
|
||||
},
|
||||
{`package s2; func f[T any, P interface{~*T}](x []T) {}; func _(x []int) { f(x) }`,
|
||||
`f`,
|
||||
[]string{`int`, `*int`},
|
||||
`func(x []int)`,
|
||||
},
|
||||
{`package s3; type C[T any] interface{~chan<- T}; func f[T any, P C[T]](x []T) {}; func _(x []int) { f(x) }`,
|
||||
`f`,
|
||||
[]string{`int`, `chan<- int`},
|
||||
`func(x []int)`,
|
||||
},
|
||||
{`package s4; type C[T any] interface{~chan<- T}; func f[T any, P C[T], Q C[[]*P]](x []T) {}; func _(x []int) { f(x) }`,
|
||||
`f`,
|
||||
[]string{`int`, `chan<- int`, `chan<- []*chan<- int`},
|
||||
`func(x []int)`,
|
||||
},
|
||||
|
||||
{`package t1; func f[T any, P interface{~*T}]() T { panic(0) }; func _() { _ = f[string] }`,
|
||||
`f`,
|
||||
[]string{`string`, `*string`},
|
||||
`func() string`,
|
||||
},
|
||||
{`package t2; func f[T any, P interface{~*T}]() T { panic(0) }; func _() { _ = (f[string]) }`,
|
||||
`f`,
|
||||
[]string{`string`, `*string`},
|
||||
`func() string`,
|
||||
},
|
||||
{`package t3; type C[T any] interface{~chan<- T}; func f[T any, P C[T]]() []T { return nil }; func _() { _ = f[int] }`,
|
||||
`f`,
|
||||
[]string{`int`, `chan<- int`},
|
||||
`func() []int`,
|
||||
},
|
||||
{`package t4; type C[T any] interface{~chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T { return nil }; func _() { _ = f[int] }`,
|
||||
`f`,
|
||||
[]string{`int`, `chan<- int`, `chan<- []*chan<- int`},
|
||||
`func() []int`,
|
||||
},
|
||||
{`package i0; import "lib"; func _() { lib.F(42) }`,
|
||||
`F`,
|
||||
[]string{`int`},
|
||||
`func(int)`,
|
||||
},
|
||||
{`package type0; type T[P interface{~int}] struct{ x P }; var _ T[int]`,
|
||||
`T`,
|
||||
[]string{`int`},
|
||||
`struct{x int}`,
|
||||
},
|
||||
{`package type1; type T[P interface{~int}] struct{ x P }; var _ (T[int])`,
|
||||
`T`,
|
||||
[]string{`int`},
|
||||
`struct{x int}`,
|
||||
},
|
||||
{`package type2; type T[P interface{~int}] struct{ x P }; var _ T[(int)]`,
|
||||
`T`,
|
||||
[]string{`int`},
|
||||
`struct{x int}`,
|
||||
},
|
||||
{`package type3; type T[P1 interface{~[]P2}, P2 any] struct{ x P1; y P2 }; var _ T[[]int, int]`,
|
||||
`T`,
|
||||
[]string{`[]int`, `int`},
|
||||
`struct{x []int; y int}`,
|
||||
},
|
||||
{`package type4; import "lib"; var _ lib.T[int]`,
|
||||
`T`,
|
||||
[]string{`int`},
|
||||
`[]int`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
const lib = `package lib
|
||||
const lib = `package lib
|
||||
|
||||
func F[P any](P) {}
|
||||
|
||||
type T[P any] []P
|
||||
`
|
||||
|
||||
type testInst struct {
|
||||
name string
|
||||
targs []string
|
||||
typ string
|
||||
}
|
||||
|
||||
var tests = []struct {
|
||||
src string
|
||||
instances []testInst // recorded instances in source order
|
||||
}{
|
||||
{`package p0; func f[T any](T) {}; func _() { f(42) }`,
|
||||
[]testInst{{`f`, []string{`int`}, `func(int)`}},
|
||||
},
|
||||
{`package p1; func f[T any](T) T { panic(0) }; func _() { f('@') }`,
|
||||
[]testInst{{`f`, []string{`rune`}, `func(rune) rune`}},
|
||||
},
|
||||
{`package p2; func f[T any](...T) T { panic(0) }; func _() { f(0i) }`,
|
||||
[]testInst{{`f`, []string{`complex128`}, `func(...complex128) complex128`}},
|
||||
},
|
||||
{`package p3; func f[A, B, C any](A, *B, []C) {}; func _() { f(1.2, new(string), []byte{}) }`,
|
||||
[]testInst{{`f`, []string{`float64`, `string`, `byte`}, `func(float64, *string, []byte)`}},
|
||||
},
|
||||
{`package p4; func f[A, B any](A, *B, ...[]B) {}; func _() { f(1.2, new(byte)) }`,
|
||||
[]testInst{{`f`, []string{`float64`, `byte`}, `func(float64, *byte, ...[]byte)`}},
|
||||
},
|
||||
|
||||
{`package s1; func f[T any, P interface{*T}](x T) {}; func _(x string) { f(x) }`,
|
||||
[]testInst{{`f`, []string{`string`, `*string`}, `func(x string)`}},
|
||||
},
|
||||
{`package s2; func f[T any, P interface{*T}](x []T) {}; func _(x []int) { f(x) }`,
|
||||
[]testInst{{`f`, []string{`int`, `*int`}, `func(x []int)`}},
|
||||
},
|
||||
{`package s3; type C[T any] interface{chan<- T}; func f[T any, P C[T]](x []T) {}; func _(x []int) { f(x) }`,
|
||||
[]testInst{
|
||||
{`C`, []string{`T`}, `interface{chan<- T}`},
|
||||
{`f`, []string{`int`, `chan<- int`}, `func(x []int)`},
|
||||
},
|
||||
},
|
||||
{`package s4; type C[T any] interface{chan<- T}; func f[T any, P C[T], Q C[[]*P]](x []T) {}; func _(x []int) { f(x) }`,
|
||||
[]testInst{
|
||||
{`C`, []string{`T`}, `interface{chan<- T}`},
|
||||
{`C`, []string{`[]*P`}, `interface{chan<- []*P}`},
|
||||
{`f`, []string{`int`, `chan<- int`, `chan<- []*chan<- int`}, `func(x []int)`},
|
||||
},
|
||||
},
|
||||
|
||||
{`package t1; func f[T any, P interface{*T}]() T { panic(0) }; func _() { _ = f[string] }`,
|
||||
[]testInst{{`f`, []string{`string`, `*string`}, `func() string`}},
|
||||
},
|
||||
{`package t2; func f[T any, P interface{*T}]() T { panic(0) }; func _() { _ = (f[string]) }`,
|
||||
[]testInst{{`f`, []string{`string`, `*string`}, `func() string`}},
|
||||
},
|
||||
{`package t3; type C[T any] interface{chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T { return nil }; func _() { _ = f[int] }`,
|
||||
[]testInst{
|
||||
{`C`, []string{`T`}, `interface{chan<- T}`},
|
||||
{`C`, []string{`[]*P`}, `interface{chan<- []*P}`},
|
||||
{`f`, []string{`int`, `chan<- int`, `chan<- []*chan<- int`}, `func() []int`},
|
||||
},
|
||||
},
|
||||
{`package t4; type C[T any] interface{chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T { return nil }; func _() { _ = (f[int]) }`,
|
||||
[]testInst{
|
||||
{`C`, []string{`T`}, `interface{chan<- T}`},
|
||||
{`C`, []string{`[]*P`}, `interface{chan<- []*P}`},
|
||||
{`f`, []string{`int`, `chan<- int`, `chan<- []*chan<- int`}, `func() []int`},
|
||||
},
|
||||
},
|
||||
{`package i0; import "lib"; func _() { lib.F(42) }`,
|
||||
[]testInst{{`F`, []string{`int`}, `func(int)`}},
|
||||
},
|
||||
|
||||
{`package duplfunc0; func f[T any](T) {}; func _() { f(42); f("foo"); f[int](3) }`,
|
||||
[]testInst{
|
||||
{`f`, []string{`int`}, `func(int)`},
|
||||
{`f`, []string{`string`}, `func(string)`},
|
||||
{`f`, []string{`int`}, `func(int)`},
|
||||
},
|
||||
},
|
||||
{`package duplfunc1; import "lib"; func _() { lib.F(42); lib.F("foo"); lib.F(3) }`,
|
||||
[]testInst{
|
||||
{`F`, []string{`int`}, `func(int)`},
|
||||
{`F`, []string{`string`}, `func(string)`},
|
||||
{`F`, []string{`int`}, `func(int)`},
|
||||
},
|
||||
},
|
||||
|
||||
{`package type0; type T[P interface{~int}] struct{ x P }; var _ T[int]`,
|
||||
[]testInst{{`T`, []string{`int`}, `struct{x int}`}},
|
||||
},
|
||||
{`package type1; type T[P interface{~int}] struct{ x P }; var _ (T[int])`,
|
||||
[]testInst{{`T`, []string{`int`}, `struct{x int}`}},
|
||||
},
|
||||
{`package type2; type T[P interface{~int}] struct{ x P }; var _ T[(int)]`,
|
||||
[]testInst{{`T`, []string{`int`}, `struct{x int}`}},
|
||||
},
|
||||
{`package type3; type T[P1 interface{~[]P2}, P2 any] struct{ x P1; y P2 }; var _ T[[]int, int]`,
|
||||
[]testInst{{`T`, []string{`[]int`, `int`}, `struct{x []int; y int}`}},
|
||||
},
|
||||
{`package type4; import "lib"; var _ lib.T[int]`,
|
||||
[]testInst{{`T`, []string{`int`}, `[]int`}},
|
||||
},
|
||||
|
||||
{`package dupltype0; type T[P interface{~int}] struct{ x P }; var x T[int]; var y T[int]`,
|
||||
[]testInst{
|
||||
{`T`, []string{`int`}, `struct{x int}`},
|
||||
{`T`, []string{`int`}, `struct{x int}`},
|
||||
},
|
||||
},
|
||||
{`package dupltype1; type T[P ~int] struct{ x P }; func (r *T[Q]) add(z T[Q]) { r.x += z.x }`,
|
||||
[]testInst{
|
||||
{`T`, []string{`Q`}, `struct{x Q}`},
|
||||
{`T`, []string{`Q`}, `struct{x Q}`},
|
||||
},
|
||||
},
|
||||
{`package dupltype1; import "lib"; var x lib.T[int]; var y lib.T[int]; var z lib.T[string]`,
|
||||
[]testInst{
|
||||
{`T`, []string{`int`}, `[]int`},
|
||||
{`T`, []string{`int`}, `[]int`},
|
||||
{`T`, []string{`string`}, `[]string`},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
imports := make(testImporter)
|
||||
conf := Config{Importer: imports}
|
||||
instances := make(map[*ast.Ident]Instance)
|
||||
uses := make(map[*ast.Ident]Object)
|
||||
instMap := make(map[*ast.Ident]Instance)
|
||||
useMap := make(map[*ast.Ident]Object)
|
||||
makePkg := func(src string) *Package {
|
||||
f, err := parser.ParseFile(fset, "p.go", src, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pkg, err := conf.Check("", fset, []*ast.File{f}, &Info{Instances: instances, Uses: uses})
|
||||
pkg, err := conf.Check("", fset, []*ast.File{f}, &Info{Instances: instMap, Uses: useMap})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -567,60 +585,71 @@ type T[P any] []P
|
|||
makePkg(lib)
|
||||
pkg := makePkg(test.src)
|
||||
|
||||
// look for instance information
|
||||
var targs []Type
|
||||
var typ Type
|
||||
for ident, inst := range instances {
|
||||
if ExprString(ident) == test.name {
|
||||
for i := 0; i < inst.TypeArgs.Len(); i++ {
|
||||
targs = append(targs, inst.TypeArgs.At(i))
|
||||
}
|
||||
typ = inst.Type
|
||||
t.Run(pkg.Name(), func(t *testing.T) {
|
||||
// Sort instances in source order for stability.
|
||||
instances := sortedInstances(instMap)
|
||||
if got, want := len(instances), len(test.instances); got != want {
|
||||
t.Fatalf("got %d instances, want %d", got, want)
|
||||
}
|
||||
|
||||
// Check that we can find the corresponding parameterized type.
|
||||
ptype := uses[ident].Type()
|
||||
// Pairwise compare with the expected instances.
|
||||
for ii, inst := range instances {
|
||||
var targs []Type
|
||||
for i := 0; i < inst.Inst.TypeArgs.Len(); i++ {
|
||||
targs = append(targs, inst.Inst.TypeArgs.At(i))
|
||||
}
|
||||
typ := inst.Inst.Type
|
||||
|
||||
testInst := test.instances[ii]
|
||||
if got := inst.Ident.Name; got != testInst.name {
|
||||
t.Fatalf("got name %s, want %s", got, testInst.name)
|
||||
}
|
||||
if len(targs) != len(testInst.targs) {
|
||||
t.Fatalf("got %d type arguments; want %d", len(targs), len(testInst.targs))
|
||||
}
|
||||
for i, targ := range targs {
|
||||
if got := targ.String(); got != testInst.targs[i] {
|
||||
t.Errorf("type argument %d: got %s; want %s", i, got, testInst.targs[i])
|
||||
}
|
||||
}
|
||||
if got := typ.Underlying().String(); got != testInst.typ {
|
||||
t.Errorf("package %s: got %s; want %s", pkg.Name(), got, testInst.typ)
|
||||
}
|
||||
|
||||
// Verify the invariant that re-instantiating the corresponding generic
|
||||
// type with TypeArgs results in an identical instance.
|
||||
ptype := useMap[inst.Ident].Type()
|
||||
lister, _ := ptype.(interface{ TypeParams() *TypeParamList })
|
||||
if lister == nil || lister.TypeParams().Len() == 0 {
|
||||
t.Errorf("package %s: info.Types[%v] = %v, want parameterized type", pkg.Name(), ident, ptype)
|
||||
continue
|
||||
t.Fatalf("info.Types[%v] = %v, want parameterized type", inst.Ident, ptype)
|
||||
}
|
||||
|
||||
// Verify the invariant that re-instantiating the generic type with
|
||||
// TypeArgs results in an equivalent type.
|
||||
inst2, err := Instantiate(nil, ptype, targs, true)
|
||||
if err != nil {
|
||||
t.Errorf("Instantiate(%v, %v) failed: %v", ptype, targs, err)
|
||||
}
|
||||
if !Identical(inst.Type, inst2) {
|
||||
t.Errorf("%v and %v are not identical", inst.Type, inst2)
|
||||
if !Identical(inst.Inst.Type, inst2) {
|
||||
t.Errorf("%v and %v are not identical", inst.Inst.Type, inst2)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if targs == nil {
|
||||
t.Errorf("package %s: no instance information found for %s", pkg.Name(), test.name)
|
||||
continue
|
||||
}
|
||||
|
||||
// check that type arguments are correct
|
||||
if len(targs) != len(test.targs) {
|
||||
t.Errorf("package %s: got %d type arguments; want %d", pkg.Name(), len(targs), len(test.targs))
|
||||
continue
|
||||
}
|
||||
for i, targ := range targs {
|
||||
if got := targ.String(); got != test.targs[i] {
|
||||
t.Errorf("package %s, %d. type argument: got %s; want %s", pkg.Name(), i, got, test.targs[i])
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// check that the types match
|
||||
if got := typ.Underlying().String(); got != test.typ {
|
||||
t.Errorf("package %s: got %s; want %s", pkg.Name(), got, test.typ)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type recordedInstance struct {
|
||||
Ident *ast.Ident
|
||||
Inst Instance
|
||||
}
|
||||
|
||||
func sortedInstances(m map[*ast.Ident]Instance) (instances []recordedInstance) {
|
||||
for id, inst := range m {
|
||||
instances = append(instances, recordedInstance{id, inst})
|
||||
}
|
||||
sort.Slice(instances, func(i, j int) bool {
|
||||
return instances[i].Ident.Pos() < instances[j].Ident.Pos()
|
||||
})
|
||||
return instances
|
||||
}
|
||||
|
||||
func TestDefsInfo(t *testing.T) {
|
||||
var tests = []struct {
|
||||
src string
|
||||
|
@ -1690,7 +1719,7 @@ func F(){
|
|||
var F = /*F=func:12*/ F /*F=var:17*/ ; _ = F
|
||||
|
||||
var a []int
|
||||
for i, x := range /*i=undef*/ /*x=var:16*/ a /*i=var:20*/ /*x=var:20*/ { _ = i; _ = x }
|
||||
for i, x := range a /*i=undef*/ /*x=var:16*/ { _ = i; _ = x }
|
||||
|
||||
var i interface{}
|
||||
switch y := i.(type) { /*y=undef*/
|
||||
|
|
|
@ -290,15 +290,14 @@ func (check *Checker) typesSummary(list []Type, variadic bool) string {
|
|||
return "(" + strings.Join(res, ", ") + ")"
|
||||
}
|
||||
|
||||
func (check *Checker) assignError(rhs []ast.Expr, nvars, nvals int) {
|
||||
measure := func(x int, unit string) string {
|
||||
s := fmt.Sprintf("%d %s", x, unit)
|
||||
if x != 1 {
|
||||
s += "s"
|
||||
}
|
||||
return s
|
||||
func measure(x int, unit string) string {
|
||||
if x != 1 {
|
||||
unit += "s"
|
||||
}
|
||||
return fmt.Sprintf("%d %s", x, unit)
|
||||
}
|
||||
|
||||
func (check *Checker) assignError(rhs []ast.Expr, nvars, nvals int) {
|
||||
vars := measure(nvars, "variable")
|
||||
vals := measure(nvals, "value")
|
||||
rhs0 := rhs[0]
|
||||
|
|
|
@ -429,7 +429,7 @@ var cgoPrefixes = [...]string{
|
|||
"_Cmacro_", // function to evaluate the expanded expression
|
||||
}
|
||||
|
||||
func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
|
||||
func (check *Checker) selector(x *operand, e *ast.SelectorExpr, def *Named) {
|
||||
// these must be declared before the "goto Error" statements
|
||||
var (
|
||||
obj Object
|
||||
|
@ -527,7 +527,18 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
|
|||
}
|
||||
|
||||
check.exprOrType(x, e.X, false)
|
||||
if x.mode == invalid {
|
||||
switch x.mode {
|
||||
case typexpr:
|
||||
// don't crash for "type T T.x" (was issue #51509)
|
||||
if def != nil && x.typ == def {
|
||||
check.cycleError([]Object{def.obj})
|
||||
goto Error
|
||||
}
|
||||
case builtin:
|
||||
// types2 uses the position of '.' for the error
|
||||
check.errorf(e.Sel, _UncalledBuiltin, "cannot select on %s", x)
|
||||
goto Error
|
||||
case invalid:
|
||||
goto Error
|
||||
}
|
||||
|
||||
|
|
|
@ -133,7 +133,7 @@ type Checker struct {
|
|||
untyped map[ast.Expr]exprInfo // map of expressions without final type
|
||||
delayed []action // stack of delayed action segments; segments are processed in FIFO order
|
||||
objPath []Object // path of object dependencies during type inference (for cycle reporting)
|
||||
defTypes []*Named // defined types created during type checking, for final validation.
|
||||
cleaners []cleaner // list of types that may need a final cleanup at the end of type-checking
|
||||
|
||||
// environment within which the current object is type-checked (valid only
|
||||
// for the duration of type-checking a specific object)
|
||||
|
@ -212,6 +212,16 @@ func (check *Checker) pop() Object {
|
|||
return obj
|
||||
}
|
||||
|
||||
type cleaner interface {
|
||||
cleanup()
|
||||
}
|
||||
|
||||
// needsCleanup records objects/types that implement the cleanup method
|
||||
// which will be called at the end of type-checking.
|
||||
func (check *Checker) needsCleanup(c cleaner) {
|
||||
check.cleaners = append(check.cleaners, c)
|
||||
}
|
||||
|
||||
// NewChecker returns a new Checker instance for a given package.
|
||||
// Package files may be added incrementally via checker.Files.
|
||||
func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Checker {
|
||||
|
@ -255,6 +265,8 @@ func (check *Checker) initFiles(files []*ast.File) {
|
|||
check.methods = nil
|
||||
check.untyped = nil
|
||||
check.delayed = nil
|
||||
check.objPath = nil
|
||||
check.cleaners = nil
|
||||
|
||||
// determine package name and collect valid files
|
||||
pkg := check.pkg
|
||||
|
@ -304,22 +316,37 @@ func (check *Checker) checkFiles(files []*ast.File) (err error) {
|
|||
|
||||
defer check.handleBailout(&err)
|
||||
|
||||
print := func(msg string) {
|
||||
if trace {
|
||||
fmt.Println()
|
||||
fmt.Println(msg)
|
||||
}
|
||||
}
|
||||
|
||||
print("== initFiles ==")
|
||||
check.initFiles(files)
|
||||
|
||||
print("== collectObjects ==")
|
||||
check.collectObjects()
|
||||
|
||||
print("== packageObjects ==")
|
||||
check.packageObjects()
|
||||
|
||||
print("== processDelayed ==")
|
||||
check.processDelayed(0) // incl. all functions
|
||||
|
||||
check.expandDefTypes()
|
||||
print("== cleanup ==")
|
||||
check.cleanup()
|
||||
|
||||
print("== initOrder ==")
|
||||
check.initOrder()
|
||||
|
||||
if !check.conf.DisableUnusedImportCheck {
|
||||
print("== unusedImports ==")
|
||||
check.unusedImports()
|
||||
}
|
||||
|
||||
print("== recordUntyped ==")
|
||||
check.recordUntyped()
|
||||
|
||||
if check.firstErr == nil {
|
||||
|
@ -337,7 +364,6 @@ func (check *Checker) checkFiles(files []*ast.File) (err error) {
|
|||
check.recvTParamMap = nil
|
||||
check.brokenAliases = nil
|
||||
check.unionTypeSets = nil
|
||||
check.defTypes = nil
|
||||
check.ctxt = nil
|
||||
|
||||
// TODO(rFindley) There's more memory we should release at this point.
|
||||
|
@ -365,27 +391,13 @@ func (check *Checker) processDelayed(top int) {
|
|||
check.delayed = check.delayed[:top]
|
||||
}
|
||||
|
||||
func (check *Checker) expandDefTypes() {
|
||||
// Ensure that every defined type created in the course of type-checking has
|
||||
// either non-*Named underlying, or is unresolved.
|
||||
//
|
||||
// This guarantees that we don't leak any types whose underlying is *Named,
|
||||
// because any unresolved instances will lazily compute their underlying by
|
||||
// substituting in the underlying of their origin. The origin must have
|
||||
// either been imported or type-checked and expanded here, and in either case
|
||||
// its underlying will be fully expanded.
|
||||
for i := 0; i < len(check.defTypes); i++ {
|
||||
n := check.defTypes[i]
|
||||
switch n.underlying.(type) {
|
||||
case nil:
|
||||
if n.resolver == nil {
|
||||
panic("nil underlying")
|
||||
}
|
||||
case *Named:
|
||||
n.under() // n.under may add entries to check.defTypes
|
||||
}
|
||||
n.check = nil
|
||||
// cleanup runs cleanup for all collected cleaners.
|
||||
func (check *Checker) cleanup() {
|
||||
// Don't use a range clause since Named.cleanup may add more cleaners.
|
||||
for i := 0; i < len(check.cleaners); i++ {
|
||||
check.cleaners[i].cleanup()
|
||||
}
|
||||
check.cleaners = nil
|
||||
}
|
||||
|
||||
func (check *Checker) record(x *operand) {
|
||||
|
|
|
@ -48,11 +48,14 @@ func (check *Checker) conversion(x *operand, T Type) {
|
|||
// have specific types, constant x cannot be
|
||||
// converted.
|
||||
ok = T.(*TypeParam).underIs(func(u Type) bool {
|
||||
// t is nil if there are no specific type terms
|
||||
// u is nil if there are no specific type terms
|
||||
if u == nil {
|
||||
cause = check.sprintf("%s does not contain specific types", T)
|
||||
return false
|
||||
}
|
||||
if isString(x.typ) && isBytesOrRunes(u) {
|
||||
return true
|
||||
}
|
||||
if !constConvertibleTo(u, nil) {
|
||||
cause = check.sprintf("cannot convert %s to %s (in %s)", x, u, T)
|
||||
return false
|
||||
|
|
|
@ -624,7 +624,6 @@ func (check *Checker) collectTypeParams(dst **TypeParamList, list *ast.FieldList
|
|||
}()
|
||||
|
||||
index := 0
|
||||
var bounds []Type
|
||||
for _, f := range list.List {
|
||||
var bound Type
|
||||
// NOTE: we may be able to assert that f.Type != nil here, but this is not
|
||||
|
@ -642,7 +641,6 @@ func (check *Checker) collectTypeParams(dst **TypeParamList, list *ast.FieldList
|
|||
} else {
|
||||
bound = Typ[Invalid]
|
||||
}
|
||||
bounds = append(bounds, bound)
|
||||
for i := range f.Names {
|
||||
tparams[index+i].bound = bound
|
||||
}
|
||||
|
|
|
@ -1339,11 +1339,6 @@ const (
|
|||
// func _() {
|
||||
// f()
|
||||
// }
|
||||
//
|
||||
// Example:
|
||||
// type N[P, Q any] struct{}
|
||||
//
|
||||
// var _ N[int]
|
||||
_CannotInferTypeArgs
|
||||
|
||||
// _InvalidTypeArg occurs when a type argument does not satisfy its
|
||||
|
|
|
@ -37,8 +37,8 @@ func Eval(fset *token.FileSet, pkg *Package, pos token.Pos, expr string) (_ Type
|
|||
|
||||
// CheckExpr type checks the expression expr as if it had appeared at position
|
||||
// pos of package pkg. Type information about the expression is recorded in
|
||||
// info. The expression may be an uninstantiated parameterized function or
|
||||
// type.
|
||||
// info. The expression may be an identifier denoting an uninstantiated generic
|
||||
// function or type.
|
||||
//
|
||||
// If pkg == nil, the Universe scope is used and the provided
|
||||
// position pos is ignored. If pkg != nil, and pos is invalid,
|
||||
|
|
|
@ -859,7 +859,7 @@ func (check *Checker) incomparableCause(typ Type) string {
|
|||
}
|
||||
// see if we can extract a more specific error
|
||||
var cause string
|
||||
comparable(typ, nil, func(format string, args ...interface{}) {
|
||||
comparable(typ, true, nil, func(format string, args ...interface{}) {
|
||||
cause = check.sprintf(format, args...)
|
||||
})
|
||||
return cause
|
||||
|
@ -1339,6 +1339,10 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
|||
// no composite literal type present - use hint (element type of enclosing type)
|
||||
typ = hint
|
||||
base, _ = deref(coreType(typ)) // *T implies &T{}
|
||||
if base == nil {
|
||||
check.errorf(e, _InvalidLit, "invalid composite literal element type %s: no core type", typ)
|
||||
goto Error
|
||||
}
|
||||
|
||||
default:
|
||||
// TODO(gri) provide better error messages depending on context
|
||||
|
@ -1529,7 +1533,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
|||
return kind
|
||||
|
||||
case *ast.SelectorExpr:
|
||||
check.selector(x, e)
|
||||
check.selector(x, e, nil)
|
||||
|
||||
case *ast.IndexExpr, *ast.IndexListExpr:
|
||||
ix := typeparams.UnpackIndexExpr(e)
|
||||
|
@ -1584,6 +1588,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
|||
case invalid:
|
||||
goto Error
|
||||
case typexpr:
|
||||
check.validVarType(e.X, x.typ)
|
||||
x.typ = &Pointer{base: x.typ}
|
||||
default:
|
||||
var base Type
|
||||
|
|
|
@ -183,6 +183,7 @@ func (check *Checker) indexExpr(x *operand, e *typeparams.IndexExpr) (isFuncInst
|
|||
}
|
||||
|
||||
if !valid {
|
||||
// types2 uses the position of '[' for the error
|
||||
check.invalidOp(x, _NonIndexableOperand, "cannot index %s", x)
|
||||
x.mode = invalid
|
||||
return false
|
||||
|
|
|
@ -487,21 +487,88 @@ func (check *Checker) inferB(posn positioner, tparams []*TypeParam, targs []Type
|
|||
}
|
||||
}
|
||||
|
||||
// If a constraint has a core type, unify the corresponding type parameter with it.
|
||||
for _, tpar := range tparams {
|
||||
if ctype := adjCoreType(tpar); ctype != nil {
|
||||
if !u.unify(tpar, ctype) {
|
||||
// TODO(gri) improve error message by providing the type arguments
|
||||
// which we know already
|
||||
check.errorf(posn, _InvalidTypeArg, "%s does not match %s", tpar, ctype)
|
||||
return nil, 0
|
||||
// Repeatedly apply constraint type inference as long as
|
||||
// there are still unknown type arguments and progress is
|
||||
// being made.
|
||||
//
|
||||
// This is an O(n^2) algorithm where n is the number of
|
||||
// type parameters: if there is progress (and iteration
|
||||
// continues), at least one type argument is inferred
|
||||
// per iteration and we have a doubly nested loop.
|
||||
// In practice this is not a problem because the number
|
||||
// of type parameters tends to be very small (< 5 or so).
|
||||
// (It should be possible for unification to efficiently
|
||||
// signal newly inferred type arguments; then the loops
|
||||
// here could handle the respective type parameters only,
|
||||
// but that will come at a cost of extra complexity which
|
||||
// may not be worth it.)
|
||||
for n := u.x.unknowns(); n > 0; {
|
||||
nn := n
|
||||
|
||||
for i, tpar := range tparams {
|
||||
// If there is a core term (i.e., a core type with tilde information)
|
||||
// unify the type parameter with the core type.
|
||||
if core, single := coreTerm(tpar); core != nil {
|
||||
// A type parameter can be unified with its core type in two cases.
|
||||
tx := u.x.at(i)
|
||||
switch {
|
||||
case tx != nil:
|
||||
// The corresponding type argument tx is known.
|
||||
// In this case, if the core type has a tilde, the type argument's underlying
|
||||
// type must match the core type, otherwise the type argument and the core type
|
||||
// must match.
|
||||
// If tx is an external type parameter, don't consider its underlying type
|
||||
// (which is an interface). Core type unification will attempt to unify against
|
||||
// core.typ.
|
||||
// Note also that even with inexact unification we cannot leave away the under
|
||||
// call here because it's possible that both tx and core.typ are named types,
|
||||
// with under(tx) being a (named) basic type matching core.typ. Such cases do
|
||||
// not match with inexact unification.
|
||||
if core.tilde && !isTypeParam(tx) {
|
||||
tx = under(tx)
|
||||
}
|
||||
if !u.unify(tx, core.typ) {
|
||||
// TODO(gri) improve error message by providing the type arguments
|
||||
// which we know already
|
||||
// Don't use term.String() as it always qualifies types, even if they
|
||||
// are in the current package.
|
||||
tilde := ""
|
||||
if core.tilde {
|
||||
tilde = "~"
|
||||
}
|
||||
check.errorf(posn, _InvalidTypeArg, "%s does not match %s%s", tpar, tilde, core.typ)
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
case single && !core.tilde:
|
||||
// The corresponding type argument tx is unknown and there's a single
|
||||
// specific type and no tilde.
|
||||
// In this case the type argument must be that single type; set it.
|
||||
u.x.set(i, core.typ)
|
||||
|
||||
default:
|
||||
// Unification is not possible and no progress was made.
|
||||
continue
|
||||
}
|
||||
|
||||
// The number of known type arguments may have changed.
|
||||
nn = u.x.unknowns()
|
||||
if nn == 0 {
|
||||
break // all type arguments are known
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(nn <= n)
|
||||
if nn == n {
|
||||
break // no progress
|
||||
}
|
||||
n = nn
|
||||
}
|
||||
|
||||
// u.x.types() now contains the incoming type arguments plus any additional type
|
||||
// arguments which were inferred from core types. The newly inferred non-
|
||||
// nil entries may still contain references to other type parameters.
|
||||
// arguments which were inferred from core terms. The newly inferred non-nil
|
||||
// entries may still contain references to other type parameters.
|
||||
// For instance, for [A any, B interface{ []C }, C interface{ *A }], if A == int
|
||||
// was given, unification produced the type list [int, []C, *A]. We eliminate the
|
||||
// remaining type parameters by substituting the type parameters in this type list
|
||||
|
@ -590,17 +657,40 @@ func (check *Checker) inferB(posn positioner, tparams []*TypeParam, targs []Type
|
|||
return
|
||||
}
|
||||
|
||||
func adjCoreType(tpar *TypeParam) Type {
|
||||
// If the type parameter embeds a single, possibly named
|
||||
// type, use that one instead of the core type (which is
|
||||
// always the underlying type of that single type).
|
||||
if single := tpar.singleType(); single != nil {
|
||||
if debug {
|
||||
assert(under(single) == coreType(tpar))
|
||||
// If the type parameter has a single specific type S, coreTerm returns (S, true).
|
||||
// Otherwise, if tpar has a core type T, it returns a term corresponding to that
|
||||
// core type and false. In that case, if any term of tpar has a tilde, the core
|
||||
// term has a tilde. In all other cases coreTerm returns (nil, false).
|
||||
func coreTerm(tpar *TypeParam) (*term, bool) {
|
||||
n := 0
|
||||
var single *term // valid if n == 1
|
||||
var tilde bool
|
||||
tpar.is(func(t *term) bool {
|
||||
if t == nil {
|
||||
assert(n == 0)
|
||||
return false // no terms
|
||||
}
|
||||
return single
|
||||
n++
|
||||
single = t
|
||||
if t.tilde {
|
||||
tilde = true
|
||||
}
|
||||
return true
|
||||
})
|
||||
if n == 1 {
|
||||
if debug {
|
||||
assert(debug && under(single.typ) == coreType(tpar))
|
||||
}
|
||||
return single, true
|
||||
}
|
||||
return coreType(tpar)
|
||||
if typ := coreType(tpar); typ != nil {
|
||||
// A core type is always an underlying type.
|
||||
// If any term of tpar has a tilde, we don't
|
||||
// have a precise core type and we must return
|
||||
// a tilde as well.
|
||||
return &term{tilde, typ}, false
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
type cycleFinder struct {
|
||||
|
@ -648,8 +738,6 @@ func (w *cycleFinder) typ(typ Type) {
|
|||
// in signatures where they are handled explicitly.
|
||||
|
||||
case *Signature:
|
||||
// There are no "method types" so we should never see a recv.
|
||||
assert(t.recv == nil)
|
||||
if t.params != nil {
|
||||
w.varList(t.params.vars)
|
||||
}
|
||||
|
|
|
@ -15,10 +15,10 @@ import (
|
|||
|
||||
// Instantiate instantiates the type orig with the given type arguments targs.
|
||||
// orig must be a *Named or a *Signature type. If there is no error, the
|
||||
// resulting Type is a new, instantiated (not parameterized) type of the same
|
||||
// kind (either a *Named or a *Signature). Methods attached to a *Named type
|
||||
// are also instantiated, and associated with a new *Func that has the same
|
||||
// position as the original method, but nil function scope.
|
||||
// resulting Type is an instantiated type of the same kind (either a *Named or
|
||||
// a *Signature). Methods attached to a *Named type are also instantiated, and
|
||||
// associated with a new *Func that has the same position as the original
|
||||
// method, but nil function scope.
|
||||
//
|
||||
// If ctxt is non-nil, it may be used to de-duplicate the instance against
|
||||
// previous instances with the same identity. As a special case, generic
|
||||
|
@ -204,7 +204,7 @@ func (check *Checker) implements(V, T Type) error {
|
|||
// If T is comparable, V must be comparable.
|
||||
// Remember as a pending error and report only if we don't have a more specific error.
|
||||
var pending error
|
||||
if Ti.IsComparable() && ((Vi != nil && !Vi.IsComparable()) || (Vi == nil && !Comparable(V))) {
|
||||
if Ti.IsComparable() && !comparable(V, false, nil, nil) {
|
||||
pending = errorf("%s does not implement comparable", V)
|
||||
}
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ func NewInterfaceType(methods []*Func, embeddeds []Type) *Interface {
|
|||
}
|
||||
|
||||
// set method receivers if necessary
|
||||
typ := new(Interface)
|
||||
typ := (*Checker)(nil).newInterface()
|
||||
for _, m := range methods {
|
||||
if sig := m.typ.(*Signature); sig.recv == nil {
|
||||
sig.recv = NewVar(m.pos, m.pkg, "", typ)
|
||||
|
@ -73,6 +73,15 @@ func NewInterfaceType(methods []*Func, embeddeds []Type) *Interface {
|
|||
return typ
|
||||
}
|
||||
|
||||
// check may be nil
|
||||
func (check *Checker) newInterface() *Interface {
|
||||
typ := &Interface{check: check}
|
||||
if check != nil {
|
||||
check.needsCleanup(typ)
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
// MarkImplicit marks the interface t as implicit, meaning this interface
|
||||
// corresponds to a constraint literal such as ~T or A|B without explicit
|
||||
// interface embedding. MarkImplicit should be called before any concurrent use
|
||||
|
@ -141,6 +150,11 @@ func (t *Interface) String() string { return TypeString(t, nil) }
|
|||
// ----------------------------------------------------------------------------
|
||||
// Implementation
|
||||
|
||||
func (t *Interface) cleanup() {
|
||||
t.check = nil
|
||||
t.embedPos = nil
|
||||
}
|
||||
|
||||
func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, def *Named) {
|
||||
addEmbedded := func(pos token.Pos, typ Type) {
|
||||
ityp.embeddeds = append(ityp.embeddeds, typ)
|
||||
|
@ -210,16 +224,10 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
|
|||
sortMethods(ityp.methods)
|
||||
// (don't sort embeddeds: they must correspond to *embedPos entries)
|
||||
|
||||
// Compute type set with a non-nil *Checker as soon as possible
|
||||
// to report any errors. Subsequent uses of type sets will use
|
||||
// this computed type set and won't need to pass in a *Checker.
|
||||
//
|
||||
// Pin the checker to the interface type in the interim, in case the type set
|
||||
// must be used before delayed funcs are processed (see issue #48234).
|
||||
// TODO(rfindley): clean up use of *Checker with computeInterfaceTypeSet
|
||||
ityp.check = check
|
||||
// Compute type set as soon as possible to report any errors.
|
||||
// Subsequent uses of type sets will use this computed type
|
||||
// set and won't need to pass in a *Checker.
|
||||
check.later(func() {
|
||||
computeInterfaceTypeSet(check, iface.Pos(), ityp)
|
||||
ityp.check = nil
|
||||
}).describef(iface, "compute type set for %s", ityp)
|
||||
}
|
||||
|
|
|
@ -70,7 +70,8 @@ func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
|
|||
// see if there is a matching field (but not a method, those need to be declared
|
||||
// explicitly in the constraint). If the constraint is a named pointer type (see
|
||||
// above), we are ok here because only fields are accepted as results.
|
||||
if obj == nil && isTypeParam(T) {
|
||||
const enableTParamFieldLookup = false // see issue #51576
|
||||
if enableTParamFieldLookup && obj == nil && isTypeParam(T) {
|
||||
if t := coreType(T); t != nil {
|
||||
obj, index, indirect = lookupFieldOrMethod(t, addressable, pkg, name, false)
|
||||
if _, ok := obj.(*Var); !ok {
|
||||
|
|
|
@ -72,18 +72,38 @@ func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, tpar
|
|||
}
|
||||
// Ensure that typ is always expanded and sanity-checked.
|
||||
if check != nil {
|
||||
check.defTypes = append(check.defTypes, typ)
|
||||
check.needsCleanup(typ)
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
func (t *Named) cleanup() {
|
||||
// Ensure that every defined type created in the course of type-checking has
|
||||
// either non-*Named underlying, or is unresolved.
|
||||
//
|
||||
// This guarantees that we don't leak any types whose underlying is *Named,
|
||||
// because any unresolved instances will lazily compute their underlying by
|
||||
// substituting in the underlying of their origin. The origin must have
|
||||
// either been imported or type-checked and expanded here, and in either case
|
||||
// its underlying will be fully expanded.
|
||||
switch t.underlying.(type) {
|
||||
case nil:
|
||||
if t.resolver == nil {
|
||||
panic("nil underlying")
|
||||
}
|
||||
case *Named:
|
||||
t.under() // t.under may add entries to check.cleaners
|
||||
}
|
||||
t.check = nil
|
||||
}
|
||||
|
||||
// Obj returns the type name for the declaration defining the named type t. For
|
||||
// instantiated types, this is the type name of the base type.
|
||||
// instantiated types, this is same as the type name of the origin type.
|
||||
func (t *Named) Obj() *TypeName {
|
||||
return t.orig.obj // for non-instances this is the same as t.obj
|
||||
}
|
||||
|
||||
// Origin returns the parameterized type from which the named type t is
|
||||
// Origin returns the generic type from which the named type t is
|
||||
// instantiated. If t is not an instantiated type, the result is t.
|
||||
func (t *Named) Origin() *Named { return t.orig }
|
||||
|
||||
|
@ -91,7 +111,7 @@ func (t *Named) Origin() *Named { return t.orig }
|
|||
// between parameterized instantiated and non-instantiated types.
|
||||
|
||||
// TypeParams returns the type parameters of the named type t, or nil.
|
||||
// The result is non-nil for an (originally) parameterized type even if it is instantiated.
|
||||
// The result is non-nil for an (originally) generic type even if it is instantiated.
|
||||
func (t *Named) TypeParams() *TypeParamList { return t.resolve(nil).tparams }
|
||||
|
||||
// SetTypeParams sets the type parameters of the named type t.
|
||||
|
@ -104,7 +124,11 @@ func (t *Named) SetTypeParams(tparams []*TypeParam) {
|
|||
// TypeArgs returns the type arguments used to instantiate the named type t.
|
||||
func (t *Named) TypeArgs() *TypeList { return t.targs }
|
||||
|
||||
// NumMethods returns the number of explicit methods whose receiver is named type t.
|
||||
// NumMethods returns the number of explicit methods defined for t.
|
||||
//
|
||||
// For an ordinary or instantiated type t, the receiver base type of these
|
||||
// methods will be the named type t. For an uninstantiated generic type t, each
|
||||
// method receiver will be instantiated with its receiver type parameters.
|
||||
func (t *Named) NumMethods() int { return t.resolve(nil).methods.Len() }
|
||||
|
||||
// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
|
||||
|
@ -362,11 +386,11 @@ func expandNamed(ctxt *Context, n *Named, instPos token.Pos) (tparams *TypeParam
|
|||
// that it wasn't substituted. In this case we need to create a new
|
||||
// *Interface before modifying receivers.
|
||||
if iface == n.orig.underlying {
|
||||
iface = &Interface{
|
||||
embeddeds: iface.embeddeds,
|
||||
complete: iface.complete,
|
||||
implicit: iface.implicit, // should be false but be conservative
|
||||
}
|
||||
old := iface
|
||||
iface = check.newInterface()
|
||||
iface.embeddeds = old.embeddeds
|
||||
iface.complete = old.complete
|
||||
iface.implicit = old.implicit // should be false but be conservative
|
||||
underlying = iface
|
||||
}
|
||||
iface.methods = methods
|
||||
|
|
|
@ -104,11 +104,12 @@ func isGeneric(t Type) bool {
|
|||
|
||||
// Comparable reports whether values of type T are comparable.
|
||||
func Comparable(T Type) bool {
|
||||
return comparable(T, nil, nil)
|
||||
return comparable(T, true, nil, nil)
|
||||
}
|
||||
|
||||
// If dynamic is set, non-type parameter interfaces are always comparable.
|
||||
// If reportf != nil, it may be used to report why T is not comparable.
|
||||
func comparable(T Type, seen map[Type]bool, reportf func(string, ...interface{})) bool {
|
||||
func comparable(T Type, dynamic bool, seen map[Type]bool, reportf func(string, ...interface{})) bool {
|
||||
if seen[T] {
|
||||
return true
|
||||
}
|
||||
|
@ -126,7 +127,7 @@ func comparable(T Type, seen map[Type]bool, reportf func(string, ...interface{})
|
|||
return true
|
||||
case *Struct:
|
||||
for _, f := range t.fields {
|
||||
if !comparable(f.typ, seen, nil) {
|
||||
if !comparable(f.typ, dynamic, seen, nil) {
|
||||
if reportf != nil {
|
||||
reportf("struct containing %s cannot be compared", f.typ)
|
||||
}
|
||||
|
@ -135,7 +136,7 @@ func comparable(T Type, seen map[Type]bool, reportf func(string, ...interface{})
|
|||
}
|
||||
return true
|
||||
case *Array:
|
||||
if !comparable(t.elem, seen, nil) {
|
||||
if !comparable(t.elem, dynamic, seen, nil) {
|
||||
if reportf != nil {
|
||||
reportf("%s cannot be compared", t)
|
||||
}
|
||||
|
@ -143,7 +144,7 @@ func comparable(T Type, seen map[Type]bool, reportf func(string, ...interface{})
|
|||
}
|
||||
return true
|
||||
case *Interface:
|
||||
return !isTypeParam(T) || t.typeSet().IsComparable(seen)
|
||||
return dynamic && !isTypeParam(T) || t.typeSet().IsComparable(seen)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -112,7 +112,8 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast
|
|||
// - the receiver specification acts as local declaration for its type parameters, which may be blank
|
||||
_, rname, rparams := check.unpackRecv(recvPar.List[0].Type, true)
|
||||
if len(rparams) > 0 {
|
||||
sig.rparams = bindTParams(check.declareTypeParams(nil, rparams))
|
||||
tparams := check.declareTypeParams(nil, rparams)
|
||||
sig.rparams = bindTParams(tparams)
|
||||
// Blank identifiers don't get declared, so naive type-checking of the
|
||||
// receiver type expression would fail in Checker.collectParams below,
|
||||
// when Checker.ident cannot resolve the _ to a type.
|
||||
|
@ -122,11 +123,10 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast
|
|||
// lookup in the scope.
|
||||
for i, p := range rparams {
|
||||
if p.Name == "_" {
|
||||
tpar := sig.rparams.At(i)
|
||||
if check.recvTParamMap == nil {
|
||||
check.recvTParamMap = make(map[*ast.Ident]*TypeParam)
|
||||
}
|
||||
check.recvTParamMap[p] = tpar
|
||||
check.recvTParamMap[p] = tparams[i]
|
||||
}
|
||||
}
|
||||
// determine receiver type to get its type parameters
|
||||
|
@ -142,22 +142,23 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast
|
|||
}
|
||||
}
|
||||
// provide type parameter bounds
|
||||
// - only do this if we have the right number (otherwise an error is reported elsewhere)
|
||||
if sig.RecvTypeParams().Len() == len(recvTParams) {
|
||||
// We have a list of *TypeNames but we need a list of Types.
|
||||
list := make([]Type, sig.RecvTypeParams().Len())
|
||||
for i, t := range sig.RecvTypeParams().list() {
|
||||
list[i] = t
|
||||
check.mono.recordCanon(t, recvTParams[i])
|
||||
}
|
||||
smap := makeSubstMap(recvTParams, list)
|
||||
for i, tpar := range sig.RecvTypeParams().list() {
|
||||
bound := recvTParams[i].bound
|
||||
// bound is (possibly) parameterized in the context of the
|
||||
// receiver type declaration. Substitute parameters for the
|
||||
// current context.
|
||||
tpar.bound = check.subst(tpar.obj.pos, bound, smap, nil)
|
||||
if len(tparams) == len(recvTParams) {
|
||||
smap := makeRenameMap(recvTParams, tparams)
|
||||
for i, tpar := range tparams {
|
||||
recvTPar := recvTParams[i]
|
||||
check.mono.recordCanon(tpar, recvTPar)
|
||||
// recvTPar.bound is (possibly) parameterized in the context of the
|
||||
// receiver type declaration. Substitute parameters for the current
|
||||
// context.
|
||||
tpar.bound = check.subst(tpar.obj.pos, recvTPar.bound, smap, nil)
|
||||
}
|
||||
} else if len(tparams) < len(recvTParams) {
|
||||
// Reporting an error here is a stop-gap measure to avoid crashes in the
|
||||
// compiler when a type parameter/argument cannot be inferred later. It
|
||||
// may lead to follow-on errors (see issues #51339, #51343).
|
||||
// TODO(gri) find a better solution
|
||||
got := measure(len(tparams), "type parameter")
|
||||
check.errorf(recvPar, _BadRecv, "got %s, but receiver base type declares %d", got, len(recvTParams))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -192,66 +193,77 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast
|
|||
switch len(recvList) {
|
||||
case 0:
|
||||
// error reported by resolver
|
||||
recv = NewParam(0, nil, "", Typ[Invalid]) // ignore recv below
|
||||
recv = NewParam(token.NoPos, nil, "", Typ[Invalid]) // ignore recv below
|
||||
default:
|
||||
// more than one receiver
|
||||
check.error(recvList[len(recvList)-1], _BadRecv, "method must have exactly one receiver")
|
||||
check.error(recvList[len(recvList)-1], _InvalidRecv, "method must have exactly one receiver")
|
||||
fallthrough // continue with first receiver
|
||||
case 1:
|
||||
recv = recvList[0]
|
||||
}
|
||||
sig.recv = recv
|
||||
|
||||
// TODO(gri) We should delay rtyp expansion to when we actually need the
|
||||
// receiver; thus all checks here should be delayed to later.
|
||||
rtyp, _ := deref(recv.typ)
|
||||
// Delay validation of receiver type as it may cause premature expansion
|
||||
// of types the receiver type is dependent on (see issues #51232, #51233).
|
||||
check.later(func() {
|
||||
rtyp, _ := deref(recv.typ)
|
||||
|
||||
// spec: "The receiver type must be of the form T or *T where T is a type name."
|
||||
// (ignore invalid types - error was reported before)
|
||||
if rtyp != Typ[Invalid] {
|
||||
var err string
|
||||
switch T := rtyp.(type) {
|
||||
case *Named:
|
||||
T.resolve(check.bestContext(nil))
|
||||
// The receiver type may be an instantiated type referred to
|
||||
// by an alias (which cannot have receiver parameters for now).
|
||||
if T.TypeArgs() != nil && sig.RecvTypeParams() == nil {
|
||||
check.errorf(atPos(recv.pos), _InvalidRecv, "cannot define methods on instantiated type %s", recv.typ)
|
||||
break
|
||||
}
|
||||
// spec: "The type denoted by T is called the receiver base type; it must not
|
||||
// be a pointer or interface type and it must be declared in the same package
|
||||
// as the method."
|
||||
if T.obj.pkg != check.pkg {
|
||||
err = "type not defined in this package"
|
||||
} else {
|
||||
// The underlying type of a receiver base type can be a type parameter;
|
||||
// e.g. for methods with a generic receiver T[P] with type T[P any] P.
|
||||
underIs(T, func(u Type) bool {
|
||||
switch u := u.(type) {
|
||||
case *Basic:
|
||||
// unsafe.Pointer is treated like a regular pointer
|
||||
if u.kind == UnsafePointer {
|
||||
err = "unsafe.Pointer"
|
||||
// spec: "The receiver type must be of the form T or *T where T is a type name."
|
||||
// (ignore invalid types - error was reported before)
|
||||
if rtyp != Typ[Invalid] {
|
||||
var err string
|
||||
switch T := rtyp.(type) {
|
||||
case *Named:
|
||||
T.resolve(check.bestContext(nil))
|
||||
// The receiver type may be an instantiated type referred to
|
||||
// by an alias (which cannot have receiver parameters for now).
|
||||
if T.TypeArgs() != nil && sig.RecvTypeParams() == nil {
|
||||
check.errorf(recv, _InvalidRecv, "cannot define methods on instantiated type %s", recv.typ)
|
||||
break
|
||||
}
|
||||
// spec: "The type denoted by T is called the receiver base type; it must not
|
||||
// be a pointer or interface type and it must be declared in the same package
|
||||
// as the method."
|
||||
if T.obj.pkg != check.pkg {
|
||||
err = "type not defined in this package"
|
||||
if compilerErrorMessages {
|
||||
check.errorf(recv, _InvalidRecv, "cannot define new methods on non-local type %s", recv.typ)
|
||||
err = ""
|
||||
}
|
||||
} else {
|
||||
// The underlying type of a receiver base type can be a type parameter;
|
||||
// e.g. for methods with a generic receiver T[P] with type T[P any] P.
|
||||
// TODO(gri) Such declarations are currently disallowed.
|
||||
// Revisit the need for underIs.
|
||||
underIs(T, func(u Type) bool {
|
||||
switch u := u.(type) {
|
||||
case *Basic:
|
||||
// unsafe.Pointer is treated like a regular pointer
|
||||
if u.kind == UnsafePointer {
|
||||
err = "unsafe.Pointer"
|
||||
return false
|
||||
}
|
||||
case *Pointer, *Interface:
|
||||
err = "pointer or interface type"
|
||||
return false
|
||||
}
|
||||
case *Pointer, *Interface:
|
||||
err = "pointer or interface type"
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
return true
|
||||
})
|
||||
}
|
||||
case *Basic:
|
||||
err = "basic or unnamed type"
|
||||
if compilerErrorMessages {
|
||||
check.errorf(recv, _InvalidRecv, "cannot define new methods on non-local type %s", recv.typ)
|
||||
err = ""
|
||||
}
|
||||
default:
|
||||
check.errorf(recv, _InvalidRecv, "invalid receiver type %s", recv.typ)
|
||||
}
|
||||
if err != "" {
|
||||
check.errorf(recv, _InvalidRecv, "invalid receiver type %s (%s)", recv.typ, err)
|
||||
}
|
||||
case *Basic:
|
||||
err = "basic or unnamed type"
|
||||
default:
|
||||
check.errorf(recv, _InvalidRecv, "invalid receiver type %s", recv.typ)
|
||||
}
|
||||
if err != "" {
|
||||
check.errorf(recv, _InvalidRecv, "invalid receiver type %s (%s)", recv.typ, err)
|
||||
// ok to continue
|
||||
}
|
||||
}
|
||||
sig.recv = recv
|
||||
}).describef(recv, "validate receiver %s", recv)
|
||||
}
|
||||
|
||||
sig.params = NewTuple(params...)
|
||||
|
|
|
@ -821,8 +821,6 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
|||
|
||||
case *ast.RangeStmt:
|
||||
inner |= breakOk | continueOk
|
||||
check.openScope(s, "for")
|
||||
defer check.closeScope()
|
||||
|
||||
// check expression to iterate over
|
||||
var x operand
|
||||
|
@ -857,6 +855,11 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
|||
}
|
||||
}
|
||||
|
||||
// Open the for-statement block scope now, after the range clause.
|
||||
// Iteration variables declared with := need to go in this scope (was issue #51437).
|
||||
check.openScope(s, "range")
|
||||
defer check.closeScope()
|
||||
|
||||
// check assignment to/declaration of iteration variables
|
||||
// (irregular assignment, cannot easily map to existing assignment checks)
|
||||
|
||||
|
@ -865,9 +868,7 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
|||
rhs := [2]Type{key, val} // key, val may be nil
|
||||
|
||||
if s.Tok == token.DEFINE {
|
||||
// short variable declaration; variable scope starts after the range clause
|
||||
// (the for loop opens a new scope, so variables on the lhs never redeclare
|
||||
// previously declared variables)
|
||||
// short variable declaration
|
||||
var vars []*Var
|
||||
for i, lhs := range lhs {
|
||||
if lhs == nil {
|
||||
|
@ -904,12 +905,8 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
|||
|
||||
// declare variables
|
||||
if len(vars) > 0 {
|
||||
scopePos := s.X.End()
|
||||
scopePos := s.Body.Pos()
|
||||
for _, obj := range vars {
|
||||
// spec: "The scope of a constant or variable identifier declared inside
|
||||
// a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl
|
||||
// for short variable declarations) and ends at the end of the innermost
|
||||
// containing block."
|
||||
check.declare(check.scope, nil /* recordDef already called */, obj, scopePos)
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -160,7 +160,10 @@ func (subst *subster) typ(typ Type) Type {
|
|||
methods, mcopied := subst.funcList(t.methods)
|
||||
embeddeds, ecopied := subst.typeList(t.embeddeds)
|
||||
if mcopied || ecopied {
|
||||
iface := &Interface{embeddeds: embeddeds, implicit: t.implicit, complete: t.complete}
|
||||
iface := subst.check.newInterface()
|
||||
iface.embeddeds = embeddeds
|
||||
iface.implicit = t.implicit
|
||||
iface.complete = t.complete
|
||||
// If we've changed the interface type, we may need to replace its
|
||||
// receiver if the receiver type is the original interface. Receivers of
|
||||
// *Named type are replaced during named type expansion.
|
||||
|
|
|
@ -92,15 +92,6 @@ func (xl termlist) norm() termlist {
|
|||
return rl
|
||||
}
|
||||
|
||||
// If the type set represented by xl is specified by a single (non-𝓤) term,
|
||||
// singleType returns that type. Otherwise it returns nil.
|
||||
func (xl termlist) singleType() Type {
|
||||
if nl := xl.norm(); len(nl) == 1 {
|
||||
return nl[0].typ // if nl.isAll() then typ is nil, which is ok
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// union returns the union xl ∪ yl.
|
||||
func (xl termlist) union(yl termlist) termlist {
|
||||
return append(xl, yl...).norm()
|
||||
|
|
|
@ -106,35 +106,6 @@ func TestTermlistNorm(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestTermlistSingleType(t *testing.T) {
|
||||
// helper to deal with nil types
|
||||
tstring := func(typ Type) string {
|
||||
if typ == nil {
|
||||
return "nil"
|
||||
}
|
||||
return typ.String()
|
||||
}
|
||||
|
||||
for test, want := range map[string]string{
|
||||
"∅": "nil",
|
||||
"𝓤": "nil",
|
||||
"int": "int",
|
||||
"myInt": "myInt",
|
||||
"~int": "int",
|
||||
"~int ∪ string": "nil",
|
||||
"~int ∪ myInt": "int",
|
||||
"∅ ∪ int": "int",
|
||||
"∅ ∪ ~int": "int",
|
||||
"∅ ∪ ~int ∪ string": "nil",
|
||||
} {
|
||||
xl := maketl(test)
|
||||
got := tstring(xl.singleType())
|
||||
if got != want {
|
||||
t.Errorf("(%v).singleType() == %v; want %v", test, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTermlistUnion(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
xl, yl, want string
|
||||
|
|
|
@ -78,7 +78,7 @@ func _() {
|
|||
related1(si, "foo" /* ERROR cannot use "foo" */ )
|
||||
}
|
||||
|
||||
func related2[Elem any, Slice interface{~[]Elem}](e Elem, s Slice) {}
|
||||
func related2[Elem any, Slice interface{[]Elem}](e Elem, s Slice) {}
|
||||
|
||||
func _() {
|
||||
// related2 can be called with explicit instantiation.
|
||||
|
@ -109,16 +109,8 @@ func _() {
|
|||
related3[int, []int]()
|
||||
related3[byte, List[byte]]()
|
||||
|
||||
// Alternatively, the 2nd type argument can be inferred
|
||||
// from the first one through constraint type inference.
|
||||
related3[int]()
|
||||
|
||||
// The inferred type is the core type of the Slice
|
||||
// type parameter.
|
||||
var _ []int = related3[int]()
|
||||
|
||||
// It is not the defined parameterized type List.
|
||||
type anotherList []float32
|
||||
var _ anotherList = related3[float32]() // valid
|
||||
var _ anotherList = related3 /* ERROR cannot use .* \(value of type List\[float32\]\) as anotherList */ [float32, List[float32]]()
|
||||
// The 2nd type argument cannot be inferred from the first
|
||||
// one because there's two possible choices: []Elem and
|
||||
// List[Elem].
|
||||
related3 /* ERROR cannot infer Slice */ [int]()
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ func (t T1[[ /* ERROR must be an identifier */ ]int]) m2() {}
|
|||
// 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
|
||||
// This is no different from locally re-declaring 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.
|
||||
|
|
|
@ -47,7 +47,7 @@ type _ struct{
|
|||
}
|
||||
|
||||
type _ struct{
|
||||
I3 // ERROR interface is .* comparable
|
||||
I3 // ERROR interface contains type constraints
|
||||
}
|
||||
|
||||
// General composite types.
|
||||
|
@ -59,19 +59,19 @@ type (
|
|||
_ []I1 // ERROR interface is .* comparable
|
||||
_ []I2 // ERROR interface contains type constraints
|
||||
|
||||
_ *I3 // ERROR interface is .* comparable
|
||||
_ *I3 // ERROR interface contains type constraints
|
||||
_ map[I1 /* ERROR interface is .* comparable */ ]I2 // ERROR interface contains type constraints
|
||||
_ chan I3 // ERROR interface is .* comparable
|
||||
_ chan I3 // ERROR interface contains type constraints
|
||||
_ func(I1 /* ERROR interface is .* comparable */ )
|
||||
_ func() I2 // ERROR interface contains type constraints
|
||||
)
|
||||
|
||||
// Other cases.
|
||||
|
||||
var _ = [...]I3 /* ERROR interface is .* comparable */ {}
|
||||
var _ = [...]I3 /* ERROR interface contains type constraints */ {}
|
||||
|
||||
func _(x interface{}) {
|
||||
_ = x.(I3 /* ERROR interface is .* comparable */ )
|
||||
_ = x.(I3 /* ERROR interface contains type constraints */ )
|
||||
}
|
||||
|
||||
type T1[_ any] struct{}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
package p
|
||||
|
||||
func f[F interface{~*Q}, G interface{~*R}, Q, R any](q Q, r R) {}
|
||||
func f[F interface{*Q}, G interface{*R}, Q, R any](q Q, r R) {}
|
||||
|
||||
func _() {
|
||||
f[*float64, *int](1, 2)
|
||||
|
|
164
libgo/go/go/types/testdata/fixedbugs/issue51229.go2
vendored
Normal file
164
libgo/go/go/types/testdata/fixedbugs/issue51229.go2
vendored
Normal file
|
@ -0,0 +1,164 @@
|
|||
// Copyright 2022 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 p
|
||||
|
||||
// Constraint type inference should be independent of the
|
||||
// ordering of the type parameter declarations. Try all
|
||||
// permutations in the test case below.
|
||||
// Permutations produced by https://go.dev/play/p/PHcZNGJTEBZ.
|
||||
|
||||
func f00[S1 ~[]E1, S2 ~[]E2, E1 ~byte, E2 ~byte](S1, S2) {}
|
||||
func f01[S2 ~[]E2, S1 ~[]E1, E1 ~byte, E2 ~byte](S1, S2) {}
|
||||
func f02[E1 ~byte, S1 ~[]E1, S2 ~[]E2, E2 ~byte](S1, S2) {}
|
||||
func f03[S1 ~[]E1, E1 ~byte, S2 ~[]E2, E2 ~byte](S1, S2) {}
|
||||
func f04[S2 ~[]E2, E1 ~byte, S1 ~[]E1, E2 ~byte](S1, S2) {}
|
||||
func f05[E1 ~byte, S2 ~[]E2, S1 ~[]E1, E2 ~byte](S1, S2) {}
|
||||
func f06[E2 ~byte, S2 ~[]E2, S1 ~[]E1, E1 ~byte](S1, S2) {}
|
||||
func f07[S2 ~[]E2, E2 ~byte, S1 ~[]E1, E1 ~byte](S1, S2) {}
|
||||
func f08[S1 ~[]E1, E2 ~byte, S2 ~[]E2, E1 ~byte](S1, S2) {}
|
||||
func f09[E2 ~byte, S1 ~[]E1, S2 ~[]E2, E1 ~byte](S1, S2) {}
|
||||
func f10[S2 ~[]E2, S1 ~[]E1, E2 ~byte, E1 ~byte](S1, S2) {}
|
||||
func f11[S1 ~[]E1, S2 ~[]E2, E2 ~byte, E1 ~byte](S1, S2) {}
|
||||
func f12[S1 ~[]E1, E1 ~byte, E2 ~byte, S2 ~[]E2](S1, S2) {}
|
||||
func f13[E1 ~byte, S1 ~[]E1, E2 ~byte, S2 ~[]E2](S1, S2) {}
|
||||
func f14[E2 ~byte, S1 ~[]E1, E1 ~byte, S2 ~[]E2](S1, S2) {}
|
||||
func f15[S1 ~[]E1, E2 ~byte, E1 ~byte, S2 ~[]E2](S1, S2) {}
|
||||
func f16[E1 ~byte, E2 ~byte, S1 ~[]E1, S2 ~[]E2](S1, S2) {}
|
||||
func f17[E2 ~byte, E1 ~byte, S1 ~[]E1, S2 ~[]E2](S1, S2) {}
|
||||
func f18[E2 ~byte, E1 ~byte, S2 ~[]E2, S1 ~[]E1](S1, S2) {}
|
||||
func f19[E1 ~byte, E2 ~byte, S2 ~[]E2, S1 ~[]E1](S1, S2) {}
|
||||
func f20[S2 ~[]E2, E2 ~byte, E1 ~byte, S1 ~[]E1](S1, S2) {}
|
||||
func f21[E2 ~byte, S2 ~[]E2, E1 ~byte, S1 ~[]E1](S1, S2) {}
|
||||
func f22[E1 ~byte, S2 ~[]E2, E2 ~byte, S1 ~[]E1](S1, S2) {}
|
||||
func f23[S2 ~[]E2, E1 ~byte, E2 ~byte, S1 ~[]E1](S1, S2) {}
|
||||
|
||||
type myByte byte
|
||||
|
||||
func _(a []byte, b []myByte) {
|
||||
f00(a, b)
|
||||
f01(a, b)
|
||||
f02(a, b)
|
||||
f03(a, b)
|
||||
f04(a, b)
|
||||
f05(a, b)
|
||||
f06(a, b)
|
||||
f07(a, b)
|
||||
f08(a, b)
|
||||
f09(a, b)
|
||||
f10(a, b)
|
||||
f11(a, b)
|
||||
f12(a, b)
|
||||
f13(a, b)
|
||||
f14(a, b)
|
||||
f15(a, b)
|
||||
f16(a, b)
|
||||
f17(a, b)
|
||||
f18(a, b)
|
||||
f19(a, b)
|
||||
f20(a, b)
|
||||
f21(a, b)
|
||||
f22(a, b)
|
||||
f23(a, b)
|
||||
}
|
||||
|
||||
// Constraint type inference may have to iterate.
|
||||
// Again, the order of the type parameters shouldn't matter.
|
||||
|
||||
func g0[S ~[]E, M ~map[string]S, E any](m M) {}
|
||||
func g1[M ~map[string]S, S ~[]E, E any](m M) {}
|
||||
func g2[E any, S ~[]E, M ~map[string]S](m M) {}
|
||||
func g3[S ~[]E, E any, M ~map[string]S](m M) {}
|
||||
func g4[M ~map[string]S, E any, S ~[]E](m M) {}
|
||||
func g5[E any, M ~map[string]S, S ~[]E](m M) {}
|
||||
|
||||
func _(m map[string][]byte) {
|
||||
g0(m)
|
||||
g1(m)
|
||||
g2(m)
|
||||
g3(m)
|
||||
g4(m)
|
||||
g5(m)
|
||||
}
|
||||
|
||||
// Worst-case scenario.
|
||||
// There are 10 unknown type parameters. In each iteration of
|
||||
// constraint type inference we infer one more, from right to left.
|
||||
// Each iteration looks repeatedly at all 11 type parameters,
|
||||
// requiring a total of 10*11 = 110 iterations with the current
|
||||
// implementation. Pathological case.
|
||||
|
||||
func h[K any, J ~*K, I ~*J, H ~*I, G ~*H, F ~*G, E ~*F, D ~*E, C ~*D, B ~*C, A ~*B](x A) {}
|
||||
|
||||
func _(x **********int) {
|
||||
h(x)
|
||||
}
|
||||
|
||||
// Examples with channel constraints and tilde.
|
||||
|
||||
func ch1[P chan<- int]() (_ P) { return } // core(P) == chan<- int (single type, no tilde)
|
||||
func ch2[P ~chan int]() { return } // core(P) == ~chan<- int (tilde)
|
||||
func ch3[P chan E, E any](E) { return } // core(P) == chan<- E (single type, no tilde)
|
||||
func ch4[P chan E | ~chan<- E, E any](E) { return } // core(P) == ~chan<- E (tilde)
|
||||
func ch5[P chan int | chan<- int]() { return } // core(P) == chan<- int (not a single type)
|
||||
|
||||
func _() {
|
||||
// P can be inferred as there's a single specific type and no tilde.
|
||||
var _ chan int = ch1 /* ERROR cannot use ch1.*value of type chan<- int */ ()
|
||||
var _ chan<- int = ch1()
|
||||
|
||||
// P cannot be inferred as there's a tilde.
|
||||
ch2 /* ERROR cannot infer P */ ()
|
||||
type myChan chan int
|
||||
ch2[myChan]()
|
||||
|
||||
// P can be inferred as there's a single specific type and no tilde.
|
||||
var e int
|
||||
ch3(e)
|
||||
|
||||
// P cannot be inferred as there's more than one specific type and a tilde.
|
||||
ch4 /* ERROR cannot infer P */ (e)
|
||||
_ = ch4[chan int]
|
||||
|
||||
// P cannot be inferred as there's more than one specific type.
|
||||
ch5 /* ERROR cannot infer P */ ()
|
||||
ch5[chan<- int]()
|
||||
}
|
||||
|
||||
// test case from issue
|
||||
|
||||
func equal[M1 ~map[K1]V1, M2 ~map[K2]V2, K1, K2 ~uint32, V1, V2 ~string](m1 M1, m2 M2) bool {
|
||||
if len(m1) != len(m2) {
|
||||
return false
|
||||
}
|
||||
for k, v1 := range m1 {
|
||||
if v2, ok := m2[K2(k)]; !ok || V2(v1) != v2 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func equalFixed[K1, K2 ~uint32, V1, V2 ~string](m1 map[K1]V1, m2 map[K2]V2) bool {
|
||||
if len(m1) != len(m2) {
|
||||
return false
|
||||
}
|
||||
for k, v1 := range m1 {
|
||||
if v2, ok := m2[K2(k)]; !ok || v1 != V1(v2) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type (
|
||||
someNumericID uint32
|
||||
someStringID string
|
||||
)
|
||||
|
||||
func _() {
|
||||
foo := map[uint32]string{10: "bar"}
|
||||
bar := map[someNumericID]someStringID{10: "bar"}
|
||||
equal(foo, bar)
|
||||
}
|
30
libgo/go/go/types/testdata/fixedbugs/issue51232.go2
vendored
Normal file
30
libgo/go/go/types/testdata/fixedbugs/issue51232.go2
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
// Copyright 2022 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 p
|
||||
|
||||
type RC[RG any] interface {
|
||||
~[]RG
|
||||
}
|
||||
|
||||
type Fn[RCT RC[RG], RG any] func(RCT)
|
||||
|
||||
type F[RCT RC[RG], RG any] interface {
|
||||
Fn() Fn /* ERROR got 1 arguments */ [RCT]
|
||||
}
|
||||
|
||||
type concreteF[RCT RC[RG], RG any] struct {
|
||||
makeFn func() Fn /* ERROR got 1 arguments */ [RCT]
|
||||
}
|
||||
|
||||
func (c *concreteF[RCT, RG]) Fn() Fn /* ERROR got 1 arguments */ [RCT] {
|
||||
return c.makeFn()
|
||||
}
|
||||
|
||||
func NewConcrete[RCT RC[RG], RG any](Rc RCT) F /* ERROR got 1 arguments */ [RCT] {
|
||||
// TODO(rfindley): eliminate the duplicate error below.
|
||||
return & /* ERROR cannot use .* as F\[RCT\] */ concreteF /* ERROR got 1 arguments */ [RCT]{
|
||||
makeFn: nil,
|
||||
}
|
||||
}
|
27
libgo/go/go/types/testdata/fixedbugs/issue51233.go2
vendored
Normal file
27
libgo/go/go/types/testdata/fixedbugs/issue51233.go2
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2022 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 p
|
||||
|
||||
// As of issue #51527, type-type inference has been disabled.
|
||||
|
||||
type RC[RG any] interface {
|
||||
~[]RG
|
||||
}
|
||||
|
||||
type Fn[RCT RC[RG], RG any] func(RCT)
|
||||
|
||||
type FFn[RCT RC[RG], RG any] func() Fn /* ERROR got 1 arguments */ [RCT]
|
||||
|
||||
type F[RCT RC[RG], RG any] interface {
|
||||
Fn() Fn /* ERROR got 1 arguments */ [RCT]
|
||||
}
|
||||
|
||||
type concreteF[RCT RC[RG], RG any] struct {
|
||||
makeFn FFn /* ERROR got 1 arguments */ [RCT]
|
||||
}
|
||||
|
||||
func (c *concreteF[RCT, RG]) Fn() Fn /* ERROR got 1 arguments */ [RCT] {
|
||||
return c.makeFn()
|
||||
}
|
46
libgo/go/go/types/testdata/fixedbugs/issue51257.go2
vendored
Normal file
46
libgo/go/go/types/testdata/fixedbugs/issue51257.go2
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2022 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 p
|
||||
|
||||
func f[_ comparable]() {}
|
||||
|
||||
type S1 struct{ x int }
|
||||
type S2 struct{ x any }
|
||||
type S3 struct{ x [10]interface{ m() } }
|
||||
|
||||
func _[P1 comparable, P2 S2]() {
|
||||
_ = f[S1]
|
||||
_ = f[S2 /* ERROR S2 does not implement comparable */ ]
|
||||
_ = f[S3 /* ERROR S3 does not implement comparable */ ]
|
||||
|
||||
type L1 struct { x P1 }
|
||||
type L2 struct { x P2 }
|
||||
_ = f[L1]
|
||||
_ = f[L2 /* ERROR L2 does not implement comparable */ ]
|
||||
}
|
||||
|
||||
|
||||
// example from issue
|
||||
|
||||
type Set[T comparable] map[T]struct{}
|
||||
|
||||
func NewSetFromSlice[T comparable](items []T) *Set[T] {
|
||||
s := Set[T]{}
|
||||
|
||||
for _, item := range items {
|
||||
s[item] = struct{}{}
|
||||
}
|
||||
|
||||
return &s
|
||||
}
|
||||
|
||||
type T struct{ x any }
|
||||
|
||||
func main() {
|
||||
NewSetFromSlice /* ERROR T does not implement comparable */ ([]T{
|
||||
{"foo"},
|
||||
{5},
|
||||
})
|
||||
}
|
16
libgo/go/go/types/testdata/fixedbugs/issue51335.go2
vendored
Normal file
16
libgo/go/go/types/testdata/fixedbugs/issue51335.go2
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2022 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 p
|
||||
|
||||
type S1 struct{}
|
||||
type S2 struct{}
|
||||
|
||||
func _[P *S1|*S2]() {
|
||||
_= []P{{ /* ERROR invalid composite literal element type P: no core type */ }}
|
||||
}
|
||||
|
||||
func _[P *S1|S1]() {
|
||||
_= []P{{ /* ERROR invalid composite literal element type P: no core type */ }}
|
||||
}
|
18
libgo/go/go/types/testdata/fixedbugs/issue51339.go2
vendored
Normal file
18
libgo/go/go/types/testdata/fixedbugs/issue51339.go2
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
// Copyright 2022 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 is tested when running "go test -run Manual"
|
||||
// without source arguments. Use for one-off debugging.
|
||||
|
||||
package p
|
||||
|
||||
type T[P any, B *P] struct{}
|
||||
|
||||
func (T /* ERROR cannot use generic type */ ) m0() {}
|
||||
|
||||
// TODO(rfindley): eliminate the duplicate errors here.
|
||||
func (/* ERROR got 1 type parameter, but receiver base type declares 2 */ T /* ERROR got 1 arguments but 2 type parameters */ [_]) m1() {}
|
||||
func (T[_, _]) m2() {}
|
||||
// TODO(gri) this error is unfortunate (issue #51343)
|
||||
func (T /* ERROR got 3 arguments but 2 type parameters */ [_, _, _]) m3() {}
|
13
libgo/go/go/types/testdata/fixedbugs/issue51360.go
vendored
Normal file
13
libgo/go/go/types/testdata/fixedbugs/issue51360.go
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Copyright 2022 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 p
|
||||
|
||||
func _() {
|
||||
len.Println /* ERROR cannot select on len */
|
||||
len.Println /* ERROR cannot select on len */ ()
|
||||
_ = len.Println /* ERROR cannot select on len */
|
||||
_ = len /* ERROR cannot index len */ [0]
|
||||
_ = *len /* ERROR cannot indirect len */
|
||||
}
|
24
libgo/go/go/types/testdata/fixedbugs/issue51376.go2
vendored
Normal file
24
libgo/go/go/types/testdata/fixedbugs/issue51376.go2
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
// Copyright 2022 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 p
|
||||
|
||||
type Map map[string]int
|
||||
|
||||
func f[M ~map[K]V, K comparable, V any](M) {}
|
||||
func g[M map[K]V, K comparable, V any](M) {}
|
||||
|
||||
func _[M1 ~map[K]V, M2 map[K]V, K comparable, V any]() {
|
||||
var m1 M1
|
||||
f(m1)
|
||||
g /* ERROR M1 does not implement map\[K\]V */ (m1) // M1 has tilde
|
||||
|
||||
var m2 M2
|
||||
f(m2)
|
||||
g(m2) // M1 does not have tilde
|
||||
|
||||
var m3 Map
|
||||
f(m3)
|
||||
g /* ERROR Map does not implement map\[string\]int */ (m3) // M in g does not have tilde
|
||||
}
|
17
libgo/go/go/types/testdata/fixedbugs/issue51386.go2
vendored
Normal file
17
libgo/go/go/types/testdata/fixedbugs/issue51386.go2
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Copyright 2022 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 p
|
||||
|
||||
type myString string
|
||||
|
||||
func _[P ~string | ~[]byte | ~[]rune]() {
|
||||
_ = P("")
|
||||
const s myString = ""
|
||||
_ = P(s)
|
||||
}
|
||||
|
||||
func _[P myString]() {
|
||||
_ = P("")
|
||||
}
|
17
libgo/go/go/types/testdata/fixedbugs/issue51437.go
vendored
Normal file
17
libgo/go/go/types/testdata/fixedbugs/issue51437.go
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Copyright 2022 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 p
|
||||
|
||||
type T struct{}
|
||||
|
||||
func (T) m() []int { return nil }
|
||||
|
||||
func f(x T) {
|
||||
for _, x := range func() []int {
|
||||
return x.m() // x declared in parameter list of f
|
||||
}() {
|
||||
_ = x // x declared by range clause
|
||||
}
|
||||
}
|
54
libgo/go/go/types/testdata/fixedbugs/issue51472.go2
vendored
Normal file
54
libgo/go/go/types/testdata/fixedbugs/issue51472.go2
vendored
Normal file
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2022 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 p
|
||||
|
||||
func _[T comparable](x T) {
|
||||
_ = x == x
|
||||
}
|
||||
|
||||
func _[T interface{interface{comparable}}](x T) {
|
||||
_ = x == x
|
||||
}
|
||||
|
||||
func _[T interface{comparable; interface{comparable}}](x T) {
|
||||
_ = x == x
|
||||
}
|
||||
|
||||
func _[T interface{comparable; ~int}](x T) {
|
||||
_ = x == x
|
||||
}
|
||||
|
||||
func _[T interface{comparable; ~[]byte}](x T) {
|
||||
_ = x /* ERROR cannot compare */ == x
|
||||
}
|
||||
|
||||
// TODO(gri) The error message here should be better. See issue #51525.
|
||||
func _[T interface{comparable; ~int; ~string}](x T) {
|
||||
_ = x /* ERROR cannot compare */ == x
|
||||
}
|
||||
|
||||
// TODO(gri) The error message here should be better. See issue #51525.
|
||||
func _[T interface{~int; ~string}](x T) {
|
||||
_ = x /* ERROR cannot compare */ == x
|
||||
}
|
||||
|
||||
func _[T interface{comparable; interface{~int}; interface{int|float64}}](x T) {
|
||||
_ = x == x
|
||||
}
|
||||
|
||||
func _[T interface{interface{comparable; ~int}; interface{~float64; comparable; m()}}](x T) {
|
||||
_ = x /* ERROR cannot compare */ == x
|
||||
}
|
||||
|
||||
// test case from issue
|
||||
|
||||
func f[T interface{comparable; []byte|string}](x T) {
|
||||
_ = x == x
|
||||
}
|
||||
|
||||
func _(s []byte) {
|
||||
f /* ERROR \[\]byte does not implement interface{comparable; \[\]byte\|string} */ (s)
|
||||
_ = f[[ /* ERROR does not implement */ ]byte]
|
||||
}
|
7
libgo/go/go/types/testdata/fixedbugs/issue51509.go
vendored
Normal file
7
libgo/go/go/types/testdata/fixedbugs/issue51509.go
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
// Copyright 2022 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 p
|
||||
|
||||
type T /* ERROR illegal cycle */ T.x
|
17
libgo/go/go/types/testdata/fixedbugs/issue51578.go2
vendored
Normal file
17
libgo/go/go/types/testdata/fixedbugs/issue51578.go2
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Copyright 2022 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 p
|
||||
|
||||
var _ = (*interface /* ERROR interface contains type constraints */ {int})(nil)
|
||||
|
||||
// abbreviated test case from issue
|
||||
|
||||
type TypeSet interface{ int | string }
|
||||
|
||||
func _() {
|
||||
f((*TypeSet /* ERROR interface contains type constraints */)(nil))
|
||||
}
|
||||
|
||||
func f(any) {}
|
13
libgo/go/go/types/testdata/fixedbugs/issue51593.go2
vendored
Normal file
13
libgo/go/go/types/testdata/fixedbugs/issue51593.go2
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Copyright 2022 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 p
|
||||
|
||||
func f[P interface{ m(R) }, R any]() {}
|
||||
|
||||
type T = interface { m(int) }
|
||||
|
||||
func _() {
|
||||
_ = f /* ERROR cannot infer R */ [T] // don't crash in type inference
|
||||
}
|
|
@ -7,9 +7,7 @@ package types
|
|||
// A Type represents a type of Go.
|
||||
// All types implement the Type interface.
|
||||
type Type interface {
|
||||
// Underlying returns the underlying type of a type
|
||||
// w/o following forwarding chains. Only used by
|
||||
// client packages.
|
||||
// Underlying returns the underlying type of a type.
|
||||
Underlying() Type
|
||||
|
||||
// String returns a string representation of a type.
|
||||
|
|
|
@ -30,11 +30,13 @@ type TypeParam struct {
|
|||
// or Signature type by calling SetTypeParams. Setting a type parameter on more
|
||||
// than one type will result in a panic.
|
||||
//
|
||||
// The constraint argument can be nil, and set later via SetConstraint.
|
||||
// The constraint argument can be nil, and set later via SetConstraint. If the
|
||||
// constraint is non-nil, it must be fully defined.
|
||||
func NewTypeParam(obj *TypeName, constraint Type) *TypeParam {
|
||||
return (*Checker)(nil).newTypeParam(obj, constraint)
|
||||
}
|
||||
|
||||
// check may be nil
|
||||
func (check *Checker) newTypeParam(obj *TypeName, constraint Type) *TypeParam {
|
||||
// Always increment lastID, even if it is not used.
|
||||
id := nextID()
|
||||
|
@ -49,9 +51,7 @@ func (check *Checker) newTypeParam(obj *TypeName, constraint Type) *TypeParam {
|
|||
// iface may mutate typ.bound, so we must ensure that iface() is called
|
||||
// at least once before the resulting TypeParam escapes.
|
||||
if check != nil {
|
||||
check.later(func() {
|
||||
typ.iface()
|
||||
})
|
||||
check.needsCleanup(typ)
|
||||
} else if constraint != nil {
|
||||
typ.iface()
|
||||
}
|
||||
|
@ -74,8 +74,10 @@ func (t *TypeParam) Constraint() Type {
|
|||
|
||||
// SetConstraint sets the type constraint for t.
|
||||
//
|
||||
// SetConstraint should not be called concurrently, but once SetConstraint
|
||||
// returns the receiver t is safe for concurrent use.
|
||||
// It must be called by users of NewTypeParam after the bound's underlying is
|
||||
// fully defined, and before using the type parameter in any way other than to
|
||||
// form other types. Once SetConstraint returns the receiver, t is safe for
|
||||
// concurrent use.
|
||||
func (t *TypeParam) SetConstraint(bound Type) {
|
||||
if bound == nil {
|
||||
panic("nil constraint")
|
||||
|
@ -95,9 +97,12 @@ func (t *TypeParam) String() string { return TypeString(t, nil) }
|
|||
// ----------------------------------------------------------------------------
|
||||
// Implementation
|
||||
|
||||
func (t *TypeParam) cleanup() {
|
||||
t.iface()
|
||||
t.check = nil
|
||||
}
|
||||
|
||||
// iface returns the constraint interface of t.
|
||||
// TODO(gri) If we make tparamIsIface the default, this should be renamed to under
|
||||
// (similar to Named.under).
|
||||
func (t *TypeParam) iface() *Interface {
|
||||
bound := t.bound
|
||||
|
||||
|
@ -138,16 +143,6 @@ func (t *TypeParam) iface() *Interface {
|
|||
return ityp
|
||||
}
|
||||
|
||||
// singleType returns the single type of the type parameter constraint; or nil.
|
||||
func (t *TypeParam) singleType() Type {
|
||||
return t.iface().typeSet().singleType()
|
||||
}
|
||||
|
||||
// hasTerms reports whether the type parameter constraint has specific type terms.
|
||||
func (t *TypeParam) hasTerms() bool {
|
||||
return t.iface().typeSet().hasTerms()
|
||||
}
|
||||
|
||||
// is calls f with the specific type terms of t's constraint and reports whether
|
||||
// all calls to f returned true. If there are no specific terms, is
|
||||
// returns the result of f(nil).
|
||||
|
|
|
@ -15,18 +15,25 @@ import (
|
|||
// API
|
||||
|
||||
// A _TypeSet represents the type set of an interface.
|
||||
// Because of existing language restrictions, methods can be "factored out"
|
||||
// from the terms. The actual type set is the intersection of the type set
|
||||
// implied by the methods and the type set described by the terms and the
|
||||
// comparable bit. To test whether a type is included in a type set
|
||||
// ("implements" relation), the type must implement all methods _and_ be
|
||||
// an element of the type set described by the terms and the comparable bit.
|
||||
// If the term list describes the set of all types and comparable is true,
|
||||
// only comparable types are meant; in all other cases comparable is false.
|
||||
type _TypeSet struct {
|
||||
comparable bool // if set, the interface is or embeds comparable
|
||||
// TODO(gri) consider using a set for the methods for faster lookup
|
||||
methods []*Func // all methods of the interface; sorted by unique ID
|
||||
terms termlist // type terms of the type set
|
||||
methods []*Func // all methods of the interface; sorted by unique ID
|
||||
terms termlist // type terms of the type set
|
||||
comparable bool // invariant: !comparable || terms.isAll()
|
||||
}
|
||||
|
||||
// IsEmpty reports whether type set s is the empty set.
|
||||
func (s *_TypeSet) IsEmpty() bool { return s.terms.isEmpty() }
|
||||
|
||||
// IsAll reports whether type set s is the set of all types (corresponding to the empty interface).
|
||||
func (s *_TypeSet) IsAll() bool { return !s.comparable && len(s.methods) == 0 && s.terms.isAll() }
|
||||
func (s *_TypeSet) IsAll() bool { return s.IsMethodSet() && len(s.methods) == 0 }
|
||||
|
||||
// IsMethodSet reports whether the interface t is fully described by its method set.
|
||||
func (s *_TypeSet) IsMethodSet() bool { return !s.comparable && s.terms.isAll() }
|
||||
|
@ -37,17 +44,10 @@ func (s *_TypeSet) IsComparable(seen map[Type]bool) bool {
|
|||
return s.comparable
|
||||
}
|
||||
return s.is(func(t *term) bool {
|
||||
return t != nil && comparable(t.typ, seen, nil)
|
||||
return t != nil && comparable(t.typ, false, seen, nil)
|
||||
})
|
||||
}
|
||||
|
||||
// TODO(gri) IsTypeSet is not a great name for this predicate. Find a better one.
|
||||
|
||||
// IsTypeSet reports whether the type set s is represented by a finite set of underlying types.
|
||||
func (s *_TypeSet) IsTypeSet() bool {
|
||||
return !s.comparable && len(s.methods) == 0
|
||||
}
|
||||
|
||||
// NumMethods returns the number of methods available.
|
||||
func (s *_TypeSet) NumMethods() int { return len(s.methods) }
|
||||
|
||||
|
@ -101,9 +101,6 @@ func (s *_TypeSet) String() string {
|
|||
// hasTerms reports whether the type set has specific type terms.
|
||||
func (s *_TypeSet) hasTerms() bool { return !s.terms.isEmpty() && !s.terms.isAll() }
|
||||
|
||||
// singleType returns the single type in s if there is exactly one; otherwise the result is nil.
|
||||
func (s *_TypeSet) singleType() Type { return s.terms.singleType() }
|
||||
|
||||
// subsetOf reports whether s1 ⊆ s2.
|
||||
func (s1 *_TypeSet) subsetOf(s2 *_TypeSet) bool { return s1.terms.subsetOf(s2.terms) }
|
||||
|
||||
|
@ -220,12 +217,12 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
|
|||
|
||||
var todo []*Func
|
||||
var seen objset
|
||||
var methods []*Func
|
||||
var allMethods []*Func
|
||||
mpos := make(map[*Func]token.Pos) // method specification or method embedding position, for good error messages
|
||||
addMethod := func(pos token.Pos, m *Func, explicit bool) {
|
||||
switch other := seen.insert(m); {
|
||||
case other == nil:
|
||||
methods = append(methods, m)
|
||||
allMethods = append(allMethods, m)
|
||||
mpos[m] = pos
|
||||
case explicit:
|
||||
if check == nil {
|
||||
|
@ -260,7 +257,8 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
|
|||
}
|
||||
|
||||
// collect embedded elements
|
||||
var allTerms = allTermlist
|
||||
allTerms := allTermlist
|
||||
allComparable := false
|
||||
for i, typ := range ityp.embeddeds {
|
||||
// The embedding position is nil for imported interfaces
|
||||
// and also for interface copies after substitution (but
|
||||
|
@ -269,6 +267,7 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
|
|||
if ityp.embedPos != nil {
|
||||
pos = (*ityp.embedPos)[i]
|
||||
}
|
||||
var comparable bool
|
||||
var terms termlist
|
||||
switch u := under(typ).(type) {
|
||||
case *Interface:
|
||||
|
@ -280,9 +279,7 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
|
|||
check.errorf(atPos(pos), _UnsupportedFeature, "embedding constraint interface %s requires go1.18 or later", typ)
|
||||
continue
|
||||
}
|
||||
if tset.comparable {
|
||||
ityp.tset.comparable = true
|
||||
}
|
||||
comparable = tset.comparable
|
||||
for _, m := range tset.methods {
|
||||
addMethod(pos, m, false) // use embedding position pos rather than m.pos
|
||||
}
|
||||
|
@ -296,6 +293,8 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
|
|||
if tset == &invalidTypeSet {
|
||||
continue // ignore invalid unions
|
||||
}
|
||||
assert(!tset.comparable)
|
||||
assert(len(tset.methods) == 0)
|
||||
terms = tset.terms
|
||||
default:
|
||||
if u == Typ[Invalid] {
|
||||
|
@ -307,11 +306,11 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
|
|||
}
|
||||
terms = termlist{{false, typ}}
|
||||
}
|
||||
// The type set of an interface is the intersection
|
||||
// of the type sets of all its elements.
|
||||
// Intersection cannot produce longer termlists and
|
||||
// thus cannot overflow.
|
||||
allTerms = allTerms.intersect(terms)
|
||||
|
||||
// The type set of an interface is the intersection of the type sets of all its elements.
|
||||
// Due to language restrictions, only embedded interfaces can add methods, they are handled
|
||||
// separately. Here we only need to intersect the term lists and comparable bits.
|
||||
allTerms, allComparable = intersectTermLists(allTerms, allComparable, terms, comparable)
|
||||
}
|
||||
ityp.embedPos = nil // not needed anymore (errors have been reported)
|
||||
|
||||
|
@ -324,15 +323,46 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T
|
|||
}
|
||||
}
|
||||
|
||||
if methods != nil {
|
||||
sort.Sort(byUniqueMethodName(methods))
|
||||
ityp.tset.methods = methods
|
||||
ityp.tset.comparable = allComparable
|
||||
if len(allMethods) != 0 {
|
||||
sortMethods(allMethods)
|
||||
ityp.tset.methods = allMethods
|
||||
}
|
||||
ityp.tset.terms = allTerms
|
||||
|
||||
return ityp.tset
|
||||
}
|
||||
|
||||
// TODO(gri) The intersectTermLists function belongs to the termlist implementation.
|
||||
// The comparable type set may also be best represented as a term (using
|
||||
// a special type).
|
||||
|
||||
// intersectTermLists computes the intersection of two term lists and respective comparable bits.
|
||||
// xcomp, ycomp are valid only if xterms.isAll() and yterms.isAll() respectively.
|
||||
func intersectTermLists(xterms termlist, xcomp bool, yterms termlist, ycomp bool) (termlist, bool) {
|
||||
terms := xterms.intersect(yterms)
|
||||
// If one of xterms or yterms is marked as comparable,
|
||||
// the result must only include comparable types.
|
||||
comp := xcomp || ycomp
|
||||
if comp && !terms.isAll() {
|
||||
// only keep comparable terms
|
||||
i := 0
|
||||
for _, t := range terms {
|
||||
assert(t.typ != nil)
|
||||
if Comparable(t.typ) {
|
||||
terms[i] = t
|
||||
i++
|
||||
}
|
||||
}
|
||||
terms = terms[:i]
|
||||
if !terms.isAll() {
|
||||
comp = false
|
||||
}
|
||||
}
|
||||
assert(!comp || terms.isAll()) // comparable invariant
|
||||
return terms, comp
|
||||
}
|
||||
|
||||
func sortMethods(list []*Func) {
|
||||
sort.Sort(byUniqueMethodName(list))
|
||||
}
|
||||
|
|
|
@ -26,9 +26,9 @@ func TestTypeSetString(t *testing.T) {
|
|||
"{int; string}": "∅",
|
||||
|
||||
"{comparable}": "{comparable}",
|
||||
"{comparable; int}": "{comparable; int}",
|
||||
"{~int; comparable}": "{comparable; ~int}",
|
||||
"{int|string; comparable}": "{comparable; int ∪ string}",
|
||||
"{comparable; int}": "{int}",
|
||||
"{~int; comparable}": "{~int}",
|
||||
"{int|string; comparable}": "{int ∪ string}",
|
||||
"{comparable; int; string}": "∅",
|
||||
|
||||
"{m()}": "{func (p.T).m()}",
|
||||
|
@ -38,8 +38,8 @@ func TestTypeSetString(t *testing.T) {
|
|||
"{m1(); comparable; m2() int }": "{comparable; func (p.T).m1(); func (p.T).m2() int}",
|
||||
"{comparable; error}": "{comparable; func (error).Error() string}",
|
||||
|
||||
"{m(); comparable; int|float32|string}": "{comparable; func (p.T).m(); int ∪ float32 ∪ string}",
|
||||
"{m1(); int; m2(); comparable }": "{comparable; func (p.T).m1(); func (p.T).m2(); int}",
|
||||
"{m(); comparable; int|float32|string}": "{func (p.T).m(); int ∪ float32 ∪ string}",
|
||||
"{m1(); int; m2(); comparable }": "{func (p.T).m1(); func (p.T).m2(); int}",
|
||||
|
||||
"{E}; type E interface{}": "𝓤",
|
||||
"{E}; type E interface{int;string}": "∅",
|
||||
|
|
|
@ -144,10 +144,16 @@ func (check *Checker) typ(e ast.Expr) Type {
|
|||
// constraint interface.
|
||||
func (check *Checker) varType(e ast.Expr) Type {
|
||||
typ := check.definedType(e, nil)
|
||||
check.validVarType(e, typ)
|
||||
return typ
|
||||
}
|
||||
|
||||
// validVarType reports an error if typ is a constraint interface.
|
||||
// The expression e is used for error reporting, if any.
|
||||
func (check *Checker) validVarType(e ast.Expr, typ Type) {
|
||||
// If we have a type parameter there's nothing to do.
|
||||
if isTypeParam(typ) {
|
||||
return typ
|
||||
return
|
||||
}
|
||||
|
||||
// We don't want to call under() or complete interfaces while we are in
|
||||
|
@ -165,8 +171,6 @@ func (check *Checker) varType(e ast.Expr) Type {
|
|||
}
|
||||
}
|
||||
})
|
||||
|
||||
return typ
|
||||
}
|
||||
|
||||
// definedType is like typ but also accepts a type name def.
|
||||
|
@ -254,7 +258,7 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) {
|
|||
|
||||
case *ast.SelectorExpr:
|
||||
var x operand
|
||||
check.selector(&x, e)
|
||||
check.selector(&x, e, def)
|
||||
|
||||
switch x.mode {
|
||||
case typexpr:
|
||||
|
@ -323,7 +327,7 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) {
|
|||
return typ
|
||||
|
||||
case *ast.InterfaceType:
|
||||
typ := new(Interface)
|
||||
typ := check.newInterface()
|
||||
def.setUnderlying(typ)
|
||||
if def != nil {
|
||||
typ.obj = def.obj
|
||||
|
@ -415,10 +419,14 @@ func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *Named) (re
|
|||
// evaluate arguments
|
||||
targs := check.typeList(ix.Indices)
|
||||
if targs == nil {
|
||||
def.setUnderlying(Typ[Invalid]) // avoid later errors due to lazy instantiation
|
||||
def.setUnderlying(Typ[Invalid]) // avoid errors later due to lazy instantiation
|
||||
return Typ[Invalid]
|
||||
}
|
||||
|
||||
// enableTypeTypeInference controls whether to infer missing type arguments
|
||||
// using constraint type inference. See issue #51527.
|
||||
const enableTypeTypeInference = false
|
||||
|
||||
// create the instance
|
||||
ctxt := check.bestContext(nil)
|
||||
h := ctxt.instanceHash(orig, targs)
|
||||
|
@ -438,19 +446,18 @@ func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *Named) (re
|
|||
def.setUnderlying(inst)
|
||||
|
||||
inst.resolver = func(ctxt *Context, n *Named) (*TypeParamList, Type, *methodList) {
|
||||
tparams := orig.TypeParams().list()
|
||||
tparams := n.orig.TypeParams().list()
|
||||
|
||||
inferred := targs
|
||||
if len(targs) < len(tparams) {
|
||||
targs := n.targs.list()
|
||||
if enableTypeTypeInference && len(targs) < len(tparams) {
|
||||
// If inference fails, len(inferred) will be 0, and inst.underlying will
|
||||
// be set to Typ[Invalid] in expandNamed.
|
||||
inferred = check.infer(ix.Orig, tparams, targs, nil, nil)
|
||||
inferred := check.infer(ix.Orig, tparams, targs, nil, nil)
|
||||
if len(inferred) > len(targs) {
|
||||
inst.targs = newTypeList(inferred)
|
||||
n.targs = newTypeList(inferred)
|
||||
}
|
||||
}
|
||||
|
||||
check.recordInstance(ix.Orig, inferred, inst)
|
||||
return expandNamed(ctxt, n, pos)
|
||||
}
|
||||
|
||||
|
@ -463,6 +470,7 @@ func (check *Checker) instantiatedType(ix *typeparams.IndexExpr, def *Named) (re
|
|||
// Since check is non-nil, we can still mutate inst. Unpinning the resolver
|
||||
// frees some memory.
|
||||
inst.resolver = nil
|
||||
check.recordInstance(ix.Orig, inst.TypeArgs().list(), inst)
|
||||
|
||||
if check.validateTArgLen(pos, inst.tparams.Len(), inst.targs.Len()) {
|
||||
if i, err := check.verify(pos, inst.tparams.list(), inst.targs.list()); err != nil {
|
||||
|
|
|
@ -247,6 +247,17 @@ func (d *tparamsList) set(i int, typ Type) {
|
|||
}
|
||||
}
|
||||
|
||||
// unknowns returns the number of type parameters for which no type has been set yet.
|
||||
func (d *tparamsList) unknowns() int {
|
||||
n := 0
|
||||
for _, ti := range d.indices {
|
||||
if ti <= 0 {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// types returns the list of inferred types (via unification) for the type parameters
|
||||
// described by d, and an index. If all types were inferred, the returned index is < 0.
|
||||
// Otherwise, it is the index of the first type parameter which couldn't be inferred;
|
||||
|
@ -349,12 +360,16 @@ func (u *unifier) nify(x, y Type, p *ifacePair) (result bool) {
|
|||
if enableCoreTypeUnification && !u.exact {
|
||||
if isTypeParam(x) && !hasName(y) {
|
||||
// When considering the type parameter for unification
|
||||
// we look at the adjusted core type (adjCoreType).
|
||||
// we look at the adjusted core term (adjusted core type
|
||||
// with tilde information).
|
||||
// If the adjusted core type is a named type N; the
|
||||
// corresponding core type is under(N). Since !u.exact
|
||||
// and y doesn't have a name, unification will end up
|
||||
// comparing under(N) to y, so we can just use the core
|
||||
// type instead. Optimization.
|
||||
// type instead. And we can ignore the tilde because we
|
||||
// already look at the underlying types on both sides
|
||||
// and we have known types on both sides.
|
||||
// Optimization.
|
||||
if cx := coreType(x); cx != nil {
|
||||
if traceInference {
|
||||
u.tracef("core %s ≡ %s", x, y)
|
||||
|
|
|
@ -103,25 +103,27 @@ func parseUnion(check *Checker, uexpr ast.Expr) Type {
|
|||
|
||||
if !Identical(u, t.typ) {
|
||||
check.errorf(tlist[i], _InvalidUnion, "invalid use of ~ (underlying type of %s is %s)", t.typ, u)
|
||||
continue // don't report another error for t
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Stand-alone embedded interfaces are ok and are handled by the single-type case
|
||||
// in the beginning. Embedded interfaces with tilde are excluded above. If we reach
|
||||
// here, we must have at least two terms in the union.
|
||||
if f != nil && !f.typeSet().IsTypeSet() {
|
||||
// here, we must have at least two terms in the syntactic term list (but not necessarily
|
||||
// in the term list of the union's type set).
|
||||
if f != nil {
|
||||
tset := f.typeSet()
|
||||
switch {
|
||||
case f.typeSet().NumMethods() != 0:
|
||||
case tset.NumMethods() != 0:
|
||||
check.errorf(tlist[i], _InvalidUnion, "cannot use %s in union (%s contains methods)", t, t)
|
||||
continue
|
||||
case t.typ == universeComparable.Type():
|
||||
check.error(tlist[i], _InvalidUnion, "cannot use comparable in union")
|
||||
case f.typeSet().comparable:
|
||||
continue
|
||||
case tset.comparable:
|
||||
check.errorf(tlist[i], _InvalidUnion, "cannot use %s in union (%s embeds comparable)", t, t)
|
||||
default:
|
||||
panic("not a type set but no methods and not comparable")
|
||||
continue
|
||||
}
|
||||
continue // don't report another error for t
|
||||
}
|
||||
|
||||
// Report overlapping (non-disjoint) terms such as
|
||||
|
|
|
@ -112,7 +112,7 @@ func defPredeclaredTypes() {
|
|||
typ := NewNamed(obj, nil, nil)
|
||||
|
||||
// interface{} // marked as comparable
|
||||
ityp := &Interface{obj: obj, complete: true, tset: &_TypeSet{true, nil, allTermlist}}
|
||||
ityp := &Interface{obj: obj, complete: true, tset: &_TypeSet{nil, allTermlist, true}}
|
||||
|
||||
typ.SetUnderlying(ityp)
|
||||
def(obj)
|
||||
|
|
|
@ -79,7 +79,7 @@ func (check *Checker) validType0(typ Type, env *tparamEnv, path []Object) typeIn
|
|||
// would have reported a type cycle and couldn't have been
|
||||
// imported in the first place.
|
||||
assert(t.obj.pkg == check.pkg)
|
||||
t.underlying = Typ[Invalid] // t is in the current package (no race possibilty)
|
||||
t.underlying = Typ[Invalid] // t is in the current package (no race possibility)
|
||||
// Find the starting point of the cycle and report it.
|
||||
for i, tn := range path {
|
||||
if tn == t.obj {
|
||||
|
|
|
@ -66,6 +66,17 @@ func countNewCoverageBits(base, snapshot []byte) int {
|
|||
return n
|
||||
}
|
||||
|
||||
// isCoverageSubset returns true if all the base coverage bits are set in
|
||||
// snapshot
|
||||
func isCoverageSubset(base, snapshot []byte) bool {
|
||||
for i, v := range base {
|
||||
if v&snapshot[i] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// hasCoverageBit returns true if snapshot has at least one bit set that is
|
||||
// also set in base.
|
||||
func hasCoverageBit(base, snapshot []byte) bool {
|
||||
|
|
|
@ -10,7 +10,9 @@ import (
|
|||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"math"
|
||||
"strconv"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// encVersion1 will be the first line of a file with version 1 encoding.
|
||||
|
@ -27,13 +29,64 @@ func marshalCorpusFile(vals ...any) []byte {
|
|||
// instead of changing to byte and rune respectively.
|
||||
for _, val := range vals {
|
||||
switch t := val.(type) {
|
||||
case int, int8, int16, int64, uint, uint16, uint32, uint64, float32, float64, bool:
|
||||
case int, int8, int16, int64, uint, uint16, uint32, uint64, bool:
|
||||
fmt.Fprintf(b, "%T(%v)\n", t, t)
|
||||
case float32:
|
||||
if math.IsNaN(float64(t)) && math.Float32bits(t) != math.Float32bits(float32(math.NaN())) {
|
||||
// We encode unusual NaNs as hex values, because that is how users are
|
||||
// likely to encounter them in literature about floating-point encoding.
|
||||
// This allows us to reproduce fuzz failures that depend on the specific
|
||||
// NaN representation (for float32 there are about 2^24 possibilities!),
|
||||
// not just the fact that the value is *a* NaN.
|
||||
//
|
||||
// Note that the specific value of float32(math.NaN()) can vary based on
|
||||
// whether the architecture represents signaling NaNs using a low bit
|
||||
// (as is common) or a high bit (as commonly implemented on MIPS
|
||||
// hardware before around 2012). We believe that the increase in clarity
|
||||
// from identifying "NaN" with math.NaN() is worth the slight ambiguity
|
||||
// from a platform-dependent value.
|
||||
fmt.Fprintf(b, "math.Float32frombits(0x%x)\n", math.Float32bits(t))
|
||||
} else {
|
||||
// We encode all other values — including the NaN value that is
|
||||
// bitwise-identical to float32(math.Nan()) — using the default
|
||||
// formatting, which is equivalent to strconv.FormatFloat with format
|
||||
// 'g' and can be parsed by strconv.ParseFloat.
|
||||
//
|
||||
// For an ordinary floating-point number this format includes
|
||||
// sufficiently many digits to reconstruct the exact value. For positive
|
||||
// or negative infinity it is the string "+Inf" or "-Inf". For positive
|
||||
// or negative zero it is "0" or "-0". For NaN, it is the string "NaN".
|
||||
fmt.Fprintf(b, "%T(%v)\n", t, t)
|
||||
}
|
||||
case float64:
|
||||
if math.IsNaN(t) && math.Float64bits(t) != math.Float64bits(math.NaN()) {
|
||||
fmt.Fprintf(b, "math.Float64frombits(0x%x)\n", math.Float64bits(t))
|
||||
} else {
|
||||
fmt.Fprintf(b, "%T(%v)\n", t, t)
|
||||
}
|
||||
case string:
|
||||
fmt.Fprintf(b, "string(%q)\n", t)
|
||||
case rune: // int32
|
||||
fmt.Fprintf(b, "rune(%q)\n", t)
|
||||
// Although rune and int32 are represented by the same type, only a subset
|
||||
// of valid int32 values can be expressed as rune literals. Notably,
|
||||
// negative numbers, surrogate halves, and values above unicode.MaxRune
|
||||
// have no quoted representation.
|
||||
//
|
||||
// fmt with "%q" (and the corresponding functions in the strconv package)
|
||||
// would quote out-of-range values to the Unicode replacement character
|
||||
// instead of the original value (see https://go.dev/issue/51526), so
|
||||
// they must be treated as int32 instead.
|
||||
//
|
||||
// We arbitrarily draw the line at UTF-8 validity, which biases toward the
|
||||
// "rune" interpretation. (However, we accept either format as input.)
|
||||
if utf8.ValidRune(t) {
|
||||
fmt.Fprintf(b, "rune(%q)\n", t)
|
||||
} else {
|
||||
fmt.Fprintf(b, "int32(%v)\n", t)
|
||||
}
|
||||
case byte: // uint8
|
||||
// For bytes, we arbitrarily prefer the character interpretation.
|
||||
// (Every byte has a valid character encoding.)
|
||||
fmt.Fprintf(b, "byte(%q)\n", t)
|
||||
case []byte: // []uint8
|
||||
fmt.Fprintf(b, "[]byte(%q)\n", t)
|
||||
|
@ -105,44 +158,78 @@ func parseCorpusValue(line []byte) (any, error) {
|
|||
return []byte(s), nil
|
||||
}
|
||||
|
||||
idType, ok := call.Fun.(*ast.Ident)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected []byte or primitive type")
|
||||
}
|
||||
if idType.Name == "bool" {
|
||||
id, ok := arg.(*ast.Ident)
|
||||
var idType *ast.Ident
|
||||
if selector, ok := call.Fun.(*ast.SelectorExpr); ok {
|
||||
xIdent, ok := selector.X.(*ast.Ident)
|
||||
if !ok || xIdent.Name != "math" {
|
||||
return nil, fmt.Errorf("invalid selector type")
|
||||
}
|
||||
switch selector.Sel.Name {
|
||||
case "Float64frombits":
|
||||
idType = &ast.Ident{Name: "float64-bits"}
|
||||
case "Float32frombits":
|
||||
idType = &ast.Ident{Name: "float32-bits"}
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid selector type")
|
||||
}
|
||||
} else {
|
||||
idType, ok = call.Fun.(*ast.Ident)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("malformed bool")
|
||||
return nil, fmt.Errorf("expected []byte or primitive type")
|
||||
}
|
||||
if id.Name == "true" {
|
||||
return true, nil
|
||||
} else if id.Name == "false" {
|
||||
return false, nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("true or false required for type bool")
|
||||
if idType.Name == "bool" {
|
||||
id, ok := arg.(*ast.Ident)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("malformed bool")
|
||||
}
|
||||
if id.Name == "true" {
|
||||
return true, nil
|
||||
} else if id.Name == "false" {
|
||||
return false, nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("true or false required for type bool")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
val string
|
||||
kind token.Token
|
||||
)
|
||||
if op, ok := arg.(*ast.UnaryExpr); ok {
|
||||
// Special case for negative numbers.
|
||||
lit, ok := op.X.(*ast.BasicLit)
|
||||
if !ok || (lit.Kind != token.INT && lit.Kind != token.FLOAT) {
|
||||
switch lit := op.X.(type) {
|
||||
case *ast.BasicLit:
|
||||
if op.Op != token.SUB {
|
||||
return nil, fmt.Errorf("unsupported operation on int/float: %v", op.Op)
|
||||
}
|
||||
// Special case for negative numbers.
|
||||
val = op.Op.String() + lit.Value // e.g. "-" + "124"
|
||||
kind = lit.Kind
|
||||
case *ast.Ident:
|
||||
if lit.Name != "Inf" {
|
||||
return nil, fmt.Errorf("expected operation on int or float type")
|
||||
}
|
||||
if op.Op == token.SUB {
|
||||
val = "-Inf"
|
||||
} else {
|
||||
val = "+Inf"
|
||||
}
|
||||
kind = token.FLOAT
|
||||
default:
|
||||
return nil, fmt.Errorf("expected operation on int or float type")
|
||||
}
|
||||
if op.Op != token.SUB {
|
||||
return nil, fmt.Errorf("unsupported operation on int: %v", op.Op)
|
||||
}
|
||||
val = op.Op.String() + lit.Value // e.g. "-" + "124"
|
||||
kind = lit.Kind
|
||||
} else {
|
||||
lit, ok := arg.(*ast.BasicLit)
|
||||
if !ok {
|
||||
switch lit := arg.(type) {
|
||||
case *ast.BasicLit:
|
||||
val, kind = lit.Value, lit.Kind
|
||||
case *ast.Ident:
|
||||
if lit.Name != "NaN" {
|
||||
return nil, fmt.Errorf("literal value required for primitive type")
|
||||
}
|
||||
val, kind = "NaN", token.FLOAT
|
||||
default:
|
||||
return nil, fmt.Errorf("literal value required for primitive type")
|
||||
}
|
||||
val, kind = lit.Value, lit.Kind
|
||||
}
|
||||
|
||||
switch typ := idType.Name; typ {
|
||||
|
@ -152,6 +239,14 @@ func parseCorpusValue(line []byte) (any, error) {
|
|||
}
|
||||
return strconv.Unquote(val)
|
||||
case "byte", "rune":
|
||||
if kind == token.INT {
|
||||
switch typ {
|
||||
case "rune":
|
||||
return parseInt(val, typ)
|
||||
case "byte":
|
||||
return parseUint(val, typ)
|
||||
}
|
||||
}
|
||||
if kind != token.CHAR {
|
||||
return nil, fmt.Errorf("character literal required for byte/rune types")
|
||||
}
|
||||
|
@ -191,6 +286,24 @@ func parseCorpusValue(line []byte) (any, error) {
|
|||
return nil, fmt.Errorf("float or integer literal required for float64 type")
|
||||
}
|
||||
return strconv.ParseFloat(val, 64)
|
||||
case "float32-bits":
|
||||
if kind != token.INT {
|
||||
return nil, fmt.Errorf("integer literal required for math.Float32frombits type")
|
||||
}
|
||||
bits, err := parseUint(val, "uint32")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return math.Float32frombits(bits.(uint32)), nil
|
||||
case "float64-bits":
|
||||
if kind != token.FLOAT && kind != token.INT {
|
||||
return nil, fmt.Errorf("integer literal required for math.Float64frombits type")
|
||||
}
|
||||
bits, err := parseUint(val, "uint64")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return math.Float64frombits(bits.(uint64)), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("expected []byte or primitive type")
|
||||
}
|
||||
|
@ -200,18 +313,24 @@ func parseCorpusValue(line []byte) (any, error) {
|
|||
func parseInt(val, typ string) (any, error) {
|
||||
switch typ {
|
||||
case "int":
|
||||
return strconv.Atoi(val)
|
||||
// The int type may be either 32 or 64 bits. If 32, the fuzz tests in the
|
||||
// corpus may include 64-bit values produced by fuzzing runs on 64-bit
|
||||
// architectures. When running those tests, we implicitly wrap the values to
|
||||
// fit in a regular int. (The test case is still “interesting”, even if the
|
||||
// specific values of its inputs are platform-dependent.)
|
||||
i, err := strconv.ParseInt(val, 0, 64)
|
||||
return int(i), err
|
||||
case "int8":
|
||||
i, err := strconv.ParseInt(val, 10, 8)
|
||||
i, err := strconv.ParseInt(val, 0, 8)
|
||||
return int8(i), err
|
||||
case "int16":
|
||||
i, err := strconv.ParseInt(val, 10, 16)
|
||||
i, err := strconv.ParseInt(val, 0, 16)
|
||||
return int16(i), err
|
||||
case "int32":
|
||||
i, err := strconv.ParseInt(val, 10, 32)
|
||||
case "int32", "rune":
|
||||
i, err := strconv.ParseInt(val, 0, 32)
|
||||
return int32(i), err
|
||||
case "int64":
|
||||
return strconv.ParseInt(val, 10, 64)
|
||||
return strconv.ParseInt(val, 0, 64)
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
|
@ -221,19 +340,19 @@ func parseInt(val, typ string) (any, error) {
|
|||
func parseUint(val, typ string) (any, error) {
|
||||
switch typ {
|
||||
case "uint":
|
||||
i, err := strconv.ParseUint(val, 10, 0)
|
||||
i, err := strconv.ParseUint(val, 0, 64)
|
||||
return uint(i), err
|
||||
case "uint8":
|
||||
i, err := strconv.ParseUint(val, 10, 8)
|
||||
case "uint8", "byte":
|
||||
i, err := strconv.ParseUint(val, 0, 8)
|
||||
return uint8(i), err
|
||||
case "uint16":
|
||||
i, err := strconv.ParseUint(val, 10, 16)
|
||||
i, err := strconv.ParseUint(val, 0, 16)
|
||||
return uint16(i), err
|
||||
case "uint32":
|
||||
i, err := strconv.ParseUint(val, 10, 32)
|
||||
i, err := strconv.ParseUint(val, 0, 32)
|
||||
return uint32(i), err
|
||||
case "uint64":
|
||||
return strconv.ParseUint(val, 10, 64)
|
||||
return strconv.ParseUint(val, 0, 64)
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
|
|
|
@ -5,85 +5,104 @@
|
|||
package fuzz
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
func TestUnmarshalMarshal(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in string
|
||||
ok bool
|
||||
desc string
|
||||
in string
|
||||
reject bool
|
||||
want string // if different from in
|
||||
}{
|
||||
{
|
||||
in: "int(1234)",
|
||||
ok: false, // missing version
|
||||
desc: "missing version",
|
||||
in: "int(1234)",
|
||||
reject: true,
|
||||
},
|
||||
{
|
||||
desc: "malformed string",
|
||||
in: `go test fuzz v1
|
||||
string("a"bcad")`,
|
||||
ok: false, // malformed
|
||||
reject: true,
|
||||
},
|
||||
{
|
||||
desc: "empty value",
|
||||
in: `go test fuzz v1
|
||||
int()`,
|
||||
ok: false, // empty value
|
||||
reject: true,
|
||||
},
|
||||
{
|
||||
desc: "negative uint",
|
||||
in: `go test fuzz v1
|
||||
uint(-32)`,
|
||||
ok: false, // invalid negative uint
|
||||
reject: true,
|
||||
},
|
||||
{
|
||||
desc: "int8 too large",
|
||||
in: `go test fuzz v1
|
||||
int8(1234456)`,
|
||||
ok: false, // int8 too large
|
||||
reject: true,
|
||||
},
|
||||
{
|
||||
desc: "multiplication in int value",
|
||||
in: `go test fuzz v1
|
||||
int(20*5)`,
|
||||
ok: false, // expression in int value
|
||||
reject: true,
|
||||
},
|
||||
{
|
||||
desc: "double negation",
|
||||
in: `go test fuzz v1
|
||||
int(--5)`,
|
||||
ok: false, // expression in int value
|
||||
reject: true,
|
||||
},
|
||||
{
|
||||
desc: "malformed bool",
|
||||
in: `go test fuzz v1
|
||||
bool(0)`,
|
||||
ok: false, // malformed bool
|
||||
reject: true,
|
||||
},
|
||||
{
|
||||
desc: "malformed byte",
|
||||
in: `go test fuzz v1
|
||||
byte('aa)`,
|
||||
ok: false, // malformed byte
|
||||
reject: true,
|
||||
},
|
||||
{
|
||||
desc: "byte out of range",
|
||||
in: `go test fuzz v1
|
||||
byte('☃')`,
|
||||
ok: false, // byte out of range
|
||||
reject: true,
|
||||
},
|
||||
{
|
||||
desc: "extra newline",
|
||||
in: `go test fuzz v1
|
||||
string("has final newline")
|
||||
string("has extra newline")
|
||||
`,
|
||||
ok: true, // has final newline
|
||||
want: `go test fuzz v1
|
||||
string("has extra newline")`,
|
||||
},
|
||||
{
|
||||
desc: "trailing spaces",
|
||||
in: `go test fuzz v1
|
||||
string("extra")
|
||||
[]byte("spacing")
|
||||
`,
|
||||
ok: true, // extra spaces in the final newline
|
||||
want: `go test fuzz v1
|
||||
string("extra")
|
||||
[]byte("spacing")`,
|
||||
},
|
||||
{
|
||||
desc: "float types",
|
||||
in: `go test fuzz v1
|
||||
float64(0)
|
||||
float32(0)`,
|
||||
ok: true, // will be an integer literal since there is no decimal
|
||||
},
|
||||
{
|
||||
desc: "various types",
|
||||
in: `go test fuzz v1
|
||||
int(-23)
|
||||
int8(-2)
|
||||
|
@ -101,19 +120,112 @@ bool(true)
|
|||
string("hello\\xbd\\xb2=\\xbc ⌘")
|
||||
float64(-12.5)
|
||||
float32(2.5)`,
|
||||
ok: true,
|
||||
},
|
||||
{
|
||||
desc: "float edge cases",
|
||||
// The two IEEE 754 bit patterns used for the math.Float{64,32}frombits
|
||||
// encodings are non-math.NAN quiet-NaN values. Since they are not equal
|
||||
// to math.NaN(), they should be re-encoded to their bit patterns. They
|
||||
// are, respectively:
|
||||
// * math.Float64bits(math.NaN())+1
|
||||
// * math.Float32bits(float32(math.NaN()))+1
|
||||
in: `go test fuzz v1
|
||||
float32(-0)
|
||||
float64(-0)
|
||||
float32(+Inf)
|
||||
float32(-Inf)
|
||||
float32(NaN)
|
||||
float64(+Inf)
|
||||
float64(-Inf)
|
||||
float64(NaN)
|
||||
math.Float64frombits(0x7ff8000000000002)
|
||||
math.Float32frombits(0x7fc00001)`,
|
||||
},
|
||||
{
|
||||
desc: "int variations",
|
||||
// Although we arbitrarily choose default integer bases (0 or 16), we may
|
||||
// want to change those arbitrary choices in the future and should not
|
||||
// break the parser. Verify that integers in the opposite bases still
|
||||
// parse correctly.
|
||||
in: `go test fuzz v1
|
||||
int(0x0)
|
||||
int32(0x41)
|
||||
int64(0xfffffffff)
|
||||
uint32(0xcafef00d)
|
||||
uint64(0xffffffffffffffff)
|
||||
uint8(0b0000000)
|
||||
byte(0x0)
|
||||
byte('\000')
|
||||
byte('\u0000')
|
||||
byte('\'')
|
||||
math.Float64frombits(9221120237041090562)
|
||||
math.Float32frombits(2143289345)`,
|
||||
want: `go test fuzz v1
|
||||
int(0)
|
||||
rune('A')
|
||||
int64(68719476735)
|
||||
uint32(3405705229)
|
||||
uint64(18446744073709551615)
|
||||
byte('\x00')
|
||||
byte('\x00')
|
||||
byte('\x00')
|
||||
byte('\x00')
|
||||
byte('\'')
|
||||
math.Float64frombits(0x7ff8000000000002)
|
||||
math.Float32frombits(0x7fc00001)`,
|
||||
},
|
||||
{
|
||||
desc: "rune validation",
|
||||
in: `go test fuzz v1
|
||||
rune(0)
|
||||
rune(0x41)
|
||||
rune(-1)
|
||||
rune(0xfffd)
|
||||
rune(0xd800)
|
||||
rune(0x10ffff)
|
||||
rune(0x110000)
|
||||
`,
|
||||
want: `go test fuzz v1
|
||||
rune('\x00')
|
||||
rune('A')
|
||||
int32(-1)
|
||||
rune('<27>')
|
||||
int32(55296)
|
||||
rune('\U0010ffff')
|
||||
int32(1114112)`,
|
||||
},
|
||||
{
|
||||
desc: "int overflow",
|
||||
in: `go test fuzz v1
|
||||
int(0x7fffffffffffffff)
|
||||
uint(0xffffffffffffffff)`,
|
||||
want: func() string {
|
||||
switch strconv.IntSize {
|
||||
case 32:
|
||||
return `go test fuzz v1
|
||||
int(-1)
|
||||
uint(4294967295)`
|
||||
case 64:
|
||||
return `go test fuzz v1
|
||||
int(9223372036854775807)
|
||||
uint(18446744073709551615)`
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}(),
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.in, func(t *testing.T) {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
vals, err := unmarshalCorpusFile([]byte(test.in))
|
||||
if test.ok && err != nil {
|
||||
t.Fatalf("unmarshal unexpected error: %v", err)
|
||||
} else if !test.ok && err == nil {
|
||||
t.Fatalf("unmarshal unexpected success")
|
||||
if test.reject {
|
||||
if err == nil {
|
||||
t.Fatalf("unmarshal unexpected success")
|
||||
}
|
||||
return
|
||||
}
|
||||
if !test.ok {
|
||||
return // skip the rest of the test
|
||||
if err != nil {
|
||||
t.Fatalf("unmarshal unexpected error: %v", err)
|
||||
}
|
||||
newB := marshalCorpusFile(vals...)
|
||||
if err != nil {
|
||||
|
@ -122,9 +234,15 @@ float32(2.5)`,
|
|||
if newB[len(newB)-1] != '\n' {
|
||||
t.Error("didn't write final newline to corpus file")
|
||||
}
|
||||
before, after := strings.TrimSpace(test.in), strings.TrimSpace(string(newB))
|
||||
if before != after {
|
||||
t.Errorf("values changed after unmarshal then marshal\nbefore: %q\nafter: %q", before, after)
|
||||
|
||||
want := test.want
|
||||
if want == "" {
|
||||
want = test.in
|
||||
}
|
||||
want += "\n"
|
||||
got := string(newB)
|
||||
if got != want {
|
||||
t.Errorf("unexpected marshaled value\ngot:\n%s\nwant:\n%s", got, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -170,3 +288,117 @@ func BenchmarkUnmarshalCorpusFile(b *testing.B) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestByteRoundTrip(t *testing.T) {
|
||||
for x := 0; x < 256; x++ {
|
||||
b1 := byte(x)
|
||||
buf := marshalCorpusFile(b1)
|
||||
vs, err := unmarshalCorpusFile(buf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b2 := vs[0].(byte)
|
||||
if b2 != b1 {
|
||||
t.Fatalf("unmarshaled %v, want %v:\n%s", b2, b1, buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInt8RoundTrip(t *testing.T) {
|
||||
for x := -128; x < 128; x++ {
|
||||
i1 := int8(x)
|
||||
buf := marshalCorpusFile(i1)
|
||||
vs, err := unmarshalCorpusFile(buf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
i2 := vs[0].(int8)
|
||||
if i2 != i1 {
|
||||
t.Fatalf("unmarshaled %v, want %v:\n%s", i2, i1, buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzFloat64RoundTrip(f *testing.F) {
|
||||
f.Add(math.Float64bits(0))
|
||||
f.Add(math.Float64bits(math.Copysign(0, -1)))
|
||||
f.Add(math.Float64bits(math.MaxFloat64))
|
||||
f.Add(math.Float64bits(math.SmallestNonzeroFloat64))
|
||||
f.Add(math.Float64bits(math.NaN()))
|
||||
f.Add(uint64(0x7FF0000000000001)) // signaling NaN
|
||||
f.Add(math.Float64bits(math.Inf(1)))
|
||||
f.Add(math.Float64bits(math.Inf(-1)))
|
||||
|
||||
f.Fuzz(func(t *testing.T, u1 uint64) {
|
||||
x1 := math.Float64frombits(u1)
|
||||
|
||||
b := marshalCorpusFile(x1)
|
||||
t.Logf("marshaled math.Float64frombits(0x%x):\n%s", u1, b)
|
||||
|
||||
xs, err := unmarshalCorpusFile(b)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(xs) != 1 {
|
||||
t.Fatalf("unmarshaled %d values", len(xs))
|
||||
}
|
||||
x2 := xs[0].(float64)
|
||||
u2 := math.Float64bits(x2)
|
||||
if u2 != u1 {
|
||||
t.Errorf("unmarshaled %v (bits 0x%x)", x2, u2)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func FuzzRuneRoundTrip(f *testing.F) {
|
||||
f.Add(rune(-1))
|
||||
f.Add(rune(0xd800))
|
||||
f.Add(rune(0xdfff))
|
||||
f.Add(rune(unicode.ReplacementChar))
|
||||
f.Add(rune(unicode.MaxASCII))
|
||||
f.Add(rune(unicode.MaxLatin1))
|
||||
f.Add(rune(unicode.MaxRune))
|
||||
f.Add(rune(unicode.MaxRune + 1))
|
||||
f.Add(rune(-0x80000000))
|
||||
f.Add(rune(0x7fffffff))
|
||||
|
||||
f.Fuzz(func(t *testing.T, r1 rune) {
|
||||
b := marshalCorpusFile(r1)
|
||||
t.Logf("marshaled rune(0x%x):\n%s", r1, b)
|
||||
|
||||
rs, err := unmarshalCorpusFile(b)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(rs) != 1 {
|
||||
t.Fatalf("unmarshaled %d values", len(rs))
|
||||
}
|
||||
r2 := rs[0].(rune)
|
||||
if r2 != r1 {
|
||||
t.Errorf("unmarshaled rune(0x%x)", r2)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func FuzzStringRoundTrip(f *testing.F) {
|
||||
f.Add("")
|
||||
f.Add("\x00")
|
||||
f.Add(string([]rune{unicode.ReplacementChar}))
|
||||
|
||||
f.Fuzz(func(t *testing.T, s1 string) {
|
||||
b := marshalCorpusFile(s1)
|
||||
t.Logf("marshaled %q:\n%s", s1, b)
|
||||
|
||||
rs, err := unmarshalCorpusFile(b)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(rs) != 1 {
|
||||
t.Fatalf("unmarshaled %d values", len(rs))
|
||||
}
|
||||
s2 := rs[0].(string)
|
||||
if s2 != s1 {
|
||||
t.Errorf("unmarshaled %q", s2)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -800,6 +800,7 @@ func (ws *workerServer) minimize(ctx context.Context, args minimizeArgs) (resp m
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
inpHash := sha256.Sum256(mem.valueCopy())
|
||||
if args.Timeout != 0 {
|
||||
var cancel func()
|
||||
ctx, cancel = context.WithTimeout(ctx, args.Timeout)
|
||||
|
@ -811,12 +812,22 @@ func (ws *workerServer) minimize(ctx context.Context, args minimizeArgs) (resp m
|
|||
success, err := ws.minimizeInput(ctx, vals, mem, args)
|
||||
if success {
|
||||
writeToMem(vals, mem)
|
||||
outHash := sha256.Sum256(mem.valueCopy())
|
||||
mem.header().rawInMem = false
|
||||
resp.WroteToMem = true
|
||||
if err != nil {
|
||||
resp.Err = err.Error()
|
||||
} else {
|
||||
resp.CoverageData = coverageSnapshot
|
||||
// If the values didn't change during minimization then coverageSnapshot is likely
|
||||
// a dirty snapshot which represents the very last step of minimization, not the
|
||||
// coverage for the initial input. In that case just return the coverage we were
|
||||
// given initially, since it more accurately represents the coverage map for the
|
||||
// input we are returning.
|
||||
if outHash != inpHash {
|
||||
resp.CoverageData = coverageSnapshot
|
||||
} else {
|
||||
resp.CoverageData = args.KeepCoverage
|
||||
}
|
||||
}
|
||||
}
|
||||
return resp
|
||||
|
@ -883,7 +894,8 @@ func (ws *workerServer) minimizeInput(ctx context.Context, vals []any, mem *shar
|
|||
}
|
||||
return true
|
||||
}
|
||||
if keepCoverage != nil && hasCoverageBit(keepCoverage, coverageSnapshot) {
|
||||
// Minimization should preserve coverage bits.
|
||||
if keepCoverage != nil && isCoverageSubset(keepCoverage, coverageSnapshot) {
|
||||
return true
|
||||
}
|
||||
vals[args.Index] = prev
|
||||
|
|
|
@ -703,6 +703,12 @@ var (
|
|||
_ io.Reader = (*Buffers)(nil)
|
||||
)
|
||||
|
||||
// WriteTo writes contents of the buffers to w.
|
||||
//
|
||||
// WriteTo implements io.WriterTo for Buffers.
|
||||
//
|
||||
// WriteTo modifies the slice v as well as v[i] for 0 <= i < len(v),
|
||||
// but does not modify v[i][j] for any i, j.
|
||||
func (v *Buffers) WriteTo(w io.Writer) (n int64, err error) {
|
||||
if wv, ok := w.(buffersWriter); ok {
|
||||
return wv.writeBuffers(v)
|
||||
|
@ -719,6 +725,12 @@ func (v *Buffers) WriteTo(w io.Writer) (n int64, err error) {
|
|||
return n, nil
|
||||
}
|
||||
|
||||
// Read from the buffers.
|
||||
//
|
||||
// Read implements io.Reader for Buffers.
|
||||
//
|
||||
// Read modifies the slice v as well as v[i] for 0 <= i < len(v),
|
||||
// but does not modify v[i][j] for any i, j.
|
||||
func (v *Buffers) Read(p []byte) (n int, err error) {
|
||||
for len(p) > 0 && len(*v) > 0 {
|
||||
n0 := copy(p, (*v)[0])
|
||||
|
|
|
@ -45,6 +45,15 @@ func TestFinalizerType(t *testing.T) {
|
|||
{func(x *int) any { return Tintptr(x) }, func(v *int) { finalize(v) }},
|
||||
{func(x *int) any { return (*Tint)(x) }, func(v *Tint) { finalize((*int)(v)) }},
|
||||
{func(x *int) any { return (*Tint)(x) }, func(v Tinter) { finalize((*int)(v.(*Tint))) }},
|
||||
// Test case for argument spill slot.
|
||||
// If the spill slot was not counted for the frame size, it will (incorrectly) choose
|
||||
// call32 as the result has (exactly) 32 bytes. When the argument actually spills,
|
||||
// it clobbers the caller's frame (likely the return PC).
|
||||
{func(x *int) any { return x }, func(v any) [4]int64 {
|
||||
print() // force spill
|
||||
finalize(v.(*int))
|
||||
return [4]int64{}
|
||||
}},
|
||||
}
|
||||
|
||||
for i, tt := range finalizerTests {
|
||||
|
|
|
@ -10,7 +10,7 @@ package main
|
|||
// This is a regression test for issue 14599, where profiling fails when the
|
||||
// function is the first C function. Exported functions are the first C
|
||||
// functions, so we use an exported function. Exported functions are created in
|
||||
// lexigraphical order of source files, so this file is named aprof.go to
|
||||
// lexicographical order of source files, so this file is named aprof.go to
|
||||
// ensure its function is first.
|
||||
|
||||
// extern void CallGoNop();
|
||||
|
|
|
@ -17,10 +17,9 @@ type Builder struct {
|
|||
buf []byte
|
||||
}
|
||||
|
||||
// noescape hides a pointer from escape analysis. noescape is
|
||||
// the identity function but escape analysis doesn't think the
|
||||
// output depends on the input. noescape is inlined and currently
|
||||
// compiles down to zero instructions.
|
||||
// noescape hides a pointer from escape analysis. It is the identity function
|
||||
// but escape analysis doesn't think the output depends on the input.
|
||||
// noescape is inlined and currently compiles down to zero instructions.
|
||||
// USE CAREFULLY!
|
||||
// This was copied from the runtime; see issues 23382 and 7921.
|
||||
//go:nosplit
|
||||
|
|
|
@ -328,33 +328,6 @@ func TestUnixRightsRoundtrip(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRlimit(t *testing.T) {
|
||||
var rlimit, zero syscall.Rlimit
|
||||
if err := syscall.Getrlimit(syscall.RLIMIT_CPU, &rlimit); err != nil {
|
||||
t.Fatalf("Getrlimit: save failed: %v", err)
|
||||
}
|
||||
if zero == rlimit {
|
||||
t.Fatalf("Getrlimit: save failed: got zero value %#v", rlimit)
|
||||
}
|
||||
set := rlimit
|
||||
set.Cur = set.Max - 1
|
||||
if err := syscall.Setrlimit(syscall.RLIMIT_CPU, &set); err != nil {
|
||||
t.Fatalf("Setrlimit: set failed: %#v %v", set, err)
|
||||
}
|
||||
var get syscall.Rlimit
|
||||
if err := syscall.Getrlimit(syscall.RLIMIT_CPU, &get); err != nil {
|
||||
t.Fatalf("Getrlimit: get failed: %v", err)
|
||||
}
|
||||
set = rlimit
|
||||
set.Cur = set.Max - 1
|
||||
if set != get {
|
||||
t.Fatalf("Rlimit: change failed: wanted %#v got %#v", set, get)
|
||||
}
|
||||
if err := syscall.Setrlimit(syscall.RLIMIT_CPU, &rlimit); err != nil {
|
||||
t.Fatalf("Setrlimit: restore failed: %#v %v", rlimit, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSeekFailure(t *testing.T) {
|
||||
_, err := syscall.Seek(-1, 0, io.SeekStart)
|
||||
if err == nil {
|
||||
|
|
|
@ -198,7 +198,7 @@ done
|
|||
libgofile=${libgotd}/$f
|
||||
merge ${name} ${oldfile} ${newfile} ${libgofile}
|
||||
done
|
||||
(cd ${newtd} & git ls-files .) | while read f; do
|
||||
(cd ${newtd} && git ls-files .) | while read f; do
|
||||
if test "`basename -- $f`" = ".gitignore"; then
|
||||
continue
|
||||
fi
|
||||
|
|
|
@ -63,7 +63,7 @@ func TestASAN(t *testing.T) {
|
|||
// sanitizer library needs a
|
||||
// symbolizer program and can't find it.
|
||||
const noSymbolizer = "external symbolizer"
|
||||
// Check if -asan option can correctly print where the error occured.
|
||||
// Check if -asan option can correctly print where the error occurred.
|
||||
if tc.errorLocation != "" &&
|
||||
!strings.Contains(out, tc.errorLocation) &&
|
||||
!strings.Contains(out, noSymbolizer) &&
|
||||
|
|
Loading…
Add table
Reference in a new issue