libgo: update to final Go 1.18 release
Change-Id: Iabbc0f21c727d09b712e70cad7ebe20c39b0119b
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/393377
Trust: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
diff --git a/libgo/MERGE b/libgo/MERGE
index 11da9a2..85536ad 100644
--- a/libgo/MERGE
+++ b/libgo/MERGE
@@ -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.
diff --git a/libgo/VERSION b/libgo/VERSION
index 6a12199..39560f0 100644
--- a/libgo/VERSION
+++ b/libgo/VERSION
@@ -1 +1 @@
-go1.18rc1
+go1.18
diff --git a/libgo/go/cmd/go/alldocs.go b/libgo/go/cmd/go/alldocs.go
index 63e7900..420529b 100644
--- a/libgo/go/cmd/go/alldocs.go
+++ b/libgo/go/cmd/go/alldocs.go
@@ -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
//
diff --git a/libgo/go/cmd/go/internal/modfetch/coderepo.go b/libgo/go/cmd/go/internal/modfetch/coderepo.go
index 2206c7c..dfaf16d 100644
--- a/libgo/go/cmd/go/internal/modfetch/coderepo.go
+++ b/libgo/go/cmd/go/internal/modfetch/coderepo.go
@@ -305,17 +305,46 @@
//
// (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 @@
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 @@
// 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 @@
// 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.
diff --git a/libgo/go/cmd/go/internal/modfetch/coderepo_test.go b/libgo/go/cmd/go/internal/modfetch/coderepo_test.go
index d98ea87..bb9268a 100644
--- a/libgo/go/cmd/go/internal/modfetch/coderepo_test.go
+++ b/libgo/go/cmd/go/internal/modfetch/coderepo_test.go
@@ -458,6 +458,54 @@
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) {
diff --git a/libgo/go/cmd/go/internal/modload/init.go b/libgo/go/cmd/go/internal/modload/init.go
index a070666..f960edd 100644
--- a/libgo/go/cmd/go/internal/modload/init.go
+++ b/libgo/go/cmd/go/internal/modload/init.go
@@ -288,6 +288,11 @@
// 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 = ""
diff --git a/libgo/go/cmd/go/internal/modload/modfile.go b/libgo/go/cmd/go/internal/modload/modfile.go
index 627cf1d..75c278a 100644
--- a/libgo/go/cmd/go/internal/modload/modfile.go
+++ b/libgo/go/cmd/go/internal/modload/modfile.go
@@ -802,7 +802,7 @@
// 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
diff --git a/libgo/go/cmd/go/internal/run/run.go b/libgo/go/cmd/go/internal/run/run.go
index 00a3e4b..312b49e 100644
--- a/libgo/go/cmd/go/internal/run/run.go
+++ b/libgo/go/cmd/go/internal/run/run.go
@@ -73,8 +73,6 @@
}
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 @@
modload.RootMode = modload.NoRoot
modload.AllowMissingModuleImports()
modload.Init()
+ } else {
+ modload.InitWorkfile()
}
+
work.BuildInit()
var b work.Builder
b.Init()
diff --git a/libgo/go/cmd/go/internal/vcs/vcs.go b/libgo/go/cmd/go/internal/vcs/vcs.go
index fd521b2..2acabf7 100644
--- a/libgo/go/cmd/go/internal/vcs/vcs.go
+++ b/libgo/go/cmd/go/internal/vcs/vcs.go
@@ -312,7 +312,7 @@
// 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 {
diff --git a/libgo/go/cmd/go/internal/workcmd/edit.go b/libgo/go/cmd/go/internal/workcmd/edit.go
index 05f4f3d..1478c19 100644
--- a/libgo/go/cmd/go/internal/workcmd/edit.go
+++ b/libgo/go/cmd/go/internal/workcmd/edit.go
@@ -84,9 +84,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.
`,
}
diff --git a/libgo/go/cmd/go/internal/workcmd/init.go b/libgo/go/cmd/go/internal/workcmd/init.go
index 63bee6e..c2513ba 100644
--- a/libgo/go/cmd/go/internal/workcmd/init.go
+++ b/libgo/go/cmd/go/internal/workcmd/init.go
@@ -27,6 +27,8 @@
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,
}
diff --git a/libgo/go/cmd/go/internal/workcmd/sync.go b/libgo/go/cmd/go/internal/workcmd/sync.go
index b0f61c5..7712eb6 100644
--- a/libgo/go/cmd/go/internal/workcmd/sync.go
+++ b/libgo/go/cmd/go/internal/workcmd/sync.go
@@ -33,6 +33,9 @@
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,
}
diff --git a/libgo/go/cmd/go/internal/workcmd/use.go b/libgo/go/cmd/go/internal/workcmd/use.go
index 1ee2d4e..e20041f 100644
--- a/libgo/go/cmd/go/internal/workcmd/use.go
+++ b/libgo/go/cmd/go/internal/workcmd/use.go
@@ -20,7 +20,7 @@
)
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 @@
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 @@
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 @@
// 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)
}
diff --git a/libgo/go/cmd/go/internal/workcmd/work.go b/libgo/go/cmd/go/internal/workcmd/work.go
index d3cc250..39c81e8 100644
--- a/libgo/go/cmd/go/internal/workcmd/work.go
+++ b/libgo/go/cmd/go/internal/workcmd/work.go
@@ -12,7 +12,7 @@
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 @@
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
diff --git a/libgo/go/cmd/go/testdata/script/run_work_versioned.txt b/libgo/go/cmd/go/testdata/script/run_work_versioned.txt
new file mode 100644
index 0000000..eb0f22d
--- /dev/null
+++ b/libgo/go/cmd/go/testdata/script/run_work_versioned.txt
@@ -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
diff --git a/libgo/go/cmd/go/testdata/script/test_fuzz_minimize_dirty_cov.txt b/libgo/go/cmd/go/testdata/script/test_fuzz_minimize_dirty_cov.txt
new file mode 100644
index 0000000..571bf75
--- /dev/null
+++ b/libgo/go/cmd/go/testdata/script/test_fuzz_minimize_dirty_cov.txt
@@ -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)
+}
diff --git a/libgo/go/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt b/libgo/go/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt
index 5d0de17..a09e85b 100644
--- a/libgo/go/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt
+++ b/libgo/go/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt
@@ -127,19 +127,8 @@
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
diff --git a/libgo/go/cmd/go/testdata/script/version_buildvcs_git.txt b/libgo/go/cmd/go/testdata/script/version_buildvcs_git.txt
index 86d1de0..4470687 100644
--- a/libgo/go/cmd/go/testdata/script/version_buildvcs_git.txt
+++ b/libgo/go/cmd/go/testdata/script/version_buildvcs_git.txt
@@ -111,7 +111,7 @@
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
diff --git a/libgo/go/cmd/go/testdata/script/work.txt b/libgo/go/cmd/go/testdata/script/work.txt
index a10bf5a..fa1558f 100644
--- a/libgo/go/cmd/go/testdata/script/work.txt
+++ b/libgo/go/cmd/go/testdata/script/work.txt
@@ -4,7 +4,7 @@
! 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 @@
../src/a
)
-- go.work.want --
-go 1.18
+go $goversion
use (
./a
diff --git a/libgo/go/cmd/go/testdata/script/work_edit.txt b/libgo/go/cmd/go/testdata/script/work_edit.txt
index 71959ca..278afb7 100644
--- a/libgo/go/cmd/go/testdata/script/work_edit.txt
+++ b/libgo/go/cmd/go/testdata/script/work_edit.txt
@@ -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 @@
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
diff --git a/libgo/go/cmd/go/testdata/script/work_init_path.txt b/libgo/go/cmd/go/testdata/script/work_init_path.txt
new file mode 100644
index 0000000..e397788
--- /dev/null
+++ b/libgo/go/cmd/go/testdata/script/work_init_path.txt
@@ -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
diff --git a/libgo/go/cmd/go/testdata/script/work_use.txt b/libgo/go/cmd/go/testdata/script/work_use.txt
index f5ea89c..12c8cec 100644
--- a/libgo/go/cmd/go/testdata/script/work_use.txt
+++ b/libgo/go/cmd/go/testdata/script/work_use.txt
@@ -14,16 +14,16 @@
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
diff --git a/libgo/go/cmd/go/testdata/script/work_use_deleted.txt b/libgo/go/cmd/go/testdata/script/work_use_deleted.txt
index 660eb56..b379cbc 100644
--- a/libgo/go/cmd/go/testdata/script/work_use_deleted.txt
+++ b/libgo/go/cmd/go/testdata/script/work_use_deleted.txt
@@ -6,13 +6,13 @@
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
diff --git a/libgo/go/cmd/go/testdata/script/work_use_dot.txt b/libgo/go/cmd/go/testdata/script/work_use_dot.txt
index ccd83d6..8f21042 100644
--- a/libgo/go/cmd/go/testdata/script/work_use_dot.txt
+++ b/libgo/go/cmd/go/testdata/script/work_use_dot.txt
@@ -31,7 +31,7 @@
# 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.work.rel --
go 1.18
-use bar/baz
+use ./bar/baz
-- bar/baz/go.mod --
module example/bar/baz
go 1.18
diff --git a/libgo/go/cmd/go/testdata/script/work_use_noargs.txt b/libgo/go/cmd/go/testdata/script/work_use_noargs.txt
new file mode 100644
index 0000000..ca05434
--- /dev/null
+++ b/libgo/go/cmd/go/testdata/script/work_use_noargs.txt
@@ -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
diff --git a/libgo/go/encoding/xml/marshal.go b/libgo/go/encoding/xml/marshal.go
index 6859be0..7792ac7 100644
--- a/libgo/go/encoding/xml/marshal.go
+++ b/libgo/go/encoding/xml/marshal.go
@@ -512,7 +512,7 @@
}
fv := finfo.value(val, dontInitNilPointers)
- if finfo.flags&fOmitEmpty != 0 && isEmptyValue(fv) {
+ if finfo.flags&fOmitEmpty != 0 && (!fv.IsValid() || isEmptyValue(fv)) {
continue
}
diff --git a/libgo/go/encoding/xml/marshal_test.go b/libgo/go/encoding/xml/marshal_test.go
index 115a93f..0b6965d 100644
--- a/libgo/go/encoding/xml/marshal_test.go
+++ b/libgo/go/encoding/xml/marshal_test.go
@@ -2499,3 +2499,39 @@
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)
+ }
+}
diff --git a/libgo/go/go/internal/gcimporter/iimport.go b/libgo/go/go/internal/gcimporter/iimport.go
index 8ec4c54..bff1c09 100644
--- a/libgo/go/go/internal/gcimporter/iimport.go
+++ b/libgo/go/go/internal/gcimporter/iimport.go
@@ -181,6 +181,15 @@
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 @@
return localpkg, nil
}
+type setConstraintArgs struct {
+ t *types.TypeParam
+ constraint types.Type
+}
+
type iimporter struct {
exportVersion int64
ipath string
@@ -211,6 +225,9 @@
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 @@
}
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()
diff --git a/libgo/go/go/printer/nodes.go b/libgo/go/go/printer/nodes.go
index f2170db..9a09d58 100644
--- a/libgo/go/go/printer/nodes.go
+++ b/libgo/go/go/printer/nodes.go
@@ -319,9 +319,17 @@
}
}
-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 @@
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 (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 @@
p.expr(stripParensAlways(res.List[0].Type))
return
}
- p.parameters(res, false)
+ p.parameters(res, funcParam)
}
}
@@ -1639,7 +1647,7 @@
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 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)
diff --git a/libgo/go/go/printer/testdata/generics.golden b/libgo/go/go/printer/testdata/generics.golden
index 4fac2c9..c3a7df8 100644
--- a/libgo/go/go/printer/testdata/generics.golden
+++ b/libgo/go/go/printer/testdata/generics.golden
@@ -64,3 +64,29 @@
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,
+]() {
+}
diff --git a/libgo/go/go/printer/testdata/generics.input b/libgo/go/go/printer/testdata/generics.input
index fde9d32..66e1554 100644
--- a/libgo/go/go/printer/testdata/generics.input
+++ b/libgo/go/go/printer/testdata/generics.input
@@ -61,3 +61,28 @@
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,
+]() {}
diff --git a/libgo/go/go/types/api.go b/libgo/go/go/types/api.go
index e5ae240..248db18 100644
--- a/libgo/go/go/types/api.go
+++ b/libgo/go/go/types/api.go
@@ -201,12 +201,12 @@
// 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 @@
}
// 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 @@
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.
diff --git a/libgo/go/go/types/api_test.go b/libgo/go/go/types/api_test.go
index b67af8c..5003ce2 100644
--- a/libgo/go/go/types/api_test.go
+++ b/libgo/go/go/types/api_test.go
@@ -16,6 +16,7 @@
"internal/testenv"
"reflect"
"regexp"
+ "sort"
"strings"
"testing"
@@ -435,129 +436,146 @@
}
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 @@
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 @@
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*/
diff --git a/libgo/go/go/types/assignments.go b/libgo/go/go/types/assignments.go
index f75b8b6..f5e22c2 100644
--- a/libgo/go/go/types/assignments.go
+++ b/libgo/go/go/types/assignments.go
@@ -290,15 +290,14 @@
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]
diff --git a/libgo/go/go/types/call.go b/libgo/go/go/types/call.go
index 3dab284..5d1f60d 100644
--- a/libgo/go/go/types/call.go
+++ b/libgo/go/go/types/call.go
@@ -429,7 +429,7 @@
"_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 @@
}
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
}
diff --git a/libgo/go/go/types/check.go b/libgo/go/go/types/check.go
index 6e1da04..2313637 100644
--- a/libgo/go/go/types/check.go
+++ b/libgo/go/go/types/check.go
@@ -133,7 +133,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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) {
diff --git a/libgo/go/go/types/conversions.go b/libgo/go/go/types/conversions.go
index 8474135..c5a69cd 100644
--- a/libgo/go/go/types/conversions.go
+++ b/libgo/go/go/types/conversions.go
@@ -48,11 +48,14 @@
// 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
diff --git a/libgo/go/go/types/decl.go b/libgo/go/go/types/decl.go
index cd6f709..93a37d7 100644
--- a/libgo/go/go/types/decl.go
+++ b/libgo/go/go/types/decl.go
@@ -624,7 +624,6 @@
}()
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 @@
} else {
bound = Typ[Invalid]
}
- bounds = append(bounds, bound)
for i := range f.Names {
tparams[index+i].bound = bound
}
diff --git a/libgo/go/go/types/errorcodes.go b/libgo/go/go/types/errorcodes.go
index a7514b3..64cf24c 100644
--- a/libgo/go/go/types/errorcodes.go
+++ b/libgo/go/go/types/errorcodes.go
@@ -1339,11 +1339,6 @@
// func _() {
// f()
// }
- //
- // Example:
- // type N[P, Q any] struct{}
- //
- // var _ N[int]
_CannotInferTypeArgs
// _InvalidTypeArg occurs when a type argument does not satisfy its
diff --git a/libgo/go/go/types/eval.go b/libgo/go/go/types/eval.go
index c8bb005..5700cbf 100644
--- a/libgo/go/go/types/eval.go
+++ b/libgo/go/go/types/eval.go
@@ -37,8 +37,8 @@
// 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,
diff --git a/libgo/go/go/types/expr.go b/libgo/go/go/types/expr.go
index 8747838..e24bd60 100644
--- a/libgo/go/go/types/expr.go
+++ b/libgo/go/go/types/expr.go
@@ -859,7 +859,7 @@
}
// 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 @@
// 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 @@
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 @@
case invalid:
goto Error
case typexpr:
+ check.validVarType(e.X, x.typ)
x.typ = &Pointer{base: x.typ}
default:
var base Type
diff --git a/libgo/go/go/types/index.go b/libgo/go/go/types/index.go
index eac6017..33075ed 100644
--- a/libgo/go/go/types/index.go
+++ b/libgo/go/go/types/index.go
@@ -183,6 +183,7 @@
}
if !valid {
+ // types2 uses the position of '[' for the error
check.invalidOp(x, _NonIndexableOperand, "cannot index %s", x)
x.mode = invalid
return false
diff --git a/libgo/go/go/types/infer.go b/libgo/go/go/types/infer.go
index 8f22144..6bed55c 100644
--- a/libgo/go/go/types/infer.go
+++ b/libgo/go/go/types/infer.go
@@ -487,21 +487,88 @@
}
}
- // 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 @@
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 @@
// 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)
}
diff --git a/libgo/go/go/types/instantiate.go b/libgo/go/go/types/instantiate.go
index 4aeaeb7..a481746 100644
--- a/libgo/go/go/types/instantiate.go
+++ b/libgo/go/go/types/instantiate.go
@@ -15,10 +15,10 @@
// 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 @@
// 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)
}
diff --git a/libgo/go/go/types/interface.go b/libgo/go/go/types/interface.go
index b9d4660..3db3580 100644
--- a/libgo/go/go/types/interface.go
+++ b/libgo/go/go/types/interface.go
@@ -56,7 +56,7 @@
}
// 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 @@
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 @@
// ----------------------------------------------------------------------------
// 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 @@
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)
}
diff --git a/libgo/go/go/types/lookup.go b/libgo/go/go/types/lookup.go
index 501c230..335fada 100644
--- a/libgo/go/go/types/lookup.go
+++ b/libgo/go/go/types/lookup.go
@@ -70,7 +70,8 @@
// 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 {
diff --git a/libgo/go/go/types/named.go b/libgo/go/go/types/named.go
index 5e84c39..876f7e8 100644
--- a/libgo/go/go/types/named.go
+++ b/libgo/go/go/types/named.go
@@ -72,18 +72,38 @@
}
// 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 @@
// 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 @@
// 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 @@
// 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
diff --git a/libgo/go/go/types/predicates.go b/libgo/go/go/types/predicates.go
index 14e99bf..0360f27 100644
--- a/libgo/go/go/types/predicates.go
+++ b/libgo/go/go/types/predicates.go
@@ -104,11 +104,12 @@
// 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 @@
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 @@
}
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 @@
}
return true
case *Interface:
- return !isTypeParam(T) || t.typeSet().IsComparable(seen)
+ return dynamic && !isTypeParam(T) || t.typeSet().IsComparable(seen)
}
return false
}
diff --git a/libgo/go/go/types/signature.go b/libgo/go/go/types/signature.go
index 8f89e93..a340ac7 100644
--- a/libgo/go/go/types/signature.go
+++ b/libgo/go/go/types/signature.go
@@ -112,7 +112,8 @@
// - 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 @@
// 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 @@
}
}
// 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])
+ 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)
}
- 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)
- }
+ } 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 @@
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)
}
- 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)
+ }
}
- 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...)
diff --git a/libgo/go/go/types/stmt.go b/libgo/go/go/types/stmt.go
index a5aee48..9ebfbb6 100644
--- a/libgo/go/go/types/stmt.go
+++ b/libgo/go/go/types/stmt.go
@@ -821,8 +821,6 @@
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 @@
}
}
+ // 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 @@
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 @@
// 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 {
diff --git a/libgo/go/go/types/subst.go b/libgo/go/go/types/subst.go
index 53247a3..4b4a0f4 100644
--- a/libgo/go/go/types/subst.go
+++ b/libgo/go/go/types/subst.go
@@ -160,7 +160,10 @@
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.
diff --git a/libgo/go/go/types/termlist.go b/libgo/go/go/types/termlist.go
index c4ab0e0..94e49ca 100644
--- a/libgo/go/go/types/termlist.go
+++ b/libgo/go/go/types/termlist.go
@@ -92,15 +92,6 @@
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()
diff --git a/libgo/go/go/types/termlist_test.go b/libgo/go/go/types/termlist_test.go
index dddca7a..f0d58ac 100644
--- a/libgo/go/go/types/termlist_test.go
+++ b/libgo/go/go/types/termlist_test.go
@@ -106,35 +106,6 @@
}
}
-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
diff --git a/libgo/go/go/types/testdata/examples/inference.go2 b/libgo/go/go/types/testdata/examples/inference.go2
index 70d393b..e59a544 100644
--- a/libgo/go/go/types/testdata/examples/inference.go2
+++ b/libgo/go/go/types/testdata/examples/inference.go2
@@ -78,7 +78,7 @@
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 @@
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]()
}
diff --git a/libgo/go/go/types/testdata/examples/methods.go2 b/libgo/go/go/types/testdata/examples/methods.go2
index 1d76d55..a46f789 100644
--- a/libgo/go/go/types/testdata/examples/methods.go2
+++ b/libgo/go/go/types/testdata/examples/methods.go2
@@ -35,7 +35,7 @@
// 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.
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue41124.go2 b/libgo/go/go/types/testdata/fixedbugs/issue41124.go2
index 7f55ba8..4550dd7 100644
--- a/libgo/go/go/types/testdata/fixedbugs/issue41124.go2
+++ b/libgo/go/go/types/testdata/fixedbugs/issue41124.go2
@@ -47,7 +47,7 @@
}
type _ struct{
- I3 // ERROR interface is .* comparable
+ I3 // ERROR interface contains type constraints
}
// General composite types.
@@ -59,19 +59,19 @@
_ []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{}
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue45548.go2 b/libgo/go/go/types/testdata/fixedbugs/issue45548.go2
index b8ba0ad..01c9672 100644
--- a/libgo/go/go/types/testdata/fixedbugs/issue45548.go2
+++ b/libgo/go/go/types/testdata/fixedbugs/issue45548.go2
@@ -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)
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue51229.go2 b/libgo/go/go/types/testdata/fixedbugs/issue51229.go2
new file mode 100644
index 0000000..808b647
--- /dev/null
+++ b/libgo/go/go/types/testdata/fixedbugs/issue51229.go2
@@ -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)
+}
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue51232.go2 b/libgo/go/go/types/testdata/fixedbugs/issue51232.go2
new file mode 100644
index 0000000..3fa6a05
--- /dev/null
+++ b/libgo/go/go/types/testdata/fixedbugs/issue51232.go2
@@ -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,
+ }
+}
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue51233.go2 b/libgo/go/go/types/testdata/fixedbugs/issue51233.go2
new file mode 100644
index 0000000..9c15028
--- /dev/null
+++ b/libgo/go/go/types/testdata/fixedbugs/issue51233.go2
@@ -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()
+}
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue51257.go2 b/libgo/go/go/types/testdata/fixedbugs/issue51257.go2
new file mode 100644
index 0000000..8a3eb32
--- /dev/null
+++ b/libgo/go/go/types/testdata/fixedbugs/issue51257.go2
@@ -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},
+ })
+}
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue51335.go2 b/libgo/go/go/types/testdata/fixedbugs/issue51335.go2
new file mode 100644
index 0000000..0b5a1af
--- /dev/null
+++ b/libgo/go/go/types/testdata/fixedbugs/issue51335.go2
@@ -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 */ }}
+}
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue51339.go2 b/libgo/go/go/types/testdata/fixedbugs/issue51339.go2
new file mode 100644
index 0000000..38f8610
--- /dev/null
+++ b/libgo/go/go/types/testdata/fixedbugs/issue51339.go2
@@ -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() {}
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue51360.go b/libgo/go/go/types/testdata/fixedbugs/issue51360.go
new file mode 100644
index 0000000..fe3de04
--- /dev/null
+++ b/libgo/go/go/types/testdata/fixedbugs/issue51360.go
@@ -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 */
+}
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue51376.go2 b/libgo/go/go/types/testdata/fixedbugs/issue51376.go2
new file mode 100644
index 0000000..d51607b
--- /dev/null
+++ b/libgo/go/go/types/testdata/fixedbugs/issue51376.go2
@@ -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
+}
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue51386.go2 b/libgo/go/go/types/testdata/fixedbugs/issue51386.go2
new file mode 100644
index 0000000..ef62239
--- /dev/null
+++ b/libgo/go/go/types/testdata/fixedbugs/issue51386.go2
@@ -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("")
+}
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue51437.go b/libgo/go/go/types/testdata/fixedbugs/issue51437.go
new file mode 100644
index 0000000..3762615
--- /dev/null
+++ b/libgo/go/go/types/testdata/fixedbugs/issue51437.go
@@ -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
+ }
+}
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue51472.go2 b/libgo/go/go/types/testdata/fixedbugs/issue51472.go2
new file mode 100644
index 0000000..3126770
--- /dev/null
+++ b/libgo/go/go/types/testdata/fixedbugs/issue51472.go2
@@ -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]
+}
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue51509.go b/libgo/go/go/types/testdata/fixedbugs/issue51509.go
new file mode 100644
index 0000000..5ae4717
--- /dev/null
+++ b/libgo/go/go/types/testdata/fixedbugs/issue51509.go
@@ -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
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue51578.go2 b/libgo/go/go/types/testdata/fixedbugs/issue51578.go2
new file mode 100644
index 0000000..5c204ba
--- /dev/null
+++ b/libgo/go/go/types/testdata/fixedbugs/issue51578.go2
@@ -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) {}
\ No newline at end of file
diff --git a/libgo/go/go/types/testdata/fixedbugs/issue51593.go2 b/libgo/go/go/types/testdata/fixedbugs/issue51593.go2
new file mode 100644
index 0000000..e06c39f
--- /dev/null
+++ b/libgo/go/go/types/testdata/fixedbugs/issue51593.go2
@@ -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
+}
diff --git a/libgo/go/go/types/type.go b/libgo/go/go/types/type.go
index 323365a..1306375 100644
--- a/libgo/go/go/types/type.go
+++ b/libgo/go/go/types/type.go
@@ -7,9 +7,7 @@
// 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.
diff --git a/libgo/go/go/types/typeparam.go b/libgo/go/go/types/typeparam.go
index 71e6861..40d96ac 100644
--- a/libgo/go/go/types/typeparam.go
+++ b/libgo/go/go/types/typeparam.go
@@ -30,11 +30,13 @@
// 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 @@
// 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 @@
// 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 @@
// ----------------------------------------------------------------------------
// 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 @@
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).
diff --git a/libgo/go/go/types/typeset.go b/libgo/go/go/types/typeset.go
index e1f7301..6603383 100644
--- a/libgo/go/go/types/typeset.go
+++ b/libgo/go/go/types/typeset.go
@@ -15,18 +15,25 @@
// 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 @@
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 @@
// 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 @@
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 @@
}
// 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 @@
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 @@
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 @@
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 @@
}
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 @@
}
}
- 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))
}
diff --git a/libgo/go/go/types/typeset_test.go b/libgo/go/go/types/typeset_test.go
index 1c0eece..2bbe611 100644
--- a/libgo/go/go/types/typeset_test.go
+++ b/libgo/go/go/types/typeset_test.go
@@ -26,9 +26,9 @@
"{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 @@
"{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}": "∅",
diff --git a/libgo/go/go/types/typexpr.go b/libgo/go/go/types/typexpr.go
index db6a904..5bb2d8f 100644
--- a/libgo/go/go/types/typexpr.go
+++ b/libgo/go/go/types/typexpr.go
@@ -144,10 +144,16 @@
// 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 @@
}
}
})
-
- return typ
}
// definedType is like typ but also accepts a type name def.
@@ -254,7 +258,7 @@
case *ast.SelectorExpr:
var x operand
- check.selector(&x, e)
+ check.selector(&x, e, def)
switch x.mode {
case typexpr:
@@ -323,7 +327,7 @@
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 @@
// 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 @@
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 @@
// 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 {
diff --git a/libgo/go/go/types/unify.go b/libgo/go/go/types/unify.go
index ac904d6..7b9aeee 100644
--- a/libgo/go/go/types/unify.go
+++ b/libgo/go/go/types/unify.go
@@ -247,6 +247,17 @@
}
}
+// 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 @@
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)
diff --git a/libgo/go/go/types/union.go b/libgo/go/go/types/union.go
index 9c59279..8397d65 100644
--- a/libgo/go/go/types/union.go
+++ b/libgo/go/go/types/union.go
@@ -103,25 +103,27 @@
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
diff --git a/libgo/go/go/types/universe.go b/libgo/go/go/types/universe.go
index 3421634..303ada4 100644
--- a/libgo/go/go/types/universe.go
+++ b/libgo/go/go/types/universe.go
@@ -112,7 +112,7 @@
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)
diff --git a/libgo/go/go/types/validtype.go b/libgo/go/go/types/validtype.go
index c4ec2f2..7d7029b 100644
--- a/libgo/go/go/types/validtype.go
+++ b/libgo/go/go/types/validtype.go
@@ -79,7 +79,7 @@
// 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 {
diff --git a/libgo/go/internal/fuzz/coverage.go b/libgo/go/internal/fuzz/coverage.go
index 3dee73b..88f98a1 100644
--- a/libgo/go/internal/fuzz/coverage.go
+++ b/libgo/go/internal/fuzz/coverage.go
@@ -66,6 +66,17 @@
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 {
diff --git a/libgo/go/internal/fuzz/encoding.go b/libgo/go/internal/fuzz/encoding.go
index 2bfa02b..c95d9e0 100644
--- a/libgo/go/internal/fuzz/encoding.go
+++ b/libgo/go/internal/fuzz/encoding.go
@@ -10,7 +10,9 @@
"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 @@
// 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 @@
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 @@
}
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 @@
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 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 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")
}
diff --git a/libgo/go/internal/fuzz/encoding_test.go b/libgo/go/internal/fuzz/encoding_test.go
index b429d42..8e3800e 100644
--- a/libgo/go/internal/fuzz/encoding_test.go
+++ b/libgo/go/internal/fuzz/encoding_test.go
@@ -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 @@
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('�')
+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 @@
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 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)
+ }
+ })
+}
diff --git a/libgo/go/internal/fuzz/worker.go b/libgo/go/internal/fuzz/worker.go
index e984ba7..6e4c4e2 100644
--- a/libgo/go/internal/fuzz/worker.go
+++ b/libgo/go/internal/fuzz/worker.go
@@ -800,6 +800,7 @@
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 @@
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 @@
}
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
diff --git a/libgo/go/net/net.go b/libgo/go/net/net.go
index 77e54a9..d91e743 100644
--- a/libgo/go/net/net.go
+++ b/libgo/go/net/net.go
@@ -703,6 +703,12 @@
_ 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 @@
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])
diff --git a/libgo/go/runtime/mfinal_test.go b/libgo/go/runtime/mfinal_test.go
index 81c924f..2eb60b9 100644
--- a/libgo/go/runtime/mfinal_test.go
+++ b/libgo/go/runtime/mfinal_test.go
@@ -45,6 +45,15 @@
{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 {
diff --git a/libgo/go/runtime/testdata/testprogcgo/aprof.go b/libgo/go/runtime/testdata/testprogcgo/aprof.go
index c70d633..1687014 100644
--- a/libgo/go/runtime/testdata/testprogcgo/aprof.go
+++ b/libgo/go/runtime/testdata/testprogcgo/aprof.go
@@ -10,7 +10,7 @@
// 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();
diff --git a/libgo/go/strings/builder.go b/libgo/go/strings/builder.go
index 547e52e..ba4df61 100644
--- a/libgo/go/strings/builder.go
+++ b/libgo/go/strings/builder.go
@@ -17,10 +17,9 @@
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
diff --git a/libgo/go/syscall/syscall_unix_test.go b/libgo/go/syscall/syscall_unix_test.go
index 8bfbc93..9fed7c5 100644
--- a/libgo/go/syscall/syscall_unix_test.go
+++ b/libgo/go/syscall/syscall_unix_test.go
@@ -328,33 +328,6 @@
}
}
-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 {
diff --git a/libgo/merge.sh b/libgo/merge.sh
index 2ad0fe9..cd2510b 100755
--- a/libgo/merge.sh
+++ b/libgo/merge.sh
@@ -198,7 +198,7 @@
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
diff --git a/libgo/misc/cgo/testsanitizers/asan_test.go b/libgo/misc/cgo/testsanitizers/asan_test.go
index 1b70bce..22dcf23 100644
--- a/libgo/misc/cgo/testsanitizers/asan_test.go
+++ b/libgo/misc/cgo/testsanitizers/asan_test.go
@@ -63,7 +63,7 @@
// 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) &&