[dev.go2go] all: merge dev.typeparams (dc122c7) into dev.go2go
This merge resolved all conflicts by taking the dev.typeparams copy. The
only additional changes were to delete exprstring.go,
exprstring_test.go, methodset.go, and walk.go, which had been deleted
from dev.typeparams but were not present in the merge base.
go/build/deps_test.go was updated to allow go2go dependencies.
Change-Id: I2d6da3c1e44a4da888ee71165d5cb3702fec5f88
diff --git a/README.go2go.md b/README.go2go.md
new file mode 100644
index 0000000..6930889
--- /dev/null
+++ b/README.go2go.md
@@ -0,0 +1,36 @@
+# dev.go2go branch
+
+This branch contains a type checker and a translation tool for
+experimentation with generics in Go.
+This implements the [generics design draft](https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md).
+
+You can build this branch [as
+usual](https://golang.org/doc/install/source).
+You can then use the new `go2go` tool.
+Write your generic code in a file with the extension `.go2` instead of
+`.go`.
+Run it using `go tool go2go run x.go2`.
+There are some sample packages in `cmd/go2go/testdata/go2path/src`.
+You can see the full documentation for the tool by running `go doc
+cmd/go2go`.
+
+The `go2go` tool will look for `.go2` files using the environment variable `GO2PATH`.
+You can find some useful packages that you might want to experiment with by
+setting `GO2PATH=$GOROOT/src/cmd/go2go/testdata/go2path`.
+
+If you find bugs in the updated type checker or in the translation
+tool, they should be filed in the [standard Go issue
+tracker](https://golang.org/issue).
+Please start the issue title with `cmd/go2go`.
+Note that the issue tracker should only be used for reporting bugs in
+the tools, not for discussion of changes to the language.
+
+Unlike typical dev branches, we do not intend any eventual merge of
+this code into the master branch.
+We will update it for bug fixes and change to the generic design
+draft.
+If a generics language proposal is accepted, it will be implemented in
+the standard compiler, not via a translation tool.
+The necessary changes to the go/* packages (go/ast, go/parser, and so
+forth) may re-use code from this prototype but will follow the usual
+code review process.
diff --git a/src/cmd/compile/internal/types2/NOTES b/src/cmd/compile/internal/types2/NOTES
new file mode 100644
index 0000000..446b6b6
--- /dev/null
+++ b/src/cmd/compile/internal/types2/NOTES
@@ -0,0 +1,183 @@
+This file serves as a notebook/implementation log.
+
+----------------------------------------------------------------------------------------------------
+TODO (implementation issues)
+
+- report a better error when a type is not in a type list (investigate)
+- better error message when we need parentheses around a parameterized function parameter type
+- review handling of fields of instantiated generic types (do we need to make them non-parameterized,
+ similar to what we did for the embedded interfaces created by contract embedding?)
+- use []*TypeParam for tparams in subst? (unclear)
+- should we use nil instead of &emptyInterface for no type bounds (as an optimization)?
+- TBD: in prose, should we use "generic" or "parameterized" (try to be consistent)
+
+----------------------------------------------------------------------------------------------------
+KNOWN ISSUES
+
+- type parameter constraints are ignored when checking if a parameterized method implements the
+ matching method in an interface
+- iteration over generic variables doesn't report certain channel errors (see TODOs in code)
+- cannot handle mutually recursive parameterized interfaces using themselves as type bounds
+ example: type B(type P B(P)) interface{ m() } (need to delay all checking after seting up declarations)
+- invoking a method of a parameterized embedded type doesn't work (cannot properly determine receiver yet)
+- pointer designation is incorrectly handled when checking type list constraint satisfaction
+
+----------------------------------------------------------------------------------------------------
+OBSERVATIONS
+
+- 2/20/2020: Because we permit parenthesized types anywhere for consistency, also in parameter lists (mea
+ culpa), we have parsing ambiguities when using instantiated types in parameter lists w/o argument names.
+ We could disallow the use of parentheses at the top level of type literals and then we might not have
+ this problem. This is not a backward-compatible change but perhaps worthwhile investigating. Specifically,
+ will this always work (look specifically at channel types where we need parentheses for disambiguation
+ and possibly function types). File a proposal?
+
+- 6/3/2020: Observation: gofmt already removes superflous parentheses around types in parameter lists,
+ so in gofmt'ed code there won't be any such parentheses. Thus we can perhaps make the suggested language
+ change above without too many problems.
+
+- 2/21/2020: We do need top-level parentheses around types in certain situations such as conversions
+ or composite literals. We could disallow parentheses around types in parameter lists only, but that
+ seems quite a bit less elegant.
+
+- 6/13/2020: When comparing against the type list of an interface (to see if the interface is satisfied),
+ we must either recompute the underlying types of the type list entries after the interface was instantiated,
+ or we must compute the underlying type of each entry before comparing. (A type list may contain type
+ parameters which may be substituted with defined types when the interface is instantiated.) With the
+ latter approach (which is what's implemented now), we could relax some of the constraints that we have
+ on type lists entries: We could allow any type and just say that for interface satisfaction we always
+ look at the underlying types.
+
+----------------------------------------------------------------------------------------------------
+OPEN QUESTIONS
+
+- Parsing _ = [](a(int)){} requires parentheses around `(a(int))` - should the parser be smarter in
+ these cases? Another example: []a(b, c){} This cannot be a conversion. Could fix such cases by re-
+ associating the AST when we see a {. Need to be careful, and need to take into account additional
+ complexity of spec.
+- For len/cap(x) where x is of type parameter type and the bound contains arrays only, should the
+ result be a constant? (right now it is not). What are the implications for alternative, non-
+ monomorphizing implementation methods?
+- Confirm that it's ok to use inference in missingMethod to compare parameterized methods.
+
+----------------------------------------------------------------------------------------------------
+DESIGN/IMPLEMENTATION
+
+- 11/19/2019: For type parameters with interface bounds to work, the scope of all type parameters in
+ a type parameter list starts at the "type" keyword. This makes all type parameters visible for all
+ type parameter bounds (interfaces that may be parameterized with the type parameters).
+
+- 12/4/2019: do not allow parenthesized generic uninstantiated types (unless instantiated implicitly)
+ In other words: generic types must always be instantiated before they can be used in any form
+ More generally: Only permit type instantiation T(x) in type context, when the type is a named type.
+ Do not permit it in general in type context: e.g., disallow []T(x) because we consider that a
+ conversion, in general. Same for ([]T)(x).
+
+- 12/12/2019: represent type bounds always as (possibly unnamed) interfaces
+ (contracts are user syntactic sugar)
+
+- 12/19/2019: Type parameters don't act like type aliases. For instance:
+
+ func f(type T1, T2)(x T1) T2 { return x }
+
+ is not valid, no matter how T1 and T2 are instantiated (but if T1 and T2 were type aliases with
+ both of them having type int, the return x would be valid). In fact, the type parameters act more
+ like named types with the methods described by their type bound. But type parameters are never
+ interfaces. To determine: Given a type parameter P, is P == underlying(P) (like for basic types),
+ or is the the underlying type of P something else (like for defined types). Is there an observable
+ difference?
+
+- 12/19/2019: Rewrote contract handling: they are now treated as Objects (rather than Types) throughout.
+
+- 12/20/2019: Decided to start moving type parameters to types (from TypeName to Named), need to do the
+ same for Func. This make more sense as in general any type (conceptually even literal types) could
+ have type parameters. It's a property of the type, not the type name. It also simplified the code.
+
+- 12/20/2019: Type parameters may be part of type lists in contracts/interfaces. It just falls out
+ naturally. Added test cases.
+
+- 12/23/2019: Decision: Type parameters and ordinary (value) parameters are in the same block, notably
+ the function block. The scope of type parameters starts at the 'type' keyword; the scope of ordinary
+ parameters starts with the (opening '{' of the) function body. Both scopes end with the closing '}'
+ of the function body (i.e., the end of the function block).
+
+- 1/2/2020: Implementation decision: contracts can only be declared at the package level.
+
+- 1/6/2020: Experimental: First steps towards permitting type parameters in methods as a generalization.
+ Type-checking problems ooccurring from this are likely to highlight general problematic areas.
+ First consequence: Scope of type parameters starts at "func" keyword which means that receiver type
+ name cannot be a type parameter name declared later (or by the receiver type specification). This
+ seems reasonable and should help avoid confusion which is possible otherwise.
+
+- 1/7/2020: We distinguish embedded instantiated (parameterized) interfaces from methods by enclosing
+ the embedded interfaces in parentheses (the design draft recommends this change). Since this opens
+ the possibility for any parenthesized type (that is an interface), we can also allow (parenthesized)
+ interface literals as it is simpler to permit those than forbid them.
+
+- 1/7/2020: The current implementation permits empty type parameter lists as in: "func f(type)(x int)"
+ but we cannot call such a function as "f()(1)"; the empty type argument list causes problems.
+ Document that we allow empty type parameter list declarations, but not empty actual type parameter
+ lists. (We could allow them for types, but that doesn't seem consistent and probably is confusing).
+
+- 2/19/2020: We accept parenthesized embedded struct fields so we can distinguish between a named
+ field with a parenthesized type foo (T) and an embedded parameterized type (foo(T)), similarly
+ to interace embedding.
+
+- 2/19/2020: Permit parentheses around embedded contracts for symmetry with embedding in structs
+ and interfaces.
+
+- 2/20/2020: Receiver type parameters must always be provided in the receiver parameter list of
+ a method, even if they are not used by the method. Since the receiver acts like an implicit
+ declaration of those type parameters, they may be blank, as with any other declaration.
+
+- 3/20/2020: Local type declarations with an underlying type that is a type parameter lose the
+ methods declared with the type parameter bound. But they don't lose the properties of the
+ underlying type, i.e., the properties of the type parameter bound's type list.
+ This is something to consider if we were contemplating moving to a methods-only approach
+ (no type lists), even though local type declarations are exceedingly rare if they exist at
+ all in the wild.
+
+- 3/24/2020: Implemented initial support for bidirection type unification which could make
+ type inference more powerful if we decided to go that route. Specifically, this would
+ permit type inference for the type parameters of a generic function argument. Given:
+ func h(f func(int)); func g(type T)(T); one could allow the call: h(g) and the type argument
+ T of g would be inferred to be int. While not hard to implement, this would be a special case
+ of the rule that all generic types/functions must be instantiated before they are used except
+ for function calls where the type arguments can be inferred from the actual arguments.
+ Decided that for now we leave things as is, since it's not clear the extra complexity is
+ worth the (probably small) convenience.
+
+- 3/25/2020: We can probably simplify the contract syntax again and only permit one of three
+ possible constraint entries: 1) an embedded contract, 2) a type parameter followed by a
+ method signature, and 3) a type parameter followed by a type list. This is what the type
+ checker currently supports and the printer can print. (The parser still accepts a list of
+ method signatures or types, freely mixed.)
+
+- 4/24/2020: Permit omission of explicit type bound instantiation if the type bound is an
+ interface that applies to exactly one type parameter and the type bound expects exactly
+ one type argument. This makes parameterized interface type bounds easier to use in a common
+ case.
+
+- 5/?/2020: Relaxed above syntactic sugar and permit omission of explicit type bound instantiation
+ in any case where the type bound only accepts a single type parameter.
+ We may not want to permit this in the first version as this is something we can always add later.
+
+- 6/3/2020: A function type parameter acts like a named type. If its type bound has a type list
+ that type list determines the underlying type of the type parameter. If the bound doesn't have
+ a type list, the underlying type of a type parameter is itself. (We may decide that in this
+ latter case there may no underlying type at all which would perhaps more consistent. But this
+ is not clear cut.)
+
+- 6/6/2020: The underlying type of a type parameter must _always_ be itself, otherwise a declaration
+ such as: type T(type P interface{type int}) P would have an underlying type int which is wrong.
+
+- 6/7/2020: Introduced the notion of an operational type which is used when determining what
+ operations are supported by a value of a given type. The operational type of a type parameter
+ is determined by its type bound. The operational type of any other type is its underlying
+ type. This approach appears to complete the picture about the nature of type parameters.
+
+- 6/7/2020: Removed support for contracts to match the latest design draft.
+
+- 6/15/2020: Disabled check that types in type lists must only contain basic types or composite types
+ composed of basic types. It is not needed since we always must recompute the underlying types
+ after a (possible) instantiation anyway (see observation from 6/3/2020).
diff --git a/src/cmd/compile/internal/types2/README b/src/cmd/compile/internal/types2/README
new file mode 100644
index 0000000..8a0c56a
--- /dev/null
+++ b/src/cmd/compile/internal/types2/README
@@ -0,0 +1,68 @@
+This version of go/types is part of the prototype that implements the
+extended Go language as outlined by the latest generics draft design.
+
+DISCLAIMER
+
+This code is based on the std/library's go/types package but it has
+been modified heavily. Please bear in mind that:
+
+- This is a prototype. Expect bugs, incomplete parts, and suboptimal
+ and (gulp) possibly ugly code.
+
+- While all the major aspects of the generics design draft have been
+ implemented, parts are missing, especially around the operations permitted
+ on operands of generic types. In such cases, the result is most often
+ an error reporting that a certain operation is not permitted even though
+ it should be according to the draft design.
+
+- As prototyping continues, expect refinement and possibly divergence
+ from the most recent draft design published.
+
+EXAMPLE CODE
+
+For tests and example code (with detailed comments) look for files
+with suffix .go2 in the testdata and examples subdirectories.
+
+(The suffix .go2 is solely to distinguish them from regular Go code and
+to prevent gofmt from touching them. We expect a proper implementation to
+keep using .go .)
+
+RUNNING THE TYPE-CHECKER
+
+The type-checker is automatically run as part of the go2go tool.
+To experiment with generic code, it is recommened to use that tool.
+
+But the type-checker can also be run stand-alone on source code if
+so desired. There are two primary ways:
+
+1) By building the gotype tool in this source directory:
+
+ go build gotype.go
+
+ and then running the resulting binary on some input files
+
+ ./gotype <package files>
+
+ (Make sure to use the correct go tool when building.)
+
+2) By using the type-checker's TestCheck test function which
+ accepts a list of user provided files:
+
+ go test -run Check$ -errlist -files <package files>
+
+ For instance:
+
+ go test -run Check$ -files examples/types.go2
+
+ TestCheck will match any type errors against ERROR comments
+ in the source files and complain about discrepancies.
+ To report the errors instead, use:
+
+ go test -run Check$ -errlist -files examples/types.go2
+
+ To see a trace of the type checker progress, use the -v flag:
+
+ go test -v -run Check$ -errlist -files examples/types.go2
+
+ (When playing with this code, sometimes go vet gets in the
+ way, so it can be helpful to also provide -vet off.)
diff --git a/src/cmd/go2go/doc.go b/src/cmd/go2go/doc.go
new file mode 100644
index 0000000..c78fd9d
--- /dev/null
+++ b/src/cmd/go2go/doc.go
@@ -0,0 +1,61 @@
+// Copyright 2020 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.
+
+// go2go is a command for trying out generic Go code.
+// It supports a small number of commands similar to cmd/go.
+//
+// Usage:
+//
+// go2go [options] <command> [arguments]
+//
+// Commands:
+//
+// build translate and then run "go build packages"
+// run translate and then run a list of files
+// test translate and then run "go test packages"
+// translate translate .go2 files into .go files for listed packages
+//
+// A package is expected to contain .go2 files but no .go files.
+//
+// Non-local imported packages will be first looked up using the GO2PATH
+// environment variable, which should point to a GOPATH-like directory.
+// For example, import "x" will first look for GO2PATHDIR/src/x,
+// for each colon-separated component in GO2PATH. If not found in GO2PATH,
+// imports will be looked up in the usual way. If an import includes
+// .go2 files, they will be translated into .go files.
+//
+// There is a sample GO2PATH in cmd/go2go/testdata/go2path. It provides
+// several packages that serve as examples of using generics, and may
+// be useful in experimenting with your own generic code.
+//
+// Translation into standard Go requires generating Go code with mangled names.
+// The mangled names will always include Odia (Oriya) digits, such as ୦ and ୮.
+// Do not use Oriya digits in identifiers in your own code.
+//
+// Because this tool generates Go files, and because instantiated types
+// and functions need to refer to the types with which they are instantiated,
+// using function-local types as type arguments is not supported.
+// Similarly, function-local parameterized types do not work.
+// These are deficiencies of the tool, they will work as expected in
+// any complete implementation.
+//
+// Similarly, generic function and type bodies that refer to unexported,
+// non-generic, names can't be instantiated by different packages.
+//
+// Because this tool generates Go files, and because it generates type
+// and function instantiations alongside other code in the package that
+// instantiates those functions and types, and because those instantiatations
+// may refer to names in packages imported by the original generic code,
+// this tool will add imports as necessary to support the instantiations.
+// Therefore, packages that use generic code must not use top level
+// definitions whose names are the same as the names of packages imported
+// by the generic code. For example, don't write, in package scope,
+//
+// var strings = []string{"a", "b"}
+//
+// because if the generic code imports "strings", the variable name will
+// conflict with the package name, even if your code doesn't import "strings".
+// This is a deficiency of the tool, it will not be a deficiency in
+// any complete implementation.
+package main
diff --git a/src/cmd/go2go/go2go_test.go b/src/cmd/go2go/go2go_test.go
new file mode 100644
index 0000000..296fa3e
--- /dev/null
+++ b/src/cmd/go2go/go2go_test.go
@@ -0,0 +1,323 @@
+// Copyright 2020 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 main_test
+
+import (
+ "fmt"
+ "internal/testenv"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "sort"
+ "strings"
+ "sync"
+ "testing"
+)
+
+func TestMain(m *testing.M) {
+ os.Exit(testMain(m))
+}
+
+func testMain(m *testing.M) int {
+ dir, err := ioutil.TempDir("", "go2gotest")
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ defer os.RemoveAll(dir)
+ testTempDir = dir
+
+ return m.Run()
+}
+
+// testTempDir is a temporary directory the tests can use.
+var testTempDir string
+
+// testGo2go is the version of cmd/go2go run by the tests.
+var testGo2go string
+
+// testGo2goOnce ensures that testGo2go is built only once.
+var testGo2goOnce sync.Once
+
+// testGo2goErr is an error that occurred when building testGo2go.
+// In the normal case this is nil.
+var testGo2goErr error
+
+// buildGo2go builds an up-to-date version of cmd/go2go.
+// This is not run from TestMain because it's simpler if it has a *testing.T.
+func buildGo2go(t *testing.T) {
+ t.Helper()
+ testenv.MustHaveGoBuild(t)
+ testGo2goOnce.Do(func() {
+ testGo2go = filepath.Join(testTempDir, "go2go.exe")
+ t.Logf("running [go build -o %s]", testGo2go)
+ out, err := exec.Command(testenv.GoToolPath(t), "build", "-o", testGo2go).CombinedOutput()
+ if len(out) > 0 {
+ t.Logf("%s", out)
+ }
+ testGo2goErr = err
+ })
+ if testGo2goErr != nil {
+ t.Fatal("failed to build testgo2go program:", testGo2goErr)
+ }
+}
+
+func TestGO2PATH(t *testing.T) {
+ t.Parallel()
+ buildGo2go(t)
+
+ copyFile := func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ t.Fatal(err)
+ }
+ newPath := filepath.Join(testTempDir, path)
+ if info.IsDir() {
+ if err := os.MkdirAll(newPath, 0o755); err != nil {
+ t.Fatal(err)
+ }
+ return nil
+ }
+ data, err := ioutil.ReadFile(path)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := ioutil.WriteFile(newPath, data, 0o444); err != nil {
+ t.Fatal(err)
+ }
+ return nil
+ }
+
+ if err := filepath.Walk("testdata/go2path/src", copyFile); err != nil {
+ t.Fatal(err)
+ }
+
+ d, err := os.Open(filepath.Join(testTempDir, "testdata/go2path/src"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer d.Close()
+ dirs, err := d.Readdirnames(-1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ sort.Strings(dirs)
+
+ for _, dir := range dirs {
+ t.Run(dir, func(t *testing.T) {
+ cmd := exec.Command(testGo2go, "test")
+ cmd.Dir = filepath.Join(testTempDir, "testdata", "go2path", "src", dir)
+ cmd.Env = append(os.Environ(), "GO2PATH="+filepath.Join(testTempDir, "testdata", "go2path"))
+ t.Logf("running [%s test] in %s", testGo2go, cmd.Dir)
+ out, err := cmd.CombinedOutput()
+ if len(out) > 0 {
+ t.Log(dir)
+ t.Logf("%s", out)
+ }
+ if err != nil {
+ t.Errorf("error testing %s: %v", dir, err)
+ }
+ })
+ }
+}
+
+type testFile struct {
+ name, contents string
+}
+
+type testFiles []testFile
+
+func (tfs testFiles) create(t *testing.T, gopath string) {
+ t.Helper()
+ for _, tf := range tfs {
+ fn := filepath.Join(gopath, "src", tf.name)
+ if err := os.MkdirAll(filepath.Dir(fn), 0o755); err != nil {
+ t.Fatal(err)
+ }
+ if err := ioutil.WriteFile(fn, []byte(tf.contents), 0o444); err != nil {
+ t.Fatal(err)
+ }
+ }
+}
+
+func TestGO2PATHEqGOPATH(t *testing.T) {
+ t.Parallel()
+ buildGo2go(t)
+
+ gopath := t.TempDir()
+ testFiles{
+ {
+ "pkg/p.go",
+ `package pkg; func F() {}`,
+ },
+ {
+ "cmd/cmd.go2",
+ `package main; import "pkg"; func main() { pkg.F() }`,
+ },
+ }.create(t, gopath)
+
+ t.Log("go2go build")
+ cmdDir := filepath.Join(gopath, "src", "cmd")
+ cmd := exec.Command(testGo2go, "build")
+ cmd.Dir = cmdDir
+ cmd.Env = append(os.Environ(),
+ "GOPATH="+gopath,
+ "GO2PATH="+gopath,
+ "GO111MODULE=off",
+ )
+ out, err := cmd.CombinedOutput()
+ if len(out) > 0 {
+ t.Logf("%s", out)
+ }
+ if err != nil {
+ t.Fatalf(`error running "go2go build": %v`, err)
+ }
+
+ t.Log("./cmd")
+ cmdName := "./cmd"
+ if runtime.GOOS == "windows" {
+ cmdName += ".exe"
+ }
+ cmd = exec.Command(cmdName)
+ cmd.Dir = cmdDir
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("error running %q: %v", cmdName, err)
+ }
+ if len(out) != 0 {
+ t.Errorf("unexpected output: %q", out)
+ }
+}
+
+const buildSource = `
+package main
+
+import "fmt"
+
+func PrintSlice[Elem any](s []Elem) {
+ for _, v := range s {
+ fmt.Println(v)
+ }
+}
+
+func main() {
+ PrintSlice([]string{"hello", "world"})
+}
+`
+
+func TestBuild(t *testing.T) {
+ t.Parallel()
+ buildGo2go(t)
+
+ gopath := t.TempDir()
+ testFiles{
+ {
+ "hello/hello.go2",
+ buildSource,
+ },
+ }.create(t, gopath)
+
+ t.Log("go2go build")
+ dir := filepath.Join(gopath, "src", "hello")
+ cmd := exec.Command(testGo2go, "build")
+ cmd.Dir = dir
+ out, err := cmd.CombinedOutput()
+ if len(out) > 0 {
+ t.Logf("%s", out)
+ }
+ if err != nil {
+ t.Fatalf(`error running "go2go build": %v`, err)
+ }
+
+ runHello(t, dir)
+}
+
+func runHello(t *testing.T, dir string) {
+ cmdName := "./hello"
+ if runtime.GOOS == "windows" {
+ cmdName += ".exe"
+ }
+ cmd := exec.Command(cmdName)
+ cmd.Dir = dir
+ out, err := cmd.CombinedOutput()
+ t.Log("./hello")
+ if len(out) > 0 {
+ t.Logf("%s", out)
+ }
+ if err != nil {
+ t.Fatalf("error running hello: %v", err)
+ }
+ got := strings.Split(strings.TrimSpace(string(out)), "\n")
+ want := []string{"hello", "world"}
+ if !reflect.DeepEqual(got, want) {
+ t.Errorf("hello output %v, want %v", got, want)
+ }
+}
+
+func TestBuildPackage(t *testing.T) {
+ t.Parallel()
+ buildGo2go(t)
+
+ gopath := t.TempDir()
+ testFiles{
+ {
+ "cmd/hello/hello.go2",
+ buildSource,
+ },
+ }.create(t, gopath)
+
+ t.Log("go2go build")
+ cmd := exec.Command(testGo2go, "build", "cmd/hello")
+ cmd.Dir = gopath
+ cmd.Env = append(os.Environ(),
+ "GO2PATH="+gopath,
+ )
+ out, err := cmd.CombinedOutput()
+ if len(out) > 0 {
+ t.Logf("%s", out)
+ }
+ if err != nil {
+ t.Fatalf(`error running "go2go build": %v`, err)
+ }
+
+ runHello(t, gopath)
+}
+
+func TestTransitiveGo1(t *testing.T) {
+ t.Parallel()
+ buildGo2go(t)
+
+ gopath := t.TempDir()
+ testFiles{
+ {
+ "a/a.go2",
+ `package a; func ident[T any](v T) T { return v }; func F1(v int) int { return ident(v) }`,
+ },
+ {
+ "b/b.go",
+ `package b; import "a"; func F2(v int) int { return a.F1(v) }`,
+ },
+ {
+ "c/c.go2",
+ `package main; import ("fmt"; "b"); func main() { fmt.Println(b.F2(42)) }`,
+ },
+ }.create(t, gopath)
+
+ t.Log("go2go build")
+ cmd := exec.Command(testGo2go, "build", "c")
+ cmd.Dir = gopath
+ cmd.Env = append(os.Environ(),
+ "GO2PATH="+gopath,
+ )
+ out, err := cmd.CombinedOutput()
+ if len(out) > 0 {
+ t.Logf("%s", out)
+ }
+ if err != nil {
+ t.Fatalf(`error running "go2go build": %v`, err)
+ }
+}
diff --git a/src/cmd/go2go/main.go b/src/cmd/go2go/main.go
new file mode 100644
index 0000000..a354361
--- /dev/null
+++ b/src/cmd/go2go/main.go
@@ -0,0 +1,185 @@
+// Copyright 2020 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 main
+
+import (
+ "flag"
+ "fmt"
+ "go/go2go"
+ "io/ioutil"
+ "log"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "strings"
+)
+
+var gotool = filepath.Join(runtime.GOROOT(), "bin", "go")
+
+var cmds = map[string]bool{
+ "build": true,
+ "run": true,
+ "test": true,
+ "translate": true,
+}
+
+func main() {
+ flag.Usage = usage
+ flag.Parse()
+
+ args := flag.Args()
+ if len(args) < 1 {
+ usage()
+ }
+
+ if !cmds[args[0]] {
+ usage()
+ }
+
+ importerTmpdir, err := ioutil.TempDir("", "go2go")
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer os.RemoveAll(importerTmpdir)
+
+ importer := go2go.NewImporter(importerTmpdir)
+
+ var rundir string
+ if args[0] == "run" {
+ tmpdir := copyToTmpdir(args[1:])
+ defer os.RemoveAll(tmpdir)
+ translate(importer, tmpdir)
+ nargs := []string{"run"}
+ for _, arg := range args[1:] {
+ base := filepath.Base(arg)
+ f := strings.TrimSuffix(base, ".go2") + ".go"
+ nargs = append(nargs, f)
+ }
+ args = nargs
+ rundir = tmpdir
+ } else if args[0] == "translate" && isGo2Files(args[1:]...) {
+ for _, arg := range args[1:] {
+ translateFile(importer, arg)
+ }
+ } else {
+ for _, dir := range expandPackages(args[1:]) {
+ translate(importer, dir)
+ }
+ }
+
+ if args[0] != "translate" {
+ cmd := exec.Command(gotool, args...)
+ cmd.Stdin = os.Stdin
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ cmd.Dir = rundir
+ gopath := importerTmpdir
+ if go2path := os.Getenv("GO2PATH"); go2path != "" {
+ gopath += string(os.PathListSeparator) + go2path
+ }
+ if oldGopath := os.Getenv("GOPATH"); oldGopath != "" {
+ gopath += string(os.PathListSeparator) + oldGopath
+ }
+ cmd.Env = append(os.Environ(),
+ "GOPATH="+gopath,
+ "GO111MODULE=off",
+ )
+ if err := cmd.Run(); err != nil {
+ die(fmt.Sprintf("%s %v failed: %v", gotool, args, err))
+ }
+ }
+}
+
+// isGo2Files reports whether the arguments are a list of .go2 files.
+func isGo2Files(args ...string) bool {
+ for _, arg := range args {
+ if filepath.Ext(arg) != ".go2" {
+ return false
+ }
+ }
+ return true
+}
+
+// expandPackages returns a list of directories expanded from packages.
+func expandPackages(pkgs []string) []string {
+ if len(pkgs) == 0 {
+ return []string{"."}
+ }
+ go2path := os.Getenv("GO2PATH")
+ var dirs []string
+pkgloop:
+ for _, pkg := range pkgs {
+ if go2path != "" {
+ for _, pd := range strings.Split(go2path, string(os.PathListSeparator)) {
+ d := filepath.Join(pd, "src", pkg)
+ if fi, err := os.Stat(d); err == nil && fi.IsDir() {
+ dirs = append(dirs, d)
+ continue pkgloop
+ }
+ }
+ }
+
+ cmd := exec.Command(gotool, "list", "-f", "{{.Dir}}", pkg)
+ cmd.Stderr = os.Stderr
+ if go2path != "" {
+ gopath := go2path
+ if oldGopath := os.Getenv("GOPATH"); oldGopath != "" {
+ gopath += string(os.PathListSeparator) + oldGopath
+ }
+ cmd.Env = append(os.Environ(),
+ "GOPATH="+gopath,
+ "GO111MODULE=off",
+ )
+ }
+ out, err := cmd.Output()
+ if err != nil {
+ die(fmt.Sprintf("%s list %q failed: %v", gotool, pkg, err))
+ }
+ dirs = append(dirs, strings.Split(string(out), "\n")...)
+ }
+ return dirs
+}
+
+// copyToTmpdir copies files into a temporary directory.
+func copyToTmpdir(files []string) string {
+ if len(files) == 0 {
+ die("no files to run")
+ }
+ tmpdir, err := ioutil.TempDir("", "go2go-run")
+ if err != nil {
+ die(err.Error())
+ }
+ for _, file := range files {
+ data, err := ioutil.ReadFile(file)
+ if err != nil {
+ die(err.Error())
+ }
+ if err := ioutil.WriteFile(filepath.Join(tmpdir, filepath.Base(file)), data, 0444); err != nil {
+ die(err.Error())
+ }
+ }
+ return tmpdir
+}
+
+// usage reports a usage message and exits with failure.
+func usage() {
+ fmt.Fprint(os.Stderr, `Usage: go2go <command> [arguments]
+
+The commands are:
+
+ build translate and build packages
+ run translate and run list of files
+ test translate and test packages
+ translate translate .go2 files into .go files
+`)
+ os.Exit(2)
+}
+
+// die reports an error and exits.
+func die(msg string) {
+ fmt.Fprintln(os.Stderr, msg)
+ os.Exit(1)
+}
diff --git a/src/cmd/go2go/testdata/go2path/src/alg/alg.go2 b/src/cmd/go2go/testdata/go2path/src/alg/alg.go2
new file mode 100644
index 0000000..c2e48bf
--- /dev/null
+++ b/src/cmd/go2go/testdata/go2path/src/alg/alg.go2
@@ -0,0 +1,24 @@
+// Copyright 2020 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 alg provides basic algorithms.
+package alg
+
+import "constraints"
+
+// Max returns the maximum of two values of some ordered type.
+func Max[T constraints.Ordered](a, b T) T {
+ if a < b {
+ return b
+ }
+ return a
+}
+
+// Min returns the minimum of two values of some ordered type.
+func Min[T constraints.Ordered](a, b T) T {
+ if a < b {
+ return a
+ }
+ return b
+}
diff --git a/src/cmd/go2go/testdata/go2path/src/chans/chans.go2 b/src/cmd/go2go/testdata/go2path/src/chans/chans.go2
new file mode 100644
index 0000000..c5369ab
--- /dev/null
+++ b/src/cmd/go2go/testdata/go2path/src/chans/chans.go2
@@ -0,0 +1,215 @@
+// Copyright 2020 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 chans provides utility functions for working with channels.
+package chans
+
+import (
+ "context"
+ "runtime"
+)
+
+// ReadAll reads from c until the channel is closed or the context is
+// canceled, returning all the values read.
+func ReadAll[Elem any](ctx context.Context, c <-chan Elem) []Elem {
+ var r []Elem
+ for {
+ select {
+ case <-ctx.Done():
+ return r
+ case v, ok := <-c:
+ if !ok {
+ return r
+ }
+ r = append(r, v)
+ }
+ }
+}
+
+// Merge merges two channels into a single channel.
+// This will leave a goroutine running until either both channels are closed
+// or the context is canceled, at which point the returned channel is closed.
+func Merge[Elem any](ctx context.Context, c1, c2 <-chan Elem) <-chan Elem {
+ r := make(chan Elem)
+ go func(ctx context.Context, c1, c2 <-chan Elem, r chan<- Elem) {
+ defer close(r)
+ for c1 != nil || c2 != nil {
+ select {
+ case <-ctx.Done():
+ return
+ case v1, ok := <-c1:
+ if ok {
+ r <- v1
+ } else {
+ c1 = nil
+ }
+ case v2, ok := <-c2:
+ if ok {
+ r <- v2
+ } else {
+ c2 = nil
+ }
+ }
+ }
+ }(ctx, c1, c2, r)
+ return r
+}
+
+// Filter calls f on each value read from c. If f returns true the value
+// is sent on the returned channel. This will leave a goroutine running
+// until c is closed or the context is canceled, at which point the
+// returned channel is closed.
+func Filter[Elem any](ctx context.Context, c <-chan Elem, f func(Elem) bool) <-chan Elem {
+ r := make(chan Elem)
+ go func(ctx context.Context, c <-chan Elem, f func(Elem) bool, r chan<- Elem) {
+ defer close(r)
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ case v, ok := <-c:
+ if !ok {
+ return
+ }
+ if f(v) {
+ r <- v
+ }
+ }
+ }
+ }(ctx, c, f, r)
+ return r
+}
+
+// Sink returns a channel that discards all values sent to it.
+// This will leave a goroutine running until the context is canceled
+// or the returned channel is closed.
+func Sink[Elem any](ctx context.Context) chan<- Elem {
+ r := make(chan Elem)
+ go func(ctx context.Context, r <-chan Elem) {
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ case _, ok := <-r:
+ if !ok {
+ return
+ }
+ }
+ }
+ }(ctx, r)
+ return r
+}
+
+// An Exclusive is a value that may only be used by a single goroutine
+// at a time. This is implemented using channels rather than a mutex.
+type Exclusive[Val any] struct {
+ c chan Val
+}
+
+// MakeExclusive makes an initialized exclusive value.
+func MakeExclusive[Val any](initial Val) *Exclusive[Val] {
+ r := &Exclusive[Val]{
+ c: make(chan Val, 1),
+ }
+ r.c <- initial
+ return r
+}
+
+// Acquire acquires the exclusive value for private use.
+// It must be released using the Release method.
+func (e *Exclusive[Val]) Acquire() Val {
+ return <-e.c
+}
+
+// TryAcquire attempts to acquire the value. The ok result reports whether
+// the value was acquired. If the value is acquired, it must be released
+// using the Release method.
+func (e *Exclusive[Val]) TryAcquire() (v Val, ok bool) {
+ select {
+ case r := <-e.c:
+ return r, true
+ default:
+ return v, false
+ }
+}
+
+// Release updates and releases the value.
+// This method panics if the value has not been acquired.
+func (e *Exclusive[Val]) Release(v Val) {
+ select {
+ case e.c <- v:
+ default:
+ panic("Exclusive Release without Acquire")
+ }
+}
+
+// Ranger returns a Sender and a Receiver. The Receiver provides a
+// Next method to retrieve values. The Sender provides a Send method
+// to send values and a Close method to stop sending values. The Next
+// method indicates when the Sender has been closed, and the Send
+// method indicates when the Receiver has been freed.
+//
+// This is a convenient way to exit a goroutine sending values when
+// the receiver stops reading them.
+func Ranger[Elem any]() (*Sender[Elem], *Receiver[Elem]) {
+ c := make(chan Elem)
+ d := make(chan struct{})
+ s := &Sender[Elem]{
+ values: c,
+ done: d,
+ }
+ r := &Receiver[Elem] {
+ values: c,
+ done: d,
+ }
+ runtime.SetFinalizer(r, (*Receiver[Elem]).finalize)
+ return s, r
+}
+
+// A Sender is used to send values to a Receiver.
+type Sender[Elem any] struct {
+ values chan<- Elem
+ done <-chan struct{}
+}
+
+// Send sends a value to the receiver. It reports whether the value was sent.
+// The value will not be sent if the context is closed or the receiver
+// is freed.
+func (s *Sender[Elem]) Send(ctx context.Context, v Elem) bool {
+ select {
+ case <-ctx.Done():
+ return false
+ case s.values <- v:
+ return true
+ case <-s.done:
+ return false
+ }
+}
+
+// Close tells the receiver that no more values will arrive.
+// After Close is called, the Sender may no longer be used.
+func (s *Sender[Elem]) Close() {
+ close(s.values)
+}
+
+// A Receiver receives values from a Sender.
+type Receiver[Elem any] struct {
+ values <-chan Elem
+ done chan<- struct{}
+}
+
+// Next returns the next value from the channel. The bool result indicates
+// whether the value is valid.
+func (r *Receiver[Elem]) Next(ctx context.Context) (v Elem, ok bool) {
+ select {
+ case <-ctx.Done():
+ case v, ok = <-r.values:
+ }
+ return v, ok
+}
+
+// finalize is a finalizer for the receiver.
+func (r *Receiver[Elem]) finalize() {
+ close(r.done)
+}
diff --git a/src/cmd/go2go/testdata/go2path/src/chans/chans_test.go2 b/src/cmd/go2go/testdata/go2path/src/chans/chans_test.go2
new file mode 100644
index 0000000..8bafbc9
--- /dev/null
+++ b/src/cmd/go2go/testdata/go2path/src/chans/chans_test.go2
@@ -0,0 +1,180 @@
+// Copyright 2020 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 chans
+
+import (
+ "context"
+ "runtime"
+ "sort"
+ "sync"
+ "testing"
+ "time"
+
+ "slices"
+)
+
+func TestReadAll(t *testing.T) {
+ c := make(chan int)
+ go func() {
+ c <- 4
+ c <- 2
+ c <- 5
+ close(c)
+ }()
+ got := ReadAll(context.Background(), c)
+ want := []int{4, 2, 5}
+ if !slices.Equal(got, want) {
+ t.Errorf("ReadAll returned %v, want %v", got, want)
+ }
+}
+
+func TestMerge(t *testing.T) {
+ c1 := make(chan int)
+ c2 := make(chan int)
+ go func() {
+ c1 <- 1
+ c1 <- 3
+ c1 <- 5
+ close(c1)
+ }()
+ go func() {
+ c2 <- 2
+ c2 <- 4
+ c2 <- 6
+ close(c2)
+ }()
+ ctx := context.Background()
+ got := ReadAll(ctx, Merge(ctx, c1, c2))
+ sort.Ints(got)
+ want := []int{1, 2, 3, 4, 5, 6}
+ if !slices.Equal(got, want) {
+ t.Errorf("Merge returned %v, want %v", got, want)
+ }
+}
+
+func TestFilter(t *testing.T) {
+ c := make(chan int)
+ go func() {
+ c <- 1
+ c <- 2
+ c <- 3
+ close(c)
+ }()
+ even := func(i int) bool { return i%2 == 0 }
+ ctx := context.Background()
+ got := ReadAll(ctx, Filter(ctx, c, even))
+ want := []int{2}
+ if !slices.Equal(got, want) {
+ t.Errorf("Filter returned %v, want %v", got, want)
+ }
+}
+
+func TestSink(t *testing.T) {
+ c := Sink(int)(context.Background())
+ after := time.NewTimer(time.Minute)
+ defer after.Stop()
+ send := func(v int) {
+ select {
+ case c <- v:
+ case <-after.C:
+ t.Error("timed out sending to Sink")
+ }
+ }
+ send(1)
+ send(2)
+ send(3)
+ close(c)
+}
+
+func TestExclusive(t *testing.T) {
+ val := 0
+ ex := MakeExclusive(&val)
+
+ var wg sync.WaitGroup
+ f := func() {
+ defer wg.Done()
+ for i := 0; i < 10; i++ {
+ p := ex.Acquire()
+ (*p)++
+ ex.Release(p)
+ }
+ }
+
+ wg.Add(2)
+ go f()
+ go f()
+
+ wg.Wait()
+ if val != 20 {
+ t.Errorf("after Acquire/Release loop got %d, want 20", val)
+ }
+}
+
+func TestExclusiveTry(t *testing.T) {
+ s := ""
+ ex := MakeExclusive(&s)
+ p, ok := ex.TryAcquire()
+ if !ok {
+ t.Fatalf("TryAcquire failed")
+ }
+ *p = "a"
+
+ var wg sync.WaitGroup
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ _, ok := ex.TryAcquire()
+ if ok {
+ t.Errorf("TryAcquire succeeded unexpectedly")
+ }
+ }()
+ wg.Wait()
+
+ ex.Release(p)
+
+ p, ok = ex.TryAcquire()
+ if !ok {
+ t.Errorf("TryAcquire failed")
+ }
+}
+
+func TestRanger(t *testing.T) {
+ s, r := Ranger(int)()
+
+ ctx := context.Background()
+ go func() {
+ // Receive one value then exit.
+ v, ok := r.Next(ctx)
+ if !ok {
+ t.Errorf("did not receive any values")
+ } else if v != 1 {
+ t.Errorf("received %d, want 1", v)
+ }
+ }()
+
+ c1 := make(chan bool)
+ c2 := make(chan bool)
+ go func() {
+ defer close(c2)
+ if !s.Send(ctx, 1) {
+ t.Errorf("Send failed unexpectedly")
+ }
+ close(c1)
+ if s.Send(ctx, 2) {
+ t.Errorf("Send succeeded unexpectedly")
+ }
+ }()
+
+ <-c1
+
+ // Force a garbage collection to try to get the finalizers to run.
+ runtime.GC()
+
+ select {
+ case <-c2:
+ case <-time.After(time.Minute):
+ t.Error("Ranger Send should have failed, but timed out")
+ }
+}
diff --git a/src/cmd/go2go/testdata/go2path/src/constraints/constraints.go2 b/src/cmd/go2go/testdata/go2path/src/constraints/constraints.go2
new file mode 100644
index 0000000..ee757c4
--- /dev/null
+++ b/src/cmd/go2go/testdata/go2path/src/constraints/constraints.go2
@@ -0,0 +1,31 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package constraints defines some useful type constraints.
+package constraints
+
+// Ordered permits any ordered type: any type that supports
+// the operations <, <=, >=, >, as well as == and !=.
+type Ordered interface {
+ type int, int8, int16, int32, int64,
+ uint, uint8, uint16, uint32, uint64, uintptr,
+ float32, float64,
+ string
+}
+
+// Integer permits any integer type.
+type Integer interface {
+ type int, int8, int16, int32, int64,
+ uint, uint8, uint16, uint32, uint64, uintptr
+}
+
+// Signed permits any signed integer type.
+type Signed interface {
+ type int, int8, int16, int32, int64
+}
+
+// Unsigned permits any unsigned integer type.
+type Unsigned interface {
+ type uint, uint8, uint16, uint32, uint64, uintptr
+}
diff --git a/src/cmd/go2go/testdata/go2path/src/graph/graph.go2 b/src/cmd/go2go/testdata/go2path/src/graph/graph.go2
new file mode 100644
index 0000000..ed90281
--- /dev/null
+++ b/src/cmd/go2go/testdata/go2path/src/graph/graph.go2
@@ -0,0 +1,72 @@
+// Copyright 2020 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 graph implements general purpose graph algorithms.
+package graph
+
+import "errors"
+
+// A Graph is a collection of nodes. A node may have an arbitrary number
+// of edges. An edge connects two nodes. Both nodes and edges must be
+// comparable. This is an undirected simple graph.
+type Graph[Node NodeC[Edge], Edge EdgeC[Node]] struct {
+ nodes []Node
+}
+
+// NodeC is the contraints on a node in a graph, given the Edge type.
+type NodeC[Edge any] interface {
+ comparable
+ Edges() []Edge
+}
+
+// Edgec is the constraints on an edge in a graph, given the Node type.
+type EdgeC[Node any] interface {
+ comparable
+ Nodes() (a, b Node)
+}
+
+// New creates a new Graph from a collection of Nodes.
+func New[Node NodeC[Edge], Edge EdgeC[Node]](nodes []Node) *Graph[Node, Edge] {
+ return &Graph[Node, Edge]{nodes: nodes}
+}
+
+// nodePath holds the path to a node during ShortestPath.
+// This should ideally be a type defined inside ShortestPath,
+// but the translator tool doesn't support that.
+type nodePath[Node NodeC[Edge], Edge EdgeC[Node]] struct {
+ node Node
+ path []Edge
+}
+
+// ShortestPath returns the shortest path between two nodes,
+// as an ordered list of edges. If there are multiple shortest paths,
+// which one is returned is unpredictable.
+func (g *Graph[Node, Edge]) ShortestPath(from, to Node) ([]Edge, error) {
+ visited := make(map[Node]bool)
+ visited[from] = true
+ workqueue := []nodePath[Node, Edge]{nodePath[Node, Edge]{from, nil}}
+ for len(workqueue) > 0 {
+ current := workqueue
+ workqueue = nil
+ for _, np := range current {
+ edges := np.node.Edges()
+ for _, edge := range edges {
+ a, b := edge.Nodes()
+ if a == np.node {
+ a = b
+ }
+ if !visited[a] {
+ ve := append([]Edge(nil), np.path...)
+ ve = append(ve, edge)
+ if a == to {
+ return ve, nil
+ }
+ workqueue = append(workqueue, nodePath[Node, Edge]{a, ve})
+ visited[a] = true
+ }
+ }
+ }
+ }
+ return nil, errors.New("no path")
+}
diff --git a/src/cmd/go2go/testdata/go2path/src/graph/graph_test.go2 b/src/cmd/go2go/testdata/go2path/src/graph/graph_test.go2
new file mode 100644
index 0000000..c36fc52
--- /dev/null
+++ b/src/cmd/go2go/testdata/go2path/src/graph/graph_test.go2
@@ -0,0 +1,143 @@
+// Copyright 2020 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 graph
+
+import (
+ "fmt"
+ "testing"
+
+ "slices"
+)
+
+type direction int
+
+const (
+ north direction = iota
+ ne
+ east
+ se
+ south
+ sw
+ west
+ nw
+ up
+ down
+)
+
+func (dir direction) String() string {
+ strs := map[direction]string{
+ north: "north",
+ ne: "ne",
+ east: "east",
+ se: "se",
+ south: "south",
+ sw: "sw",
+ west: "west",
+ nw: "nw",
+ up: "up",
+ down: "down",
+ }
+ if str, ok := strs[dir]; ok {
+ return str
+ }
+ return fmt.Sprintf("direction %d", dir)
+}
+
+type mazeRoom struct {
+ index int
+ exits [10]int
+}
+
+type mazeEdge struct {
+ from, to int
+ dir direction
+}
+
+// Edges returns the exits from the room.
+func (m mazeRoom) Edges() []mazeEdge {
+ var r []mazeEdge
+ for i, exit := range m.exits {
+ if exit != 0 {
+ r = append(r, mazeEdge{
+ from: m.index,
+ to: exit,
+ dir: direction(i),
+ })
+ }
+ }
+ return r
+}
+
+// Nodes returns the rooms connected by an edge.
+func (e mazeEdge) Nodes() (mazeRoom, mazeRoom) {
+ m1, ok := zork[e.from]
+ if !ok {
+ panic("bad edge")
+ }
+ m2, ok := zork[e.to]
+ if !ok {
+ panic("bad edge")
+ }
+ return m1, m2
+}
+
+// The first maze in Zork. Room indexes based on original Fortran data file.
+// You are in a maze of twisty little passages, all alike.
+var zork = map[int]mazeRoom{
+ 11: {exits: [10]int{north: 11, south: 12, east: 14}}, // west to Troll Room
+ 12: {exits: [10]int{south: 11, north: 14, east: 13}},
+ 13: {exits: [10]int{west: 12, north: 14, up: 16}},
+ 14: {exits: [10]int{west: 13, north: 11, east: 15}},
+ 15: {exits: [10]int{south: 14}}, // Dead End
+ 16: {exits: [10]int{east: 17, north: 13, sw: 18}}, // skeleton, etc.
+ 17: {exits: [10]int{west: 16}}, // Dead End
+ 18: {exits: [10]int{down: 16, east: 19, west: 18, up: 22}},
+ 19: {exits: [10]int{up: 29, west: 18, ne: 15, east: 20, south: 30}},
+ 20: {exits: [10]int{ne: 19, west: 20, se: 21}},
+ 21: {exits: [10]int{north: 20}}, // Dead End
+ 22: {exits: [10]int{north: 18, east: 24, down: 23, south: 28, west: 26, nw: 22}},
+ 23: {exits: [10]int{east: 22, west: 28, up: 24}},
+ 24: {exits: [10]int{ne: 25, down: 23, nw: 28, sw: 26}},
+ 25: {exits: [10]int{sw: 24}}, // Grating room (up to Clearing)
+ 26: {exits: [10]int{west: 16, sw: 24, east: 28, up: 22, north: 27}},
+ 27: {exits: [10]int{south: 26}}, // Dead End
+ 28: {exits: [10]int{east: 22, down: 26, south: 23, west: 24}},
+ 29: {exits: [10]int{west: 30, nw: 29, ne: 19, south: 19}},
+ 30: {exits: [10]int{west: 29, south: 19}}, // ne to Cyclops Room
+}
+
+func TestShortestPath(t *testing.T) {
+ // The Zork maze is not a proper undirected simple graph,
+ // as there are some one way paths (e.g., 19 -> 15),
+ // but for this test that doesn't matter.
+
+ // Set the index field in the map. Simpler than doing it in the
+ // composite literal.
+ for k := range zork {
+ r := zork[k]
+ r.index = k
+ zork[k] = r
+ }
+
+ var nodes []mazeRoom
+ for idx, room := range zork {
+ mridx := room
+ mridx.index = idx
+ nodes = append(nodes, mridx)
+ }
+ g := New[mazeRoom, mazeEdge](nodes)
+ path, err := g.ShortestPath(zork[11], zork[30])
+ if err != nil {
+ t.Fatal(err)
+ }
+ var steps []direction
+ for _, edge := range path {
+ steps = append(steps, edge.dir)
+ }
+ want := []direction{east, west, up, sw, east, south}
+ if !slices.Equal(steps, want) {
+ t.Errorf("ShortestPath returned %v, want %v", steps, want)
+ }
+}
diff --git a/src/cmd/go2go/testdata/go2path/src/gsort/gsort.go2 b/src/cmd/go2go/testdata/go2path/src/gsort/gsort.go2
new file mode 100644
index 0000000..5d4301a
--- /dev/null
+++ b/src/cmd/go2go/testdata/go2path/src/gsort/gsort.go2
@@ -0,0 +1,51 @@
+// Copyright 2020 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 gsort provides primitives for sorting slices of any type.
+package gsort
+
+import (
+ "sort"
+
+ "constraints"
+)
+
+// orderedSlice is a slice of values of some ordered type.
+type orderedSlice[Elem constraints.Ordered] []Elem
+
+// orderedSlice implements sort.Interface.
+
+func (s orderedSlice[Elem]) Len() int { return len(s) }
+func (s orderedSlice[Elem]) Less(i, j int) bool {
+ if s[i] < s[j] {
+ return true
+ }
+ isNaN := func(f Elem) bool { return f != f }
+ if isNaN(s[i]) && !isNaN(s[j]) {
+ return true
+ }
+ return false
+}
+func (s orderedSlice[Elem]) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+// OrderedSlice sorts a slice of any ordered type in ascending order.
+func OrderedSlice[Elem constraints.Ordered](s []Elem) {
+ sort.Sort(orderedSlice[Elem](s))
+}
+
+// sliceFn implements sort.Interface for a slice of any type with an
+// explicit less-than function.
+type sliceFn[Elem any] struct {
+ s []Elem
+ less func(Elem, Elem) bool
+}
+
+func (s sliceFn[Elem]) Len() int { return len(s.s) }
+func (s sliceFn[Elem]) Less(i, j int) bool { return s.less(s.s[i], s.s[j]) }
+func (s sliceFn[Elem]) Swap(i, j int) { s.s[i], s.s[j] = s.s[j], s.s[i] }
+
+// SliceFn sorts a slice of any type according to a less-than function.
+func SliceFn[Elem any](s []Elem, less func(Elem, Elem) bool) {
+ sort.Sort(sliceFn[Elem]{s, less})
+}
diff --git a/src/cmd/go2go/testdata/go2path/src/gsort/gsort_test.go2 b/src/cmd/go2go/testdata/go2path/src/gsort/gsort_test.go2
new file mode 100644
index 0000000..a7bb157
--- /dev/null
+++ b/src/cmd/go2go/testdata/go2path/src/gsort/gsort_test.go2
@@ -0,0 +1,92 @@
+// Copyright 2020 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 gsort
+
+import (
+ "math"
+ "sort"
+ "testing"
+
+ "constraints"
+ "slices"
+)
+
+// Test data copied from the standard library sort package.
+
+var ints = []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}
+var float64s = []float64{74.3, 59.0, math.Inf(1), 238.2, -784.0, 2.3, math.NaN(), math.NaN(), math.Inf(-1), 9845.768, -959.7485, 905, 7.8, 7.8}
+var strs = []string{"", "Hello", "foo", "bar", "foo", "f00", "%*&^*&^&", "***"}
+
+func TestSortOrderedInts(t *testing.T) {
+ testOrdered(t, ints, sort.Ints)
+}
+
+func TestSortOrderedFloat64s(t *testing.T) {
+ testOrdered(t, float64s, sort.Float64s)
+}
+
+func TestSortOrderedStrings(t *testing.T) {
+ testOrdered(t, strs, sort.Strings)
+}
+
+func testOrdered[Elem constraints.Ordered](t *testing.T, s []Elem, sorter func([]Elem)) {
+ s1 := make([]Elem, len(s))
+ copy(s1, s)
+ s2 := make([]Elem, len(s))
+ copy(s2, s)
+ OrderedSlice(s1)
+ sorter(s2)
+ if !slices.Equal(s1, s2) {
+ t.Fatalf("got %v, want %v", s1, s2)
+ }
+ for i := len(s1) - 1; i > 0; i-- {
+ if s1[i] < s1[i-1] {
+ t.Fatalf("element %d (%v) < element %d (%v)", i, s1[i], i - 1, s1[i - 1])
+ }
+ }
+}
+
+var slicesToSort = [][]int {
+ []int{1, 2},
+ []int{3, 2, 1},
+ []int{1},
+ []int{1, 3},
+ []int{1, 2, 3},
+}
+
+var sortedSlices = [][]int {
+ []int{1},
+ []int{1, 2},
+ []int{1, 3},
+ []int{1, 2, 3},
+ []int{3, 2, 1},
+}
+
+func sorter(s1, s2 []int) bool {
+ switch {
+ case len(s1) < len(s2):
+ return true
+ case len(s1) > len(s2):
+ return false
+ }
+ for i := range s1 {
+ switch {
+ case s1[i] < s2[i]:
+ return true
+ case s1[i] > s2[i]:
+ return false
+ }
+ }
+ return false
+}
+
+func TestSortSliceFn(t *testing.T) {
+ c := make([][]int, len(slicesToSort))
+ copy(c, slicesToSort)
+ SliceFn(c, sorter)
+ if !slices.EqualFn(c, sortedSlices, func(a, b []int) bool { return slices.Equal(int)(a, b) }) {
+ t.Errorf("got %v, want %v", c, sortedSlices)
+ }
+}
diff --git a/src/cmd/go2go/testdata/go2path/src/list/list.go2 b/src/cmd/go2go/testdata/go2path/src/list/list.go2
new file mode 100644
index 0000000..2740fd4
--- /dev/null
+++ b/src/cmd/go2go/testdata/go2path/src/list/list.go2
@@ -0,0 +1,244 @@
+// Copyright 2020 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 list provides a doubly linked list of some element type.
+package list
+
+// FIXME: This should probably be in container/list,
+// perhaps with different type names.
+
+// Element is an element of a linked list.
+type Element[TElem any] struct {
+ // Next and previous pointers in the doubly-linked list of elements.
+ // To simplify the implementation, internally a list l is implemented
+ // as a ring, such that &l.root is both the next element of the last
+ // list element (l.Back()) and the previous element of the first list
+ // element (l.Front()).
+ next, prev *Element[TElem]
+
+ // The list to which this element belongs.
+ list *List[TElem]
+
+ // The value stored with this element.
+ Value TElem
+}
+
+// Next returns the next list element or nil.
+func (e *Element[TElem]) Next() *Element[TElem] {
+ if p := e.next; e.list != nil && p != &e.list.root {
+ return p
+ }
+ return nil
+}
+
+// Prev returns the previous list element or nil.
+func (e *Element[TElem]) Prev() *Element[TElem] {
+ if p := e.prev; e.list != nil && p != &e.list.root {
+ return p
+ }
+ return nil
+}
+
+// List represents a doubly linked list.
+// The zero value for List is an empty list ready to use.
+type List[TElem any] struct {
+ root Element[TElem] // sentinel list element, only &root, root.prev, and root.next are used
+ len int // current list length excluding (this) sentinel element
+}
+
+// Init initializes or clears list l.
+func (l *List[TElem]) Init() *List[TElem] {
+ l.root.next = &l.root
+ l.root.prev = &l.root
+ l.len = 0
+ return l
+}
+
+// New returns an initialized list.
+func New[TElem any]() *List[TElem] { return new(List[TElem]).Init() }
+
+// Len returns the number of elements of list l.
+// The complexity is O(1).
+func (l *List[TElem]) Len() int { return l.len }
+
+// Front returns the first element of list l or nil if the list is empty.
+func (l *List[TElem]) Front() *Element[TElem] {
+ if l.len == 0 {
+ return nil
+ }
+ return l.root.next
+}
+
+// Back returns the last element of list l or nil if the list is empty.
+func (l *List[TElem]) Back() *Element[TElem] {
+ if l.len == 0 {
+ return nil
+ }
+ return l.root.prev
+}
+
+// lazyInit lazily initializes a zero List value.
+func (l *List[TElem]) lazyInit() {
+ if l.root.next == nil {
+ l.Init()
+ }
+}
+
+// insert inserts e after at, increments l.len, and returns e.
+func (l *List[TElem]) insert(e, at *Element[TElem]) *Element[TElem] {
+ e.prev = at
+ e.next = at.next
+ e.prev.next = e
+ e.next.prev = e
+ e.list = l
+ l.len++
+ return e
+}
+
+// insertValue is a convenience wrapper for insert(&Element[TElem]{Value: v}, at).
+func (l *List[TElem]) insertValue(v TElem, at *Element[TElem]) *Element[TElem] {
+ return l.insert(&Element[TElem]{Value: v}, at)
+}
+
+// remove removes e from its list, decrements l.len, and returns e.
+func (l *List[TElem]) remove(e *Element[TElem]) *Element[TElem] {
+ e.prev.next = e.next
+ e.next.prev = e.prev
+ e.next = nil // avoid memory leaks
+ e.prev = nil // avoid memory leaks
+ e.list = nil
+ l.len--
+ return e
+}
+
+// move moves e to next to at and returns e.
+func (l *List[TElem]) move(e, at *Element[TElem]) *Element[TElem] {
+ if e == at {
+ return e
+ }
+ e.prev.next = e.next
+ e.next.prev = e.prev
+
+ e.prev = at
+ e.next = at.next
+ e.prev.next = e
+ e.next.prev = e
+
+ return e
+}
+
+// Remove removes e from l if e is an element of list l.
+// It returns the element value e.Value.
+// The element must not be nil.
+func (l *List[TElem]) Remove(e *Element[TElem]) TElem {
+ if e.list == l {
+ // if e.list == l, l must have been initialized when e was inserted
+ // in l or l == nil (e is a zero Element) and l.remove will crash
+ l.remove(e)
+ }
+ return e.Value
+}
+
+// PushFront inserts a new element e with value v at the front of list l and returns e.
+func (l *List[TElem]) PushFront(v TElem) *Element[TElem] {
+ l.lazyInit()
+ return l.insertValue(v, &l.root)
+}
+
+// PushBack inserts a new element e with value v at the back of list l and returns e.
+func (l *List[TElem]) PushBack(v TElem) *Element[TElem] {
+ l.lazyInit()
+ return l.insertValue(v, l.root.prev)
+}
+
+// InsertBefore inserts a new element e with value v immediately before mark and returns e.
+// If mark is not an element of l, the list is not modified.
+// The mark must not be nil.
+func (l *List[TElem]) InsertBefore(v TElem, mark *Element[TElem]) *Element[TElem] {
+ if mark.list != l {
+ return nil
+ }
+ // see comment in List.Remove about initialization of l
+ return l.insertValue(v, mark.prev)
+}
+
+// InsertAfter inserts a new element e with value v immediately after mark and returns e.
+// If mark is not an element of l, the list is not modified.
+// The mark must not be nil.
+func (l *List[TElem]) InsertAfter(v TElem, mark *Element[TElem]) *Element[TElem] {
+ if mark.list != l {
+ return nil
+ }
+ // see comment in List.Remove about initialization of l
+ return l.insertValue(v, mark)
+}
+
+// MoveToFront moves element e to the front of list l.
+// If e is not an element of l, the list is not modified.
+// The element must not be nil.
+func (l *List[TElem]) MoveToFront(e *Element[TElem]) {
+ if e.list != l || l.root.next == e {
+ return
+ }
+ // see comment in List.Remove about initialization of l
+ l.move(e, &l.root)
+}
+
+// MoveToBack moves element e to the back of list l.
+// If e is not an element of l, the list is not modified.
+// The element must not be nil.
+func (l *List[TElem]) MoveToBack(e *Element[TElem]) {
+ if e.list != l || l.root.prev == e {
+ return
+ }
+ // see comment in List.Remove about initialization of l
+ l.move(e, l.root.prev)
+}
+
+// MoveBefore moves element e to its new position before mark.
+// If e or mark is not an element of l, or e == mark, the list is not modified.
+// The element and mark must not be nil.
+func (l *List[TElem]) MoveBefore(e, mark *Element[TElem]) {
+ if e.list != l || e == mark || mark.list != l {
+ return
+ }
+ l.move(e, mark.prev)
+}
+
+// MoveAfter moves element e to its new position after mark.
+// If e or mark is not an element of l, or e == mark, the list is not modified.
+// The element and mark must not be nil.
+func (l *List[TElem]) MoveAfter(e, mark *Element[TElem]) {
+ if e.list != l || e == mark || mark.list != l {
+ return
+ }
+ l.move(e, mark)
+}
+
+// PushBackList inserts a copy of an other list at the back of list l.
+// The lists l and other may be the same. They must not be nil.
+func (l *List[TElem]) PushBackList(other *List[TElem]) {
+ l.lazyInit()
+ for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() {
+ l.insertValue(e.Value, l.root.prev)
+ }
+}
+
+// PushFrontList inserts a copy of an other list at the front of list l.
+// The lists l and other may be the same. They must not be nil.
+func (l *List[TElem]) PushFrontList(other *List[TElem]) {
+ l.lazyInit()
+ for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() {
+ l.insertValue(e.Value, &l.root)
+ }
+}
+
+// Transform runs a transform function on a list returning a new list.
+func Transform[TElem1, TElem2 any](lst *List[TElem1], f func(TElem1) TElem2) *List[TElem2] {
+ ret := New[TElem2]()
+ for p := lst.Front(); p != nil; p = p.Next() {
+ ret.PushBack(f(p.Value))
+ }
+ return ret
+}
diff --git a/src/cmd/go2go/testdata/go2path/src/list/list_test.go2 b/src/cmd/go2go/testdata/go2path/src/list/list_test.go2
new file mode 100644
index 0000000..0671c76
--- /dev/null
+++ b/src/cmd/go2go/testdata/go2path/src/list/list_test.go2
@@ -0,0 +1,354 @@
+// Copyright 2020 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 list
+
+import (
+ "strconv"
+ "testing"
+)
+
+func checkListLen[TElem any](t *testing.T, l *List[TElem], len int) bool {
+ if n := l.Len(); n != len {
+ t.Errorf("l.Len() = %d, want %d", n, len)
+ return false
+ }
+ return true
+}
+
+func checkListPointers[TElem any](t *testing.T, l *List[TElem], es []*Element[TElem]) {
+ root := &l.root
+
+ if !checkListLen(t, l, len(es)) {
+ return
+ }
+
+ // zero length lists must be the zero value or properly initialized (sentinel circle)
+ if len(es) == 0 {
+ if l.root.next != nil && l.root.next != root || l.root.prev != nil && l.root.prev != root {
+ t.Errorf("l.root.next = %p, l.root.prev = %p; both should both be nil or %p", l.root.next, l.root.prev, root)
+ }
+ return
+ }
+ // len(es) > 0
+
+ // check internal and external prev/next connections
+ for i, e := range es {
+ prev := root
+ Prev := (*Element[TElem])(nil)
+ if i > 0 {
+ prev = es[i-1]
+ Prev = prev
+ }
+ if p := e.prev; p != prev {
+ t.Errorf("elt[%d](%p).prev = %p, want %p", i, e, p, prev)
+ }
+ if p := e.Prev(); p != Prev {
+ t.Errorf("elt[%d](%p).Prev() = %p, want %p", i, e, p, Prev)
+ }
+
+ next := root
+ Next := (*Element[TElem])(nil)
+ if i < len(es)-1 {
+ next = es[i+1]
+ Next = next
+ }
+ if n := e.next; n != next {
+ t.Errorf("elt[%d](%p).next = %p, want %p", i, e, n, next)
+ }
+ if n := e.Next(); n != Next {
+ t.Errorf("elt[%d](%p).Next() = %p, want %p", i, e, n, Next)
+ }
+ }
+}
+
+func TestList(t *testing.T) {
+ l := New(string)()
+ checkListPointers(t, l, []*(Element[string]){})
+
+ // Single element list
+ e := l.PushFront("a")
+ checkListPointers(t, l, []*(Element[string]){e})
+ l.MoveToFront(e)
+ checkListPointers(t, l, []*(Element[string]){e})
+ l.MoveToBack(e)
+ checkListPointers(t, l, []*(Element[string]){e})
+ l.Remove(e)
+ checkListPointers(t, l, []*(Element[string]){})
+
+ // Bigger list
+ l2 := New[int]()
+ e2 := l2.PushFront(2)
+ e1 := l2.PushFront(1)
+ e3 := l2.PushBack(3)
+ e4 := l2.PushBack(600)
+ checkListPointers(t, l2, []*(Element[int]){e1, e2, e3, e4})
+
+ l2.Remove(e2)
+ checkListPointers(t, l2, []*(Element[int]){e1, e3, e4})
+
+ l2.MoveToFront(e3) // move from middle
+ checkListPointers(t, l2, []*(Element[int]){e3, e1, e4})
+
+ l2.MoveToFront(e1)
+ l2.MoveToBack(e3) // move from middle
+ checkListPointers(t, l2, []*(Element[int]){e1, e4, e3})
+
+ l2.MoveToFront(e3) // move from back
+ checkListPointers(t, l2, []*(Element[int]){e3, e1, e4})
+ l2.MoveToFront(e3) // should be no-op
+ checkListPointers(t, l2, []*(Element[int]){e3, e1, e4})
+
+ l2.MoveToBack(e3) // move from front
+ checkListPointers(t, l2, []*(Element[int]){e1, e4, e3})
+ l2.MoveToBack(e3) // should be no-op
+ checkListPointers(t, l2, []*(Element[int]){e1, e4, e3})
+
+ e2 = l2.InsertBefore(2, e1) // insert before front
+ checkListPointers(t, l2, []*(Element[int]){e2, e1, e4, e3})
+ l2.Remove(e2)
+ e2 = l2.InsertBefore(2, e4) // insert before middle
+ checkListPointers(t, l2, []*(Element[int]){e1, e2, e4, e3})
+ l2.Remove(e2)
+ e2 = l2.InsertBefore(2, e3) // insert before back
+ checkListPointers(t, l2, []*(Element[int]){e1, e4, e2, e3})
+ l2.Remove(e2)
+
+ e2 = l2.InsertAfter(2, e1) // insert after front
+ checkListPointers(t, l2, []*(Element[int]){e1, e2, e4, e3})
+ l2.Remove(e2)
+ e2 = l2.InsertAfter(2, e4) // insert after middle
+ checkListPointers(t, l2, []*(Element[int]){e1, e4, e2, e3})
+ l2.Remove(e2)
+ e2 = l2.InsertAfter(2, e3) // insert after back
+ checkListPointers(t, l2, []*(Element[int]){e1, e4, e3, e2})
+ l2.Remove(e2)
+
+ // Check standard iteration.
+ sum := 0
+ for e := l2.Front(); e != nil; e = e.Next() {
+ sum += e.Value
+ }
+ if sum != 604 {
+ t.Errorf("sum over l = %d, want 604", sum)
+ }
+
+ // Clear all elements by iterating
+ var next *Element[int]
+ for e := l2.Front(); e != nil; e = next {
+ next = e.Next()
+ l2.Remove(e)
+ }
+ checkListPointers(t, l2, []*(Element[int]){})
+}
+
+func checkList[TElem comparable](t *testing.T, l *List[TElem], es []interface{}) {
+ if !checkListLen(t, l, len(es)) {
+ return
+ }
+
+ i := 0
+ for e := l.Front(); e != nil; e = e.Next() {
+ le := e.Value
+ if le != es[i] {
+ t.Errorf("elt[%d].Value = %v, want %v", i, le, es[i])
+ }
+ i++
+ }
+}
+
+func TestExtending(t *testing.T) {
+ l1 := New[int]()
+ l2 := New[int]()
+
+ l1.PushBack(1)
+ l1.PushBack(2)
+ l1.PushBack(3)
+
+ l2.PushBack(4)
+ l2.PushBack(5)
+
+ l3 := New[int]()
+ l3.PushBackList(l1)
+ checkList(t, l3, []interface{}{1, 2, 3})
+ l3.PushBackList(l2)
+ checkList(t, l3, []interface{}{1, 2, 3, 4, 5})
+
+ l3 = New[int]()
+ l3.PushFrontList(l2)
+ checkList(t, l3, []interface{}{4, 5})
+ l3.PushFrontList(l1)
+ checkList(t, l3, []interface{}{1, 2, 3, 4, 5})
+
+ checkList(t, l1, []interface{}{1, 2, 3})
+ checkList(t, l2, []interface{}{4, 5})
+
+ l3 = New[int]()
+ l3.PushBackList(l1)
+ checkList(t, l3, []interface{}{1, 2, 3})
+ l3.PushBackList(l3)
+ checkList(t, l3, []interface{}{1, 2, 3, 1, 2, 3})
+
+ l3 = New[int]()
+ l3.PushFrontList(l1)
+ checkList(t, l3, []interface{}{1, 2, 3})
+ l3.PushFrontList(l3)
+ checkList(t, l3, []interface{}{1, 2, 3, 1, 2, 3})
+
+ l3 = New[int]()
+ l1.PushBackList(l3)
+ checkList(t, l1, []interface{}{1, 2, 3})
+ l1.PushFrontList(l3)
+ checkList(t, l1, []interface{}{1, 2, 3})
+}
+
+func TestRemove(t *testing.T) {
+ l := New[int]()
+ e1 := l.PushBack(1)
+ e2 := l.PushBack(2)
+ checkListPointers(t, l, []*(Element[int]){e1, e2})
+ e := l.Front()
+ l.Remove(e)
+ checkListPointers(t, l, []*(Element[int]){e2})
+ l.Remove(e)
+ checkListPointers(t, l, []*(Element[int]){e2})
+}
+
+func TestIssue4103(t *testing.T) {
+ l1 := New[int]()
+ l1.PushBack(1)
+ l1.PushBack(2)
+
+ l2 := New[int]()
+ l2.PushBack(3)
+ l2.PushBack(4)
+
+ e := l1.Front()
+ l2.Remove(e) // l2 should not change because e is not an element of l2
+ if n := l2.Len(); n != 2 {
+ t.Errorf("l2.Len() = %d, want 2", n)
+ }
+
+ l1.InsertBefore(8, e)
+ if n := l1.Len(); n != 3 {
+ t.Errorf("l1.Len() = %d, want 3", n)
+ }
+}
+
+func TestIssue6349(t *testing.T) {
+ l := New[int]()
+ l.PushBack(1)
+ l.PushBack(2)
+
+ e := l.Front()
+ l.Remove(e)
+ if e.Value != 1 {
+ t.Errorf("e.value = %d, want 1", e.Value)
+ }
+ if e.Next() != nil {
+ t.Errorf("e.Next() != nil")
+ }
+ if e.Prev() != nil {
+ t.Errorf("e.Prev() != nil")
+ }
+}
+
+func TestMove(t *testing.T) {
+ l := New[int]()
+ e1 := l.PushBack(1)
+ e2 := l.PushBack(2)
+ e3 := l.PushBack(3)
+ e4 := l.PushBack(4)
+
+ l.MoveAfter(e3, e3)
+ checkListPointers(t, l, []*(Element[int]){e1, e2, e3, e4})
+ l.MoveBefore(e2, e2)
+ checkListPointers(t, l, []*(Element[int]){e1, e2, e3, e4})
+
+ l.MoveAfter(e3, e2)
+ checkListPointers(t, l, []*(Element[int]){e1, e2, e3, e4})
+ l.MoveBefore(e2, e3)
+ checkListPointers(t, l, []*(Element[int]){e1, e2, e3, e4})
+
+ l.MoveBefore(e2, e4)
+ checkListPointers(t, l, []*(Element[int]){e1, e3, e2, e4})
+ e2, e3 = e3, e2
+
+ l.MoveBefore(e4, e1)
+ checkListPointers(t, l, []*(Element[int]){e4, e1, e2, e3})
+ e1, e2, e3, e4 = e4, e1, e2, e3
+
+ l.MoveAfter(e4, e1)
+ checkListPointers(t, l, []*(Element[int]){e1, e4, e2, e3})
+ e2, e3, e4 = e4, e2, e3
+
+ l.MoveAfter(e2, e3)
+ checkListPointers(t, l, []*(Element[int]){e1, e3, e2, e4})
+ e2, e3 = e3, e2
+}
+
+// Test PushFront, PushBack, PushFrontList, PushBackList with uninitialized List
+func TestZeroList(t *testing.T) {
+ var l1 = new(List[int])
+ l1.PushFront(1)
+ checkList(t, l1, []interface{}{1})
+
+ var l2 = new(List[int])
+ l2.PushBack(1)
+ checkList(t, l2, []interface{}{1})
+
+ var l3 = new(List[int])
+ l3.PushFrontList(l1)
+ checkList(t, l3, []interface{}{1})
+
+ var l4 = new(List[int])
+ l4.PushBackList(l2)
+ checkList(t, l4, []interface{}{1})
+}
+
+// Test that a list l is not modified when calling InsertBefore with a mark that is not an element of l.
+func TestInsertBeforeUnknownMark(t *testing.T) {
+ var l List[int]
+ l.PushBack(1)
+ l.PushBack(2)
+ l.PushBack(3)
+ l.InsertBefore(1, new(Element[int]))
+ checkList(t, &l, []interface{}{1, 2, 3})
+}
+
+// Test that a list l is not modified when calling InsertAfter with a mark that is not an element of l.
+func TestInsertAfterUnknownMark(t *testing.T) {
+ var l List[int]
+ l.PushBack(1)
+ l.PushBack(2)
+ l.PushBack(3)
+ l.InsertAfter(1, new(Element[int]))
+ checkList(t, &l, []interface{}{1, 2, 3})
+}
+
+// Test that a list l is not modified when calling MoveAfter or MoveBefore with a mark that is not an element of l.
+func TestMoveUnknownMark(t *testing.T) {
+ var l1 List[int]
+ e1 := l1.PushBack(1)
+
+ var l2 List[int]
+ e2 := l2.PushBack(2)
+
+ l1.MoveAfter(e1, e2)
+ checkList(t, &l1, []interface{}{1})
+ checkList(t, &l2, []interface{}{2})
+
+ l1.MoveBefore(e1, e2)
+ checkList(t, &l1, []interface{}{1})
+ checkList(t, &l2, []interface{}{2})
+}
+
+// Test the Transform function.
+func TestTransform(t *testing.T) {
+ l1 := New[int]()
+ l1.PushBack(1)
+ l1.PushBack(2)
+ l2 := Transform(l1, strconv.Itoa)
+ checkList(t, l2, []interface{}{"1", "2"})
+}
diff --git a/src/cmd/go2go/testdata/go2path/src/maps/maps.go2 b/src/cmd/go2go/testdata/go2path/src/maps/maps.go2
new file mode 100644
index 0000000..77e9898
--- /dev/null
+++ b/src/cmd/go2go/testdata/go2path/src/maps/maps.go2
@@ -0,0 +1,91 @@
+// Copyright 2020 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 maps implements simple functions to manipulate maps in various ways.
+package maps
+
+// Keys returns the keys of the map m.
+// The keys will be an indeterminate order.
+func Keys[K comparable, V any](m map[K]V) []K {
+ r := make([]K, 0, len(m))
+ for k := range m {
+ r = append(r, k)
+ }
+ return r
+}
+
+// Values returns the values of the map m.
+// The values will be in an indeterminate order.
+func Values[K comparable, V any](m map[K]V) []V {
+ r := make([]V, 0, len(m))
+ for _, v := range m {
+ r = append(r, v)
+ }
+ return r
+}
+
+// Equal reports whether two maps contain the same key/value pairs.
+// Values are compared using ==.
+func Equal[K, V comparable](m1, m2 map[K]V) bool {
+ if len(m1) != len(m2) {
+ return false
+ }
+ for k, v1 := range m1 {
+ if v2, ok := m2[k]; !ok || v1 != v2 {
+ return false
+ }
+ }
+ return true
+}
+
+// Copy returns a copy of m.
+func Copy[K comparable, V any](m map[K]V) map[K]V {
+ r := make(map[K]V, len(m))
+ for k, v := range m {
+ r[k] = v
+ }
+ return r
+}
+
+// Add adds all key/value pairs in m2 to m1. Keys in m2 that are already
+// present in m1 will be overwritten with the value in m2.
+func Add[K comparable, V any](m1, m2 map[K]V) {
+ for k, v := range m2 {
+ m1[k] = v
+ }
+}
+
+// Sub removes all keys in m2 from m1. Keys in m2 that are not present
+// in m1 are ignored. The values in m2 are ignored.
+func Sub[K comparable, V any](m1, m2 map[K]V) {
+ for k := range m2 {
+ delete(m1, k)
+ }
+}
+
+// Intersect removes all keys from m1 that are not present in m2.
+// Keys in m2 that are not in m1 are ignored. The values in m2 are ignored.
+func Intersect[K comparable, V any](m1, m2 map[K]V) {
+ for k := range m1 {
+ if _, ok := m2[k]; !ok {
+ delete(m1, k)
+ }
+ }
+}
+
+// Filter deletes any key/value pairs from m for which f returns false.
+func Filter[K comparable, V any](m map[K]V, f func(K, V) bool) {
+ for k, v := range m {
+ if !f(k, v) {
+ delete(m, k)
+ }
+ }
+}
+
+// TransformValues applies f to each value in m. The keys remain unchanged.
+func TransformValues[K comparable, V any](m map[K]V, f func(V) V) {
+ for k, v := range m {
+ m[k] = f(v)
+ }
+}
diff --git a/src/cmd/go2go/testdata/go2path/src/maps/maps_test.go2 b/src/cmd/go2go/testdata/go2path/src/maps/maps_test.go2
new file mode 100644
index 0000000..5a3aa44
--- /dev/null
+++ b/src/cmd/go2go/testdata/go2path/src/maps/maps_test.go2
@@ -0,0 +1,145 @@
+// Copyright 2020 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 maps
+
+import (
+ "math"
+ "sort"
+ "testing"
+
+ "slices"
+)
+
+var m1 = map[int]int{1: 2, 2: 4, 4: 8, 8: 16}
+var m2 = map[int]string{1: "2", 2: "4", 4: "8", 8: "16"}
+
+func TestKeys(t *testing.T) {
+ want := []int{1, 2, 4, 8}
+
+ got1 := Keys(m1)
+ sort.Ints(got1)
+ if !slices.Equal(got1, want) {
+ t.Errorf("Keys(%v) = %v, want %v", m1, got1, want)
+ }
+
+ got2 := Keys(m2)
+ sort.Ints(got2)
+ if !slices.Equal(got2, want) {
+ t.Errorf("Keys(%v) = %v, want %v", m2, got2, want)
+ }
+}
+
+func TestValues(t *testing.T) {
+ got1 := Values(m1)
+ want1 := []int{2, 4, 8, 16}
+ sort.Ints(got1)
+ if !slices.Equal(got1, want1) {
+ t.Errorf("Values(%v) = %v, want %v", m1, got1, want1)
+ }
+
+ got2 := Values(m2)
+ want2 := []string{"16", "2", "4", "8"}
+ sort.Strings(got2)
+ if !slices.Equal(got2, want2) {
+ t.Errorf("Values(%v) = %v, want %v", m2, got2, want2)
+ }
+}
+
+func TestEqual(t *testing.T) {
+ if !Equal(m1, m1) {
+ t.Errorf("Equal(%v, %v) = false, want true", m1, m1)
+ }
+ if Equal(m1, nil) {
+ t.Errorf("Equal(%v, nil) = true, want false", m1)
+ }
+ if Equal(nil, m1) {
+ t.Errorf("Equal(nil, %v) = true, want false", m1)
+ }
+ if !Equal(int, int)(nil, nil) {
+ t.Error("Equal(nil, nil) = false, want true")
+ }
+ if ms := map[int]int{1: 2}; Equal(m1, ms) {
+ t.Errorf("Equal(%v, %v) = true, want false", m1, ms)
+ }
+
+ // Comparing NaN for equality is expected to fail.
+ mf := map[int]float64{1: 0, 2: math.NaN()}
+ if Equal(mf, mf) {
+ t.Errorf("Equal(%v, %v) = true, want false", mf, mf)
+ }
+}
+
+func TestCopy(t *testing.T) {
+ m2 := Copy(m1)
+ if !Equal(m1, m2) {
+ t.Errorf("Copy(%v) = %v, want %v", m1, m2, m1)
+ }
+ m2[16] = 32
+ if Equal(m1, m2) {
+ t.Errorf("Equal(%v, %v) = true, want false", m1, m2)
+ }
+}
+
+func TestAdd(t *testing.T) {
+ mc := Copy(m1)
+ Add(mc, mc)
+ if !Equal(mc, m1) {
+ t.Errorf("Add(%v, %v) = %v, want %v", m1, m1, mc, m1)
+ }
+ Add(mc, map[int]int{16: 32})
+ want := map[int]int{1: 2, 2: 4, 4: 8, 8: 16, 16: 32}
+ if !Equal(mc, want) {
+ t.Errorf("Add result = %v, want %v", mc, want)
+ }
+}
+
+func TestSub(t *testing.T) {
+ mc := Copy(m1)
+ Sub(mc, mc)
+ if len(mc) > 0 {
+ t.Errorf("Sub(%v, %v) = %v, want empty map", m1, m1, mc)
+ }
+ mc = Copy(m1)
+ Sub(mc, map[int]int{1: 0})
+ want := map[int]int{2: 4, 4: 8, 8: 16}
+ if !Equal(mc, want) {
+ t.Errorf("Sub result = %v, want %v", mc, want)
+ }
+}
+
+func TestIntersect(t *testing.T) {
+ mc := Copy(m1)
+ Intersect(mc, mc)
+ if !Equal(mc, m1) {
+ t.Errorf("Intersect(%v, %v) = %v, want %v", m1, m1, mc, m1)
+ }
+ Intersect(mc, map[int]int{1: 0, 2: 0})
+ want := map[int]int{1: 2, 2: 4}
+ if !Equal(mc, want) {
+ t.Errorf("Intersect result = %v, want %v", mc, want)
+ }
+}
+
+func TestFilter(t *testing.T) {
+ mc := Copy(m1)
+ Filter(mc, func(int, int) bool { return true })
+ if !Equal(mc, m1) {
+ t.Errorf("Filter(%v, true) = %v, want %v", m1, mc, m1)
+ }
+ Filter(mc, func(k, v int) bool { return k < 3 })
+ want := map[int]int{1: 2, 2: 4}
+ if !Equal(mc, want) {
+ t.Errorf("Filter result = %v, want %v", mc, want)
+ }
+}
+
+func TestTransformValues(t *testing.T) {
+ mc := Copy(m1)
+ TransformValues(mc, func(i int) int { return i / 2 })
+ want := map[int]int{1: 1, 2: 2, 4: 4, 8: 8}
+ if !Equal(mc, want) {
+ t.Errorf("TransformValues result = %v, want %v", mc, want)
+ }
+}
diff --git a/src/cmd/go2go/testdata/go2path/src/metrics/metrics.go2 b/src/cmd/go2go/testdata/go2path/src/metrics/metrics.go2
new file mode 100644
index 0000000..0467fd6
--- /dev/null
+++ b/src/cmd/go2go/testdata/go2path/src/metrics/metrics.go2
@@ -0,0 +1,117 @@
+// Copyright 2020 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 metrics provides tracking arbitrary metrics composed of
+// values of comparable types.
+package metrics
+
+import (
+ "sync"
+
+ "maps"
+)
+
+// Metric1 tracks metrics of values of some type.
+type Metric1[T comparable] struct {
+ mu sync.Mutex
+ m map[T]int
+}
+
+// Add adds another instance of some value.
+func (m *Metric1[T]) Add(v T) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ if m.m == nil {
+ m.m = make(map[T]int)
+ }
+ m.m[v]++
+}
+
+// Count returns the number of instances we've seen of v.
+func (m *Metric1[T]) Count(v T) int {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ return m.m[v]
+}
+
+// Metrics returns all the values we've seen, in an indeterminate order.
+func (m *Metric1[T]) Metrics() []T {
+ return maps.Keys(m.m)
+}
+
+type key2[T1, T2 comparable] struct {
+ f1 T1
+ f2 T2
+}
+
+// Metric2 tracks metrics of pairs of values.
+type Metric2[T1, T2 comparable] struct {
+ mu sync.Mutex
+ m map[key2[T1, T2]]int
+}
+
+// Add adds another instance of some pair of values.
+func (m *Metric2[T1, T2]) Add(v1 T1, v2 T2) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ if m.m == nil {
+ m.m = make(map[key2[T1, T2]]int)
+ }
+ m.m[key2[T1, T2]{v1, v2}]++
+}
+
+// Count returns the number of instances we've seen of v1/v2.
+func (m *Metric2[T1, T2]) Count(v1 T1, v2 T2) int {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ return m.m[key2[T1, T2]{v1, v2}]
+}
+
+// Metrics returns all the values we've seen, in an indeterminate order.
+func (m *Metric2[T1, T2]) Metrics() (r1 []T1, r2 []T2) {
+ for _, k := range maps.Keys(m.m) {
+ r1 = append(r1, k.f1)
+ r2 = append(r2, k.f2)
+ }
+ return r1, r2
+}
+
+type key3[T1, T2, T3 comparable] struct {
+ f1 T1
+ f2 T2
+ f3 T3
+}
+
+// Metric3 tracks metrics of triplets of values.
+type Metric3[T1, T2, T3 comparable] struct {
+ mu sync.Mutex
+ m map[key3[T1, T2, T3]]int
+}
+
+// Add adds another instance of some triplet of values.
+func (m *Metric3[T1, T2, T3]) Add(v1 T1, v2 T2, v3 T3) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ if m.m == nil {
+ m.m = make(map[key3[T1, T2, T3]]int)
+ }
+ m.m[key3[T1, T2, T3]{v1, v2, v3}]++
+}
+
+// Count returns the number of instances we've seen of v1/v2/v3.
+func (m *Metric3[T1, T2, T3]) Count(v1 T1, v2 T2, v3 T3) int {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ return m.m[key3[T1, T2, T3]{v1, v2, v3}]
+}
+
+// Metrics returns all the values we've seen, in an indeterminate order.
+func (m *Metric3[T1, T2, T3]) Metrics() (r1 []T1, r2 []T2, r3 []T3) {
+ for k := range m.m {
+ r1 = append(r1, k.f1)
+ r2 = append(r2, k.f2)
+ r3 = append(r3, k.f3)
+ }
+ return r1, r2, r3
+}
diff --git a/src/cmd/go2go/testdata/go2path/src/metrics/metrics_test.go2 b/src/cmd/go2go/testdata/go2path/src/metrics/metrics_test.go2
new file mode 100644
index 0000000..1fbf4b8
--- /dev/null
+++ b/src/cmd/go2go/testdata/go2path/src/metrics/metrics_test.go2
@@ -0,0 +1,57 @@
+// Copyright 2020 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 metrics
+
+import (
+ "sort"
+ "testing"
+
+ "slices"
+)
+
+type S struct{ a, b, c string }
+
+func TestMetrics(t *testing.T) {
+ m1 := Metric1[string]{}
+ if got := m1.Count("a"); got != 0 {
+ t.Errorf("Count(%q) = %d, want 0", "a", got)
+ }
+ m1.Add("a")
+ m1.Add("a")
+ if got := m1.Count("a"); got != 2 {
+ t.Errorf("Count(%q) = %d, want 2", "a", got)
+ }
+ if got, want := m1.Metrics(), []string{"a"}; !slices.Equal(got, want) {
+ t.Errorf("Metrics = %v, want %v", got, want)
+ }
+
+ m2 := Metric2[int, float64]{}
+ m2.Add(1, 1)
+ m2.Add(2, 2)
+ m2.Add(3, 3)
+ m2.Add(3, 3)
+ k1, k2 := m2.Metrics()
+
+ sort.Ints(k1)
+ w1 := []int{1, 2, 3}
+ if !slices.Equal(k1, w1) {
+ t.Errorf("Metric2.Metrics first slice = %v, want %v", k1, w1)
+ }
+
+ sort.Float64s(k2)
+ w2 := []float64{1, 2, 3}
+ if !slices.Equal(k2, w2) {
+ t.Errorf("Metric2.Metrics first slice = %v, want %v", k2, w2)
+ }
+
+ m3 := Metric3[string, S, S]{}
+ m3.Add("a", S{"d", "e", "f"}, S{"g", "h", "i"})
+ m3.Add("a", S{"d", "e", "f"}, S{"g", "h", "i"})
+ m3.Add("a", S{"d", "e", "f"}, S{"g", "h", "i"})
+ m3.Add("b", S{"d", "e", "f"}, S{"g", "h", "i"})
+ if got := m3.Count("a", S{"d", "e", "f"}, S{"g", "h", "i"}); got != 3 {
+ t.Errorf("Count(%v, %v, %v) = %d, want 3", "a", S{"d", "e", "f"}, S{"g", "h", "i"}, got)
+ }
+}
diff --git a/src/cmd/go2go/testdata/go2path/src/orderedmap/orderedmap.go2 b/src/cmd/go2go/testdata/go2path/src/orderedmap/orderedmap.go2
new file mode 100644
index 0000000..e1ea5dc
--- /dev/null
+++ b/src/cmd/go2go/testdata/go2path/src/orderedmap/orderedmap.go2
@@ -0,0 +1,136 @@
+// Copyright 2020 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 orderedmap provides an ordered map, implemented as a binary tree.
+package orderedmap
+
+// FIXME: This should probably be container/orderedmap.
+
+import (
+ "context"
+
+ "chans"
+ "constraints"
+)
+
+// Map is an ordered map.
+type Map[K, V any] struct {
+ root *node[K, V]
+ compare func(K, K) int
+}
+
+// node is the type of a node in the binary tree.
+type node[K, V any] struct {
+ key K
+ val V
+ left, right *node[K, V]
+}
+
+// New returns a new map. It takes a comparison function that compares two
+// keys and returns < 0 if the first is less, == 0 if they are equal,
+// > 0 if the first is greater.
+func New[K, V any](compare func(K, K) int) *Map[K, V] {
+ return &Map[K, V]{compare: compare}
+}
+
+// NewOrdered returns a new map whose key is an ordered type.
+// This is like New, but does not require providing a compare function.
+// The map compare function uses the obvious key ordering.
+func NewOrdered[K constraints.Ordered, V any]() *Map[K, V] {
+ return New[K, V](func(k1, k2 K) int {
+ switch {
+ case k1 < k2:
+ return -1
+ case k1 == k2:
+ return 0
+ default:
+ return 1
+ }
+ })
+}
+
+// find looks up key in the map, returning either a pointer to the slot of the
+// node holding key, or a pointer to the slot where should a node would go.
+func (m *Map[K, V]) find(key K) **node[K, V] {
+ pn := &m.root
+ for *pn != nil {
+ switch cmp := m.compare(key, (*pn).key); {
+ case cmp < 0:
+ pn = &(*pn).left
+ case cmp > 0:
+ pn = &(*pn).right
+ default:
+ return pn
+ }
+ }
+ return pn
+}
+
+// Insert inserts a new key/value into the map.
+// If the key is already present, the value is replaced.
+// Reports whether this is a new key.
+func (m *Map[K, V]) Insert(key K, val V) bool {
+ pn := m.find(key)
+ if *pn != nil {
+ (*pn).val = val
+ return false
+ }
+ *pn = &node[K, V]{key: key, val: val}
+ return true
+}
+
+// Find returns the value associated with a key, or the zero value
+// if not present. The found result reports whether the key was found.
+func (m *Map[K, V]) Find(key K) (V, bool) {
+ pn := m.find(key)
+ if *pn == nil {
+ var zero V
+ return zero, false
+ }
+ return (*pn).val, true
+}
+
+// keyValue is a pair of key and value used while iterating.
+type keyValue[K, V any] struct {
+ key K
+ val V
+}
+
+// iterate returns an iterator that traverses the map.
+func (m *Map[K, V]) Iterate() *Iterator[K, V] {
+ sender, receiver := chans.Ranger(keyValue[K, V])()
+ var f func(*node[K, V]) bool
+ f = func(n *node[K, V]) bool {
+ if n == nil {
+ return true
+ }
+ // Stop the traversal if Send fails, which means that
+ // nothing is listening to the receiver.
+ return f(n.left) &&
+ sender.Send(context.Background(), keyValue[K, V]{n.key, n.val}) &&
+ f(n.right)
+ }
+ go func() {
+ f(m.root)
+ sender.Close()
+ }()
+ return &Iterator[K, V]{receiver}
+}
+
+// Iterator is used to iterate over the map.
+type Iterator[K, V any] struct {
+ r *chans.Receiver[keyValue[K, V]]
+}
+
+// Next returns the next key and value pair, and a boolean that reports
+// whether they are valid. If not valid, we have reached the end of the map.
+func (it *Iterator[K, V]) Next() (K, V, bool) {
+ keyval, ok := it.r.Next(context.Background())
+ if !ok {
+ var zerok K
+ var zerov V
+ return zerok, zerov, false
+ }
+ return keyval.key, keyval.val, true
+}
diff --git a/src/cmd/go2go/testdata/go2path/src/orderedmap/orderedmap_test.go2 b/src/cmd/go2go/testdata/go2path/src/orderedmap/orderedmap_test.go2
new file mode 100644
index 0000000..dd7306b
--- /dev/null
+++ b/src/cmd/go2go/testdata/go2path/src/orderedmap/orderedmap_test.go2
@@ -0,0 +1,63 @@
+// Copyright 2020 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 orderedmap
+
+import (
+ "bytes"
+ "testing"
+
+ "slices"
+)
+
+func TestMap(t *testing.T) {
+ m := New[[]byte, int](bytes.Compare)
+
+ if _, found := m.Find([]byte("a")); found {
+ t.Errorf("unexpectedly found %q in empty map", []byte("a"))
+ }
+ if !m.Insert([]byte("a"), 'a') {
+ t.Errorf("key %q unexpectedly already present", []byte("a"))
+ }
+ if !m.Insert([]byte("c"), 'c') {
+ t.Errorf("key %q unexpectedly already present", []byte("c"))
+ }
+ if !m.Insert([]byte("b"), 'b') {
+ t.Errorf("key %q unexpectedly already present", []byte("b"))
+ }
+ if m.Insert([]byte("c"), 'x') {
+ t.Errorf("key %q unexpectedly not present", []byte("c"))
+ }
+
+ if v, found := m.Find([]byte("a")); !found {
+ t.Errorf("did not find %q", []byte("a"))
+ } else if v != 'a' {
+ t.Errorf("key %q returned wrong value %c, expected %c", []byte("a"), v, 'a')
+ }
+ if v, found := m.Find([]byte("c")); !found {
+ t.Errorf("did not find %q", []byte("c"))
+ } else if v != 'x' {
+ t.Errorf("key %q returned wrong value %c, expected %c", []byte("c"), v, 'x')
+ }
+
+ if _, found := m.Find([]byte("d")); found {
+ t.Errorf("unexpectedly found %q", []byte("d"))
+ }
+
+ gather := func(it *Iterator[[]byte, int]) []int {
+ var r []int
+ for {
+ _, v, ok := it.Next()
+ if !ok {
+ return r
+ }
+ r = append(r, v)
+ }
+ }
+ got := gather(m.Iterate())
+ want := []int{'a', 'b', 'x'}
+ if !slices.Equal(got, want) {
+ t.Errorf("Iterate returned %v, want %v", got, want)
+ }
+}
diff --git a/src/cmd/go2go/testdata/go2path/src/sets/sets.go2 b/src/cmd/go2go/testdata/go2path/src/sets/sets.go2
new file mode 100644
index 0000000..539a98c
--- /dev/null
+++ b/src/cmd/go2go/testdata/go2path/src/sets/sets.go2
@@ -0,0 +1,111 @@
+// Copyright 2020 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 sets implements sets of any type.
+package sets
+
+// A Set is a set of elements of some type.
+type Set[Elem comparable] struct {
+ m map[Elem]struct{}
+}
+
+// Make makes a new set.
+func Make[Elem comparable]() Set[Elem] {
+ return Set[Elem]{m: make(map[Elem]struct{})}
+}
+
+// Add adds an element to a set.
+func (s Set[Elem]) Add(v Elem) {
+ s.m[v] = struct{}{}
+}
+
+// Delete removes an element from a set. If the element is not present
+// in the set, this does nothing.
+func (s Set[Elem]) Delete(v Elem) {
+ delete(s.m, v)
+}
+
+// Contains reports whether v is in the set.
+func (s Set[Elem]) Contains(v Elem) bool {
+ _, ok := s.m[v]
+ return ok
+}
+
+// Len returns the number of elements in the set.
+func (s Set[Elem]) Len() int {
+ return len(s.m)
+}
+
+// Values returns the values in the set.
+// The values will be in an indeterminate order.
+func (s Set[Elem]) Values() []Elem {
+ r := make([]Elem, 0, len(s.m))
+ for v := range s.m {
+ r = append(r, v)
+ }
+ return r
+}
+
+// Equal reports whether two sets contain the same elements.
+func Equal[Elem comparable](s1, s2 Set[Elem]) bool {
+ if len(s1.m) != len(s2.m) {
+ return false
+ }
+ for v1 := range s1.m {
+ if !s2.Contains(v1) {
+ return false
+ }
+ }
+ return true
+}
+
+// Copy returns a copy of s.
+func (s Set[Elem]) Copy() Set[Elem] {
+ r := Set[Elem]{m: make(map[Elem]struct{}, len(s.m))}
+ for v := range s.m {
+ r.m[v] = struct{}{}
+ }
+ return r
+}
+
+// AddSet adds all the elements of s2 to s.
+func (s Set[Elem]) AddSet(s2 Set[Elem]) {
+ for v := range s2.m {
+ s.m[v] = struct{}{}
+ }
+}
+
+// SubSet removes all elements in s2 from s.
+// Values in s2 that are not in s are ignored.
+func (s Set[Elem]) SubSet(s2 Set[Elem]) {
+ for v := range s2.m {
+ delete(s.m, v)
+ }
+}
+
+// Intersect removes all elements from s that are not present in s2.
+// Values in s2 that are not in s are ignored.
+func (s Set[Elem]) Intersect(s2 Set[Elem]) {
+ for v := range s.m {
+ if !s2.Contains(v) {
+ delete(s.m, v)
+ }
+ }
+}
+
+// Iterate calls f on every element in the set.
+func (s Set[Elem]) Iterate(f func(Elem)) {
+ for v := range s.m {
+ f(v)
+ }
+}
+
+// Filter deletes any elements from s for which f returns false.
+func (s Set[Elem]) Filter(f func(Elem) bool) {
+ for v := range s.m {
+ if !f(v) {
+ delete(s.m, v)
+ }
+ }
+}
diff --git a/src/cmd/go2go/testdata/go2path/src/sets/sets_test.go2 b/src/cmd/go2go/testdata/go2path/src/sets/sets_test.go2
new file mode 100644
index 0000000..6994d9d
--- /dev/null
+++ b/src/cmd/go2go/testdata/go2path/src/sets/sets_test.go2
@@ -0,0 +1,146 @@
+// Copyright 2020 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 sets
+
+import (
+ "sort"
+ "testing"
+
+ "slices"
+)
+
+func TestSet(t *testing.T) {
+ s1 := Make[int]()
+ if got := s1.Len(); got != 0 {
+ t.Errorf("Len of empty set = %d, want 0", got)
+ }
+ s1.Add(1)
+ s1.Add(1)
+ s1.Add(1)
+ if got := s1.Len(); got != 1 {
+ t.Errorf("(%v).Len() == %d, want 1", s1, got)
+ }
+ s1.Add(2)
+ s1.Add(3)
+ s1.Add(4)
+ if got := s1.Len(); got != 4 {
+ t.Errorf("(%v).Len() == %d, want 4", s1, got)
+ }
+ if !s1.Contains(1) {
+ t.Errorf("(%v).Contains(1) == false, want true", s1)
+ }
+ if s1.Contains(5) {
+ t.Errorf("(%v).Contains(5) == true, want false", s1)
+ }
+ vals := s1.Values()
+ sort.Ints(vals)
+ w1 := []int{1, 2, 3, 4}
+ if !slices.Equal(vals, w1) {
+ t.Errorf("(%v).Values() == %v, want %v", s1, vals, w1)
+ }
+}
+
+func TestEqual(t *testing.T) {
+ s1 := Make[string]()
+ s2 := Make[string]()
+ if !Equal(s1, s2) {
+ t.Errorf("Equal(%v, %v) = false, want true", s1, s2)
+ }
+ s1.Add("hello")
+ s1.Add("world")
+ if got := s1.Len(); got != 2 {
+ t.Errorf("(%v).Len() == %d, want 2", s1, got)
+ }
+ if Equal(s1, s2) {
+ t.Errorf("Equal(%v, %v) = true, want false", s1, s2)
+ }
+}
+
+func TestCopy(t *testing.T) {
+ s1 := Make[float64]()
+ s1.Add(0)
+ s2 := s1.Copy()
+ if !Equal(s1, s2) {
+ t.Errorf("Equal(%v, %v) = false, want true", s1, s2)
+ }
+ s1.Add(1)
+ if Equal(s1, s2) {
+ t.Errorf("Equal(%v, %v) = true, want false", s1, s2)
+ }
+}
+
+func TestAddSet(t *testing.T) {
+ s1 := Make[int]()
+ s1.Add(1)
+ s1.Add(2)
+ s2 := Make[int]()
+ s2.Add(2)
+ s2.Add(3)
+ s1.AddSet(s2)
+ if got := s1.Len(); got != 3 {
+ t.Errorf("(%v).Len() == %d, want 3", s1, got)
+ }
+ s2.Add(1)
+ if !Equal(s1, s2) {
+ t.Errorf("Equal(%v, %v) = false, want true", s1, s2)
+ }
+}
+
+func TestSubSet(t *testing.T) {
+ s1 := Make[int]()
+ s1.Add(1)
+ s1.Add(2)
+ s2 := Make[int]()
+ s2.Add(2)
+ s2.Add(3)
+ s1.SubSet(s2)
+ if got := s1.Len(); got != 1 {
+ t.Errorf("(%v).Len() == %d, want 1", s1, got)
+ }
+ if vals, want := s1.Values(), []int{1}; !slices.Equal(vals, want) {
+ t.Errorf("after SubSet got %v, want %v", vals, want)
+ }
+}
+
+func TestIntersect(t *testing.T) {
+ s1 := Make[int]()
+ s1.Add(1)
+ s1.Add(2)
+ s2 := Make[int]()
+ s2.Add(2)
+ s2.Add(3)
+ s1.Intersect(s2)
+ if got := s1.Len(); got != 1 {
+ t.Errorf("(%v).Len() == %d, want 1", s1, got)
+ }
+ if vals, want := s1.Values(), []int{2}; !slices.Equal(vals, want) {
+ t.Errorf("after Intersect got %v, want %v", vals, want)
+ }
+}
+
+func TestIterate(t *testing.T) {
+ s1 := Make[int]()
+ s1.Add(1)
+ s1.Add(2)
+ s1.Add(3)
+ s1.Add(4)
+ tot := 0
+ s1.Iterate(func(i int) { tot += i })
+ if tot != 10 {
+ t.Errorf("total of %v == %d, want 10", s1, tot)
+ }
+}
+
+func TestFilter(t *testing.T) {
+ s1 := Make[int]()
+ s1.Add(1)
+ s1.Add(2)
+ s1.Add(3)
+ s1.Filter(func(v int) bool { return v%2 == 0 })
+ if vals, want := s1.Values(), []int{2}; !slices.Equal(vals, want) {
+ t.Errorf("after Filter got %v, want %v", vals, want)
+ }
+
+}
diff --git a/src/cmd/go2go/testdata/go2path/src/slices/slices.go2 b/src/cmd/go2go/testdata/go2path/src/slices/slices.go2
new file mode 100644
index 0000000..990ad43
--- /dev/null
+++ b/src/cmd/go2go/testdata/go2path/src/slices/slices.go2
@@ -0,0 +1,125 @@
+// Copyright 2020 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 slices provides functions for basic operations on
+// slices of any element type.
+package slices
+
+import (
+ "alg"
+ "constraints"
+)
+
+// Equal reports whether two slices are equal: the same length and all
+// elements equal. All floating point NaNs are considered equal.
+func Equal[Elem comparable](s1, s2 []Elem) bool {
+ if len(s1) != len(s2) {
+ return false
+ }
+ for i, v1 := range s1 {
+ v2 := s2[i]
+ if v1 != v2 {
+ isNaN := func(f Elem) bool { return f != f }
+ if !isNaN(v1) || !isNaN(v2) {
+ return false
+ }
+ }
+ }
+ return true
+}
+
+// EqualFn reports whether two slices are equal using a comparision
+// function on each element.
+func EqualFn[Elem any](s1, s2 []Elem, eq func(Elem, Elem) bool) bool {
+ if len(s1) != len(s2) {
+ return false
+ }
+ for i, v1 := range s1 {
+ v2 := s2[i]
+ if !eq(v1, v2) {
+ return false
+ }
+ }
+ return true
+}
+
+// Map turns a []Elem1 to a []Elem2 using a mapping function.
+func Map[Elem1, Elem2 any](s []Elem1, f func(Elem1) Elem2) []Elem2 {
+ r := make([]Elem2, len(s))
+ for i, v := range s {
+ r[i] = f(v)
+ }
+ return r
+}
+
+// Reduce reduces a []Elem1 to a single value of type Elem2 using
+// a reduction function.
+func Reduce[Elem1, Elem2 any](s []Elem1, initializer Elem2, f func(Elem2, Elem1) Elem2) Elem2 {
+ r := initializer
+ for _, v := range s {
+ r = f(r, v)
+ }
+ return r
+}
+
+// Filter filters values from a slice using a filter function.
+func Filter[Elem any](s []Elem, f func(Elem) bool) []Elem {
+ var r []Elem
+ for _, v := range s {
+ if f(v) {
+ r = append(r, v)
+ }
+ }
+ return r
+}
+
+// Max returns the maximum element in a slice of some ordered type.
+// If the slice is empty it returns the zero value of the element type.
+func Max[Elem constraints.Ordered](s []Elem) Elem {
+ if len(s) == 0 {
+ var zero Elem
+ return zero
+ }
+ return Reduce(s[1:], s[0], alg.Max(Elem))
+}
+
+// Min returns the minimum element in a slice of some ordered type.
+// If the slice is empty it returns the zero value of the element type.
+func Min[Elem constraints.Ordered](s []Elem) Elem {
+ if len(s) == 0 {
+ var zero Elem
+ return zero
+ }
+ return Reduce(s[1:], s[0], alg.Min(Elem))
+}
+
+// Append adds values to the end of a slice, returning a new slice.
+// This is like the predeclared append function; it's an example
+// of how to write it using generics. We used to write code like
+// this before append was added to the language, but we had to write
+// a separate copy for each type.
+func Append[T any](s []T, t ...T) []T {
+ lens := len(s)
+ tot := lens + len(t)
+ if tot <= cap(s) {
+ s = s[:tot]
+ } else {
+ news := make([]T, tot, tot + tot/2)
+ Copy(news, s)
+ s = news
+ }
+ Copy(s[lens:tot], t)
+ return s
+}
+
+// Copy copies values from t to s, stopping when either slice is full,
+// returning the number of values copied. This is like the predeclared
+// copy function; it's an example of how to write it using generics.
+func Copy[T any](s, t []T) int {
+ i := 0
+ for ; i < len(s) && i < len(t); i++ {
+ s[i] = t[i]
+ }
+ return i
+}
diff --git a/src/cmd/go2go/testdata/go2path/src/slices/slices_test.go2 b/src/cmd/go2go/testdata/go2path/src/slices/slices_test.go2
new file mode 100644
index 0000000..c90474a
--- /dev/null
+++ b/src/cmd/go2go/testdata/go2path/src/slices/slices_test.go2
@@ -0,0 +1,164 @@
+// Copyright 2020 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 slices
+
+import (
+ "math"
+ "strings"
+ "testing"
+
+ "constraints"
+)
+
+func TestEqual(t *testing.T) {
+ s1 := []int{1, 2, 3}
+ if !Equal(s1, s1) {
+ t.Errorf("Equal(%v, %v) = false, want true", s1, s1)
+ }
+ s2 := []int{1, 2, 3}
+ if !Equal(s1, s2) {
+ t.Errorf("Equal(%v, %v) = false, want true", s1, s2)
+ }
+ s2 = append(s2, 4)
+ if Equal(s1, s2) {
+ t.Errorf("Equal(%v, %v) = true, want false", s1, s2)
+ }
+
+ s3 := []float64{1, 2, math.NaN()}
+ if !Equal(s3, s3) {
+ t.Errorf("Equal(%v, %v) = false, want true", s3, s3)
+ }
+
+ if Equal(s1, nil) {
+ t.Errorf("Equal(%v, nil) = true, want false", s1)
+ }
+ if Equal(nil, s1) {
+ t.Errorf("Equal(nil, %v) = true, want false", s1)
+ }
+ if !Equal(s1[:0], nil) {
+ t.Errorf("Equal(%v, nil = false, want true", s1[:0])
+ }
+}
+
+func offByOne[Elem constraints.Integer](a, b Elem) bool {
+ return a == b + 1 || a == b - 1
+}
+
+func TestEqualFn(t *testing.T) {
+ s1 := []int{1, 2, 3}
+ s2 := []int{2, 3, 4}
+ if EqualFn(s1, s1, offByOne(int)) {
+ t.Errorf("EqualFn(%v, %v, offByOne) = true, want false", s1, s1)
+ }
+ if !EqualFn(s1, s2, offByOne(int)) {
+ t.Errorf("EqualFn(%v, %v, offByOne) = false, want true", s1, s2)
+ }
+
+ s3 := []string{"a", "b", "c"}
+ s4 := []string{"A", "B", "C"}
+ if !EqualFn(s3, s4, strings.EqualFold) {
+ t.Errorf("EqualFn(%v, %v, strings.EqualFold) = false, want true", s3, s4)
+ }
+
+ if !EqualFn(s1[:0], nil, offByOne(int)) {
+ t.Errorf("EqualFn(%v, nil, offByOne) = false, want true", s1[:0])
+ }
+}
+
+func TestMap(t *testing.T) {
+ s1 := []int{1, 2, 3}
+ s2 := Map(s1, func(i int) float64 { return float64(i) * 2.5 })
+ if want := []float64{2.5, 5, 7.5}; !Equal(s2, want) {
+ t.Errorf("Map(%v, ...) = %v, want %v", s1, s2, want)
+ }
+
+ s3 := []string{"Hello", "World"}
+ s4 := Map(s3, strings.ToLower)
+ if want := []string{"hello", "world"}; !Equal(s4, want) {
+ t.Errorf("Map(%v, strings.ToLower) = %v, want %v", s3, s4, want)
+ }
+
+ s5 := Map(nil, func(i int) int { return i })
+ if len(s5) != 0 {
+ t.Errorf("Map(nil, identity) = %v, want empty slice", s5)
+ }
+}
+
+func TestReduce(t *testing.T) {
+ s1 := []int{1, 2, 3}
+ r := Reduce(s1, 0, func(f float64, i int) float64 { return float64(i) * 2.5 + f })
+ if want := 15.0; r != want {
+ t.Errorf("Reduce(%v, 0, ...) = %v, want %v", s1, r, want)
+ }
+
+ if got := Reduce(nil, 0, func(i, j int) int { return i + j}); got != 0 {
+ t.Errorf("Reduce(nil, 0, add) = %v, want 0", got)
+ }
+}
+
+func TestFilter(t *testing.T) {
+ s1 := []int{1, 2, 3}
+ s2 := Filter(s1, func(i int) bool { return i%2 == 0 })
+ if want := []int{2}; !Equal(s2, want) {
+ t.Errorf("Filter(%v, even) = %v, want %v", s1, s2, want)
+ }
+
+ if s3 := Filter(s1[:0], func(i int) bool { return true }); len(s3) > 0 {
+ t.Errorf("Filter(%v, identity) = %v, want empty slice", s1[:0], s3)
+ }
+}
+
+func TestMax(t *testing.T) {
+ s1 := []int{1, 2, 3, -5}
+ if got, want := Max(s1), 3; got != want {
+ t.Errorf("Max(%v) = %d, want %d", s1, got, want)
+ }
+
+ s2 := []string{"aaa", "a", "aa", "aaaa"}
+ if got, want := Max(s2), "aaaa"; got != want {
+ t.Errorf("Max(%v) = %q, want %q", s2, got, want)
+ }
+
+ if got, want := Max(s2[:0]), ""; got != want {
+ t.Errorf("Max(%v) = %q, want %q", s2[:0], got, want)
+ }
+}
+
+func TestMin(t *testing.T) {
+ s1 := []int{1, 2, 3, -5}
+ if got, want := Min(s1), -5; got != want {
+ t.Errorf("Min(%v) = %d, want %d", s1, got, want)
+ }
+
+ s2 := []string{"aaa", "a", "aa", "aaaa"}
+ if got, want := Min(s2), "a"; got != want {
+ t.Errorf("Min(%v) = %q, want %q", s2, got, want)
+ }
+
+ if got, want := Min(s2[:0]), ""; got != want {
+ t.Errorf("Min(%v) = %q, want %q", s2[:0], got, want)
+ }
+}
+
+func TestAppend(t *testing.T) {
+ s := []int{1, 2, 3}
+ s = Append(s, 4, 5, 6)
+ want := []int{1, 2, 3, 4, 5, 6}
+ if !Equal(s, want) {
+ t.Errorf("after Append got %v, want %v", s, want)
+ }
+}
+
+func TestCopy(t *testing.T) {
+ s1 := []int{1, 2, 3}
+ s2 := []int{4, 5}
+ if got := Copy(s1, s2); got != 2 {
+ t.Errorf("Copy returned %d, want 2", got)
+ }
+ want := []int{4, 5, 3}
+ if !Equal(s1, want) {
+ t.Errorf("after Copy got %v, want %v", s1, want)
+ }
+}
diff --git a/src/cmd/go2go/translate.go b/src/cmd/go2go/translate.go
new file mode 100644
index 0000000..a74fd4c
--- /dev/null
+++ b/src/cmd/go2go/translate.go
@@ -0,0 +1,33 @@
+// Copyright 2020 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 main
+
+import (
+ "go/go2go"
+ "io/ioutil"
+ "strings"
+)
+
+// translate writes .go files for all .go2 files in dir.
+func translate(importer *go2go.Importer, dir string) {
+ if err := go2go.Rewrite(importer, dir); err != nil {
+ die(err.Error())
+ }
+}
+
+// translateFile translates one .go2 file into a .go file.
+func translateFile(importer *go2go.Importer, file string) {
+ data, err := ioutil.ReadFile(file)
+ if err != nil {
+ die(err.Error())
+ }
+ out, err := go2go.RewriteBuffer(importer, file, data)
+ if err != nil {
+ die(err.Error())
+ }
+ if err := ioutil.WriteFile(strings.TrimSuffix(file, ".go2")+".go", out, 0644); err != nil {
+ die(err.Error())
+ }
+}
diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go
index 2793c2c..a43cda1 100644
--- a/src/cmd/gofmt/gofmt.go
+++ b/src/cmd/gofmt/gofmt.go
@@ -67,7 +67,8 @@
}
func initParserMode() {
- parserMode = parser.ParseComments
+ // Keep this in sync with go/format/format.go.
+ parserMode = parser.ParseComments | parser.ParseTypeParams
if *allErrors {
parserMode |= parser.AllErrors
}
diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go
index c97c668..322765c 100644
--- a/src/go/build/deps_test.go
+++ b/src/go/build/deps_test.go
@@ -514,6 +514,9 @@
FMT, container/heap, math/rand
< internal/trace;
+
+ # go2go: a package that should only exist on the dev.go2go branch
+ go/build, go/importer, go/printer, go/types, log < go/go2go;
`
// listStdPkgs returns the same list of packages as "go list std".
diff --git a/src/go/format/format.go b/src/go/format/format.go
index a603d96..279fc2e 100644
--- a/src/go/format/format.go
+++ b/src/go/format/format.go
@@ -38,7 +38,7 @@
var config = printer.Config{Mode: printerMode, Tabwidth: tabWidth}
-const parserMode = parser.ParseComments
+const parserMode = parser.ParseComments | parser.ParseTypeParams
// Node formats node in canonical gofmt style and writes the result to dst.
//
diff --git a/src/go/go2go/go2go.go b/src/go/go2go/go2go.go
new file mode 100644
index 0000000..b0c4873
--- /dev/null
+++ b/src/go/go2go/go2go.go
@@ -0,0 +1,272 @@
+// Copyright 2020 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 go2go rewrites polymorphic code into non-polymorphic code.
+package go2go
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/token"
+ "go/types"
+ "io"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+)
+
+// rewritePrefix is what we put at the start of each newly generated .go file.
+const rewritePrefix = "// Code generated by go2go; DO NOT EDIT.\n\n"
+
+// Rewrite rewrites the contents of a single directory.
+// It looks for all files with the extension .go2, and parses
+// them as a single package. It writes out a .go file with any
+// polymorphic code rewritten into normal code.
+func Rewrite(importer *Importer, dir string) error {
+ _, err := rewriteToPkgs(importer, "", dir)
+ return err
+}
+
+// rewriteToPkgs rewrites the contents of a single directory,
+// and returns the types.Packages that it computes.
+func rewriteToPkgs(importer *Importer, importPath, dir string) ([]*types.Package, error) {
+ go2files, gofiles, err := go2Files(dir)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := checkAndRemoveGofiles(dir, gofiles); err != nil {
+ return nil, err
+ }
+
+ return rewriteFilesInPath(importer, importPath, dir, go2files)
+}
+
+// namedAST holds a file name and the AST parsed from that file.
+type namedAST struct {
+ name string
+ ast *ast.File
+}
+
+// RewriteFiles rewrites a set of .go2 files in dir.
+func RewriteFiles(importer *Importer, dir string, go2files []string) ([]*types.Package, error) {
+ return rewriteFilesInPath(importer, "", dir, go2files)
+}
+
+// rewriteFilesInPath rewrites a set of .go2 files in dir for importPath.
+func rewriteFilesInPath(importer *Importer, importPath, dir string, go2files []string) ([]*types.Package, error) {
+ fset := token.NewFileSet()
+ pkgs, err := parseFiles(importer, dir, go2files, fset)
+ if err != nil {
+ return nil, err
+ }
+
+ var rpkgs []*types.Package
+ var tpkgs [][]namedAST
+ for _, pkg := range pkgs {
+ pkgfiles := make([]namedAST, 0, len(pkg.Files))
+ for n, f := range pkg.Files {
+ pkgfiles = append(pkgfiles, namedAST{n, f})
+ }
+ sort.Slice(pkgfiles, func(i, j int) bool {
+ return pkgfiles[i].name < pkgfiles[j].name
+ })
+
+ asts := make([]*ast.File, 0, len(pkgfiles))
+ for _, a := range pkgfiles {
+ asts = append(asts, a.ast)
+ }
+
+ var merr multiErr
+ conf := types.Config{
+ Importer: importer,
+ Error: merr.add,
+ }
+ path := importPath
+ if path == "" {
+ path = pkg.Name
+ }
+ tpkg, err := conf.Check(path, fset, asts, importer.info)
+ if err != nil {
+ return nil, fmt.Errorf("type checking failed for %s\n%v", pkg.Name, merr)
+ }
+
+ importer.record(pkg.Name, pkgfiles, importPath, tpkg, asts)
+
+ rpkgs = append(rpkgs, tpkg)
+ tpkgs = append(tpkgs, pkgfiles)
+ }
+
+ for i, tpkg := range tpkgs {
+ addImportable := 0
+ for j, pkgfile := range tpkg {
+ if !strings.HasSuffix(pkgfile.name, "_test.go2") {
+ addImportable = j
+ break
+ }
+ }
+
+ for j, pkgfile := range tpkg {
+ if err := rewriteFile(dir, fset, importer, importPath, rpkgs[i], pkgfile.name, pkgfile.ast, j == addImportable); err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ return rpkgs, nil
+}
+
+// RewriteBuffer rewrites the contents of a single file, in a buffer.
+// It returns a modified buffer. The filename parameter is only used
+// for error messages.
+func RewriteBuffer(importer *Importer, filename string, file []byte) ([]byte, error) {
+ fset := token.NewFileSet()
+ pf, err := parser.ParseFile(fset, filename, file, parser.ParseTypeParams)
+ if err != nil {
+ return nil, err
+ }
+ var merr multiErr
+ conf := types.Config{
+ Importer: importer,
+ Error: merr.add,
+ }
+ tpkg, err := conf.Check(pf.Name.Name, fset, []*ast.File{pf}, importer.info)
+ if err != nil {
+ return nil, fmt.Errorf("type checking failed for %s\n%v", pf.Name.Name, merr)
+ }
+ importer.addIDs(pf)
+ if err := rewriteAST(fset, importer, "", tpkg, pf, true); err != nil {
+ return nil, err
+ }
+ var buf bytes.Buffer
+ fmt.Fprintln(&buf, rewritePrefix)
+ if err := config.Fprint(&buf, fset, pf); err != nil {
+ return nil, err
+ }
+ return buf.Bytes(), nil
+}
+
+// go2Files returns the list of files in dir with a .go2 extension
+// and a list of files with a .go extension.
+// This returns an error if it finds any .go files that do not start
+// with rewritePrefix.
+func go2Files(dir string) (go2files []string, gofiles []string, err error) {
+ f, err := os.Open(dir)
+ if err != nil {
+ return nil, nil, err
+ }
+ defer f.Close()
+
+ files, err := f.Readdirnames(0)
+ if err != nil {
+ return nil, nil, fmt.Errorf("reading directory %s: %w", dir, err)
+
+ }
+
+ go2files = make([]string, 0, len(files))
+ gofiles = make([]string, 0, len(files))
+ for _, f := range files {
+ switch filepath.Ext(f) {
+ case ".go2":
+ go2files = append(go2files, f)
+ case ".go":
+ gofiles = append(gofiles, f)
+ }
+ }
+
+ return go2files, gofiles, nil
+}
+
+// checkAndRemoveGofiles looks through all the .go files.
+// Any .go file that starts with rewritePrefix is removed.
+// Any other .go file is reported as an error.
+// This is intended to make it harder for go2go to break a
+// traditional Go package.
+func checkAndRemoveGofiles(dir string, gofiles []string) error {
+ for _, f := range gofiles {
+ if err := checkGoFile(dir, f); err != nil {
+ return err
+ }
+ if err := os.Remove(filepath.Join(dir, f)); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// checkGofile reports an error if the file does not start with rewritePrefix.
+func checkGoFile(dir, f string) error {
+ o, err := os.Open(filepath.Join(dir, f))
+ if err != nil {
+ return err
+ }
+ defer o.Close()
+ var buf [100]byte
+ n, err := o.Read(buf[:])
+ if n > 0 && !strings.HasPrefix(string(buf[:n]), rewritePrefix) {
+ return fmt.Errorf("Go file %s was not created by go2go", f)
+ }
+ if err != nil && err != io.EOF {
+ return err
+ }
+ return nil
+}
+
+// parseFiles parses a list of .go2 files.
+func parseFiles(importer *Importer, dir string, go2files []string, fset *token.FileSet) ([]*ast.Package, error) {
+ pkgs := make(map[string]*ast.Package)
+ for _, go2f := range go2files {
+ mode := parser.ParseTypeParams
+
+ filename := filepath.Join(dir, go2f)
+ pf, err := parser.ParseFile(fset, filename, nil, mode)
+ if err != nil {
+ return nil, err
+ }
+
+ name := pf.Name.Name
+ pkg, ok := pkgs[name]
+ if !ok {
+ pkg = &ast.Package{
+ Name: name,
+ Files: make(map[string]*ast.File),
+ }
+ pkgs[name] = pkg
+ }
+ pkg.Files[filename] = pf
+ }
+
+ rpkgs := make([]*ast.Package, 0, len(pkgs))
+ for _, pkg := range pkgs {
+ rpkgs = append(rpkgs, pkg)
+ }
+ sort.Slice(rpkgs, func(i, j int) bool {
+ return rpkgs[i].Name < rpkgs[j].Name
+ })
+
+ return rpkgs, nil
+}
+
+// multiErr is an error value that accumulates type checking errors.
+type multiErr []error
+
+// The add methods adds another error to a multiErr.
+func (m *multiErr) add(err error) {
+ *m = append(*m, err)
+}
+
+// The Error method returns the accumulated errors.
+func (m multiErr) Error() string {
+ if len(m) == 0 {
+ return "internal error: empty multiErr"
+ }
+ var sb strings.Builder
+ for _, e := range m {
+ fmt.Fprintln(&sb, e)
+ }
+ return sb.String()
+}
diff --git a/src/go/go2go/importer.go b/src/go/go2go/importer.go
new file mode 100644
index 0000000..6c92730
--- /dev/null
+++ b/src/go/go2go/importer.go
@@ -0,0 +1,414 @@
+// Copyright 2020 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 go2go
+
+import (
+ "fmt"
+ "go/ast"
+ "go/build"
+ "go/importer"
+ "go/parser"
+ "go/token"
+ "go/types"
+ "internal/goroot"
+ "io/ioutil"
+ "log"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "sort"
+ "strings"
+)
+
+// Importer implements the types.ImporterFrom interface.
+// It looks for Go2 packages using GO2PATH.
+// Imported Go2 packages are rewritten to normal Go packages.
+// This type also tracks references across imported packages.
+type Importer struct {
+ // The default importer, for Go1 packages.
+ defaultImporter types.ImporterFrom
+
+ // Temporary directory used to rewrite packages.
+ tmpdir string
+
+ // Aggregated info from go/types.
+ info *types.Info
+
+ // Map from import path to directory holding rewritten files.
+ translated map[string]string
+
+ // Map from import path to package information.
+ packages map[string]*types.Package
+
+ // Map from import path to list of import paths that it imports.
+ imports map[string][]string
+
+ // Map from Object to AST function declaration for
+ // parameterized functions.
+ idToFunc map[types.Object]*ast.FuncDecl
+
+ // Map from Object to AST type definition for parameterized types.
+ idToTypeSpec map[types.Object]*ast.TypeSpec
+
+ // Map from a Package to the instantiations we've created
+ // for that package. This doesn't really belong here,
+ // since it doesn't deal with import information,
+ // but Importer is a useful common location to store the data.
+ instantiations map[*types.Package]*instantiations
+}
+
+var _ types.ImporterFrom = &Importer{}
+
+// NewImporter returns a new Importer.
+// The tmpdir will become a GOPATH with translated files.
+func NewImporter(tmpdir string) *Importer {
+ info := &types.Info{
+ Types: make(map[ast.Expr]types.TypeAndValue),
+ Inferred: make(map[ast.Expr]types.Inferred),
+ Defs: make(map[*ast.Ident]types.Object),
+ Uses: make(map[*ast.Ident]types.Object),
+ }
+ return &Importer{
+ defaultImporter: importer.Default().(types.ImporterFrom),
+ tmpdir: tmpdir,
+ info: info,
+ translated: make(map[string]string),
+ packages: make(map[string]*types.Package),
+ imports: make(map[string][]string),
+ idToFunc: make(map[types.Object]*ast.FuncDecl),
+ idToTypeSpec: make(map[types.Object]*ast.TypeSpec),
+ instantiations: make(map[*types.Package]*instantiations),
+ }
+}
+
+// Import should never be called. This is the old API; current code
+// uses ImportFrom. This method still needs to be defined in order
+// to implement the interface.
+func (imp *Importer) Import(path string) (*types.Package, error) {
+ log.Fatal("unexpected call to Import method")
+ return nil, nil
+}
+
+// ImportFrom looks for a Go2 package, and if not found tries the
+// default importer.
+func (imp *Importer) ImportFrom(importPath, dir string, mode types.ImportMode) (*types.Package, error) {
+ if build.IsLocalImport(importPath) {
+ return imp.localImport(importPath, dir)
+ }
+
+ if imp.translated[importPath] != "" {
+ tpkg, ok := imp.packages[importPath]
+ if !ok {
+ return nil, fmt.Errorf("circular import when processing %q", importPath)
+ }
+ return tpkg, nil
+ }
+
+ var pdir string
+ if go2path := os.Getenv("GO2PATH"); go2path != "" {
+ pdir = imp.findFromPath(go2path, importPath)
+ }
+ if pdir == "" {
+ bpkg, err := build.Import(importPath, dir, build.FindOnly)
+ if err != nil {
+ return nil, err
+ }
+ pdir = bpkg.Dir
+ }
+
+ // If the directory holds .go2 files, we need to translate them.
+ fdir, err := os.Open(pdir)
+ if err != nil {
+ return nil, err
+ }
+ defer fdir.Close()
+ names, err := fdir.Readdirnames(-1)
+ if err != nil {
+ return nil, err
+ }
+ var gofiles, go2files []string
+ for _, name := range names {
+ switch filepath.Ext(name) {
+ case ".go":
+ gofiles = append(gofiles, name)
+ case ".go2":
+ go2files = append(go2files, name)
+ }
+ }
+
+ if len(go2files) == 0 {
+ return imp.importGo1Package(importPath, dir, mode, pdir, gofiles)
+ }
+
+ if len(gofiles) > 0 {
+ for _, gofile := range gofiles {
+ if err := checkGoFile(pdir, gofile); err != nil {
+ return nil, err
+ }
+ }
+ }
+
+ tdir := filepath.Join(imp.tmpdir, "src", importPath)
+ if err := os.MkdirAll(tdir, 0755); err != nil {
+ return nil, err
+ }
+ for _, name := range go2files {
+ data, err := ioutil.ReadFile(filepath.Join(pdir, name))
+ if err != nil {
+ return nil, err
+ }
+ if err := ioutil.WriteFile(filepath.Join(tdir, name), data, 0644); err != nil {
+ return nil, err
+ }
+ }
+
+ imp.translated[importPath] = tdir
+
+ tpkgs, err := rewriteToPkgs(imp, importPath, tdir)
+ if err != nil {
+ return nil, err
+ }
+
+ switch len(tpkgs) {
+ case 1:
+ return tpkgs[0], nil
+ case 2:
+ if strings.HasSuffix(tpkgs[0].Name(), "_test") {
+ return tpkgs[1], nil
+ } else if strings.HasSuffix(tpkgs[1].Name(), "_test") {
+ return tpkgs[0], nil
+ }
+ }
+
+ return nil, fmt.Errorf("unexpected number of packages (%d) for %q (directory %q)", len(tpkgs), importPath, pdir)
+}
+
+// findFromPath looks for a directory under gopath.
+func (imp *Importer) findFromPath(gopath, dir string) string {
+ if filepath.IsAbs(dir) || build.IsLocalImport(dir) {
+ return ""
+ }
+ for _, pd := range strings.Split(gopath, string(os.PathListSeparator)) {
+ d := filepath.Join(pd, "src", dir)
+ if fi, err := os.Stat(d); err == nil && fi.IsDir() {
+ return d
+ }
+ }
+ return ""
+}
+
+// importGo1Package handles importing a package with .go files rather
+// than .go2 files. The default importer can do this if the package
+// has been installed, but not otherwise. Installing the package using
+// "go install" won't work if the Go 1 package depends on a Go 2 package.
+// So use the default importer for a package in the standard library,
+// and otherwise use go/types.
+func (imp *Importer) importGo1Package(importPath, dir string, mode types.ImportMode, pdir string, gofiles []string) (*types.Package, error) {
+ if goroot.IsStandardPackage(runtime.GOROOT(), "gc", importPath) {
+ return imp.defaultImporter.ImportFrom(importPath, dir, mode)
+ }
+
+ if len(gofiles) == 0 {
+ return nil, fmt.Errorf("importing %q: no Go files in %s", importPath, pdir)
+ }
+
+ fset := token.NewFileSet()
+ filter := func(fi os.FileInfo) bool {
+ return !strings.HasSuffix(fi.Name(), "_test.go")
+ }
+ pkgs, err := parser.ParseDir(fset, pdir, filter, 0)
+ if err != nil {
+ return nil, err
+ }
+ if len(pkgs) > 1 {
+ return nil, fmt.Errorf("importing %q: multiple Go packages in %s", importPath, pdir)
+ }
+
+ var apkg *ast.Package
+ for _, apkg = range pkgs {
+ break
+ }
+
+ var asts []*ast.File
+ for _, f := range apkg.Files {
+ asts = append(asts, f)
+ }
+ sort.Slice(asts, func(i, j int) bool {
+ return asts[i].Name.Name < asts[j].Name.Name
+ })
+
+ var merr multiErr
+ conf := types.Config{
+ Importer: imp,
+ Error: merr.add,
+ }
+ tpkg, err := conf.Check(importPath, fset, asts, imp.info)
+ if err != nil {
+ return nil, merr
+ }
+
+ return tpkg, nil
+}
+
+// installGo1Package runs "go install" to install a package.
+// This is used for Go 1 packages, because the default
+// importer looks at .a files, not sources.
+// This is best effort; we don't report an error.
+func (imp *Importer) installGo1Package(dir string) {
+ gotool := filepath.Join(runtime.GOROOT(), "bin", "go")
+ cmd := exec.Command(gotool, "install")
+ cmd.Dir = dir
+ cmd.Run()
+}
+
+// Register registers a package under an import path.
+// This is for tests that use directives like //compiledir.
+func (imp *Importer) Register(importPath string, tpkgs []*types.Package) error {
+ switch len(tpkgs) {
+ case 1:
+ imp.packages[importPath] = tpkgs[0]
+ return nil
+ case 2:
+ if strings.HasSuffix(tpkgs[0].Name(), "_test") {
+ imp.packages[importPath] = tpkgs[1]
+ return nil
+ } else if strings.HasSuffix(tpkgs[1].Name(), "_test") {
+ imp.packages[importPath] = tpkgs[0]
+ return nil
+ }
+ }
+ return fmt.Errorf("unexpected number of packages (%d) for %q", len(tpkgs), importPath)
+}
+
+// localImport handles a local import such as
+// import "./a"
+// This is for tests that use directives like //compiledir.
+func (imp *Importer) localImport(importPath, dir string) (*types.Package, error) {
+ tpkg, ok := imp.packages[strings.TrimPrefix(importPath, "./")]
+ if !ok {
+ return nil, fmt.Errorf("cannot find local import %q", importPath)
+ }
+ return tpkg, nil
+}
+
+// record records information for a package, for use when working
+// with packages that import this one.
+func (imp *Importer) record(pkgName string, pkgfiles []namedAST, importPath string, tpkg *types.Package, asts []*ast.File) {
+ if !strings.HasSuffix(pkgName, "_test") {
+ if importPath != "" {
+ imp.packages[importPath] = tpkg
+ }
+ imp.imports[importPath] = imp.collectImports(asts)
+ }
+ for _, nast := range pkgfiles {
+ imp.addIDs(nast.ast)
+ }
+}
+
+// collectImports returns all the imports paths imported by any of the ASTs.
+func (imp *Importer) collectImports(asts []*ast.File) []string {
+ m := make(map[string]bool)
+ for _, a := range asts {
+ for _, decl := range a.Decls {
+ gen, ok := decl.(*ast.GenDecl)
+ if !ok || gen.Tok != token.IMPORT {
+ continue
+ }
+ for _, spec := range gen.Specs {
+ imp := spec.(*ast.ImportSpec)
+ if imp.Name != nil {
+ // We don't try to handle import aliases.
+ continue
+ }
+ path := strings.TrimPrefix(strings.TrimSuffix(imp.Path.Value, `"`), `"`)
+ m[path] = true
+ }
+ }
+ }
+ s := make([]string, 0, len(m))
+ for p := range m {
+ s = append(s, p)
+ }
+ sort.Strings(s)
+ return s
+}
+
+// addIDs finds IDs for generic functions and types and adds them to a map.
+func (imp *Importer) addIDs(f *ast.File) {
+ for _, decl := range f.Decls {
+ switch decl := decl.(type) {
+ case *ast.FuncDecl:
+ if isParameterizedFuncDecl(decl, imp.info) {
+ obj, ok := imp.info.Defs[decl.Name]
+ if !ok {
+ panic(fmt.Sprintf("no types.Object for %q", decl.Name.Name))
+ }
+ imp.idToFunc[obj] = decl
+ }
+ case *ast.GenDecl:
+ if decl.Tok == token.TYPE {
+ for _, s := range decl.Specs {
+ ts := s.(*ast.TypeSpec)
+ obj, ok := imp.info.Defs[ts.Name]
+ if !ok {
+ panic(fmt.Sprintf("no types.Object for %q", ts.Name.Name))
+ }
+ imp.idToTypeSpec[obj] = ts
+ }
+ }
+ }
+ }
+}
+
+// lookupPackage looks up a package by path.
+func (imp *Importer) lookupPackage(path string) (*types.Package, bool) {
+ pkg, ok := imp.packages[strings.TrimPrefix(path, "./")]
+ return pkg, ok
+}
+
+// lookupFunc looks up a function by Object.
+func (imp *Importer) lookupFunc(obj types.Object) (*ast.FuncDecl, bool) {
+ decl, ok := imp.idToFunc[obj]
+ return decl, ok
+}
+
+// lookupTypeSpec looks up a type by Object.
+func (imp *Importer) lookupTypeSpec(obj types.Object) (*ast.TypeSpec, bool) {
+ ts, ok := imp.idToTypeSpec[obj]
+ return ts, ok
+}
+
+// transitiveImports returns all the transitive imports of an import path.
+func (imp *Importer) transitiveImports(path string) []string {
+ return imp.gatherTransitiveImports(path, make(map[string]bool))
+}
+
+// gatherTransitiveImports returns all the transitive imports of an import path,
+// using a map to avoid duplicate work.
+func (imp *Importer) gatherTransitiveImports(path string, m map[string]bool) []string {
+ imports := imp.imports[path]
+ if len(imports) == 0 {
+ return nil
+ }
+ var r []string
+ for _, im := range imports {
+ r = append(r, im)
+ if !m[im] {
+ m[im] = true
+ r = append(r, imp.gatherTransitiveImports(im, m)...)
+ }
+ }
+ dup := make(map[string]bool)
+ for _, p := range r {
+ dup[p] = true
+ }
+ r = make([]string, 0, len(dup))
+ for p := range dup {
+ r = append(r, p)
+ }
+ sort.Strings(r)
+ return r
+}
diff --git a/src/go/go2go/instantiate.go b/src/go/go2go/instantiate.go
new file mode 100644
index 0000000..e9566cb
--- /dev/null
+++ b/src/go/go2go/instantiate.go
@@ -0,0 +1,980 @@
+// Copyright 2020 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 go2go
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "go/types"
+)
+
+// typeArgs holds type arguments for the function that we are instantiating.
+// We can look them up either with a types.Object associated with an ast.Ident,
+// or with a types.TypeParam.
+type typeArgs struct {
+ types []types.Type // type arguments in order
+ toAST map[types.Object]ast.Expr
+ toTyp map[*types.TypeParam]types.Type
+}
+
+// newTypeArgs returns a new typeArgs value.
+func newTypeArgs(typeTypes []types.Type) *typeArgs {
+ return &typeArgs{
+ types: typeTypes,
+ toAST: make(map[types.Object]ast.Expr),
+ toTyp: make(map[*types.TypeParam]types.Type),
+ }
+}
+
+// typeArgsFromFields builds mappings from a list of type parameters
+// expressed as ast.Field values.
+func typeArgsFromFields(t *translator, astTypes []ast.Expr, typeTypes []types.Type, tparams []*ast.Field) *typeArgs {
+ ta := newTypeArgs(typeTypes)
+ i := 0
+ for _, tf := range tparams {
+ for _, tn := range tf.Names {
+ obj, ok := t.importer.info.Defs[tn]
+ if !ok {
+ panic(fmt.Sprintf("no object for type parameter %q", tn))
+ }
+ objType := obj.Type()
+ objParam, ok := objType.(*types.TypeParam)
+ if !ok {
+ panic(fmt.Sprintf("%v is not a TypeParam", objParam))
+ }
+ ta.add(obj, objParam, astTypes[i], typeTypes[i])
+ i++
+ }
+ }
+ return ta
+}
+
+// typeArgsFromExprs builds mappings from a list of type parameters
+// expressed as ast.Expr values.
+func typeArgsFromExprs(t *translator, astTypes []ast.Expr, typeTypes []types.Type, tparams []ast.Expr) *typeArgs {
+ ta := newTypeArgs(typeTypes)
+ for i, ti := range tparams {
+ obj, ok := t.importer.info.Defs[ti.(*ast.Ident)]
+ if !ok {
+ panic(fmt.Sprintf("no object for type parameter %q", ti))
+ }
+ objType := obj.Type()
+ objParam, ok := objType.(*types.TypeParam)
+ if !ok {
+ panic(fmt.Sprintf("%v is not a TypeParam", objParam))
+ }
+ ta.add(obj, objParam, astTypes[i], typeTypes[i])
+ }
+ return ta
+}
+
+// add adds mappings for obj to ast and typ.
+func (ta *typeArgs) add(obj types.Object, objParam *types.TypeParam, ast ast.Expr, typ types.Type) {
+ ta.toAST[obj] = ast
+ ta.toTyp[objParam] = typ
+}
+
+// ast returns the AST for obj, and reports whether it exists.
+func (ta *typeArgs) ast(obj types.Object) (ast.Expr, bool) {
+ e, ok := ta.toAST[obj]
+ return e, ok
+}
+
+// typ returns the Type for param, and reports whether it exists.
+func (ta *typeArgs) typ(param *types.TypeParam) (types.Type, bool) {
+ t, ok := ta.toTyp[param]
+ return t, ok
+}
+
+// instantiateFunction creates a new instantiation of a function.
+func (t *translator) instantiateFunction(qid qualifiedIdent, astTypes []ast.Expr, typeTypes []types.Type) (*ast.Ident, error) {
+ name, err := t.instantiatedName(qid, typeTypes)
+ if err != nil {
+ return nil, err
+ }
+
+ decl, err := t.findFuncDecl(qid)
+ if err != nil {
+ return nil, err
+ }
+
+ ta := typeArgsFromFields(t, astTypes, typeTypes, decl.Type.TParams.List)
+
+ instIdent := ast.NewIdent(name)
+
+ newDecl := &ast.FuncDecl{
+ Doc: decl.Doc,
+ Recv: t.instantiateFieldList(ta, decl.Recv),
+ Name: instIdent,
+ Type: t.instantiateExpr(ta, decl.Type).(*ast.FuncType),
+ Body: t.instantiateBlockStmt(ta, decl.Body),
+ }
+ t.newDecls = append(t.newDecls, newDecl)
+
+ return instIdent, nil
+}
+
+// findFuncDecl looks for the FuncDecl for qid.
+func (t *translator) findFuncDecl(qid qualifiedIdent) (*ast.FuncDecl, error) {
+ obj := t.findTypesObject(qid)
+ if obj == nil {
+ return nil, fmt.Errorf("could not find Object for %q", qid)
+ }
+ decl, ok := t.importer.lookupFunc(obj)
+ if !ok {
+ return nil, fmt.Errorf("could not find function body for %q", qid)
+ }
+ return decl, nil
+}
+
+// findTypesObject looks up the types.Object for qid.
+// It returns nil if the ID is not found.
+func (t *translator) findTypesObject(qid qualifiedIdent) types.Object {
+ if qid.pkg == nil {
+ if obj := t.importer.info.ObjectOf(qid.ident); obj != nil {
+ // Ignore an embedded struct field.
+ // We want the type, not the field.
+ if _, ok := obj.(*types.Var); !ok {
+ return obj
+ }
+ }
+ return t.tpkg.Scope().Lookup(qid.ident.Name)
+ } else {
+ return qid.pkg.Scope().Lookup(qid.ident.Name)
+ }
+}
+
+// instantiateType creates a new instantiation of a type.
+func (t *translator) instantiateTypeDecl(qid qualifiedIdent, typ *types.Named, astTypes []ast.Expr, typeTypes []types.Type, instIdent *ast.Ident) (types.Type, error) {
+ spec, err := t.findTypeSpec(qid)
+ if err != nil {
+ return nil, err
+ }
+
+ ta := typeArgsFromFields(t, astTypes, typeTypes, spec.TParams.List)
+
+ newSpec := &ast.TypeSpec{
+ Doc: spec.Doc,
+ Name: instIdent,
+ Assign: spec.Assign,
+ Type: t.instantiateExpr(ta, spec.Type),
+ Comment: spec.Comment,
+ }
+ newDecl := &ast.GenDecl{
+ Tok: token.TYPE,
+ Specs: []ast.Spec{newSpec},
+ }
+ t.newDecls = append(t.newDecls, newDecl)
+
+ // If typ already has type arguments, then they should be correct.
+ // If it doesn't, we want to use typeTypes.
+ typeWithTargs := typ
+ if len(typ.TArgs()) == 0 {
+ typeWithTargs = t.updateTArgs(typ, typeTypes)
+ }
+
+ instType := t.instantiateType(ta, typeWithTargs)
+
+ t.setType(instIdent, instType)
+
+ nm := typ.NumMethods()
+ for i := 0; i < nm; i++ {
+ method := typ.Method(i)
+ mast, ok := t.importer.lookupFunc(method)
+ if !ok {
+ panic(fmt.Sprintf("no AST for method %v", method))
+ }
+ rtyp := mast.Recv.List[0].Type
+ newRtype := ast.Expr(ast.NewIdent(instIdent.Name))
+ if p, ok := rtyp.(*ast.StarExpr); ok {
+ rtyp = p.X
+ newRtype = &ast.StarExpr{
+ X: newRtype,
+ }
+ }
+ var tparams []ast.Expr
+ switch rtyp := rtyp.(type) {
+ case *ast.CallExpr:
+ tparams = rtyp.Args
+ case *ast.IndexExpr:
+ tparams = []ast.Expr{rtyp.Index}
+ default:
+ panic("unexpected AST type")
+ }
+ ta := typeArgsFromExprs(t, astTypes, typeTypes, tparams)
+ var names []*ast.Ident
+ if mnames := mast.Recv.List[0].Names; len(mnames) > 0 {
+ names = []*ast.Ident{mnames[0]}
+ }
+ newDecl := &ast.FuncDecl{
+ Doc: mast.Doc,
+ Recv: &ast.FieldList{
+ Opening: mast.Recv.Opening,
+ List: []*ast.Field{
+ {
+ Doc: mast.Recv.List[0].Doc,
+ Names: names,
+ Type: newRtype,
+ Comment: mast.Recv.List[0].Comment,
+ },
+ },
+ Closing: mast.Recv.Closing,
+ },
+ Name: mast.Name,
+ Type: t.instantiateExpr(ta, mast.Type).(*ast.FuncType),
+ Body: t.instantiateBlockStmt(ta, mast.Body),
+ }
+ t.newDecls = append(t.newDecls, newDecl)
+ }
+
+ return instType, nil
+}
+
+// findTypeSpec looks for the TypeSpec for qid.
+func (t *translator) findTypeSpec(qid qualifiedIdent) (*ast.TypeSpec, error) {
+ obj := t.findTypesObject(qid)
+ if obj == nil {
+ return nil, fmt.Errorf("could not find Object for %q", qid)
+ }
+ spec, ok := t.importer.lookupTypeSpec(obj)
+ if !ok {
+ return nil, fmt.Errorf("could not find type spec for %q", qid)
+ }
+
+ if spec.Assign != token.NoPos {
+ // This is a type alias that we need to resolve.
+ typ := t.lookupType(spec.Type)
+ if typ != nil {
+ if named, ok := typ.(*types.Named); ok {
+ if s, ok := t.importer.lookupTypeSpec(named.Obj()); ok {
+ spec = s
+ }
+ }
+ }
+ }
+
+ if spec.TParams == nil {
+ return nil, fmt.Errorf("found type spec for %q but it has no type parameters", qid)
+ }
+
+ return spec, nil
+}
+
+// instantiateDecl instantiates a declaration.
+func (t *translator) instantiateDecl(ta *typeArgs, d ast.Decl) ast.Decl {
+ switch d := d.(type) {
+ case nil:
+ return nil
+ case *ast.GenDecl:
+ if len(d.Specs) == 0 {
+ return d
+ }
+ nspecs := make([]ast.Spec, len(d.Specs))
+ changed := false
+ for i, s := range d.Specs {
+ ns := t.instantiateSpec(ta, s)
+ if ns != s {
+ changed = true
+ }
+ nspecs[i] = ns
+ }
+ if !changed {
+ return d
+ }
+ return &ast.GenDecl{
+ Doc: d.Doc,
+ TokPos: d.TokPos,
+ Tok: d.Tok,
+ Lparen: d.Lparen,
+ Specs: nspecs,
+ Rparen: d.Rparen,
+ }
+ default:
+ panic(fmt.Sprintf("unimplemented Decl %T", d))
+ }
+}
+
+// instantiateSpec instantiates a spec node.
+func (t *translator) instantiateSpec(ta *typeArgs, s ast.Spec) ast.Spec {
+ switch s := s.(type) {
+ case nil:
+ return nil
+ case *ast.ValueSpec:
+ typ := t.instantiateExpr(ta, s.Type)
+ values, changed := t.instantiateExprList(ta, s.Values)
+ if typ == s.Type && !changed {
+ return s
+ }
+ return &ast.ValueSpec{
+ Doc: s.Doc,
+ Names: s.Names,
+ Type: typ,
+ Values: values,
+ Comment: s.Comment,
+ }
+ case *ast.TypeSpec:
+ if s.TParams != nil {
+ t.err = fmt.Errorf("%s: go2go tool does not support local parameterized types", t.fset.Position(s.Pos()))
+ return nil
+ }
+ typ := t.instantiateExpr(ta, s.Type)
+ if typ == s.Type {
+ return s
+ }
+ return &ast.TypeSpec{
+ Doc: s.Doc,
+ Name: s.Name,
+ Assign: s.Assign,
+ Type: typ,
+ Comment: s.Comment,
+ }
+ default:
+ panic(fmt.Sprintf("unimplemented Spec %T", s))
+ }
+}
+
+// instantiateStmt instantiates a statement.
+func (t *translator) instantiateStmt(ta *typeArgs, s ast.Stmt) ast.Stmt {
+ switch s := s.(type) {
+ case nil:
+ return nil
+ case *ast.DeclStmt:
+ decl := t.instantiateDecl(ta, s.Decl)
+ if decl == s.Decl {
+ return s
+ }
+ return &ast.DeclStmt{
+ Decl: decl,
+ }
+ case *ast.EmptyStmt:
+ return s
+ case *ast.LabeledStmt:
+ stmt := t.instantiateStmt(ta, s.Stmt)
+ if stmt == s.Stmt {
+ return s
+ }
+ return &ast.LabeledStmt{
+ Label: s.Label,
+ Colon: s.Colon,
+ Stmt: stmt,
+ }
+ case *ast.ExprStmt:
+ x := t.instantiateExpr(ta, s.X)
+ if x == s.X {
+ return s
+ }
+ return &ast.ExprStmt{
+ X: x,
+ }
+ case *ast.SendStmt:
+ ch := t.instantiateExpr(ta, s.Chan)
+ value := t.instantiateExpr(ta, s.Value)
+ if ch == s.Chan && value == s.Value {
+ return s
+ }
+ return &ast.SendStmt{
+ Chan: ch,
+ Arrow: s.Arrow,
+ Value: value,
+ }
+ case *ast.IncDecStmt:
+ x := t.instantiateExpr(ta, s.X)
+ if x == s.X {
+ return s
+ }
+ return &ast.IncDecStmt{
+ X: x,
+ TokPos: s.TokPos,
+ Tok: s.Tok,
+ }
+ case *ast.AssignStmt:
+ lhs, lchanged := t.instantiateExprList(ta, s.Lhs)
+ rhs, rchanged := t.instantiateExprList(ta, s.Rhs)
+ if !lchanged && !rchanged {
+ return s
+ }
+ return &ast.AssignStmt{
+ Lhs: lhs,
+ TokPos: s.TokPos,
+ Tok: s.Tok,
+ Rhs: rhs,
+ }
+ case *ast.GoStmt:
+ call := t.instantiateExpr(ta, s.Call).(*ast.CallExpr)
+ if call == s.Call {
+ return s
+ }
+ return &ast.GoStmt{
+ Go: s.Go,
+ Call: call,
+ }
+ case *ast.DeferStmt:
+ call := t.instantiateExpr(ta, s.Call).(*ast.CallExpr)
+ if call == s.Call {
+ return s
+ }
+ return &ast.DeferStmt{
+ Defer: s.Defer,
+ Call: call,
+ }
+ case *ast.ReturnStmt:
+ results, changed := t.instantiateExprList(ta, s.Results)
+ if !changed {
+ return s
+ }
+ return &ast.ReturnStmt{
+ Return: s.Return,
+ Results: results,
+ }
+ case *ast.BranchStmt:
+ return s
+ case *ast.BlockStmt:
+ return t.instantiateBlockStmt(ta, s)
+ case *ast.IfStmt:
+ init := t.instantiateStmt(ta, s.Init)
+ cond := t.instantiateExpr(ta, s.Cond)
+ body := t.instantiateBlockStmt(ta, s.Body)
+ els := t.instantiateStmt(ta, s.Else)
+ if init == s.Init && cond == s.Cond && body == s.Body && els == s.Else {
+ return s
+ }
+ return &ast.IfStmt{
+ If: s.If,
+ Init: init,
+ Cond: cond,
+ Body: body,
+ Else: els,
+ }
+ case *ast.CaseClause:
+ list, listChanged := t.instantiateExprList(ta, s.List)
+ body, bodyChanged := t.instantiateStmtList(ta, s.Body)
+ if !listChanged && !bodyChanged {
+ return s
+ }
+ return &ast.CaseClause{
+ Case: s.Case,
+ List: list,
+ Colon: s.Colon,
+ Body: body,
+ }
+ case *ast.SwitchStmt:
+ init := t.instantiateStmt(ta, s.Init)
+ tag := t.instantiateExpr(ta, s.Tag)
+ body := t.instantiateBlockStmt(ta, s.Body)
+ if init == s.Init && tag == s.Tag && body == s.Body {
+ return s
+ }
+ return &ast.SwitchStmt{
+ Switch: s.Switch,
+ Init: init,
+ Tag: tag,
+ Body: body,
+ }
+ case *ast.TypeSwitchStmt:
+ init := t.instantiateStmt(ta, s.Init)
+ assign := t.instantiateStmt(ta, s.Assign)
+ body := t.instantiateBlockStmt(ta, s.Body)
+ if init == s.Init && assign == s.Assign && body == s.Body {
+ return s
+ }
+ return &ast.TypeSwitchStmt{
+ Switch: s.Switch,
+ Init: init,
+ Assign: assign,
+ Body: body,
+ }
+ case *ast.CommClause:
+ comm := t.instantiateStmt(ta, s.Comm)
+ body, bodyChanged := t.instantiateStmtList(ta, s.Body)
+ if comm == s.Comm && !bodyChanged {
+ return s
+ }
+ return &ast.CommClause{
+ Case: s.Case,
+ Comm: comm,
+ Colon: s.Colon,
+ Body: body,
+ }
+ case *ast.SelectStmt:
+ body := t.instantiateBlockStmt(ta, s.Body)
+ if body == s.Body {
+ return s
+ }
+ return &ast.SelectStmt{
+ Select: s.Select,
+ Body: body,
+ }
+ case *ast.ForStmt:
+ init := t.instantiateStmt(ta, s.Init)
+ cond := t.instantiateExpr(ta, s.Cond)
+ post := t.instantiateStmt(ta, s.Post)
+ body := t.instantiateBlockStmt(ta, s.Body)
+ if init == s.Init && cond == s.Cond && post == s.Post && body == s.Body {
+ return s
+ }
+ return &ast.ForStmt{
+ For: s.For,
+ Init: init,
+ Cond: cond,
+ Post: post,
+ Body: body,
+ }
+ case *ast.RangeStmt:
+ key := t.instantiateExpr(ta, s.Key)
+ value := t.instantiateExpr(ta, s.Value)
+ x := t.instantiateExpr(ta, s.X)
+ body := t.instantiateBlockStmt(ta, s.Body)
+ if key == s.Key && value == s.Value && x == s.X && body == s.Body {
+ return s
+ }
+ return &ast.RangeStmt{
+ For: s.For,
+ Key: key,
+ Value: value,
+ TokPos: s.TokPos,
+ Tok: s.Tok,
+ X: x,
+ Body: body,
+ }
+ default:
+ panic(fmt.Sprintf("unimplemented Stmt %T", s))
+ }
+}
+
+// instantiateBlockStmt instantiates a BlockStmt.
+func (t *translator) instantiateBlockStmt(ta *typeArgs, pbs *ast.BlockStmt) *ast.BlockStmt {
+ if pbs == nil {
+ return nil
+ }
+ changed := false
+ stmts := make([]ast.Stmt, len(pbs.List))
+ for i, s := range pbs.List {
+ is := t.instantiateStmt(ta, s)
+ stmts[i] = is
+ if is != s {
+ changed = true
+ }
+ }
+ if !changed {
+ return pbs
+ }
+ return &ast.BlockStmt{
+ Lbrace: pbs.Lbrace,
+ List: stmts,
+ Rbrace: pbs.Rbrace,
+ }
+}
+
+// instantiateStmtList instantiates a statement list.
+func (t *translator) instantiateStmtList(ta *typeArgs, sl []ast.Stmt) ([]ast.Stmt, bool) {
+ nsl := make([]ast.Stmt, len(sl))
+ changed := false
+ for i, s := range sl {
+ ns := t.instantiateStmt(ta, s)
+ if ns != s {
+ changed = true
+ }
+ nsl[i] = ns
+ }
+ if !changed {
+ return sl, false
+ }
+ return nsl, true
+}
+
+// instantiateFieldList instantiates a field list.
+func (t *translator) instantiateFieldList(ta *typeArgs, fl *ast.FieldList) *ast.FieldList {
+ if fl == nil {
+ return nil
+ }
+ nfl := make([]*ast.Field, len(fl.List))
+ changed := false
+ for i, f := range fl.List {
+ nf := t.instantiateField(ta, f)
+ if nf != f {
+ changed = true
+ }
+ nfl[i] = nf
+ }
+ if !changed {
+ return fl
+ }
+ return &ast.FieldList{
+ Opening: fl.Opening,
+ List: nfl,
+ Closing: fl.Closing,
+ }
+}
+
+// instantiateField instantiates a field.
+func (t *translator) instantiateField(ta *typeArgs, f *ast.Field) *ast.Field {
+ typ := t.instantiateExpr(ta, f.Type)
+ if typ == f.Type {
+ return f
+ }
+ return &ast.Field{
+ Doc: f.Doc,
+ Names: f.Names,
+ Type: typ,
+ Tag: f.Tag,
+ Comment: f.Comment,
+ }
+}
+
+// instantiateExpr instantiates an expression.
+func (t *translator) instantiateExpr(ta *typeArgs, e ast.Expr) ast.Expr {
+ var r ast.Expr
+ switch e := e.(type) {
+ case nil:
+ return nil
+ case *ast.Ident:
+ obj := t.importer.info.ObjectOf(e)
+ if obj != nil {
+ if typ, ok := ta.ast(obj); ok {
+ return typ
+ }
+ }
+ return e
+ case *ast.Ellipsis:
+ elt := t.instantiateExpr(ta, e.Elt)
+ if elt == e.Elt {
+ return e
+ }
+ return &ast.Ellipsis{
+ Ellipsis: e.Ellipsis,
+ Elt: elt,
+ }
+ case *ast.BasicLit:
+ return e
+ case *ast.FuncLit:
+ typ := t.instantiateExpr(ta, e.Type).(*ast.FuncType)
+ body := t.instantiateBlockStmt(ta, e.Body)
+ if typ == e.Type && body == e.Body {
+ return e
+ }
+ return &ast.FuncLit{
+ Type: typ,
+ Body: body,
+ }
+ case *ast.CompositeLit:
+ typ := t.instantiateExpr(ta, e.Type)
+ elts, changed := t.instantiateExprList(ta, e.Elts)
+ if typ == e.Type && !changed {
+ return e
+ }
+ return &ast.CompositeLit{
+ Type: typ,
+ Lbrace: e.Lbrace,
+ Elts: elts,
+ Rbrace: e.Rbrace,
+ Incomplete: e.Incomplete,
+ }
+ case *ast.ParenExpr:
+ x := t.instantiateExpr(ta, e.X)
+ if x == e.X {
+ return e
+ }
+ return &ast.ParenExpr{
+ Lparen: e.Lparen,
+ X: x,
+ Rparen: e.Rparen,
+ }
+ case *ast.SelectorExpr:
+ x := t.instantiateExpr(ta, e.X)
+
+ // If this is a reference to an instantiated embedded field,
+ // we may need to instantiate it. The actual instantiation
+ // is at the end of the function, as long as we create a
+ // a new SelectorExpr when needed.
+ instantiate := false
+ obj := t.importer.info.ObjectOf(e.Sel)
+ if obj != nil {
+ if f, ok := obj.(*types.Var); ok && f.Embedded() {
+ if named, ok := f.Type().(*types.Named); ok && len(named.TArgs()) > 0 && obj.Name() == named.Obj().Name() {
+ instantiate = true
+ }
+ }
+ }
+
+ if x == e.X && !instantiate {
+ return e
+ }
+ r = &ast.SelectorExpr{
+ X: x,
+ Sel: e.Sel,
+ }
+ case *ast.IndexExpr:
+ x := t.instantiateExpr(ta, e.X)
+ index := t.instantiateExpr(ta, e.Index)
+ origInferred, haveInferred := t.importer.info.Inferred[e]
+ var newInferred types.Inferred
+ inferredChanged := false
+ if haveInferred {
+ for _, typ := range origInferred.Targs {
+ nt := t.instantiateType(ta, typ)
+ newInferred.Targs = append(newInferred.Targs, nt)
+ if nt != typ {
+ inferredChanged = true
+ }
+ }
+ }
+ if x == e.X && index == e.Index && !inferredChanged {
+ return e
+ }
+ r = &ast.IndexExpr{
+ X: x,
+ Lbrack: e.Lbrack,
+ Index: index,
+ Rbrack: e.Rbrack,
+ }
+ if haveInferred {
+ t.importer.info.Inferred[r] = newInferred
+ }
+ case *ast.SliceExpr:
+ x := t.instantiateExpr(ta, e.X)
+ low := t.instantiateExpr(ta, e.Low)
+ high := t.instantiateExpr(ta, e.High)
+ max := t.instantiateExpr(ta, e.Max)
+ if x == e.X && low == e.Low && high == e.High && max == e.Max {
+ return e
+ }
+ r = &ast.SliceExpr{
+ X: x,
+ Lbrack: e.Lbrack,
+ Low: low,
+ High: high,
+ Max: max,
+ Slice3: e.Slice3,
+ Rbrack: e.Rbrack,
+ }
+ case *ast.TypeAssertExpr:
+ x := t.instantiateExpr(ta, e.X)
+ typ := t.instantiateExpr(ta, e.Type)
+ if x == e.X && typ == e.Type {
+ return e
+ }
+ r = &ast.TypeAssertExpr{
+ X: x,
+ Lparen: e.Lparen,
+ Type: typ,
+ Rparen: e.Rparen,
+ }
+ case *ast.CallExpr:
+ fun := t.instantiateExpr(ta, e.Fun)
+ args, argsChanged := t.instantiateExprList(ta, e.Args)
+ origInferred, haveInferred := t.importer.info.Inferred[e]
+ var newInferred types.Inferred
+ inferredChanged := false
+ if haveInferred {
+ for _, typ := range origInferred.Targs {
+ nt := t.instantiateType(ta, typ)
+ newInferred.Targs = append(newInferred.Targs, nt)
+ if nt != typ {
+ inferredChanged = true
+ }
+ }
+ newInferred.Sig = t.instantiateType(ta, origInferred.Sig).(*types.Signature)
+ if newInferred.Sig != origInferred.Sig {
+ inferredChanged = true
+ }
+ }
+ if fun == e.Fun && !argsChanged && !inferredChanged {
+ return e
+ }
+ r = &ast.CallExpr{
+ Fun: fun,
+ Lparen: e.Lparen,
+ Args: args,
+ Ellipsis: e.Ellipsis,
+ Rparen: e.Rparen,
+ }
+ if haveInferred {
+ t.importer.info.Inferred[r] = newInferred
+ }
+ case *ast.StarExpr:
+ x := t.instantiateExpr(ta, e.X)
+ if x == e.X {
+ return e
+ }
+ r = &ast.StarExpr{
+ Star: e.Star,
+ X: x,
+ }
+ case *ast.UnaryExpr:
+ x := t.instantiateExpr(ta, e.X)
+ if x == e.X {
+ return e
+ }
+ r = &ast.UnaryExpr{
+ OpPos: e.OpPos,
+ Op: e.Op,
+ X: x,
+ }
+ case *ast.BinaryExpr:
+ x := t.instantiateExpr(ta, e.X)
+ y := t.instantiateExpr(ta, e.Y)
+ if x == e.X && y == e.Y {
+ return e
+ }
+ r = &ast.BinaryExpr{
+ X: x,
+ OpPos: e.OpPos,
+ Op: e.Op,
+ Y: y,
+ }
+ case *ast.KeyValueExpr:
+ key := t.instantiateExpr(ta, e.Key)
+ value := t.instantiateExpr(ta, e.Value)
+ if key == e.Key && value == e.Value {
+ return e
+ }
+ r = &ast.KeyValueExpr{
+ Key: key,
+ Colon: e.Colon,
+ Value: value,
+ }
+ case *ast.ArrayType:
+ ln := t.instantiateExpr(ta, e.Len)
+ elt := t.instantiateExpr(ta, e.Elt)
+ if ln == e.Len && elt == e.Elt {
+ return e
+ }
+ r = &ast.ArrayType{
+ Lbrack: e.Lbrack,
+ Len: ln,
+ Elt: elt,
+ }
+ case *ast.StructType:
+ fields := e.Fields.List
+ newFields := make([]*ast.Field, 0, len(fields))
+ changed := false
+ for _, f := range fields {
+ newFields = append(newFields, f)
+ if len(f.Names) > 0 {
+ continue
+ }
+ isPtr := false
+ ftyp := f.Type
+ if star, ok := ftyp.(*ast.StarExpr); ok {
+ ftyp = star.X
+ isPtr = true
+ }
+ id, ok := ftyp.(*ast.Ident)
+ if !ok {
+ continue
+ }
+ typ := t.lookupType(id)
+ if typ == nil {
+ continue
+ }
+ typeParam, ok := typ.(*types.TypeParam)
+ if !ok {
+ continue
+ }
+ instType := t.instantiateType(ta, typeParam)
+ if isPtr {
+ instType = types.NewPointer(instType)
+ }
+ newField := &ast.Field{
+ Doc: f.Doc,
+ Names: []*ast.Ident{id},
+ Type: t.typeToAST(instType),
+ Tag: f.Tag,
+ Comment: f.Comment,
+ }
+ newFields[len(newFields)-1] = newField
+ changed = true
+ }
+ fl := e.Fields
+ if changed {
+ fl = &ast.FieldList{
+ Opening: fl.Opening,
+ List: newFields,
+ Closing: fl.Closing,
+ }
+ }
+ newFl := t.instantiateFieldList(ta, fl)
+ if !changed && newFl == fl {
+ return e
+ }
+ r = &ast.StructType{
+ Struct: e.Struct,
+ Fields: newFl,
+ Incomplete: e.Incomplete,
+ }
+ case *ast.FuncType:
+ params := t.instantiateFieldList(ta, e.Params)
+ results := t.instantiateFieldList(ta, e.Results)
+ if e.TParams == nil && params == e.Params && results == e.Results {
+ return e
+ }
+ r = &ast.FuncType{
+ Func: e.Func,
+ TParams: nil,
+ Params: params,
+ Results: results,
+ }
+ case *ast.InterfaceType:
+ eMethods, eTypes := splitFieldList(e.Methods)
+ methods := t.instantiateFieldList(ta, eMethods)
+ types, typesChanged := t.instantiateExprList(ta, eTypes)
+ if methods == e.Methods && !typesChanged {
+ return e
+ }
+ r = &ast.InterfaceType{
+ Interface: e.Interface,
+ Methods: mergeFieldList(methods, types),
+ Incomplete: e.Incomplete,
+ }
+ case *ast.MapType:
+ key := t.instantiateExpr(ta, e.Key)
+ value := t.instantiateExpr(ta, e.Value)
+ if key == e.Key && value == e.Value {
+ return e
+ }
+ r = &ast.MapType{
+ Map: e.Map,
+ Key: key,
+ Value: value,
+ }
+ case *ast.ChanType:
+ value := t.instantiateExpr(ta, e.Value)
+ if value == e.Value {
+ return e
+ }
+ r = &ast.ChanType{
+ Begin: e.Begin,
+ Arrow: e.Arrow,
+ Dir: e.Dir,
+ Value: value,
+ }
+ default:
+ panic(fmt.Sprintf("unimplemented Expr %T", e))
+ }
+
+ if et := t.lookupType(e); et != nil {
+ t.setType(r, t.instantiateType(ta, et))
+ }
+
+ return r
+}
+
+// instantiateExprList instantiates an expression list.
+func (t *translator) instantiateExprList(ta *typeArgs, el []ast.Expr) ([]ast.Expr, bool) {
+ nel := make([]ast.Expr, len(el))
+ changed := false
+ for i, e := range el {
+ ne := t.instantiateExpr(ta, e)
+ if ne != e {
+ changed = true
+ }
+ nel[i] = ne
+ }
+ if !changed {
+ return el, false
+ }
+ return nel, true
+}
diff --git a/src/go/go2go/names.go b/src/go/go2go/names.go
new file mode 100644
index 0000000..ec3bef2
--- /dev/null
+++ b/src/go/go2go/names.go
@@ -0,0 +1,75 @@
+// Copyright 2020 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 go2go
+
+import (
+ "fmt"
+ "go/types"
+ "strings"
+ "unicode"
+)
+
+// We use Oriya digit zero as a separator.
+// Do not use this character in your own identifiers.
+const nameSep = '୦'
+
+// We use Oriya digit eight to introduce a special character code.
+// Do not use this character in your own identifiers.
+const nameIntro = '୮'
+
+var nameCodes = map[rune]int{
+ ' ': 0,
+ '*': 1,
+ ';': 2,
+ ',': 3,
+ '{': 4,
+ '}': 5,
+ '[': 6,
+ ']': 7,
+ '(': 8,
+ ')': 9,
+ '.': 10,
+ '<': 11,
+ '-': 12,
+ '/': 13,
+ nameSep: 14,
+ nameIntro: 15,
+}
+
+// instantiatedName returns the name of a newly instantiated function.
+func (t *translator) instantiatedName(qid qualifiedIdent, types []types.Type) (string, error) {
+ var sb strings.Builder
+ fmt.Fprintf(&sb, "instantiate%c", nameSep)
+ if qid.pkg != nil {
+ fmt.Fprintf(&sb, qid.pkg.Name())
+ }
+ fmt.Fprintf(&sb, "%c%s", nameSep, qid.ident.Name)
+ for _, typ := range types {
+ sb.WriteRune(nameSep)
+ s := t.withoutTags(typ).String()
+
+ // We have to uniquely translate s into a valid Go identifier.
+ // This is not possible in general but we assume that
+ // identifiers will not contain nameSep or nameIntro.
+ for _, r := range s {
+ if (unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_') && r != nameSep && r != nameIntro {
+ sb.WriteRune(r)
+ } else {
+ code, ok := nameCodes[r]
+ if !ok {
+ panic(fmt.Sprintf("%s: unexpected type string character %q in %q", t.fset.Position(qid.ident.Pos()), r, s))
+ }
+ fmt.Fprintf(&sb, "%c%x", nameIntro, code)
+ }
+ }
+ }
+ return sb.String(), nil
+}
+
+// importableName returns a name that we define in each package, so that
+// we have something to import to avoid an unused package error.
+func (t *translator) importableName() string {
+ return "Importable" + string(nameSep)
+}
diff --git a/src/go/go2go/rewrite.go b/src/go/go2go/rewrite.go
new file mode 100644
index 0000000..89a00e0
--- /dev/null
+++ b/src/go/go2go/rewrite.go
@@ -0,0 +1,1288 @@
+// Copyright 2020 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 go2go
+
+import (
+ "bufio"
+ "fmt"
+ "go/ast"
+ "go/printer"
+ "go/token"
+ "go/types"
+ "os"
+ "path/filepath"
+ "sort"
+ "strconv"
+ "strings"
+)
+
+var config = printer.Config{
+ Mode: printer.UseSpaces | printer.TabIndent | printer.SourcePos,
+ Tabwidth: 8,
+}
+
+// isParameterizedFuncDecl reports whether fd is a parameterized function.
+func isParameterizedFuncDecl(fd *ast.FuncDecl, info *types.Info) bool {
+ if fd.Type.TParams != nil && len(fd.Type.TParams.List) > 0 {
+ return true
+ }
+ if fd.Recv != nil {
+ rtyp := info.TypeOf(fd.Recv.List[0].Type)
+ if rtyp == nil {
+ // Already instantiated.
+ return false
+ }
+ if p, ok := rtyp.(*types.Pointer); ok {
+ rtyp = p.Elem()
+ }
+ if named, ok := rtyp.(*types.Named); ok {
+ if named.TParams() != nil {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+// isTranslatableType reports whether a type spec can be translated to Go1.
+// This is false if the type spec relies on any features that use generics.
+func isTranslatableType(s ast.Spec, info *types.Info) bool {
+ if isParameterizedTypeDecl(s, info) {
+ return false
+ }
+ if isTypeBound(s, info) {
+ return false
+ }
+ if embedsComparable(s, info) {
+ return false
+ }
+ return true
+}
+
+// isParameterizedTypeDecl reports whether s is a parameterized type.
+func isParameterizedTypeDecl(s ast.Spec, info *types.Info) bool {
+ ts := s.(*ast.TypeSpec)
+ if ts.TParams != nil && len(ts.TParams.List) > 0 {
+ return true
+ }
+ if ts.Assign == token.NoPos {
+ return false
+ }
+
+ // This is a type alias. Try to resolve it.
+ typ := info.TypeOf(ts.Type)
+ if typ == nil {
+ return false
+ }
+ named, ok := typ.(*types.Named)
+ if !ok {
+ return false
+ }
+ return len(named.TParams()) > 0 && len(named.TArgs()) == 0
+}
+
+// isTypeBound reports whether s is an interface type that includes a
+// type bound or that embeds an interface that must be a type bound.
+func isTypeBound(s ast.Spec, info *types.Info) bool {
+ typ := info.TypeOf(s.(*ast.TypeSpec).Type)
+ if typ == nil {
+ return false
+ }
+ if iface, ok := typ.Underlying().(*types.Interface); ok {
+ if iface.HasTypeList() {
+ return true
+ }
+ }
+ return false
+}
+
+// embedsComparable reports whether s is an interface type that embeds
+// the predeclared type "comparable", directly or indirectly.
+func embedsComparable(s ast.Spec, info *types.Info) bool {
+ typ := info.TypeOf(s.(*ast.TypeSpec).Type)
+ return typeEmbedsComparable(typ)
+}
+
+// typeEmbedsComparable reports whether typ is an interface type
+// that embeds the predeclared type "comparable", directly or indirectly.
+// This is like embedsComparable, but for a types.Type.
+func typeEmbedsComparable(typ types.Type) bool {
+ if typ == nil {
+ return false
+ }
+ iface, ok := typ.Underlying().(*types.Interface)
+ if !ok {
+ return false
+ }
+ n := iface.NumEmbeddeds()
+ if n == 0 {
+ return false
+ }
+ comparable := types.Universe.Lookup("comparable")
+ for i := 0; i < n; i++ {
+ et := iface.EmbeddedType(i)
+ if named, ok := et.(*types.Named); ok && named.Obj() == comparable {
+ return true
+ }
+ if typeEmbedsComparable(et) {
+ return true
+ }
+ }
+ return false
+}
+
+// A translator is used to translate a file from generic Go to Go 1.
+type translator struct {
+ fset *token.FileSet
+ importer *Importer
+ tpkg *types.Package
+ types map[ast.Expr]types.Type
+ newDecls []ast.Decl
+ typePackages map[*types.Package]bool
+
+ // typeDepth tracks recursive type instantiations.
+ typeDepth int
+
+ // err is set if we have seen an error during this translation.
+ // This is used by the rewrite methods.
+ err error
+}
+
+// instantiations tracks all function and type instantiations for a package.
+type instantiations struct {
+ funcInstantiations map[string][]*funcInstantiation
+ typeInstantiations map[types.Type][]*typeInstantiation
+}
+
+// A funcInstantiation is a single instantiation of a function.
+type funcInstantiation struct {
+ types []types.Type
+ decl *ast.Ident
+}
+
+// A typeInstantiation is a single instantiation of a type.
+type typeInstantiation struct {
+ types []types.Type
+ decl *ast.Ident
+ typ types.Type
+ inProgress bool
+}
+
+// funcInstantiations fetches the function instantiations defined in
+// the current package, given a generic function name.
+func (t *translator) funcInstantiations(key string) []*funcInstantiation {
+ insts := t.importer.instantiations[t.tpkg]
+ if insts == nil {
+ return nil
+ }
+ return insts.funcInstantiations[key]
+}
+
+// addFuncInstantiation adds a new function instantiation.
+func (t *translator) addFuncInstantiation(key string, inst *funcInstantiation) {
+ insts := t.pkgInstantiations()
+ insts.funcInstantiations[key] = append(insts.funcInstantiations[key], inst)
+}
+
+// typeInstantiations fetches the type instantiations defined in
+// the current package, given a generic type.
+func (t *translator) typeInstantiations(typ types.Type) []*typeInstantiation {
+ insts := t.importer.instantiations[t.tpkg]
+ if insts == nil {
+ return nil
+ }
+ return insts.typeInstantiations[typ]
+}
+
+// addTypeInstantiations adds a new type instantiation.
+func (t *translator) addTypeInstantiation(typ types.Type, inst *typeInstantiation) {
+ insts := t.pkgInstantiations()
+ insts.typeInstantiations[typ] = append(insts.typeInstantiations[typ], inst)
+}
+
+// pkgInstantiations returns the instantiations structure for the current
+// package, creating it if necessary.
+func (t *translator) pkgInstantiations() *instantiations {
+ insts := t.importer.instantiations[t.tpkg]
+ if insts == nil {
+ insts = &instantiations{
+ funcInstantiations: make(map[string][]*funcInstantiation),
+ typeInstantiations: make(map[types.Type][]*typeInstantiation),
+ }
+ t.importer.instantiations[t.tpkg] = insts
+ }
+ return insts
+}
+
+// rewrite rewrites the contents of one file.
+func rewriteFile(dir string, fset *token.FileSet, importer *Importer, importPath string, tpkg *types.Package, filename string, file *ast.File, addImportableName bool) (err error) {
+ if err := rewriteAST(fset, importer, importPath, tpkg, file, addImportableName); err != nil {
+ return err
+ }
+
+ filename = filepath.Base(filename)
+ goFile := strings.TrimSuffix(filename, filepath.Ext(filename)) + ".go"
+ o, err := os.Create(filepath.Join(dir, goFile))
+ if err != nil {
+ return err
+ }
+ defer func() {
+ if closeErr := o.Close(); err == nil {
+ err = closeErr
+ }
+ }()
+
+ w := bufio.NewWriter(o)
+ defer func() {
+ if flushErr := w.Flush(); err == nil {
+ err = flushErr
+ }
+ }()
+ fmt.Fprintln(w, rewritePrefix)
+
+ return config.Fprint(w, fset, file)
+}
+
+// rewriteAST rewrites the AST for a file.
+func rewriteAST(fset *token.FileSet, importer *Importer, importPath string, tpkg *types.Package, file *ast.File, addImportableName bool) (err error) {
+ t := translator{
+ fset: fset,
+ importer: importer,
+ tpkg: tpkg,
+ types: make(map[ast.Expr]types.Type),
+ typePackages: make(map[*types.Package]bool),
+ }
+ t.translate(file)
+
+ // Add all the transitive imports. This is more than we need,
+ // but we're not trying to be elegant here.
+ imps := make(map[string]bool)
+
+ for _, p := range importer.transitiveImports(importPath) {
+ imps[p] = true
+ }
+ for pkg := range t.typePackages {
+ if pkg != t.tpkg {
+ imps[pkg.Path()] = true
+ }
+ }
+
+ decls := make([]ast.Decl, 0, len(file.Decls))
+ var specs []ast.Spec
+ for _, decl := range file.Decls {
+ gen, ok := decl.(*ast.GenDecl)
+ if !ok || gen.Tok != token.IMPORT {
+ decls = append(decls, decl)
+ continue
+ }
+ for _, spec := range gen.Specs {
+ imp := spec.(*ast.ImportSpec)
+ if imp.Name != nil {
+ specs = append(specs, imp)
+ }
+ // We picked up Go 2 imports above, but we still
+ // need to pick up Go 1 imports here.
+ path, err := strconv.Unquote(imp.Path.Value)
+ if err != nil || imps[path] {
+ continue
+ }
+ if imp.Name == nil {
+ // If Name != nil we are keeping the spec.
+ // Don't add the import again here,
+ // only if it is needed elsewhere.
+ imps[path] = true
+ }
+ for _, p := range importer.transitiveImports(path) {
+ imps[p] = true
+ }
+ }
+ }
+ file.Decls = decls
+
+ // If we have a ./ import, let it override a standard import
+ // we may have added due to t.typePackages.
+ for path := range imps {
+ if strings.HasPrefix(path, "./") {
+ delete(imps, strings.TrimPrefix(path, "./"))
+ }
+ }
+
+ paths := make([]string, 0, len(imps))
+ for p := range imps {
+ paths = append(paths, p)
+ }
+ sort.Strings(paths)
+
+ for _, p := range paths {
+ specs = append(specs, ast.Spec(&ast.ImportSpec{
+ Path: &ast.BasicLit{
+ Kind: token.STRING,
+ Value: strconv.Quote(p),
+ },
+ }))
+ }
+ if len(specs) > 0 {
+ first := &ast.GenDecl{
+ Tok: token.IMPORT,
+ Specs: specs,
+ }
+ file.Decls = append([]ast.Decl{first}, file.Decls...)
+ }
+
+ // Add a name that other packages can reference to avoid an error
+ // about an unused package.
+ if addImportableName {
+ file.Decls = append(file.Decls,
+ &ast.GenDecl{
+ Tok: token.TYPE,
+ Specs: []ast.Spec{
+ &ast.TypeSpec{
+ Name: ast.NewIdent(t.importableName()),
+ Type: ast.NewIdent("int"),
+ },
+ },
+ })
+ }
+
+ // Add a reference for each imported package to avoid an error
+ // about an unused package.
+ for _, decl := range file.Decls {
+ gen, ok := decl.(*ast.GenDecl)
+ if !ok || gen.Tok != token.IMPORT {
+ continue
+ }
+ for _, spec := range gen.Specs {
+ imp := spec.(*ast.ImportSpec)
+ if imp.Name != nil && imp.Name.Name == "_" {
+ continue
+ }
+ path := strings.TrimPrefix(strings.TrimSuffix(imp.Path.Value, `"`), `"`)
+ var pname string
+
+ var tok token.Token
+ var importableName string
+ if pkg, ok := importer.lookupPackage(path); ok {
+ tok = token.TYPE
+ importableName = t.importableName()
+ pname = pkg.Name()
+ } else {
+ fileDir := filepath.Dir(fset.Position(file.Name.Pos()).Filename)
+ pkg, err := importer.ImportFrom(path, fileDir, 0)
+ if err != nil {
+ return err
+ }
+ scope := pkg.Scope()
+ pname = pkg.Name()
+ names := scope.Names()
+ nameLoop:
+ for _, name := range names {
+ if !token.IsExported(name) {
+ continue
+ }
+ obj := scope.Lookup(name)
+ switch obj.(type) {
+ case *types.TypeName:
+ tok = token.TYPE
+ importableName = name
+ break nameLoop
+ case *types.Var, *types.Func:
+ tok = token.VAR
+ importableName = name
+ break nameLoop
+ case *types.Const:
+ tok = token.CONST
+ importableName = name
+ break nameLoop
+ }
+ }
+ if importableName == "" {
+ return fmt.Errorf("can't find any importable name in package %q", path)
+ }
+ }
+
+ var name string
+ if imp.Name != nil {
+ name = imp.Name.Name
+ } else {
+ name = pname
+ }
+ var spec ast.Spec
+ switch tok {
+ case token.CONST, token.VAR:
+ spec = &ast.ValueSpec{
+ Names: []*ast.Ident{
+ ast.NewIdent("_"),
+ },
+ Values: []ast.Expr{
+ &ast.SelectorExpr{
+ X: ast.NewIdent(name),
+ Sel: ast.NewIdent(importableName),
+ },
+ },
+ }
+ case token.TYPE:
+ spec = &ast.TypeSpec{
+ Name: ast.NewIdent("_"),
+ Type: &ast.SelectorExpr{
+ X: ast.NewIdent(name),
+ Sel: ast.NewIdent(importableName),
+ },
+ }
+ default:
+ panic("can't happen")
+ }
+ file.Decls = append(file.Decls,
+ &ast.GenDecl{
+ Tok: tok,
+ Specs: []ast.Spec{spec},
+ })
+ }
+ }
+
+ return t.err
+}
+
+// translate translates the AST for a file from generic Go to Go 1.
+func (t *translator) translate(file *ast.File) {
+ declsToDo := file.Decls
+ file.Decls = nil
+ c := 0
+ for len(declsToDo) > 0 {
+ if c > 50 {
+ var sb strings.Builder
+ printer.Fprint(&sb, t.fset, declsToDo[0])
+ t.err = fmt.Errorf("looping while expanding %v", &sb)
+ return
+ }
+ c++
+
+ newDecls := make([]ast.Decl, 0, len(declsToDo))
+ for i, decl := range declsToDo {
+ switch decl := decl.(type) {
+ case *ast.FuncDecl:
+ if !isParameterizedFuncDecl(decl, t.importer.info) {
+ t.translateFuncDecl(&declsToDo[i])
+ newDecls = append(newDecls, decl)
+ }
+ case *ast.GenDecl:
+ switch decl.Tok {
+ case token.TYPE:
+ newSpecs := make([]ast.Spec, 0, len(decl.Specs))
+ for j := range decl.Specs {
+ if isTranslatableType(decl.Specs[j], t.importer.info) {
+ t.translateTypeSpec(&decl.Specs[j])
+ newSpecs = append(newSpecs, decl.Specs[j])
+ }
+ }
+ if len(newSpecs) == 0 {
+ decl = nil
+ } else {
+ decl.Specs = newSpecs
+ }
+ case token.VAR, token.CONST:
+ for j := range decl.Specs {
+ t.translateValueSpec(&decl.Specs[j])
+ }
+ }
+ if decl != nil {
+ newDecls = append(newDecls, decl)
+ }
+ default:
+ newDecls = append(newDecls, decl)
+ }
+ }
+ file.Decls = append(file.Decls, newDecls...)
+ declsToDo = t.newDecls
+ t.newDecls = nil
+ }
+}
+
+// translateTypeSpec translates a type from generic Go to Go 1.
+func (t *translator) translateTypeSpec(ps *ast.Spec) {
+ ts := (*ps).(*ast.TypeSpec)
+ if ts.TParams != nil && len(ts.TParams.List) > 0 {
+ t.err = fmt.Errorf("%s: go2go tool does not support parameterized type here", t.fset.Position((*ps).Pos()))
+ return
+ }
+ t.translateExpr(&ts.Type)
+}
+
+// translateValueSpec translates a variable or constant from generic Go to Go 1.
+func (t *translator) translateValueSpec(ps *ast.Spec) {
+ vs := (*ps).(*ast.ValueSpec)
+ t.translateExpr(&vs.Type)
+ for i := range vs.Values {
+ t.translateExpr(&vs.Values[i])
+ }
+}
+
+// translateFuncDecl translates a function from generic Go to Go 1.
+func (t *translator) translateFuncDecl(pd *ast.Decl) {
+ if t.err != nil {
+ return
+ }
+ fd := (*pd).(*ast.FuncDecl)
+ if fd.Type.TParams != nil {
+ if len(fd.Type.TParams.List) > 0 {
+ panic("parameterized function")
+ }
+ fd.Type.TParams = nil
+ }
+ if fd.Recv != nil {
+ t.translateFieldList(fd.Recv)
+ }
+ t.translateFieldList(fd.Type.Params)
+ t.translateFieldList(fd.Type.Results)
+ t.translateBlockStmt(fd.Body)
+}
+
+// translateBlockStmt translates a block statement from generic Go to Go 1.
+func (t *translator) translateBlockStmt(pbs *ast.BlockStmt) {
+ if pbs == nil {
+ return
+ }
+ for i := range pbs.List {
+ t.translateStmt(&pbs.List[i])
+ }
+}
+
+// translateStmt translates a statement from generic Go to Go 1.
+func (t *translator) translateStmt(ps *ast.Stmt) {
+ if t.err != nil {
+ return
+ }
+ if *ps == nil {
+ return
+ }
+ switch s := (*ps).(type) {
+ case *ast.DeclStmt:
+ d := s.Decl.(*ast.GenDecl)
+ switch d.Tok {
+ case token.TYPE:
+ for i := range d.Specs {
+ t.translateTypeSpec(&d.Specs[i])
+ }
+ case token.CONST, token.VAR:
+ for i := range d.Specs {
+ t.translateValueSpec(&d.Specs[i])
+ }
+ default:
+ panic(fmt.Sprintf("unknown decl type %v", d.Tok))
+ }
+ case *ast.EmptyStmt:
+ case *ast.LabeledStmt:
+ t.translateStmt(&s.Stmt)
+ case *ast.ExprStmt:
+ t.translateExpr(&s.X)
+ case *ast.SendStmt:
+ t.translateExpr(&s.Chan)
+ t.translateExpr(&s.Value)
+ case *ast.IncDecStmt:
+ t.translateExpr(&s.X)
+ case *ast.AssignStmt:
+ t.translateExprList(s.Lhs)
+ t.translateExprList(s.Rhs)
+ case *ast.GoStmt:
+ e := ast.Expr(s.Call)
+ t.translateExpr(&e)
+ s.Call = e.(*ast.CallExpr)
+ case *ast.DeferStmt:
+ e := ast.Expr(s.Call)
+ t.translateExpr(&e)
+ s.Call = e.(*ast.CallExpr)
+ case *ast.ReturnStmt:
+ t.translateExprList(s.Results)
+ case *ast.BranchStmt:
+ case *ast.BlockStmt:
+ t.translateBlockStmt(s)
+ case *ast.IfStmt:
+ t.translateStmt(&s.Init)
+ t.translateExpr(&s.Cond)
+ t.translateBlockStmt(s.Body)
+ t.translateStmt(&s.Else)
+ case *ast.CaseClause:
+ t.translateExprList(s.List)
+ t.translateStmtList(s.Body)
+ case *ast.SwitchStmt:
+ t.translateStmt(&s.Init)
+ t.translateExpr(&s.Tag)
+ t.translateBlockStmt(s.Body)
+ case *ast.TypeSwitchStmt:
+ t.translateStmt(&s.Init)
+ t.translateStmt(&s.Assign)
+ t.translateBlockStmt(s.Body)
+ case *ast.CommClause:
+ t.translateStmt(&s.Comm)
+ t.translateStmtList(s.Body)
+ case *ast.SelectStmt:
+ t.translateBlockStmt(s.Body)
+ case *ast.ForStmt:
+ t.translateStmt(&s.Init)
+ t.translateExpr(&s.Cond)
+ t.translateStmt(&s.Post)
+ t.translateBlockStmt(s.Body)
+ case *ast.RangeStmt:
+ t.translateExpr(&s.Key)
+ t.translateExpr(&s.Value)
+ t.translateExpr(&s.X)
+ t.translateBlockStmt(s.Body)
+ default:
+ panic(fmt.Sprintf("unimplemented Stmt %T", s))
+ }
+}
+
+// translateStmtList translates a list of statements generic Go to Go 1.
+func (t *translator) translateStmtList(sl []ast.Stmt) {
+ for i := range sl {
+ t.translateStmt(&sl[i])
+ }
+}
+
+// translateExpr translates an expression from generic Go to Go 1.
+func (t *translator) translateExpr(pe *ast.Expr) {
+ if t.err != nil {
+ return
+ }
+ if *pe == nil {
+ return
+ }
+ switch e := (*pe).(type) {
+ case *ast.Ident:
+ t.translateIdent(pe)
+ case *ast.Ellipsis:
+ t.translateExpr(&e.Elt)
+ case *ast.BasicLit:
+ case *ast.FuncLit:
+ t.translateFieldList(e.Type.TParams)
+ t.translateFieldList(e.Type.Params)
+ t.translateFieldList(e.Type.Results)
+ t.translateBlockStmt(e.Body)
+ case *ast.CompositeLit:
+ t.translateExpr(&e.Type)
+ t.translateExprList(e.Elts)
+ case *ast.ParenExpr:
+ t.translateExpr(&e.X)
+ case *ast.SelectorExpr:
+ t.translateSelectorExpr(pe)
+ case *ast.IndexExpr:
+ if ftyp, ok := t.lookupType(e.X).(*types.Signature); ok && len(ftyp.TParams()) > 0 {
+ t.translateFunctionInstantiation(pe)
+ } else if ntyp, ok := t.lookupType(e.X).(*types.Named); ok && len(ntyp.TParams()) > 0 && len(ntyp.TArgs()) == 0 {
+ t.translateTypeInstantiation(pe)
+ }
+ t.translateExpr(&e.X)
+ t.translateExpr(&e.Index)
+ case *ast.SliceExpr:
+ t.translateExpr(&e.X)
+ t.translateExpr(&e.Low)
+ t.translateExpr(&e.High)
+ t.translateExpr(&e.Max)
+ case *ast.TypeAssertExpr:
+ t.translateExpr(&e.X)
+ t.translateExpr(&e.Type)
+ case *ast.CallExpr:
+ if ftyp, ok := t.lookupType(e.Fun).(*types.Signature); ok && len(ftyp.TParams()) > 0 {
+ t.translateFunctionInstantiation(pe)
+ } else if ntyp, ok := t.lookupType(e.Fun).(*types.Named); ok && len(ntyp.TParams()) > 0 && len(ntyp.TArgs()) == 0 {
+ t.translateTypeInstantiation(pe)
+ }
+ t.translateExprList(e.Args)
+ t.translateExpr(&e.Fun)
+ case *ast.StarExpr:
+ t.translateExpr(&e.X)
+ case *ast.UnaryExpr:
+ t.translateExpr(&e.X)
+ case *ast.BinaryExpr:
+ t.translateExpr(&e.X)
+ t.translateExpr(&e.Y)
+ case *ast.KeyValueExpr:
+ t.translateExpr(&e.Key)
+ t.translateExpr(&e.Value)
+ case *ast.ArrayType:
+ t.translateExpr(&e.Len)
+ t.translateExpr(&e.Elt)
+ case *ast.StructType:
+ t.translateFieldList(e.Fields)
+ case *ast.FuncType:
+ t.translateFieldList(e.TParams)
+ t.translateFieldList(e.Params)
+ t.translateFieldList(e.Results)
+ case *ast.InterfaceType:
+ methods, types := splitFieldList(e.Methods)
+ t.translateFieldList(methods)
+ t.translateExprList(types)
+ case *ast.MapType:
+ t.translateExpr(&e.Key)
+ t.translateExpr(&e.Value)
+ case *ast.ChanType:
+ t.translateExpr(&e.Value)
+ default:
+ panic(fmt.Sprintf("unimplemented Expr %T", e))
+ }
+}
+
+// translateIdent translates a simple identifier generic Go to Go 1.
+// These are usually fine as is, but a reference
+// to a non-generic name in another package may need a package qualifier.
+func (t *translator) translateIdent(pe *ast.Expr) {
+ e := (*pe).(*ast.Ident)
+ obj := t.importer.info.ObjectOf(e)
+ if obj == nil {
+ return
+ }
+ if named, ok := obj.Type().(*types.Named); ok && len(named.TParams()) > 0 {
+ // A generic function that will be instantiated locally.
+ return
+ }
+ ipkg := obj.Pkg()
+ if ipkg == nil || ipkg == t.tpkg {
+ // We don't need a package qualifier if it's defined
+ // in the current package.
+ return
+ }
+ if obj.Parent() != ipkg.Scope() {
+ // We only need a package qualifier if it's defined in
+ // package scope.
+ return
+ }
+
+ // Add package qualifier.
+ *pe = &ast.SelectorExpr{
+ X: ast.NewIdent(ipkg.Name()),
+ Sel: e,
+ }
+}
+
+// translateSelectorExpr translates a selector expression
+// from generic Go to Go 1.
+func (t *translator) translateSelectorExpr(pe *ast.Expr) {
+ e := (*pe).(*ast.SelectorExpr)
+
+ t.translateExpr(&e.X)
+
+ obj := t.importer.info.ObjectOf(e.Sel)
+ if obj == nil {
+ return
+ }
+
+ // Handle references to promoted fields and methods,
+ // if they go through an embedded instantiated field.
+ // We have to add a reference to the field we inserted.
+ if xType := t.lookupType(e.X); xType != nil {
+ if ptr := types.AsPointer(xType); ptr != nil {
+ xType = ptr.Elem()
+ }
+ fobj, indexes, _ := types.LookupFieldOrMethod(xType, true, obj.Pkg(), obj.Name())
+ if fobj != nil && len(indexes) > 1 {
+ for _, index := range indexes[:len(indexes)-1] {
+ xf := types.AsStruct(xType).Field(index)
+ // This must be an embedded type.
+ // If the field name is the one we expect,
+ // don't mention it explicitly,
+ // because it might not be exported.
+ if xf.Name() == types.TypeString(xf.Type(), relativeTo(xf.Pkg())) {
+ continue
+ }
+ e.X = &ast.SelectorExpr{
+ X: e.X,
+ Sel: ast.NewIdent(xf.Name()),
+ }
+ xType = xf.Type()
+ if ptr := types.AsPointer(xType); ptr != nil {
+ xType = ptr.Elem()
+ }
+ }
+ }
+ }
+
+ // Handle references to instantiated embedded fields.
+ // We have to rewrite the name to the name used in
+ // the translated struct definition.
+ if f, ok := obj.(*types.Var); ok && f.Embedded() {
+ typ := t.lookupType(e)
+ if typ == nil {
+ typ = f.Type()
+ }
+ if pt := types.AsPointer(typ); pt != nil {
+ typ = pt.Elem()
+ }
+ named, ok := typ.(*types.Named)
+ if !ok || len(named.TArgs()) == 0 {
+ return
+ }
+ if obj.Name() != named.Obj().Name() {
+ return
+ }
+ _, id := t.lookupInstantiatedType(named)
+ *pe = &ast.SelectorExpr{
+ X: e.X,
+ Sel: id,
+ }
+ }
+}
+
+// TODO(iant) refactor code and get rid of this?
+func splitFieldList(fl *ast.FieldList) (methods *ast.FieldList, types []ast.Expr) {
+ if fl == nil {
+ return
+ }
+ var mfields []*ast.Field
+ for _, f := range fl.List {
+ if len(f.Names) > 0 && f.Names[0].Name == "type" {
+ // type list type
+ types = append(types, f.Type)
+ } else {
+ mfields = append(mfields, f)
+ }
+ }
+ copy := *fl
+ copy.List = mfields
+ methods = ©
+ return
+}
+
+// TODO(iant) refactor code and get rid of this?
+func mergeFieldList(methods *ast.FieldList, types []ast.Expr) (fl *ast.FieldList) {
+ fl = methods
+ if len(types) == 0 {
+ return
+ }
+ if fl == nil {
+ fl = new(ast.FieldList)
+ }
+ name := []*ast.Ident{ast.NewIdent("type")}
+ for _, typ := range types {
+ fl.List = append(fl.List, &ast.Field{Names: name, Type: typ})
+ }
+ return
+}
+
+// translateExprList translate an expression list generic Go to Go 1.
+func (t *translator) translateExprList(el []ast.Expr) {
+ for i := range el {
+ t.translateExpr(&el[i])
+ }
+}
+
+// translateFieldList translates a field list generic Go to Go 1.
+func (t *translator) translateFieldList(fl *ast.FieldList) {
+ if fl == nil {
+ return
+ }
+ for _, f := range fl.List {
+ t.translateField(f)
+ }
+}
+
+// translateField translates a field generic Go to Go 1.
+func (t *translator) translateField(f *ast.Field) {
+ t.translateExpr(&f.Type)
+}
+
+// translateFunctionInstantiation translates an instantiated function
+// to Go 1.
+func (t *translator) translateFunctionInstantiation(pe *ast.Expr) {
+ expr := *pe
+ qid := t.instantiatedIdent(expr)
+ argList, typeList, typeArgs := t.instantiationTypes(expr)
+ if t.err != nil {
+ return
+ }
+
+ var instIdent *ast.Ident
+ key := qid.String()
+ insts := t.funcInstantiations(key)
+ for _, inst := range insts {
+ if t.sameTypes(typeList, inst.types) {
+ instIdent = inst.decl
+ break
+ }
+ }
+
+ if instIdent == nil {
+ var err error
+ instIdent, err = t.instantiateFunction(qid, argList, typeList)
+ if err != nil {
+ t.err = err
+ return
+ }
+
+ n := &funcInstantiation{
+ types: typeList,
+ decl: instIdent,
+ }
+ t.addFuncInstantiation(key, n)
+ }
+
+ if typeArgs {
+ *pe = instIdent
+ } else {
+ switch e := expr.(type) {
+ case *ast.CallExpr:
+ newCall := *e
+ newCall.Fun = instIdent
+ *pe = &newCall
+ case *ast.IndexExpr:
+ *pe = instIdent
+ default:
+ panic("unexpected AST type")
+ }
+ }
+}
+
+// translateTypeInstantiation translates an instantiated type to Go 1.
+func (t *translator) translateTypeInstantiation(pe *ast.Expr) {
+ expr := *pe
+ qid := t.instantiatedIdent(expr)
+ typ := t.lookupType(qid.ident).(*types.Named)
+ argList, typeList, typeArgs := t.instantiationTypes(expr)
+ if t.err != nil {
+ return
+ }
+ if !typeArgs {
+ panic("no type arguments for type")
+ }
+
+ var seen *typeInstantiation
+ key := t.typeWithoutArgs(typ)
+ for _, inst := range t.typeInstantiations(key) {
+ if t.sameTypes(typeList, inst.types) {
+ if inst.inProgress {
+ panic(fmt.Sprintf("%s: circular type instantiation", t.fset.Position((*pe).Pos())))
+ }
+ if inst.decl == nil {
+ // This can happen if we've instantiated
+ // the type in instantiateType.
+ seen = inst
+ break
+ }
+ *pe = inst.decl
+ return
+ }
+ }
+
+ name, err := t.instantiatedName(qid, typeList)
+ if err != nil {
+ t.err = err
+ return
+ }
+ instIdent := ast.NewIdent(name)
+
+ if seen != nil {
+ seen.decl = instIdent
+ seen.inProgress = true
+ } else {
+ seen = &typeInstantiation{
+ types: typeList,
+ decl: instIdent,
+ typ: nil,
+ inProgress: true,
+ }
+ t.addTypeInstantiation(key, seen)
+ }
+
+ defer func() {
+ seen.inProgress = false
+ }()
+
+ instType, err := t.instantiateTypeDecl(qid, typ, argList, typeList, instIdent)
+ if err != nil {
+ t.err = err
+ return
+ }
+
+ if seen.typ == nil {
+ seen.typ = instType
+ }
+
+ *pe = instIdent
+}
+
+// instantiatedIdent returns the qualified identifer that is being
+// instantiated.
+func (t *translator) instantiatedIdent(x ast.Expr) qualifiedIdent {
+ var fun ast.Expr
+ switch x := x.(type) {
+ case *ast.CallExpr:
+ fun = x.Fun
+ case *ast.IndexExpr:
+ fun = x.X
+ default:
+ panic(fmt.Sprintf("unexpected AST %T", x))
+ }
+
+ switch fun := fun.(type) {
+ case *ast.Ident:
+ if obj := t.importer.info.ObjectOf(fun); obj != nil && obj.Pkg() != t.tpkg {
+ return qualifiedIdent{pkg: obj.Pkg(), ident: fun}
+ }
+ return qualifiedIdent{ident: fun}
+ case *ast.SelectorExpr:
+ pkgname, ok := fun.X.(*ast.Ident)
+ if !ok {
+ break
+ }
+ pkgobj, ok := t.importer.info.Uses[pkgname]
+ if !ok {
+ break
+ }
+ pn, ok := pkgobj.(*types.PkgName)
+ if !ok {
+ break
+ }
+ return qualifiedIdent{pkg: pn.Imported(), ident: fun.Sel}
+ }
+ panic(fmt.Sprintf("instantiated object %T %v is not an identifier", fun, fun))
+}
+
+// instantiationTypes returns the type arguments of an instantiation.
+// It also returns the AST arguments if they are present.
+// The typeArgs result reports whether the AST arguments are types.
+func (t *translator) instantiationTypes(x ast.Expr) (argList []ast.Expr, typeList []types.Type, typeArgs bool) {
+ var args []ast.Expr
+ switch x := x.(type) {
+ case *ast.CallExpr:
+ args = x.Args
+ case *ast.IndexExpr:
+ args = []ast.Expr{x.Index}
+ default:
+ panic("unexpected AST type")
+ }
+ inferred, haveInferred := t.importer.info.Inferred[x]
+
+ if !haveInferred {
+ argList = args
+ typeList = make([]types.Type, 0, len(argList))
+ for _, arg := range argList {
+ if id, ok := arg.(*ast.Ident); ok && id.Name == "_" {
+ t.err = fmt.Errorf("%s: go2go tool does not support using _ here", t.fset.Position(arg.Pos()))
+ return
+ }
+ if at := t.lookupType(arg); at == nil {
+ panic(fmt.Sprintf("%s: no type found for %T %v", t.fset.Position(arg.Pos()), arg, arg))
+ } else {
+ typeList = append(typeList, at)
+ }
+ }
+ typeArgs = true
+ } else {
+ typeList, argList = t.typeListToASTList(inferred.Targs)
+ }
+
+ // Instantiating with a locally defined type won't work.
+ // Check that here.
+ for _, typ := range typeList {
+ if named, ok := typ.(*types.Named); ok && named.Obj().Pkg() != nil {
+ if scope := named.Obj().Parent(); scope != nil && scope != named.Obj().Pkg().Scope() {
+ t.err = fmt.Errorf("%s: go2go tool does not support using locally defined type as type argument", t.fset.Position(x.Pos()))
+ return
+ }
+ }
+ }
+
+ return
+}
+
+// lookupInstantiatedType looks for an existing instantiation of an
+// instantiated type.
+func (t *translator) lookupInstantiatedType(typ *types.Named) (types.Type, *ast.Ident) {
+ copyType := func(typ *types.Named, newName string) types.Type {
+ nm := typ.NumMethods()
+ methods := make([]*types.Func, 0, nm)
+ for i := 0; i < nm; i++ {
+ methods = append(methods, typ.Method(i))
+ }
+ obj := typ.Obj()
+ obj = types.NewTypeName(obj.Pos(), obj.Pkg(), newName, nil)
+ nt := types.NewNamed(obj, typ.Underlying(), methods)
+ nt.SetTArgs(typ.TArgs())
+ return nt
+ }
+
+ targs := typ.TArgs()
+ key := t.typeWithoutArgs(typ)
+ var seen *typeInstantiation
+ for _, inst := range t.typeInstantiations(key) {
+ if t.sameTypes(targs, inst.types) {
+ if inst.inProgress {
+ panic(fmt.Sprintf("instantiation for %v in progress", typ))
+ }
+ if inst.decl == nil {
+ // This can happen if we've instantiated
+ // the type in instantiateType.
+ seen = inst
+ break
+ }
+ if inst.typ == nil {
+ panic(fmt.Sprintf("no type for instantiation entry for %v", typ))
+ }
+ if instNamed, ok := inst.typ.(*types.Named); ok {
+ return copyType(instNamed, inst.decl.Name), inst.decl
+ }
+ return inst.typ, inst.decl
+ }
+ }
+
+ typeList, argList := t.typeListToASTList(targs)
+
+ qid := qualifiedIdent{ident: ast.NewIdent(typ.Obj().Name())}
+ if typPkg := typ.Obj().Pkg(); typPkg != t.tpkg {
+ qid.pkg = typPkg
+ }
+
+ name, err := t.instantiatedName(qid, typeList)
+ if err != nil {
+ t.err = err
+ return nil, nil
+ }
+ instIdent := ast.NewIdent(name)
+
+ if seen != nil {
+ seen.decl = instIdent
+ seen.inProgress = true
+ } else {
+ seen = &typeInstantiation{
+ types: targs,
+ decl: instIdent,
+ typ: nil,
+ inProgress: true,
+ }
+ t.addTypeInstantiation(key, seen)
+ }
+
+ defer func() {
+ seen.inProgress = false
+ }()
+
+ instType, err := t.instantiateTypeDecl(qid, typ, argList, typeList, instIdent)
+ if err != nil {
+ t.err = err
+ return nil, nil
+ }
+
+ if seen.typ == nil {
+ seen.typ = instType
+ } else {
+ instType = seen.typ
+ }
+
+ if instNamed, ok := instType.(*types.Named); ok {
+ return copyType(instNamed, instIdent.Name), instIdent
+ }
+ return instType, instIdent
+}
+
+// typeWithoutArgs takes a named type with arguments and returns the
+// same type without arguments.
+func (t *translator) typeWithoutArgs(typ *types.Named) *types.Named {
+ if len(typ.TArgs()) == 0 {
+ return typ
+ }
+ name := typ.Obj().Name()
+ fields := strings.Split(name, ".")
+ if len(fields) > 2 {
+ panic(fmt.Sprintf("unparseable instantiated name %q", name))
+ }
+ if len(fields) > 1 {
+ name = fields[1]
+ }
+
+ tpkg := typ.Obj().Pkg()
+ if tpkg == nil {
+ panic(fmt.Sprintf("can't find package for %s", name))
+ }
+ nobj := tpkg.Scope().Lookup(name)
+ if nobj == nil {
+ panic(fmt.Sprintf("can't find %q in scope of package %q", name, tpkg.Name()))
+ }
+ return nobj.Type().(*types.Named)
+}
+
+// typeListToASTList returns an AST list for a type list,
+// as well as an updated type list.
+func (t *translator) typeListToASTList(typeList []types.Type) ([]types.Type, []ast.Expr) {
+ argList := make([]ast.Expr, 0, len(typeList))
+ for _, typ := range typeList {
+ argList = append(argList, t.typeToAST(typ))
+
+ // This inferred type may introduce a reference to
+ // packages that we don't otherwise import, and that
+ // package name may wind up in arg. Record all packages
+ // seen in inferred types so that we add import
+ // statements for them, just in case.
+ t.addTypePackages(typ)
+ }
+ return typeList, argList
+}
+
+// relativeTo is like types.RelativeTo, but returns just the package name,
+// not the package path.
+func relativeTo(pkg *types.Package) types.Qualifier {
+ return func(other *types.Package) string {
+ if pkg == other {
+ return "" // same package; unqualified
+ }
+ return other.Name()
+ }
+}
+
+// sameTypes reports whether two type slices are the same.
+func (t *translator) sameTypes(a, b []types.Type) bool {
+ if len(a) != len(b) {
+ return false
+ }
+ for i, x := range a {
+ if !t.sameType(x, b[i]) {
+ return false
+ }
+ }
+ return true
+}
+
+// sameType reports whether two types are the same.
+// We have to check type arguments ourselves.
+func (t *translator) sameType(a, b types.Type) bool {
+ if types.IdenticalIgnoreTags(a, b) {
+ return true
+ }
+ if ap, bp := types.AsPointer(a), types.AsPointer(b); ap != nil && bp != nil {
+ a = ap.Elem()
+ b = bp.Elem()
+ }
+ an, ok := a.(*types.Named)
+ if !ok {
+ return false
+ }
+ bn, ok := b.(*types.Named)
+ if !ok {
+ return false
+ }
+ if an.Obj().Name() != bn.Obj().Name() {
+ return false
+ }
+ if len(an.TArgs()) == 0 || len(an.TArgs()) != len(bn.TArgs()) {
+ return false
+ }
+ for i, typ := range an.TArgs() {
+ if !t.sameType(typ, bn.TArgs()[i]) {
+ return false
+ }
+ }
+ return true
+}
+
+// qualifiedIdent is an identifier possibly qualified with a package.
+type qualifiedIdent struct {
+ pkg *types.Package // identifier's package; nil for current package
+ ident *ast.Ident
+}
+
+// String returns a printable name for qid.
+func (qid qualifiedIdent) String() string {
+ if qid.pkg == nil {
+ return qid.ident.Name
+ }
+ return qid.pkg.Path() + "." + qid.ident.Name
+}
diff --git a/src/go/go2go/types.go b/src/go/go2go/types.go
new file mode 100644
index 0000000..4af92ce
--- /dev/null
+++ b/src/go/go2go/types.go
@@ -0,0 +1,626 @@
+// Copyright 2020 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 go2go
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "go/types"
+ "strconv"
+ "strings"
+)
+
+// lookupType returns the types.Type for an AST expression.
+// Returns nil if the type is not known.
+func (t *translator) lookupType(e ast.Expr) types.Type {
+ if typ := t.importer.info.TypeOf(e); typ != nil {
+ return typ
+ }
+ if typ, ok := t.types[e]; ok {
+ return typ
+ }
+ return nil
+}
+
+// setType records the type for an AST expression. This is only used for
+// AST expressions created during function instantiation.
+// Uninstantiated AST expressions will be listed in t.importer.info.Types.
+func (t *translator) setType(e ast.Expr, nt types.Type) {
+ if ot, ok := t.importer.info.Types[e]; ok {
+ if !t.sameType(ot.Type, nt) {
+ panic("expression type changed")
+ }
+ return
+ }
+ if ot, ok := t.types[e]; ok {
+ if !t.sameType(ot, nt) {
+ panic("expression type changed")
+ }
+ return
+ }
+ t.types[e] = nt
+}
+
+// instantiateType instantiates typ using ta.
+func (t *translator) instantiateType(ta *typeArgs, typ types.Type) types.Type {
+ if t.err != nil {
+ return nil
+ }
+
+ t.typeDepth++
+ defer func() { t.typeDepth-- }()
+ if t.typeDepth > 25 {
+ t.err = fmt.Errorf("looping while instantiating %v %v", typ, ta.types)
+ return nil
+ }
+
+ var inProgress *typeInstantiation
+ for _, inst := range t.typeInstantiations(typ) {
+ if t.sameTypes(ta.types, inst.types) {
+ if inst.typ == nil {
+ inProgress = inst
+ break
+ }
+ return inst.typ
+ }
+ }
+
+ ityp := t.doInstantiateType(ta, typ)
+ if inProgress != nil {
+ if inProgress.typ == nil {
+ inProgress.typ = ityp
+ } else {
+ ityp = inProgress.typ
+ }
+ } else {
+ typinst := &typeInstantiation{
+ types: ta.types,
+ typ: ityp,
+ }
+ t.addTypeInstantiation(typ, typinst)
+ }
+
+ return ityp
+}
+
+// doInstantiateType does the work of instantiating typ using ta.
+// This should only be called from instantiateType.
+func (t *translator) doInstantiateType(ta *typeArgs, typ types.Type) types.Type {
+ switch typ := typ.(type) {
+ case *types.Basic:
+ return typ
+ case *types.Array:
+ elem := typ.Elem()
+ instElem := t.instantiateType(ta, elem)
+ if elem == instElem {
+ return typ
+ }
+ return types.NewArray(instElem, typ.Len())
+ case *types.Slice:
+ elem := typ.Elem()
+ instElem := t.instantiateType(ta, elem)
+ if elem == instElem {
+ return typ
+ }
+ return types.NewSlice(instElem)
+ case *types.Struct:
+ n := typ.NumFields()
+ fields := make([]*types.Var, n)
+ changed := false
+ tags := make([]string, n)
+ hasTag := false
+ for i := 0; i < n; i++ {
+ v := typ.Field(i)
+ instType := t.instantiateType(ta, v.Type())
+ if v.Type() != instType {
+ changed = true
+ }
+ fields[i] = types.NewVar(v.Pos(), v.Pkg(), v.Name(), instType)
+
+ tag := typ.Tag(i)
+ if tag != "" {
+ tags[i] = tag
+ hasTag = true
+ }
+ }
+ if !changed {
+ return typ
+ }
+ if !hasTag {
+ tags = nil
+ }
+ return types.NewStruct(fields, tags)
+ case *types.Pointer:
+ elem := typ.Elem()
+ instElem := t.instantiateType(ta, elem)
+ if elem == instElem {
+ return typ
+ }
+ return types.NewPointer(instElem)
+ case *types.Tuple:
+ return t.instantiateTypeTuple(ta, typ)
+ case *types.Signature:
+ params := t.instantiateTypeTuple(ta, typ.Params())
+ results := t.instantiateTypeTuple(ta, typ.Results())
+ if params == typ.Params() && results == typ.Results() {
+ return typ
+ }
+ r := types.NewSignature(typ.Recv(), params, results, typ.Variadic())
+ if tparams := typ.TParams(); tparams != nil {
+ r.SetTParams(tparams)
+ }
+ return r
+ case *types.Interface:
+ nm := typ.NumExplicitMethods()
+ methods := make([]*types.Func, nm)
+ changed := false
+ for i := 0; i < nm; i++ {
+ m := typ.ExplicitMethod(i)
+ instSig := t.instantiateType(ta, m.Type()).(*types.Signature)
+ if instSig != m.Type() {
+ m = types.NewFunc(m.Pos(), m.Pkg(), m.Name(), instSig)
+ changed = true
+ }
+ methods[i] = m
+ }
+ ne := typ.NumEmbeddeds()
+ embeddeds := make([]types.Type, ne)
+ for i := 0; i < ne; i++ {
+ e := typ.EmbeddedType(i)
+ instE := t.instantiateType(ta, e)
+ if e != instE {
+ changed = true
+ }
+ embeddeds[i] = instE
+ }
+ if !changed {
+ return typ
+ }
+ return types.NewInterfaceType(methods, embeddeds)
+ case *types.Map:
+ key := t.instantiateType(ta, typ.Key())
+ elem := t.instantiateType(ta, typ.Elem())
+ if key == typ.Key() && elem == typ.Elem() {
+ return typ
+ }
+ return types.NewMap(key, elem)
+ case *types.Chan:
+ elem := t.instantiateType(ta, typ.Elem())
+ if elem == typ.Elem() {
+ return typ
+ }
+ return types.NewChan(typ.Dir(), elem)
+ case *types.Named:
+ targs := typ.TArgs()
+ targsChanged := false
+ if len(targs) > 0 {
+ newTargs := make([]types.Type, 0, len(targs))
+ for _, targ := range targs {
+ newTarg := t.instantiateType(ta, targ)
+ if newTarg != targ {
+ targsChanged = true
+ }
+ newTargs = append(newTargs, newTarg)
+ }
+ targs = newTargs
+ }
+ if targsChanged {
+ return t.updateTArgs(typ, targs)
+ }
+ return typ
+ case *types.TypeParam:
+ if instType, ok := ta.typ(typ); ok {
+ return instType
+ }
+ return typ
+ default:
+ panic(fmt.Sprintf("unimplemented Type %T", typ))
+ }
+}
+
+// setTargs returns a new named type with updated type arguments.
+func (t *translator) updateTArgs(typ *types.Named, targs []types.Type) *types.Named {
+ nm := typ.NumMethods()
+ methods := make([]*types.Func, 0, nm)
+ for i := 0; i < nm; i++ {
+ methods = append(methods, typ.Method(i))
+ }
+ obj := typ.Obj()
+ obj = types.NewTypeName(obj.Pos(), obj.Pkg(), obj.Name(), nil)
+ nt := types.NewNamed(obj, typ.Underlying(), methods)
+ nt.SetTArgs(targs)
+ return nt
+}
+
+// instantiateTypeTuple instantiates a types.Tuple.
+func (t *translator) instantiateTypeTuple(ta *typeArgs, tuple *types.Tuple) *types.Tuple {
+ if tuple == nil {
+ return nil
+ }
+ l := tuple.Len()
+ instTypes := make([]types.Type, l)
+ changed := false
+ for i := 0; i < l; i++ {
+ typ := tuple.At(i).Type()
+ instType := t.instantiateType(ta, typ)
+ if typ != instType {
+ changed = true
+ }
+ instTypes[i] = instType
+ }
+ if !changed {
+ return tuple
+ }
+ vars := make([]*types.Var, l)
+ for i := 0; i < l; i++ {
+ v := tuple.At(i)
+ vars[i] = types.NewVar(v.Pos(), v.Pkg(), v.Name(), instTypes[i])
+ }
+ return types.NewTuple(vars...)
+}
+
+// addTypePackages adds all packages mentioned in typ to t.typePackages.
+func (t *translator) addTypePackages(typ types.Type) {
+ switch typ := typ.(type) {
+ case *types.Basic:
+ case *types.Array:
+ t.addTypePackages(typ.Elem())
+ case *types.Slice:
+ t.addTypePackages(typ.Elem())
+ case *types.Struct:
+ n := typ.NumFields()
+ for i := 0; i < n; i++ {
+ t.addTypePackages(typ.Field(i).Type())
+ }
+ case *types.Pointer:
+ t.addTypePackages(typ.Elem())
+ case *types.Tuple:
+ n := typ.Len()
+ for i := 0; i < n; i++ {
+ t.addTypePackages(typ.At(i).Type())
+ }
+ case *types.Signature:
+ // We'll have seen typ.Recv elsewhere.
+ t.addTypePackages(typ.Params())
+ t.addTypePackages(typ.Results())
+ case *types.Interface:
+ nm := typ.NumExplicitMethods()
+ for i := 0; i < nm; i++ {
+ t.addTypePackages(typ.ExplicitMethod(i).Type())
+ }
+ ne := typ.NumEmbeddeds()
+ for i := 0; i < ne; i++ {
+ t.addTypePackages(typ.EmbeddedType(i))
+ }
+ case *types.Map:
+ t.addTypePackages(typ.Key())
+ t.addTypePackages(typ.Elem())
+ case *types.Chan:
+ t.addTypePackages(typ.Elem())
+ case *types.Named:
+ // This is the point of this whole method.
+ if typ.Obj().Pkg() != nil {
+ t.typePackages[typ.Obj().Pkg()] = true
+ }
+ case *types.TypeParam:
+ default:
+ panic(fmt.Sprintf("unimplemented Type %T", typ))
+ }
+}
+
+// withoutTags returns a type with no struct tags. If typ has no
+// struct tags anyhow, this just returns typ.
+func (t *translator) withoutTags(typ types.Type) types.Type {
+ switch typ := typ.(type) {
+ case *types.Basic:
+ return typ
+ case *types.Array:
+ elem := typ.Elem()
+ elemNoTags := t.withoutTags(elem)
+ if elem == elemNoTags {
+ return typ
+ }
+ return types.NewArray(elemNoTags, typ.Len())
+ case *types.Slice:
+ elem := typ.Elem()
+ elemNoTags := t.withoutTags(elem)
+ if elem == elemNoTags {
+ return typ
+ }
+ return types.NewSlice(elemNoTags)
+ case *types.Struct:
+ n := typ.NumFields()
+ fields := make([]*types.Var, n)
+ changed := false
+ hasTag := false
+ for i := 0; i < n; i++ {
+ v := typ.Field(i)
+ typeNoTags := t.withoutTags(v.Type())
+ if v.Type() != typeNoTags {
+ changed = true
+ }
+ fields[i] = types.NewVar(v.Pos(), v.Pkg(), v.Name(), typeNoTags)
+ if typ.Tag(i) != "" {
+ hasTag = true
+ }
+ }
+ if !changed && !hasTag {
+ return typ
+ }
+ return types.NewStruct(fields, nil)
+ case *types.Pointer:
+ elem := typ.Elem()
+ elemNoTags := t.withoutTags(elem)
+ if elem == elemNoTags {
+ return typ
+ }
+ return types.NewPointer(elemNoTags)
+ case *types.Tuple:
+ return t.tupleWithoutTags(typ)
+ case *types.Signature:
+ params := t.tupleWithoutTags(typ.Params())
+ results := t.tupleWithoutTags(typ.Results())
+ if params == typ.Params() && results == typ.Results() {
+ return typ
+ }
+ r := types.NewSignature(typ.Recv(), params, results, typ.Variadic())
+ if tparams := typ.TParams(); tparams != nil {
+ r.SetTParams(tparams)
+ }
+ return r
+ case *types.Interface:
+ nm := typ.NumExplicitMethods()
+ methods := make([]*types.Func, nm)
+ changed := false
+ for i := 0; i < nm; i++ {
+ m := typ.ExplicitMethod(i)
+ sigNoTags := t.withoutTags(m.Type()).(*types.Signature)
+ if sigNoTags != m.Type() {
+ m = types.NewFunc(m.Pos(), m.Pkg(), m.Name(), sigNoTags)
+ changed = true
+ }
+ methods[i] = m
+ }
+ ne := typ.NumEmbeddeds()
+ embeddeds := make([]types.Type, ne)
+ for i := 0; i < ne; i++ {
+ e := typ.EmbeddedType(i)
+ eNoTags := t.withoutTags(e)
+ if e != eNoTags {
+ changed = true
+ }
+ embeddeds[i] = eNoTags
+ }
+ if !changed {
+ return typ
+ }
+ return types.NewInterfaceType(methods, embeddeds)
+ case *types.Map:
+ key := t.withoutTags(typ.Key())
+ elem := t.withoutTags(typ.Elem())
+ if key == typ.Key() && elem == typ.Elem() {
+ return typ
+ }
+ return types.NewMap(key, elem)
+ case *types.Chan:
+ elem := t.withoutTags(typ.Elem())
+ if elem == typ.Elem() {
+ return typ
+ }
+ return types.NewChan(typ.Dir(), elem)
+ case *types.Named:
+ targs := typ.TArgs()
+ targsChanged := false
+ if len(targs) > 0 {
+ newTargs := make([]types.Type, 0, len(targs))
+ for _, targ := range targs {
+ newTarg := t.withoutTags(targ)
+ if newTarg != targ {
+ targsChanged = true
+ }
+ newTargs = append(newTargs, newTarg)
+ }
+ targs = newTargs
+ }
+ if targsChanged {
+ return t.updateTArgs(typ, targs)
+ }
+ return typ
+ case *types.TypeParam:
+ return typ
+ default:
+ panic(fmt.Sprintf("unimplemented Type %T", typ))
+ }
+}
+
+// tupleWithoutTags returns a tuple with all struct tag removed.
+func (t *translator) tupleWithoutTags(tuple *types.Tuple) *types.Tuple {
+ if tuple == nil {
+ return nil
+ }
+ l := tuple.Len()
+ typesNoTags := make([]types.Type, l)
+ changed := false
+ for i := 0; i < l; i++ {
+ typ := tuple.At(i).Type()
+ typNoTags := t.withoutTags(typ)
+ if typ != typNoTags {
+ changed = true
+ }
+ typesNoTags[i] = typNoTags
+ }
+ if !changed {
+ return tuple
+ }
+ vars := make([]*types.Var, l)
+ for i := 0; i < l; i++ {
+ v := tuple.At(i)
+ vars[i] = types.NewVar(v.Pos(), v.Pkg(), v.Name(), typesNoTags[i])
+ }
+ return types.NewTuple(vars...)
+}
+
+// typeToAST converts a types.Type to an ast.Expr.
+func (t *translator) typeToAST(typ types.Type) ast.Expr {
+ var r ast.Expr
+ switch typ := typ.(type) {
+ case *types.Basic:
+ r = ast.NewIdent(typ.Name())
+ case *types.Array:
+ r = &ast.ArrayType{
+ Len: &ast.BasicLit{
+ Kind: token.INT,
+ Value: strconv.FormatInt(typ.Len(), 10),
+ },
+ Elt: t.typeToAST(typ.Elem()),
+ }
+ case *types.Slice:
+ r = &ast.ArrayType{
+ Elt: t.typeToAST(typ.Elem()),
+ }
+ case *types.Struct:
+ var fields []*ast.Field
+ n := typ.NumFields()
+ for i := 0; i < n; i++ {
+ tf := typ.Field(i)
+ var names []*ast.Ident
+ if !tf.Embedded() {
+ names = []*ast.Ident{
+ ast.NewIdent(tf.Name()),
+ }
+ }
+ var atag *ast.BasicLit
+ if tag := typ.Tag(i); tag != "" {
+ atag = &ast.BasicLit{
+ Kind: token.STRING,
+ Value: strconv.Quote(tag),
+ }
+ }
+ af := &ast.Field{
+ Names: names,
+ Type: t.typeToAST(tf.Type()),
+ Tag: atag,
+ }
+ fields = append(fields, af)
+ }
+ r = &ast.StructType{
+ Fields: &ast.FieldList{
+ List: fields,
+ },
+ }
+ case *types.Pointer:
+ r = &ast.StarExpr{
+ X: t.typeToAST(typ.Elem()),
+ }
+ case *types.Tuple:
+ // We should only see this in a types.Signature,
+ // where we handle it specially, since there is
+ // no ast.Expr that can represent this.
+ panic("unexpected types.Tuple")
+ case *types.Signature:
+ if len(typ.TParams()) > 0 {
+ // We should only see type parameters for
+ // a package scope function declaration.
+ panic("unexpected type parameters")
+ }
+ r = &ast.FuncType{
+ Params: t.tupleToFieldList(typ.Params()),
+ Results: t.tupleToFieldList(typ.Params()),
+ }
+ case *types.Interface:
+ var methods []*ast.Field
+ nm := typ.NumExplicitMethods()
+ for i := 0; i < nm; i++ {
+ m := typ.ExplicitMethod(i)
+ f := &ast.Field{
+ Names: []*ast.Ident{
+ ast.NewIdent(m.Name()),
+ },
+ Type: t.typeToAST(m.Type()),
+ }
+ methods = append(methods, f)
+ }
+ ne := typ.NumEmbeddeds()
+ for i := 0; i < ne; i++ {
+ e := typ.EmbeddedType(i)
+ f := &ast.Field{
+ Type: t.typeToAST(e),
+ }
+ methods = append(methods, f)
+ }
+ r = &ast.InterfaceType{
+ Methods: &ast.FieldList{
+ List: methods,
+ },
+ }
+ case *types.Map:
+ r = &ast.MapType{
+ Key: t.typeToAST(typ.Key()),
+ Value: t.typeToAST(typ.Elem()),
+ }
+ case *types.Chan:
+ var dir ast.ChanDir
+ switch typ.Dir() {
+ case types.SendRecv:
+ dir = ast.SEND | ast.RECV
+ case types.SendOnly:
+ dir = ast.SEND
+ case types.RecvOnly:
+ dir = ast.RECV
+ default:
+ panic("unsupported channel direction")
+ }
+ r = &ast.ChanType{
+ Dir: dir,
+ Value: t.typeToAST(typ.Elem()),
+ }
+ case *types.Named:
+ if len(typ.TArgs()) > 0 {
+ _, id := t.lookupInstantiatedType(typ)
+ r = id
+ } else {
+ var sb strings.Builder
+ tn := typ.Obj()
+ if tn.Pkg() != nil && tn.Pkg() != t.tpkg {
+ sb.WriteString(tn.Pkg().Name())
+ sb.WriteByte('.')
+ }
+ sb.WriteString(tn.Name())
+ r = ast.NewIdent(sb.String())
+ }
+ case *types.TypeParam:
+ // This should have been instantiated already.
+ panic("unexpected type parameter")
+ default:
+ panic(fmt.Sprintf("unimplemented Type %T", typ))
+ }
+
+ t.setType(r, typ)
+ return r
+}
+
+// tupleToFieldList converts a tupes.Tuple to a ast.FieldList.
+func (t *translator) tupleToFieldList(tuple *types.Tuple) *ast.FieldList {
+ var fields []*ast.Field
+ n := tuple.Len()
+ for i := 0; i < n; i++ {
+ v := tuple.At(i)
+ var names []*ast.Ident
+ if v.Name() != "" {
+ names = []*ast.Ident{
+ ast.NewIdent(v.Name()),
+ }
+ }
+ f := &ast.Field{
+ Names: names,
+ Type: t.typeToAST(v.Type()),
+ }
+ fields = append(fields, f)
+ }
+ return &ast.FieldList{
+ List: fields,
+ }
+}
diff --git a/src/go/internal/gcimporter/gcimporter_test.go b/src/go/internal/gcimporter/gcimporter_test.go
index 3c76aaf..b86d0bd 100644
--- a/src/go/internal/gcimporter/gcimporter_test.go
+++ b/src/go/internal/gcimporter/gcimporter_test.go
@@ -264,7 +264,8 @@
{"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"},
{"io.ReadWriter", "type ReadWriter interface{Reader; Writer}"},
{"go/ast.Node", "type Node interface{End() go/token.Pos; Pos() go/token.Pos}"},
- {"go/types.Type", "type Type interface{String() string; Underlying() Type}"},
+ // go/types.Type has grown much larger - excluded for now
+ // {"go/types.Type", "type Type interface{String() string; Underlying() Type}"},
}
func TestImportedTypes(t *testing.T) {
diff --git a/src/go/printer/testdata/genericsB.golden b/src/go/printer/testdata/genericsB.golden
new file mode 100644
index 0000000..5f09934
--- /dev/null
+++ b/src/go/printer/testdata/genericsB.golden
@@ -0,0 +1,33 @@
+// Copyright 2020 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 generics
+
+type T[P any] struct{}
+type T[P1, P2, P3 any] struct{}
+
+type T[P C] struct{}
+type T[P1, P2, P3, C] struct{}
+
+type T[P C[P]] struct{}
+type T[P1, P2, P3 C[P1, P2, P3]] struct{}
+
+func f[P any](x P)
+func f[P1, P2, P3 any](x1 P1, x2 P2, x3 P3) struct{}
+
+func f[P interface{}](x P)
+func f[P1, P2, P3 interface {
+ m1(P1)
+ type P2, P3
+}](x1 P1, x2 P2, x3 P3) struct{}
+func f[P any](T1[P], T2[P]) T3[P]
+
+func (x T[P]) m()
+func (T[P]) m(x T[P]) P
+
+func _() {
+ type _ []T[P]
+ var _ []T[P]
+ _ = []T[P]{}
+}
diff --git a/src/go/printer/testdata/genericsB.input b/src/go/printer/testdata/genericsB.input
new file mode 100644
index 0000000..f137c21
--- /dev/null
+++ b/src/go/printer/testdata/genericsB.input
@@ -0,0 +1,30 @@
+// Copyright 2020 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 generics
+
+type T[P any] struct{}
+type T[P1, P2, P3 any] struct{}
+
+type T[P C] struct{}
+type T[P1, P2, P3, C] struct{}
+
+type T[P C[P]] struct{}
+type T[P1, P2, P3 C[P1, P2, P3]] struct{}
+
+func f[P any](x P)
+func f[P1, P2, P3 any](x1 P1, x2 P2, x3 P3) struct{}
+
+func f[P interface{}](x P)
+func f[P1, P2, P3 interface{ m1(P1); type P2, P3 }](x1 P1, x2 P2, x3 P3) struct{}
+func f[P any](T1[P], T2[P]) T3[P]
+
+func (x T[P]) m()
+func ((T[P])) m(x T[P]) P
+
+func _() {
+ type _ []T[P]
+ var _ []T[P]
+ _ = []T[P]{}
+}
diff --git a/src/go/types/NOTES b/src/go/types/NOTES
new file mode 100644
index 0000000..d9a5c6f
--- /dev/null
+++ b/src/go/types/NOTES
@@ -0,0 +1,182 @@
+This file serves as a notebook/implementation log.
+
+----------------------------------------------------------------------------------------------------
+TODO (implementation issues)
+
+- report a better error when a type is not in a type list (investigate)
+- better error message when we need parentheses around a parameterized function parameter type
+- review handling of fields of instantiated generic types (do we need to make them non-parameterized?)
+- use []*TypeParam for tparams in subst? (unclear)
+- should we use nil instead of &emptyInterface for no type bounds (as an optimization)?
+- TBD: in prose, should we use "generic" or "parameterized" (try to be consistent)
+
+----------------------------------------------------------------------------------------------------
+KNOWN ISSUES
+
+- type parameter constraints are ignored when checking if a parameterized method implements the
+ matching method in an interface
+- iteration over generic variables doesn't report certain channel errors (see TODOs in code)
+- cannot handle mutually recursive parameterized interfaces using themselves as type bounds
+ example: type B(type P B(P)) interface{ m() } (need to delay all checking after seting up declarations)
+- invoking a method of a parameterized embedded type doesn't work (cannot properly determine receiver yet)
+- pointer designation is incorrectly handled when checking type list constraint satisfaction
+
+----------------------------------------------------------------------------------------------------
+OBSERVATIONS
+
+- 2/20/2020: Because we permit parenthesized types anywhere for consistency, also in parameter lists (mea
+ culpa), we have parsing ambiguities when using instantiated types in parameter lists w/o argument names.
+ We could disallow the use of parentheses at the top level of type literals and then we might not have
+ this problem. This is not a backward-compatible change but perhaps worthwhile investigating. Specifically,
+ will this always work (look specifically at channel types where we need parentheses for disambiguation
+ and possibly function types).
+
+- 6/3/2020: Observation: gofmt already removes superflous parentheses around types in parameter lists,
+ so in gofmt'ed code there won't be any such parentheses. Thus we can perhaps make the suggested language
+ change above without too many problems.
+
+- 2/21/2020: We do need top-level parentheses around types in certain situations such as conversions
+ or composite literals. We could disallow parentheses around types in parameter lists only, but that
+ seems quite a bit less elegant.
+
+- 6/13/2020: When comparing against the type list of an interface (to see if the interface is satisfied),
+ we must either recompute the underlying types of the type list entries after the interface was instantiated,
+ or we must compute the underlying type of each entry before comparing. (A type list may contain type
+ parameters which may be substituted with defined types when the interface is instantiated.) With the
+ latter approach (which is what's implemented now), we could relax some of the constraints that we have
+ on type lists entries: We could allow any type and just say that for interface satisfaction we always
+ look at the underlying types.
+
+----------------------------------------------------------------------------------------------------
+OPEN QUESTIONS
+
+- Parsing _ = [](a(int)){} requires parentheses around `(a(int))` - should the parser be smarter in
+ these cases? Another example: []a(b, c){} This cannot be a conversion. Could fix such cases by re-
+ associating the AST when we see a {. Need to be careful, and need to take into account additional
+ complexity of spec.
+- For len/cap(x) where x is of type parameter type and the bound contains arrays only, should the
+ result be a constant? (right now it is not). What are the implications for alternative, non-
+ monomorphizing implementation methods?
+- Confirm that it's ok to use inference in missingMethod to compare parameterized methods.
+
+----------------------------------------------------------------------------------------------------
+DESIGN/IMPLEMENTATION
+
+- 11/19/2019: For type parameters with interface bounds to work, the scope of all type parameters in
+ a type parameter list starts at the "type" keyword. This makes all type parameters visible for all
+ type parameter bounds (interfaces that may be parameterized with the type parameters).
+
+- 12/4/2019: do not allow parenthesized generic uninstantiated types (unless instantiated implicitly)
+ In other words: generic types must always be instantiated before they can be used in any form
+ More generally: Only permit type instantiation T(x) in type context, when the type is a named type.
+ Do not permit it in general in type context: e.g., disallow []T(x) because we consider that a
+ conversion, in general. Same for ([]T)(x).
+
+- 12/12/2019: represent type bounds always as (possibly unnamed) interfaces
+ (contracts are user syntactic sugar)
+
+- 12/19/2019: Type parameters don't act like type aliases. For instance:
+
+ func f(type T1, T2)(x T1) T2 { return x }
+
+ is not valid, no matter how T1 and T2 are instantiated (but if T1 and T2 were type aliases with
+ both of them having type int, the return x would be valid). In fact, the type parameters act more
+ like named types with the methods described by their type bound. But type parameters are never
+ interfaces. To determine: Given a type parameter P, is P == underlying(P) (like for basic types),
+ or is the the underlying type of P something else (like for defined types). Is there an observable
+ difference?
+
+- 12/19/2019: Rewrote contract handling: they are now treated as Objects (rather than Types) throughout.
+
+- 12/20/2019: Decided to start moving type parameters to types (from TypeName to Named), need to do the
+ same for Func. This make more sense as in general any type (conceptually even literal types) could
+ have type parameters. It's a property of the type, not the type name. It also simplified the code.
+
+- 12/20/2019: Type parameters may be part of type lists in contracts/interfaces. It just falls out
+ naturally. Added test cases.
+
+- 12/23/2019: Decision: Type parameters and ordinary (value) parameters are in the same block, notably
+ the function block. The scope of type parameters starts at the 'type' keyword; the scope of ordinary
+ parameters starts with the (opening '{' of the) function body. Both scopes end with the closing '}'
+ of the function body (i.e., the end of the function block).
+
+- 1/2/2020: Implementation decision: contracts can only be declared at the package level.
+
+- 1/6/2020: Experimental: First steps towards permitting type parameters in methods as a generalization.
+ Type-checking problems ooccurring from this are likely to highlight general problematic areas.
+ First consequence: Scope of type parameters starts at "func" keyword which means that receiver type
+ name cannot be a type parameter name declared later (or by the receiver type specification). This
+ seems reasonable and should help avoid confusion which is possible otherwise.
+
+- 1/7/2020: We distinguish embedded instantiated (parameterized) interfaces from methods by enclosing
+ the embedded interfaces in parentheses (the design draft recommends this change). Since this opens
+ the possibility for any parenthesized type (that is an interface), we can also allow (parenthesized)
+ interface literals as it is simpler to permit those than forbid them.
+
+- 1/7/2020: The current implementation permits empty type parameter lists as in: "func f(type)(x int)"
+ but we cannot call such a function as "f()(1)"; the empty type argument list causes problems.
+ Document that we allow empty type parameter list declarations, but not empty actual type parameter
+ lists. (We could allow them for types, but that doesn't seem consistent and probably is confusing).
+
+- 2/19/2020: We accept parenthesized embedded struct fields so we can distinguish between a named
+ field with a parenthesized type foo (T) and an embedded parameterized type (foo(T)), similarly
+ to interace embedding.
+
+- 2/19/2020: Permit parentheses around embedded contracts for symmetry with embedding in structs
+ and interfaces.
+
+- 2/20/2020: Receiver type parameters must always be provided in the receiver parameter list of
+ a method, even if they are not used by the method. Since the receiver acts like an implicit
+ declaration of those type parameters, they may be blank, as with any other declaration.
+
+- 3/20/2020: Local type declarations with an underlying type that is a type parameter lose the
+ methods declared with the type parameter bound. But they don't lose the properties of the
+ underlying type, i.e., the properties of the type parameter bound's type list.
+ This is something to consider if we were contemplating moving to a methods-only approach
+ (no type lists), even though local type declarations are exceedingly rare if they exist at
+ all in the wild.
+
+- 3/24/2020: Implemented initial support for bidirection type unification which could make
+ type inference more powerful if we decided to go that route. Specifically, this would
+ permit type inference for the type parameters of a generic function argument. Given:
+ func h(f func(int)); func g(type T)(T); one could allow the call: h(g) and the type argument
+ T of g would be inferred to be int. While not hard to implement, this would be a special case
+ of the rule that all generic types/functions must be instantiated before they are used except
+ for function calls where the type arguments can be inferred from the actual arguments.
+ Decided that for now we leave things as is, since it's not clear the extra complexity is
+ worth the (probably small) convenience.
+
+- 3/25/2020: We can probably simplify the contract syntax again and only permit one of three
+ possible constraint entries: 1) an embedded contract, 2) a type parameter followed by a
+ method signature, and 3) a type parameter followed by a type list. This is what the type
+ checker currently supports and the printer can print. (The parser still accepts a list of
+ method signatures or types, freely mixed.)
+
+- 4/24/2020: Permit omission of explicit type bound instantiation if the type bound is an
+ interface that applies to exactly one type parameter and the type bound expects exactly
+ one type argument. This makes parameterized interface type bounds easier to use in a common
+ case.
+
+- 5/?/2020: Relaxed above syntactic sugar and permit omission of explicit type bound instantiation
+ in any case where the type bound only accepts a single type parameter.
+ We may not want to permit this in the first version as this is something we can always add later.
+
+- 6/3/2020: A function type parameter acts like a named type. If its type bound has a type list
+ that type list determines the underlying type of the type parameter. If the bound doesn't have
+ a type list, the underlying type of a type parameter is itself. (We may decide that in this
+ latter case there may no underlying type at all which would perhaps more consistent. But this
+ is not clear cut.)
+
+- 6/6/2020: The underlying type of a type parameter must _always_ be itself, otherwise a declaration
+ such as: type T(type P interface{type int}) P would have an underlying type int which is wrong.
+
+- 6/7/2020: Introduced the notion of an operational type which is used when determining what
+ operations are supported by a value of a given type. The operational type of a type parameter
+ is determined by its type bound. The operational type of any other type is its underlying
+ type. This approach appears to complete the picture about the nature of type parameters.
+
+- 6/7/2020: Removed support for contracts to match the latest design draft.
+
+- 6/15/2020: Disabled check that types in type lists must only contain basic types or composite types
+ composed of basic types. It is not needed since we always must recompute the underlying types
+ after a (possible) instantiation anyway (see observation from 6/3/2020).
diff --git a/src/go/types/README b/src/go/types/README
new file mode 100644
index 0000000..8a0c56a
--- /dev/null
+++ b/src/go/types/README
@@ -0,0 +1,68 @@
+This version of go/types is part of the prototype that implements the
+extended Go language as outlined by the latest generics draft design.
+
+DISCLAIMER
+
+This code is based on the std/library's go/types package but it has
+been modified heavily. Please bear in mind that:
+
+- This is a prototype. Expect bugs, incomplete parts, and suboptimal
+ and (gulp) possibly ugly code.
+
+- While all the major aspects of the generics design draft have been
+ implemented, parts are missing, especially around the operations permitted
+ on operands of generic types. In such cases, the result is most often
+ an error reporting that a certain operation is not permitted even though
+ it should be according to the draft design.
+
+- As prototyping continues, expect refinement and possibly divergence
+ from the most recent draft design published.
+
+EXAMPLE CODE
+
+For tests and example code (with detailed comments) look for files
+with suffix .go2 in the testdata and examples subdirectories.
+
+(The suffix .go2 is solely to distinguish them from regular Go code and
+to prevent gofmt from touching them. We expect a proper implementation to
+keep using .go .)
+
+RUNNING THE TYPE-CHECKER
+
+The type-checker is automatically run as part of the go2go tool.
+To experiment with generic code, it is recommened to use that tool.
+
+But the type-checker can also be run stand-alone on source code if
+so desired. There are two primary ways:
+
+1) By building the gotype tool in this source directory:
+
+ go build gotype.go
+
+ and then running the resulting binary on some input files
+
+ ./gotype <package files>
+
+ (Make sure to use the correct go tool when building.)
+
+2) By using the type-checker's TestCheck test function which
+ accepts a list of user provided files:
+
+ go test -run Check$ -errlist -files <package files>
+
+ For instance:
+
+ go test -run Check$ -files examples/types.go2
+
+ TestCheck will match any type errors against ERROR comments
+ in the source files and complain about discrepancies.
+ To report the errors instead, use:
+
+ go test -run Check$ -errlist -files examples/types.go2
+
+ To see a trace of the type checker progress, use the -v flag:
+
+ go test -v -run Check$ -errlist -files examples/types.go2
+
+ (When playing with this code, sometimes go vet gets in the
+ way, so it can be helpful to also provide -vet off.)
diff --git a/src/go/types/errors_test.go b/src/go/types/errors_test.go
new file mode 100644
index 0000000..fdbe07c
--- /dev/null
+++ b/src/go/types/errors_test.go
@@ -0,0 +1,25 @@
+// Copyright 2020 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 types
+
+import "testing"
+
+func TestStripAnnotations(t *testing.T) {
+ for _, test := range []struct {
+ in, want string
+ }{
+ {"", ""},
+ {" ", " "},
+ {"foo", "foo"},
+ {"foo₀", "foo"},
+ {"foo(T₀)", "foo(T)"},
+ {"#foo(T₀)", "foo(T)"},
+ } {
+ got := stripAnnotations(test.in)
+ if got != test.want {
+ t.Errorf("%q: got %q; want %q", test.in, got, test.want)
+ }
+ }
+}
diff --git a/test/gen/err001.go2 b/test/gen/err001.go2
new file mode 100644
index 0000000..24b865f
--- /dev/null
+++ b/test/gen/err001.go2
@@ -0,0 +1,64 @@
+// errorcheck
+
+// Copyright 2020 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 F1[T comparable]() {}
+func F2() { F1[[]int]() } // ERROR "\[\]int does not satisfy comparable$"
+
+type C interface {
+ M()
+}
+
+func F3[T C]() {}
+func F4() { F3[int]() } // ERROR "int does not satisfy C.*method M"
+
+func F5[T any]() { F3[T]() } // ERROR "T does not satisfy C.*method M"
+
+type signed interface {
+ type int, int8, int16, int32, int64
+}
+
+type integer interface {
+ type int, int8, int16, int32, int64,
+ uint, uint8, uint16, uint32, uint64, uintptr
+}
+
+func F6[T signed](a T) bool { return a < 0 }
+func F7[T any](a T) bool { return F6(a) } // ERROR "T does not satisfy signed.*T has no type constraints"
+func F8[T integer](a T) bool { return F6(a) } // ERROR "T does not satisfy signed.*T type constraint uint not found in"
+func F9(a uint) bool { return F6(a) } // ERROR "uint does not satisfy signed.*uint not found in"
+
+type MyInt int
+type MyUint uint
+
+func F10(a MyInt) bool { return F6(a) }
+func F11(a MyUint) bool { return F6(a) } // ERROR "MyUint does not satisfy signed.*uint not found in"
+
+type C2 interface {
+ C
+ signed
+}
+
+func F20[T C2](a T) bool {
+ a.M()
+ return a < 0
+}
+
+func (MyInt) M() {}
+
+func (MyUint) M() {}
+
+type S struct {}
+func (S) M() {}
+
+func F21() bool { return F20(MyInt(0)) }
+func F22() bool { return F20(0) } // ERROR "int does not satisfy C2.*(missing method M)"
+func F23[T any](a T) bool { return F20(a) } // ERROR "T does not satisfy C2.*(missing method M)"
+func F24[T integer](a T) bool { return F20(a) } // ERROR "T does not satisfy C2.*(missing method M)"
+func F25(a uint) bool { return F20(a) } // ERROR "uint does not satisfy C2.*(missing method M)"
+func F26(a MyUint) bool { return F20(a) } // ERROR "MyUint does not satisfy C2.*uint not found in"
+func F27(a S) bool { return F20(a) } // ERROR "S does not satisfy C2.*struct{} not found in"
diff --git a/test/gen/err002.dir/a.go2 b/test/gen/err002.dir/a.go2
new file mode 100644
index 0000000..de3ef43
--- /dev/null
+++ b/test/gen/err002.dir/a.go2
@@ -0,0 +1,9 @@
+// Copyright 2020 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 a
+
+type C interface {
+ M()
+}
diff --git a/test/gen/err002.dir/b.go2 b/test/gen/err002.dir/b.go2
new file mode 100644
index 0000000..b738a4e
--- /dev/null
+++ b/test/gen/err002.dir/b.go2
@@ -0,0 +1,10 @@
+// Copyright 2020 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 b
+
+import "./a"
+
+func F1[T a.C]() {}
+func F2() { F1(int)() } // ERROR "int does not satisfy a.C.*method M"
diff --git a/test/gen/err002.go2 b/test/gen/err002.go2
new file mode 100644
index 0000000..c04e67c
--- /dev/null
+++ b/test/gen/err002.go2
@@ -0,0 +1,7 @@
+// errorcheckdir
+
+// Copyright 2020 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 ignored
diff --git a/test/gen/err003.go2 b/test/gen/err003.go2
new file mode 100644
index 0000000..36e9e63
--- /dev/null
+++ b/test/gen/err003.go2
@@ -0,0 +1,29 @@
+// errorcheck
+
+// Copyright 2020 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 Setter interface {
+ Set(string)
+}
+
+func FromStrings[T Setter](s []string) []T {
+ result := make([]T, len(s))
+ for i, v := range s {
+ result[i].Set(v)
+ }
+ return result
+}
+
+type Settable int
+
+func (p *Settable) Set(s string) {
+ *p = 0
+}
+
+func F() {
+ FromStrings[Settable]([]string{"1"}) // ERROR "Settable does not satisfy Setter"
+}
diff --git a/test/gen/err004.go2 b/test/gen/err004.go2
new file mode 100644
index 0000000..e4846ae
--- /dev/null
+++ b/test/gen/err004.go2
@@ -0,0 +1,11 @@
+// errorcheck
+
+// Copyright 2020 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() {
+ type t[T any] T // ERROR "parameterized type"
+}
diff --git a/test/gen/err005.go2 b/test/gen/err005.go2
new file mode 100644
index 0000000..257a719
--- /dev/null
+++ b/test/gen/err005.go2
@@ -0,0 +1,15 @@
+// errorcheck
+
+// Copyright 2020 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.
+
+// Issue 39738.
+package p
+
+func F1[T any]() {}
+
+func F2() {
+ type s struct{}
+ F1[s]() // ERROR "locally defined type"
+}
diff --git a/test/gen/err006.go2 b/test/gen/err006.go2
new file mode 100644
index 0000000..6881c13
--- /dev/null
+++ b/test/gen/err006.go2
@@ -0,0 +1,16 @@
+// errorcheck
+
+// Copyright 2020 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.
+
+// Issue 39743.
+package p
+
+type S[T any] struct{}
+
+func (s S[_]) M() {} // ERROR "_"
+
+func F() {
+ S[int]{}.M()
+}
diff --git a/test/gen/g001.go2 b/test/gen/g001.go2
new file mode 100644
index 0000000..cb0170e
--- /dev/null
+++ b/test/gen/g001.go2
@@ -0,0 +1,23 @@
+// run
+
+// Copyright 2020 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 main
+
+import "fmt"
+
+func Print[T any](s []T) {
+ for _, v := range s {
+ fmt.Println(v)
+ }
+}
+
+func PrintInts(s []int) {
+ Print[int](s)
+}
+
+func main() {
+ PrintInts([]int{1, 2})
+}
diff --git a/test/gen/g001.out b/test/gen/g001.out
new file mode 100644
index 0000000..1191247
--- /dev/null
+++ b/test/gen/g001.out
@@ -0,0 +1,2 @@
+1
+2
diff --git a/test/gen/g002.go2 b/test/gen/g002.go2
new file mode 100644
index 0000000..29f0b9f
--- /dev/null
+++ b/test/gen/g002.go2
@@ -0,0 +1,27 @@
+// run
+
+// Copyright 2020 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 main
+
+import "fmt"
+
+func Print1[T any](s []T) {
+ for _, v := range s {
+ fmt.Println(v)
+ }
+}
+
+func Print2[T any](s []T) {
+ Print1(T)(s)
+}
+
+func PrintInts(s []int) {
+ Print2(int)(s)
+}
+
+func main() {
+ PrintInts([]int{1, 2})
+}
diff --git a/test/gen/g002.out b/test/gen/g002.out
new file mode 100644
index 0000000..1191247
--- /dev/null
+++ b/test/gen/g002.out
@@ -0,0 +1,2 @@
+1
+2
diff --git a/test/gen/g003.go2 b/test/gen/g003.go2
new file mode 100644
index 0000000..813ed92
--- /dev/null
+++ b/test/gen/g003.go2
@@ -0,0 +1,32 @@
+// run
+
+// Copyright 2020 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 main
+
+import (
+ "fmt"
+ "unsafe"
+)
+
+type Pair[F1, F2 any] struct {
+ f1 F1
+ f2 F2
+}
+
+func main() {
+ p := Pair[int32, int64]{1, 2}
+ if got, want := unsafe.Sizeof(p.f1), uintptr(4); got != want {
+ panic(fmt.Sprintf("unexpected f1 size == %d want %d", got, want))
+ }
+ if got, want := unsafe.Sizeof(p.f2), uintptr(8); got != want {
+ panic(fmt.Sprintf("unexpected f2 size == %d want %d", got, want))
+ }
+ type MyPair struct { f1 int32; f2 int64 }
+ mp := MyPair(p)
+ if mp.f1 != 1 || mp.f2 != 2 {
+ panic(fmt.Sprintf("mp == %#v want %#v", mp, MyPair{1, 2}))
+ }
+}
diff --git a/test/gen/g004.go2 b/test/gen/g004.go2
new file mode 100644
index 0000000..940d6e3
--- /dev/null
+++ b/test/gen/g004.go2
@@ -0,0 +1,39 @@
+// run
+
+// Copyright 2020 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 main
+
+import "fmt"
+
+type Value[T any] struct {
+ val T
+}
+
+func (v *Value[T]) Get() T {
+ return v.val
+}
+
+func (v *Value[T]) Set(val T) {
+ v.val = val
+}
+
+func main() {
+ var v1 Value[int]
+ v1.Set(1)
+ if got, want := v1.Get(), 1; got != want {
+ panic(fmt.Sprintf("Get() == %d, want %d", got, want))
+ }
+ v1.Set(2)
+ if got, want := v1.Get(), 2; got != want {
+ panic(fmt.Sprintf("Get() == %d, want %d", got, want))
+ }
+
+ var v2 Value[string]
+ v2.Set("a")
+ if got, want := v2.Get(), "a"; got != want {
+ panic(fmt.Sprintf("Get() == %q, want %q", got, want))
+ }
+}
diff --git a/test/gen/g005.dir/a.go2 b/test/gen/g005.dir/a.go2
new file mode 100644
index 0000000..098d876
--- /dev/null
+++ b/test/gen/g005.dir/a.go2
@@ -0,0 +1,30 @@
+// Copyright 2020 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 a
+
+type Stack[E any] []E
+
+func (s *Stack[E]) Push(e E) {
+ *s = append(*s, e)
+}
+
+func (s *Stack[E]) Pop() (E, bool) {
+ l := len(*s)
+ if l == 0 {
+ var zero E
+ return zero, false
+ }
+ r := (*s)[l - 1]
+ *s = (*s)[:l - 1]
+ return r, true
+}
+
+func (s *Stack[E]) IsEmpty() bool {
+ return len(*s) == 0
+}
+
+func (s *Stack[E]) Len() int {
+ return len(*s)
+}
diff --git a/test/gen/g005.dir/b.go2 b/test/gen/g005.dir/b.go2
new file mode 100644
index 0000000..8523323
--- /dev/null
+++ b/test/gen/g005.dir/b.go2
@@ -0,0 +1,31 @@
+// Copyright 2020 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 main
+
+import "./a"
+
+func main() {
+ var s a.Stack[string]
+ s.Push("hi")
+ s.Push("bye")
+ if s.IsEmpty() {
+ panic("unexpected IsEmpty")
+ }
+ if s.Len() != 2 {
+ panic("bad Len")
+ }
+ if v, ok := s.Pop(); !ok || v != "bye" {
+ panic("bad Pop 1")
+ }
+ if v, ok := s.Pop(); !ok || v != "hi" {
+ panic("bad Pop 2")
+ }
+ if !s.IsEmpty() {
+ panic("expected IsEmpty")
+ }
+ if s.Len() != 0 {
+ panic("bad Len 2")
+ }
+}
diff --git a/test/gen/g005.go2 b/test/gen/g005.go2
new file mode 100644
index 0000000..4a7d9e1
--- /dev/null
+++ b/test/gen/g005.go2
@@ -0,0 +1,7 @@
+// rundir
+
+// Copyright 2020 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 ignored
diff --git a/test/gen/g006.go2 b/test/gen/g006.go2
new file mode 100644
index 0000000..8171e2a
--- /dev/null
+++ b/test/gen/g006.go2
@@ -0,0 +1,96 @@
+// run
+
+// Copyright 2020 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 main
+
+import (
+ "fmt"
+ "math"
+ "os"
+ "sort"
+)
+
+type Ordered interface {
+ type int, int8, int16, int32, int64,
+ uint, uint8, uint16, uint32, uint64, uintptr,
+ float32, float64,
+ string
+}
+
+type orderedSlice[Elem Ordered] []Elem
+
+func (s orderedSlice[Elem]) Len() int { return len(s) }
+func (s orderedSlice[Elem]) Less(i, j int) bool {
+ if s[i] < s[j] {
+ return true
+ }
+ isNaN := func(f Elem) bool { return f != f }
+ if isNaN(s[i]) && !isNaN(s[j]) {
+ return true
+ }
+ return false
+}
+func (s orderedSlice[Elem]) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+func OrderedSlice[Elem Ordered](s []Elem) {
+ sort.Sort(orderedSlice[Elem](s))
+}
+
+var ints = []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}
+var float64s = []float64{74.3, 59.0, math.Inf(1), 238.2, -784.0, 2.3, math.NaN(), math.NaN(), math.Inf(-1), 9845.768, -959.7485, 905, 7.8, 7.8}
+var strings = []string{"", "Hello", "foo", "bar", "foo", "f00", "%*&^*&^&", "***"}
+
+func TestSortOrderedInts() bool {
+ return testOrdered("ints", ints, sort.Ints)
+}
+
+func TestSortOrderedFloat64s() bool {
+ return testOrdered("float64s", float64s, sort.Float64s)
+}
+
+func TestSortOrderedStrings() bool {
+ return testOrdered("strings", strings, sort.Strings)
+}
+
+func testOrdered[Elem Ordered](name string, s []Elem, sorter func([]Elem)) bool {
+ s1 := make([]Elem, len(s))
+ copy(s1, s)
+ s2 := make([]Elem, len(s))
+ copy(s2, s)
+ OrderedSlice(s1)
+ sorter(s2)
+ ok := true
+ if !sliceEq(s1, s2) {
+ fmt.Printf("%s: got %v, want %v", name, s1, s2)
+ ok = false
+ }
+ for i := len(s1) - 1; i > 0; i-- {
+ if s1[i] < s1[i-1] {
+ fmt.Printf("%s: element %d (%v) < element %d (%v)", name, i, s1[i], i - 1, s1[i - 1])
+ ok = false
+ }
+ }
+ return ok
+}
+
+func sliceEq[Elem Ordered](s1, s2 []Elem) bool {
+ for i, v1 := range s1 {
+ v2 := s2[i]
+ if v1 != v2 {
+ isNaN := func(f Elem) bool { return f != f }
+ if !isNaN(v1) || !isNaN(v2) {
+ return false
+ }
+ }
+ }
+ return true
+}
+
+func main() {
+ if !TestSortOrderedInts() || !TestSortOrderedFloat64s() || !TestSortOrderedStrings() {
+ os.Exit(1)
+ }
+}
diff --git a/test/gen/g007.go2 b/test/gen/g007.go2
new file mode 100644
index 0000000..57f7ee2
--- /dev/null
+++ b/test/gen/g007.go2
@@ -0,0 +1,20 @@
+// compile
+
+// Copyright 2020 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.
+
+// Using a type literal based on a type parameter as a type argument
+// in a result.
+
+package p
+
+type S[T any] struct {
+ f T
+}
+
+func SliceS[T any]() S[[]T] {
+ return S[[]T]{nil}
+}
+
+var V = SliceS[int]()
diff --git a/test/gen/g008.go2 b/test/gen/g008.go2
new file mode 100644
index 0000000..94afbff
--- /dev/null
+++ b/test/gen/g008.go2
@@ -0,0 +1,41 @@
+// run
+
+// Copyright 2020 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 main
+
+type Setter interface {
+ Set(string)
+}
+
+func FromStrings[T Setter](s []string) []T {
+ result := make([]T, len(s))
+ for i, v := range s {
+ result[i].Set(v)
+ }
+ return result
+}
+
+type Settable int
+
+func (p *Settable) Set(s string) {
+ *p = 0
+}
+
+func F() {
+ defer func() {
+ if recover() == nil {
+ panic("did not panic as expected")
+ }
+ }()
+ // This should type check but should panic at run time,
+ // because it will make a slice of *Settable and then call
+ // Set on a nil value.
+ FromStrings(*Settable)([]string{"1"})
+}
+
+func main() {
+ F()
+}
diff --git a/test/gen/g009.go2 b/test/gen/g009.go2
new file mode 100644
index 0000000..aa5a8fd
--- /dev/null
+++ b/test/gen/g009.go2
@@ -0,0 +1,43 @@
+// run
+
+// Copyright 2020 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 main
+
+import (
+ "log"
+ "strconv"
+)
+
+type Setter2[B any] interface {
+ Set(string)
+ type *B
+}
+
+func FromStrings2[T any, PT Setter2[T]](s []string) []T {
+ result := make([]T, len(s))
+ for i, v := range s {
+ p := PT(&result[i])
+ p.Set(v)
+ }
+ return result
+}
+
+type Settable int
+
+func (p *Settable) Set(s string) {
+ i, err := strconv.Atoi(s)
+ if err != nil {
+ log.Fatal(err)
+ }
+ *p = Settable(i)
+}
+
+func main() {
+ s := FromStrings2[Settable, *Settable]([]string{"1"})
+ if len(s) != 1 || s[0] != 1 {
+ log.Fatalf("got %v, want %v", s, []int{1})
+ }
+}
diff --git a/test/gen/g010.go2 b/test/gen/g010.go2
new file mode 100644
index 0000000..0c4bf98
--- /dev/null
+++ b/test/gen/g010.go2
@@ -0,0 +1,38 @@
+// run
+
+// Copyright 2020 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 main
+
+import (
+ "log"
+ "strconv"
+)
+
+func FromStrings3[T any](s []string, set func(*T, string)) []T {
+ results := make([]T, len(s))
+ for i, v := range s {
+ set(&results[i], v)
+ }
+ return results
+}
+
+type Settable int
+
+func (p *Settable) Set(s string) {
+ i, err := strconv.Atoi(s)
+ if err != nil {
+ log.Fatal(err)
+ }
+ *p = Settable(i)
+}
+
+func main() {
+ s := FromStrings3[Settable]([]string{"1"},
+ func(p *Settable, s string) { p.Set(s) })
+ if len(s) != 1 || s[0] != 1 {
+ log.Fatalf("got %v, want %v", s, []int{1})
+ }
+}
diff --git a/test/gen/g011.go2 b/test/gen/g011.go2
new file mode 100644
index 0000000..2ca4660
--- /dev/null
+++ b/test/gen/g011.go2
@@ -0,0 +1,46 @@
+// run
+
+// Copyright 2020 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 main
+
+import (
+ "log"
+ "strconv"
+)
+
+type Setter[B any] interface {
+ Set(string)
+ type *B
+}
+
+func FromStrings[T any, PT Setter[T]](s []string) []T {
+ result := make([]T, len(s))
+ for i, v := range s {
+ // The type of &result[i] is *T which is in the type list
+ // of Setter, so we can convert it to PT.
+ p := PT(&result[i])
+ // PT has a Set method.
+ p.Set(v)
+ }
+ return result
+}
+
+type Settable int
+
+func (p *Settable) Set(s string) {
+ i, err := strconv.Atoi(s)
+ if err != nil {
+ log.Fatal(err)
+ }
+ *p = Settable(i)
+}
+
+func main() {
+ s := FromStrings[Settable, *Settable]([]string{"1"})
+ if len(s) != 1 || s[0] != 1 {
+ log.Fatalf("got %v, want %v", s, []int{1})
+ }
+}
diff --git a/test/gen/g012.go2 b/test/gen/g012.go2
new file mode 100644
index 0000000..46d7d61
--- /dev/null
+++ b/test/gen/g012.go2
@@ -0,0 +1,90 @@
+// run
+
+// Copyright 2020 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 main
+
+import "math"
+
+type Numeric interface {
+ type int, int8, int16, int32, int64,
+ uint, uint8, uint16, uint32, uint64, uintptr,
+ float32, float64,
+ complex64, complex128
+}
+
+// NumericAbs matches numeric types with an Abs method.
+type NumericAbs[T any] interface {
+ Numeric
+ Abs() T
+}
+
+// AbsDifference computes the absolute value of the difference of
+// a and b, where the absolute value is determined by the Abs method.
+func AbsDifference[T NumericAbs[T]](a, b T) T {
+ d := a - b
+ return d.Abs()
+}
+
+// OrderedNumeric matches numeric types that support the < operator.
+type OrderedNumeric interface {
+ type int, int8, int16, int32, int64,
+ uint, uint8, uint16, uint32, uint64, uintptr,
+ float32, float64
+}
+
+// Complex matches the two complex types, which do not have a < operator.
+type Complex interface {
+ type complex64, complex128
+}
+
+// OrderedAbs is a helper type that defines an Abs method for
+// ordered numeric types.
+type OrderedAbs[T OrderedNumeric] T
+
+func (a OrderedAbs[T]) Abs() OrderedAbs[T] {
+ if a < 0 {
+ return -a
+ }
+ return a
+}
+
+// ComplexAbs is a helper type that defines an Abs method for
+// complex types.
+type ComplexAbs[T Complex] T
+
+func (a ComplexAbs[T]) Abs() ComplexAbs[T] {
+ r := float64(real(a))
+ i := float64(imag(a))
+ d := math.Sqrt(r * r + i * i)
+ return ComplexAbs[T](complex(d, 0))
+}
+
+// OrderedAbsDifference returns the absolute value of the difference
+// between a and b, where a and b are of an ordered type.
+func OrderedAbsDifference[T OrderedNumeric](a, b T) T {
+ return T(AbsDifference(OrderedAbs[T](a), OrderedAbs[T](b)))
+}
+
+// ComplexAbsDifference returns the absolute value of the difference
+// between a and b, where a and b are of a complex type.
+func ComplexAbsDifference[T Complex](a, b T) T {
+ return T(AbsDifference(ComplexAbs[T](a), ComplexAbs[T](b)))
+}
+
+func main() {
+ if d := OrderedAbsDifference(1.0, -2.0); d != 3 {
+ panic(d)
+ }
+ if d := OrderedAbsDifference(-1.0, 2.0); d != 3 {
+ panic(d)
+ }
+ if d := ComplexAbsDifference(5.0 + 2.0i, 2.0 - 2.0i); d != 5 {
+ panic(d)
+ }
+ if d := ComplexAbsDifference(2.0 - 2.0i, 5.0 + 2.0i); d != 5 {
+ panic(d)
+ }
+}
diff --git a/test/gen/g013.go2 b/test/gen/g013.go2
new file mode 100644
index 0000000..4a57a53
--- /dev/null
+++ b/test/gen/g013.go2
@@ -0,0 +1,48 @@
+// run
+
+// Copyright 2020 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 main
+
+type Gen[A any] func() (A, bool)
+
+func combine[T1, T2, T any](g1 Gen[T1], g2 Gen[T2], join func(T1, T2) T) Gen[T] {
+ return func() (T, bool) {
+ var t T
+ t1, ok := g1()
+ if !ok {
+ return t, false
+ }
+ t2, ok := g2()
+ if !ok {
+ return t, false
+ }
+ return join(t1, t2), true
+ }
+}
+
+type Pair[A, B any] struct {
+ A A
+ B B
+}
+
+func NewPair[A, B any](a A, b B) Pair[A, B] { return Pair[A, B]{a, b} }
+
+func Combine2[A, B any](ga Gen[A], gb Gen[B]) Gen[Pair[A, B]] {
+ return combine(ga, gb, NewPair(A, B))
+}
+
+func main() {
+ var g1 Gen[int] = func() (int, bool) { return 3, true }
+ var g2 Gen[string] = func() (string, bool) { return "x", false }
+ gc := combine(g1, g2, NewPair[int, string])
+ gc2 := Combine2(g1, g2)
+ if got, ok := gc(); ok {
+ panic(got)
+ }
+ if got2, ok := gc2(); ok {
+ panic(got2)
+ }
+}
diff --git a/test/gen/g014.go2 b/test/gen/g014.go2
new file mode 100644
index 0000000..e9d1d4d
--- /dev/null
+++ b/test/gen/g014.go2
@@ -0,0 +1,44 @@
+// run
+
+// Copyright 2020 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 main
+
+type T[A any] struct {
+ V A
+}
+
+type S1 struct {
+ T[int]
+ V string
+}
+
+type Tint = T[int]
+type Tbool = T[bool]
+
+type S2 struct {
+ Tint
+ Tbool
+ V string
+}
+
+type S3 struct {
+ *T[int]
+}
+
+func main() {
+ var s1 S1
+ if s1.T.V != 0 {
+ panic(s1)
+ }
+ var s2 S2
+ if s2.Tint.V != 0 || s2.Tbool.V {
+ panic(s2)
+ }
+ var s3 S3
+ if s3.T != nil {
+ panic(s3)
+ }
+}
diff --git a/test/gen/g015.go2 b/test/gen/g015.go2
new file mode 100644
index 0000000..71d1328
--- /dev/null
+++ b/test/gen/g015.go2
@@ -0,0 +1,17 @@
+// compile
+
+// Copyright 2020 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 F1[T any](x T) {}
+
+func F2(c <-chan int) {
+ F1(c)
+}
+
+func F3(c chan <- int) {
+ F1(c)
+}
diff --git a/test/gen/g016.go2 b/test/gen/g016.go2
new file mode 100644
index 0000000..0508687
--- /dev/null
+++ b/test/gen/g016.go2
@@ -0,0 +1,92 @@
+// run
+
+// Copyright 2020 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 main
+
+import "fmt"
+
+type Any interface{}
+
+type Function[a, b any] interface {
+ Apply(x a) b
+}
+
+type incr struct{ n int }
+
+func (this incr) Apply(x int) int {
+ return x + this.n
+}
+
+type pos struct{}
+
+func (this pos) Apply(x int) bool {
+ return x > 0
+}
+
+type compose[a, b, c any] struct {
+ f Function[a, b]
+ g Function[b, c]
+}
+
+func (this compose[a, b, c]) Apply(x a) c {
+ return this.g.Apply(this.f.Apply(x))
+}
+
+type Eq[a any] interface {
+ Equal(a) bool
+}
+
+type Int int
+
+func (this Int) Equal(that int) bool {
+ return int(this) == that
+}
+
+type List[a any] interface {
+ Match(casenil Function[Nil[a], Any], casecons Function[Cons[a], Any]) Any
+}
+
+type Nil[a any] struct{}
+
+func (xs Nil[a]) Match(casenil Function[Nil[a], Any], casecons Function[Cons[a], Any]) Any {
+ return casenil.Apply(xs)
+}
+
+type Cons[a any] struct {
+ Head a
+ Tail List[a]
+}
+
+func (xs Cons[a]) Match(casenil Function[Nil[a], Any], casecons Function[Cons[a], Any]) Any {
+ return casecons.Apply(xs)
+}
+
+type mapNil[a, b any] struct{}
+
+func (m mapNil[a, b]) Apply(_ Nil[a]) Any {
+ return Nil[b]{}
+}
+
+type mapCons[a, b any] struct {
+ f Function[a, b]
+}
+
+func (m mapCons[a, b]) Apply(xs Cons[a]) Any {
+ return Cons[b]{m.f.Apply(xs.Head), Map[a, b](m.f, xs.Tail)}
+}
+
+func Map[a, b any](f Function[a, b], xs List[a]) List[b] {
+ return xs.Match(mapNil[a, b]{}, mapCons[a, b]{f}).(List[b])
+}
+
+func main() {
+ var xs List[int] = Cons[int]{3, Cons[int]{6, Nil[int]{}}}
+ var ys List[int] = Map[int, int](incr{-5}, xs)
+ var xz List[bool] = Map[int, bool](pos{}, ys)
+ if fmt.Sprint(xz) != "{false {true {}}}" {
+ panic(xz)
+ }
+}
diff --git a/test/gen/g017.go2 b/test/gen/g017.go2
new file mode 100644
index 0000000..1becc9c
--- /dev/null
+++ b/test/gen/g017.go2
@@ -0,0 +1,24 @@
+// run
+
+// Copyright 2020 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 main
+
+type Recv <-chan int
+
+type sliceOf[E any] interface {
+ type []E
+}
+
+func Append[S sliceOf[T], T any](s S, t ...T) S {
+ return append(s, t...)
+}
+
+func main() {
+ a := Append([]Recv{nil}, Recv(nil))
+ if len(a) != 2 || a[0] != nil || a[1] != nil {
+ panic(a)
+ }
+}
diff --git a/test/gen/g018.go2 b/test/gen/g018.go2
new file mode 100644
index 0000000..a02566c
--- /dev/null
+++ b/test/gen/g018.go2
@@ -0,0 +1,27 @@
+// compile
+
+// Copyright 2020 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 I1 interface {
+ comparable
+}
+
+type I2 interface {
+ I1
+}
+
+type I3 I2
+
+type I4 interface {
+ type int
+}
+
+type I5 interface {
+ I4
+}
+
+type I6 I5
diff --git a/test/gen/g019.go2 b/test/gen/g019.go2
new file mode 100644
index 0000000..5ca57c9
--- /dev/null
+++ b/test/gen/g019.go2
@@ -0,0 +1,24 @@
+// compile
+
+// Copyright 2020 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.
+
+// Issue 39662.
+package p
+
+type S1 struct{}
+func (*S1) M() {}
+
+type S2 struct{}
+func (S2) M() {}
+
+func F1[T interface{ M() }](t T) {
+ _ = T.M
+}
+
+func F2() {
+ F1(&S1{})
+ F1(S2{})
+ F1(&S2{})
+}
diff --git a/test/gen/g020.go2 b/test/gen/g020.go2
new file mode 100644
index 0000000..420035d
--- /dev/null
+++ b/test/gen/g020.go2
@@ -0,0 +1,23 @@
+// compile
+
+// Copyright 2020 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.
+
+// Issue 39653.
+package p
+
+type E struct{}
+
+type N[T any] struct {
+ i int
+ n T
+}
+
+func F1[T any](i int, n T) N[T] {
+ return N[T]{i: i, n: n}
+}
+
+func F2() {
+ F1(1, F1(2, F1(3, E{})))
+}
diff --git a/test/gen/g021.go2 b/test/gen/g021.go2
new file mode 100644
index 0000000..8b025bc
--- /dev/null
+++ b/test/gen/g021.go2
@@ -0,0 +1,50 @@
+// run
+
+// Copyright 2020 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 main
+
+import "sync"
+
+// A Lockable is a value that may be safely simultaneously accessed
+// from multiple goroutines via the Get and Set methods.
+type Lockable[T any] struct {
+ T
+ mu sync.Mutex
+}
+
+// Get returns the value stored in a Lockable.
+func (l *Lockable[T]) Get() T {
+ l.mu.Lock()
+ defer l.mu.Unlock()
+ return l.T
+}
+
+// Set sets the value in a Lockable.
+func (l *Lockable[T]) Set(v T) {
+ l.mu.Lock()
+ defer l.mu.Unlock()
+ l.T = v
+}
+
+func main() {
+ sl := Lockable[string]{T: "a"}
+ if got := sl.Get(); got != "a" {
+ panic(got)
+ }
+ sl.Set("b")
+ if got := sl.Get(); got != "b" {
+ panic(got)
+ }
+
+ il := Lockable[int]{T: 1}
+ if got := il.Get(); got != 1 {
+ panic(got)
+ }
+ il.Set(2)
+ if got := il.Get(); got != 2 {
+ panic(got)
+ }
+}
diff --git a/test/gen/g022.go2 b/test/gen/g022.go2
new file mode 100644
index 0000000..ce5977d
--- /dev/null
+++ b/test/gen/g022.go2
@@ -0,0 +1,11 @@
+// compile
+
+// Copyright 2020 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 F[T any] func(T) error
+
+type instF = F[int]
diff --git a/test/gen/g023.go2 b/test/gen/g023.go2
new file mode 100644
index 0000000..f2affee
--- /dev/null
+++ b/test/gen/g023.go2
@@ -0,0 +1,17 @@
+// compile
+
+// Copyright 2020 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
+
+import "net/url"
+
+type S[T any] struct {
+ f T
+}
+
+func F() S[url.Error] {
+ return S[url.Error]{}
+}
diff --git a/test/gen/g024.go2 b/test/gen/g024.go2
new file mode 100644
index 0000000..6ae53af
--- /dev/null
+++ b/test/gen/g024.go2
@@ -0,0 +1,23 @@
+// compile
+
+// Copyright 2020 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.
+
+// Issue 39672
+package p
+
+type S[T any] T
+
+func (r S[T]) M()
+
+func F() {
+ var x S[int]
+ _ = x
+}
+
+func G[T any](v T)
+
+func F2() {
+ G(0)
+}
diff --git a/test/gen/g025.go2 b/test/gen/g025.go2
new file mode 100644
index 0000000..0e6d5bd
--- /dev/null
+++ b/test/gen/g025.go2
@@ -0,0 +1,29 @@
+// compile
+
+// Copyright 2020 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.
+
+// Issue 39692
+package p
+
+type S1[T1, T2 any] struct {
+ f1 T1
+ f2 T2
+}
+
+type S2[T1, T2 any] struct {
+ f1 T1
+ f2 T2
+}
+
+func F1[T any](v T) T {
+ return v
+}
+
+func F() {
+ _ = S2[int, S1[S1[int, int], S1[int, int]]]{
+ f1: 0,
+ f2: S1[S1[int, int], S1[int, int]]{},
+ }
+}
diff --git a/test/gen/g026.go2 b/test/gen/g026.go2
new file mode 100644
index 0000000..0122307
--- /dev/null
+++ b/test/gen/g026.go2
@@ -0,0 +1,16 @@
+// run
+
+// Copyright 2020 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.
+
+// Issue 39625.
+package main
+
+import "io/ioutil"
+
+func F[T1, T2 any](f func(T1) (T2, error)) {}
+
+func main() {
+ F(ioutil.ReadAll)
+}
diff --git a/test/gen/g027.go2 b/test/gen/g027.go2
new file mode 100644
index 0000000..b268df0
--- /dev/null
+++ b/test/gen/g027.go2
@@ -0,0 +1,34 @@
+// compile
+
+// Copyright 2020 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.
+
+// Issue 39678, 39881.
+package p
+
+type B[T any] struct {
+ T
+}
+
+type C struct {
+ F int
+}
+
+type BC B[C]
+
+func F(s BC) {
+ s.F = 7
+}
+
+type Pair[T, U any] struct {
+ T
+ U
+}
+
+func (p Pair[T, U]) M() {
+ _, _ = p.T, p.U
+}
+
+var V1 Pair[Pair[int, int], int]
+var V2 Pair[struct { p Pair[int, int] }, int]
diff --git a/test/gen/g028.go2 b/test/gen/g028.go2
new file mode 100644
index 0000000..8d263d0
--- /dev/null
+++ b/test/gen/g028.go2
@@ -0,0 +1,14 @@
+// compile
+
+// Copyright 2020 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.
+
+// Issue 39688.
+package p
+
+type S[T any] T
+
+func (*S[T]) M() {}
+
+var V S[int]
diff --git a/test/gen/g029.go2 b/test/gen/g029.go2
new file mode 100644
index 0000000..94072b7
--- /dev/null
+++ b/test/gen/g029.go2
@@ -0,0 +1,16 @@
+// compile
+
+// Copyright 2020 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.
+
+// Issue 39741.
+package p
+
+func F[T any]() {
+ type S struct{}
+}
+
+func G() {
+ F[int]()
+}
diff --git a/test/gen/g030.dir/a.go2 b/test/gen/g030.dir/a.go2
new file mode 100644
index 0000000..e32797c
--- /dev/null
+++ b/test/gen/g030.dir/a.go2
@@ -0,0 +1,20 @@
+// Copyright 2020 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 main
+
+func Len[T any](s []T) int {
+ return len(s)
+}
+
+func F1(s []int) int {
+ return Len(s)
+}
+
+func main() {
+ got := F1(nil) + F2(nil)
+ if got != 0 {
+ panic(got)
+ }
+}
diff --git a/test/gen/g030.dir/b.go2 b/test/gen/g030.dir/b.go2
new file mode 100644
index 0000000..4f3ddc7
--- /dev/null
+++ b/test/gen/g030.dir/b.go2
@@ -0,0 +1,9 @@
+// Copyright 2020 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 main
+
+func F2(s []int) int {
+ return Len(s)
+}
diff --git a/test/gen/g030.go2 b/test/gen/g030.go2
new file mode 100644
index 0000000..4a7d9e1
--- /dev/null
+++ b/test/gen/g030.go2
@@ -0,0 +1,7 @@
+// rundir
+
+// Copyright 2020 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 ignored
diff --git a/test/gen/g031.go2 b/test/gen/g031.go2
new file mode 100644
index 0000000..c2e5ff5
--- /dev/null
+++ b/test/gen/g031.go2
@@ -0,0 +1,14 @@
+// compile
+
+// Copyright 2020 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.
+
+// Issue 39737.
+package p
+
+func F1[T any]() {}
+
+func F2() {
+ F1(struct { F string `json:"f"` })()
+}
diff --git a/test/gen/g032.go2 b/test/gen/g032.go2
new file mode 100644
index 0000000..1901b86
--- /dev/null
+++ b/test/gen/g032.go2
@@ -0,0 +1,16 @@
+// compile
+
+// Copyright 2020 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.
+
+// Issue 39744.
+package p
+
+import "net/http"
+
+func F1[T any](f func() T) { f() }
+
+func F2() {
+ F1(func() *http.Request { return nil })
+}
diff --git a/test/gen/g033.dir/a.go2 b/test/gen/g033.dir/a.go2
new file mode 100644
index 0000000..1048354
--- /dev/null
+++ b/test/gen/g033.dir/a.go2
@@ -0,0 +1,9 @@
+// Copyright 2020 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 a
+
+type S[T any] struct {
+ F T
+}
diff --git a/test/gen/g033.dir/b.go2 b/test/gen/g033.dir/b.go2
new file mode 100644
index 0000000..19c656d
--- /dev/null
+++ b/test/gen/g033.dir/b.go2
@@ -0,0 +1,16 @@
+// Copyright 2020 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 main
+
+import "./a"
+
+type S = a.S
+
+func main() {
+ var s S[int]
+ if s.F != 0 {
+ panic(s.F)
+ }
+}
diff --git a/test/gen/g033.go2 b/test/gen/g033.go2
new file mode 100644
index 0000000..4a7d9e1
--- /dev/null
+++ b/test/gen/g033.go2
@@ -0,0 +1,7 @@
+// rundir
+
+// Copyright 2020 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 ignored
diff --git a/test/gen/g034.go2 b/test/gen/g034.go2
new file mode 100644
index 0000000..83a1cc2
--- /dev/null
+++ b/test/gen/g034.go2
@@ -0,0 +1,20 @@
+// compile
+
+// Copyright 2020 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 I interface{}
+
+type S[T any] struct {
+ *T
+}
+
+func F() {
+ v := S[I]{}
+ if v.T != nil {
+ panic(v)
+ }
+}
diff --git a/test/gen/g035.dir/a.go2 b/test/gen/g035.dir/a.go2
new file mode 100644
index 0000000..04d848d
--- /dev/null
+++ b/test/gen/g035.dir/a.go2
@@ -0,0 +1,9 @@
+// Copyright 2020 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 a
+
+type S[T any] struct {
+ f T
+}
diff --git a/test/gen/g035.dir/b.go2 b/test/gen/g035.dir/b.go2
new file mode 100644
index 0000000..c4a4846
--- /dev/null
+++ b/test/gen/g035.dir/b.go2
@@ -0,0 +1,20 @@
+// Copyright 2020 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 main
+
+import "./a"
+
+type S = a.S
+
+func F1[T any](T) {}
+
+func F2() { F1(S[string]{}) }
+
+func F3() { F1[S[string]](S[string]{}) }
+
+func main() {
+ F2()
+ F3()
+}
diff --git a/test/gen/g035.go2 b/test/gen/g035.go2
new file mode 100644
index 0000000..4a7d9e1
--- /dev/null
+++ b/test/gen/g035.go2
@@ -0,0 +1,7 @@
+// rundir
+
+// Copyright 2020 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 ignored
diff --git a/test/gen/g036.dir/a.go2 b/test/gen/g036.dir/a.go2
new file mode 100644
index 0000000..2ec4445
--- /dev/null
+++ b/test/gen/g036.dir/a.go2
@@ -0,0 +1,13 @@
+// Copyright 2020 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 a
+
+type S[T any] struct {
+ F T
+}
+
+func NewS[T any](v T) S[T] {
+ return S[T]{F: v}
+}
diff --git a/test/gen/g036.dir/b.go2 b/test/gen/g036.dir/b.go2
new file mode 100644
index 0000000..46bb989
--- /dev/null
+++ b/test/gen/g036.dir/b.go2
@@ -0,0 +1,23 @@
+// Copyright 2020 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 main
+
+import "./a"
+
+type S[T any] struct {
+ F T
+}
+
+func NewS[T any](v T) S[T] {
+ return S[T]{F: v}
+}
+
+func main() {
+ v1 := a.NewS(0)
+ v2 := NewS(0)
+ if v1.F != v2.F {
+ panic(v1)
+ }
+}
diff --git a/test/gen/g036.go2 b/test/gen/g036.go2
new file mode 100644
index 0000000..4a7d9e1
--- /dev/null
+++ b/test/gen/g036.go2
@@ -0,0 +1,7 @@
+// rundir
+
+// Copyright 2020 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 ignored
diff --git a/test/gen/g037.dir/a.go2 b/test/gen/g037.dir/a.go2
new file mode 100644
index 0000000..b49ab07
--- /dev/null
+++ b/test/gen/g037.dir/a.go2
@@ -0,0 +1,11 @@
+// Copyright 2020 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 a
+
+func F() {}
+
+func G[T any]() {
+ F()
+}
diff --git a/test/gen/g037.dir/b.go2 b/test/gen/g037.dir/b.go2
new file mode 100644
index 0000000..dc18c7a
--- /dev/null
+++ b/test/gen/g037.dir/b.go2
@@ -0,0 +1,11 @@
+// Copyright 2020 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 main
+
+import "./a"
+
+func main() {
+ a.G[int]()
+}
diff --git a/test/gen/g037.go2 b/test/gen/g037.go2
new file mode 100644
index 0000000..4a7d9e1
--- /dev/null
+++ b/test/gen/g037.go2
@@ -0,0 +1,7 @@
+// rundir
+
+// Copyright 2020 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 ignored
diff --git a/test/gen/g038.go2 b/test/gen/g038.go2
new file mode 100644
index 0000000..2c08e93
--- /dev/null
+++ b/test/gen/g038.go2
@@ -0,0 +1,15 @@
+// compile
+
+// Copyright 2020 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 main
+
+func F[T comparable](a T) bool {
+ return a == a
+}
+
+func G() {
+ F(error(nil))
+}
diff --git a/test/gen/g039.go2 b/test/gen/g039.go2
new file mode 100644
index 0000000..b829a45
--- /dev/null
+++ b/test/gen/g039.go2
@@ -0,0 +1,29 @@
+// compile
+
+// Copyright 2020 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 top[T, T2 any] struct {
+ p *parent[T, T2]
+}
+
+type parent[T, T2 any] struct {
+ child[T, T2]
+}
+
+type child[T, T2 any] struct {
+ T2
+}
+
+func (d *top[T, T2]) foo() *T {
+ return d.p.child.foo()
+}
+
+func (rb *child[T, T2]) foo() *T {
+ return nil
+}
+
+var V top[int, int]
diff --git a/test/gen/g040.go2 b/test/gen/g040.go2
new file mode 100644
index 0000000..6942f3b
--- /dev/null
+++ b/test/gen/g040.go2
@@ -0,0 +1,19 @@
+// compile
+
+// Copyright 2020 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[T any] struct{}
+
+type S2[K comparable] struct{}
+
+type S3[K comparable] struct {
+ f map[K]*S1[*S2[K]]
+}
+
+var V = S3[int]{
+ f: make(map[int]*S1[*S2[int]]),
+}
diff --git a/test/gen/g041.go2 b/test/gen/g041.go2
new file mode 100644
index 0000000..69d7b8e
--- /dev/null
+++ b/test/gen/g041.go2
@@ -0,0 +1,12 @@
+// errorcheck
+
+// Copyright 2020 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.
+
+// Issue 40015.
+package p
+
+func F1[](s []int) {} // ERROR "empty type parameter list"
+
+func F2() { F1([]int{1, 2}) }
diff --git a/test/gen/g042.go2 b/test/gen/g042.go2
new file mode 100644
index 0000000..b594ab7
--- /dev/null
+++ b/test/gen/g042.go2
@@ -0,0 +1,18 @@
+// compile
+
+// Copyright 2020 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.
+
+// Issue 40318
+package p
+
+import (
+ crand "crypto/rand"
+ "math/rand"
+)
+
+func F() {
+ _ = crand.Reader
+ _ = rand.Source(nil)
+}
diff --git a/test/gen/g043.go2 b/test/gen/g043.go2
new file mode 100644
index 0000000..ecc81ee
--- /dev/null
+++ b/test/gen/g043.go2
@@ -0,0 +1,38 @@
+// compile
+
+// Copyright 2020 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.
+
+// Issue 40859.
+package main
+
+import (
+ "fmt"
+)
+
+type Number interface {
+ type int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr, float32, float64
+}
+
+type MySlice []int
+
+type SC[E any] interface {
+ type []E
+}
+
+func DoubleDefined[S SC[E], E Number](s S) S {
+ r := make(S, len(s))
+ for i, v := range s {
+ r[i] = v + v
+ }
+ return r
+}
+
+func main() {
+ // Expicit types work fine
+ fmt.Println(DoubleDefined[MySlice, int](MySlice{1}))
+
+ // Constraint type inference does not work?
+ fmt.Println(DoubleDefined(MySlice{1}))
+}
diff --git a/test/gen/g044.go2 b/test/gen/g044.go2
new file mode 100644
index 0000000..bc3ec32
--- /dev/null
+++ b/test/gen/g044.go2
@@ -0,0 +1,50 @@
+// run
+
+// Copyright 2020 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.
+
+// Issue 41216.
+
+package main
+
+func main() {
+ ps := FromStrings2[X]([]string{"x", "y"})
+ if len(ps) != 2 || ps[0] != (X{"x"}) || ps[1] != (X{"y"}) {
+ panic(ps)
+ }
+}
+
+type X struct {
+ s string
+}
+
+func (x *X) Set(s string) {
+ x.s = s
+}
+
+// Setter2 is a type constraint that requires that the type
+// implement a Set method that sets the value from a string,
+// and also requires that the type be a pointer to its type parameter.
+type Setter2[B any] interface {
+ Set(string)
+ type *B
+}
+
+// FromStrings2 takes a slice of strings and returns a slice of T,
+// calling the Set method to set each returned value.
+//
+// We use two different type parameters so that we can return
+// a slice of type T but call methods on *T aka PT.
+// The Setter2 constraint ensures that PT is a pointer to T.
+func FromStrings2[T any, PT Setter2[T]](s []string) []T {
+ result := make([]T, len(s))
+ for i, v := range s {
+ // The type of &result[i] is *T which is in the type list
+ // of Setter2, so we can convert it to PT.
+ p := PT(&result[i])
+ // PT has a Set method.
+ p.Set(v)
+ }
+ return result
+}
diff --git a/test/run.go b/test/run.go
index 30cab82..bae51c1 100644
--- a/test/run.go
+++ b/test/run.go
@@ -12,6 +12,7 @@
"errors"
"flag"
"fmt"
+ "go/go2go"
"hash/fnv"
"io"
"io/fs"
@@ -23,6 +24,7 @@
"path/filepath"
"regexp"
"runtime"
+ "runtime/debug"
"sort"
"strconv"
"strings"
@@ -54,6 +56,13 @@
return os.Getenv("GO_BUILDER_NAME") == "linux-amd64"
}
+type fileType int
+
+const (
+ go1Files fileType = iota
+ go2Files
+)
+
var (
goos, goarch string
@@ -109,7 +118,7 @@
for _, baseGoFile := range goFiles(arg) {
tests = append(tests, startTest(arg, baseGoFile))
}
- } else if strings.HasSuffix(arg, ".go") {
+ } else if strings.HasSuffix(arg, ".go") || strings.HasSuffix(arg, ".go2") {
dir, file := filepath.Split(arg)
tests = append(tests, startTest(dir, file))
} else {
@@ -204,7 +213,7 @@
}
names := []string{}
for _, name := range dirnames {
- if !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") && shardMatch(name) {
+ if !strings.HasPrefix(name, ".") && (strings.HasSuffix(name, ".go") || strings.HasSuffix(name, ".go2")) && shardMatch(name) {
names = append(names, name)
}
}
@@ -224,7 +233,43 @@
return runcmd(cmd...)
}
-func compileInDir(runcmd runCmd, dir string, flags []string, localImports bool, names ...string) (out []byte, err error) {
+func compileInDir(runcmd runCmd, dir string, ft fileType, importer *go2go.Importer, flags []string, localImports bool, names ...string) (out []byte, err error) {
+ gofiles := names
+ if ft == go2Files {
+ defer func() {
+ if r := recover(); r != nil {
+ if err == nil {
+ err = fmt.Errorf("panic: %v", r)
+ }
+ fmt.Fprintf(os.Stderr, "%s", debug.Stack())
+ }
+ }()
+ tpkgs, err := go2go.RewriteFiles(importer, dir, names)
+ if err != nil {
+ // The error returned by RewriteFiles is similar
+ // to the output of the compiler, so return it
+ // as a []byte, without the first line.
+ errStr := go2Error(err.Error())
+ return []byte(errStr), err
+ }
+ if err := importer.Register(tpkgs[0].Path(), tpkgs); err != nil {
+ return nil, err
+ }
+
+ gofiles = make([]string, len(names))
+ for i, name := range names {
+ gofiles[i] = strings.TrimSuffix(name, ".go2") + ".go"
+ }
+
+ if !*keep {
+ defer func() {
+ for _, name := range gofiles {
+ os.Remove(filepath.Join(dir, name))
+ }
+ }()
+ }
+ }
+
cmd := []string{goTool(), "tool", "compile", "-e"}
if localImports {
// Set relative path for local imports and import search path to current dir.
@@ -234,14 +279,25 @@
if *linkshared {
cmd = append(cmd, "-dynlink", "-installsuffix=dynlink")
}
- for _, name := range names {
+ for _, name := range gofiles {
cmd = append(cmd, filepath.Join(dir, name))
}
return runcmd(cmd...)
}
+// go2Error takes an error returned by go/go2go and strips the prefix,
+// to make it look more like an error from "go tool compile".
+func go2Error(s string) string {
+ fields := strings.SplitN(s, "\n", 2)
+ if len(fields) > 1 && strings.HasPrefix(fields[0], "type checking failed for") {
+ return fields[1]
+ }
+ return s
+}
+
func linkFile(runcmd runCmd, goname string, ldflags []string) (err error) {
- pfile := strings.Replace(goname, ".go", ".o", -1)
+ pfile := strings.Replace(goname, ".go2", ".o", -1)
+ pfile = strings.Replace(pfile, ".go", ".o", -1)
cmd := []string{goTool(), "tool", "link", "-w", "-o", "a.exe", "-L", "."}
if *linkshared {
cmd = append(cmd, "-linkshared", "-installsuffix=dynlink")
@@ -313,13 +369,22 @@
return filepath.Join(t.dir, strings.Replace(t.gofile, ".go", ".dir", -1))
}
-func goDirFiles(longdir string) (filter []os.FileInfo, err error) {
+func goDirFiles(longdir string, ft fileType) (filter []os.FileInfo, err error) {
files, dirErr := ioutil.ReadDir(longdir)
if dirErr != nil {
return nil, dirErr
}
+ var ext string
+ switch ft {
+ case go1Files:
+ ext = ".go"
+ case go2Files:
+ ext = ".go2"
+ default:
+ log.Fatalf("invalid file type %v", ft)
+ }
for _, gofile := range files {
- if filepath.Ext(gofile.Name()) == ".go" {
+ if filepath.Ext(gofile.Name()) == ext {
filter = append(filter, gofile)
}
}
@@ -342,8 +407,8 @@
// If singlefilepkgs is set, each file is considered a separate package
// even if the package names are the same.
-func goDirPackages(longdir string, singlefilepkgs bool) ([][]string, error) {
- files, err := goDirFiles(longdir)
+func goDirPackages(longdir string, singlefilepkgs bool, ft fileType) ([][]string, error) {
+ files, err := goDirFiles(longdir, ft)
if err != nil {
return nil, err
}
@@ -597,6 +662,48 @@
defer os.RemoveAll(t.tempDir)
}
+ ft := go1Files
+ var importer *go2go.Importer
+ if strings.HasSuffix(t.gofile, ".go2") {
+ defer func() {
+ if r := recover(); r != nil {
+ if t.err == nil {
+ t.err = fmt.Errorf("panic: %v", r)
+ }
+ fmt.Fprintf(os.Stderr, "%s", debug.Stack())
+ }
+ }()
+ importer = go2go.NewImporter(t.dir)
+ go2Bytes, err := go2go.RewriteBuffer(importer, t.gofile, srcBytes)
+ if err != nil {
+ if !wantError {
+ t.err = err
+ return
+ }
+ if action != "errorcheck" {
+ t.err = fmt.Errorf("%s not supported for .go2 files", action)
+ return
+ }
+ errStr := go2Error(err.Error())
+ long := filepath.Join(cwd, t.goFileName())
+ if *updateErrors {
+ t.updateErrors(errStr, long)
+ }
+ t.err = t.errorCheck(errStr, false, long, t.gofile)
+ return
+ }
+ srcBytes = go2Bytes
+ t.gofile = strings.TrimSuffix(t.gofile, ".go2") + ".go"
+ ft = go2Files
+ if err := ioutil.WriteFile(filepath.Join(t.dir, t.gofile), srcBytes, 0644); err != nil {
+ t.err = err
+ return
+ }
+ if !*keep {
+ defer os.Remove(filepath.Join(t.dir, t.gofile))
+ }
+ }
+
err = ioutil.WriteFile(filepath.Join(t.tempDir, t.gofile), srcBytes, 0644)
if err != nil {
log.Fatal(err)
@@ -817,13 +924,13 @@
case "compiledir":
// Compile all files in the directory as packages in lexicographic order.
longdir := filepath.Join(cwd, t.goDirName())
- pkgs, err := goDirPackages(longdir, singlefilepkgs)
+ pkgs, err := goDirPackages(longdir, singlefilepkgs, ft)
if err != nil {
t.err = err
return
}
for _, gofiles := range pkgs {
- _, t.err = compileInDir(runcmd, longdir, flags, localImports, gofiles...)
+ _, t.err = compileInDir(runcmd, longdir, ft, importer, flags, localImports, gofiles...)
if t.err != nil {
return
}
@@ -834,7 +941,7 @@
// If errorcheckdir and wantError, compilation of the last package must fail.
// If errorcheckandrundir and wantError, compilation of the package prior the last must fail.
longdir := filepath.Join(cwd, t.goDirName())
- pkgs, err := goDirPackages(longdir, singlefilepkgs)
+ pkgs, err := goDirPackages(longdir, singlefilepkgs, ft)
if err != nil {
t.err = err
return
@@ -846,7 +953,7 @@
errPkg--
}
for i, gofiles := range pkgs {
- out, err := compileInDir(runcmd, longdir, flags, localImports, gofiles...)
+ out, err := compileInDir(runcmd, longdir, ft, importer, flags, localImports, gofiles...)
if i == errPkg {
if wantError && err == nil {
t.err = fmt.Errorf("compilation succeeded unexpectedly\n%s", out)
@@ -879,7 +986,7 @@
// Link as if the last file is the main package, run it.
// Verify the expected output.
longdir := filepath.Join(cwd, t.goDirName())
- pkgs, err := goDirPackages(longdir, singlefilepkgs)
+ pkgs, err := goDirPackages(longdir, singlefilepkgs, ft)
if err != nil {
t.err = err
return
@@ -905,7 +1012,7 @@
}
pflags = append(pflags, "-p", pkgname)
}
- _, err := compileInDir(runcmd, longdir, pflags, localImports, gofiles...)
+ _, err := compileInDir(runcmd, longdir, ft, importer, pflags, localImports, gofiles...)
// Allow this package compilation fail based on conditions below;
// its errors were checked in previous case.
if err != nil && !(wantError && action == "errorcheckandrundir" && i == len(pkgs)-2) {