aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Griesemer <gri@golang.org>2020-10-19 15:28:22 -0700
committerRobert Griesemer <gri@golang.org>2020-10-21 00:51:12 +0000
commitca36ba83ab86b9eb1ddc076f0ebfda648ce31d6b (patch)
treec061b8a1ffa5064e361e9ba58ea6693fab6ab0e0
parent6ff16fe3ee46f8e35c18226d04bd38a396eb4175 (diff)
downloadgo-ca36ba83ab86b9eb1ddc076f0ebfda648ce31d6b.tar.gz
go-ca36ba83ab86b9eb1ddc076f0ebfda648ce31d6b.zip
[dev.typeparams] cmd/compile/internal/importer, types2: initial check-in of types2 and importer
This is a copy of the importer and types2 (unreviewed) prototype version excluding the testdata directory containing tests (see below). Each file is marked with the comment // UNREVIEWED on the first line. The plan is to check in this code wholesale (it runs and passes all tests) and then review the code file-by-file via subsequent CLs and remove the "// UNREVIEWED" comments as we review the files. Since most tests are unchanged from the original go/types, the next CL will commit those tests as they don't need to be reviewed again. (Eventually we may want to factor them out and share them from a single place, e.g. the test directory.) The existing file fmtmap_test.go was updated. Change-Id: I9bd0ad1a7e7188b501423483a44d18e623c0fe71 Reviewed-on: https://go-review.googlesource.com/c/go/+/263624 Trust: Robert Griesemer <gri@golang.org> Trust: Keith Randall <khr@golang.org> Run-TryBot: Robert Griesemer <gri@golang.org> Run-TryBot: Keith Randall <khr@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org> Reviewed-by: Matthew Dempsky <mdempsky@google.com>
-rw-r--r--src/cmd/compile/fmtmap_test.go51
-rw-r--r--src/cmd/compile/internal/importer/exportdata.go92
-rw-r--r--src/cmd/compile/internal/importer/gcimporter.go175
-rw-r--r--src/cmd/compile/internal/importer/gcimporter_test.go612
-rw-r--r--src/cmd/compile/internal/importer/iimport.go616
-rw-r--r--src/cmd/compile/internal/importer/support.go144
-rw-r--r--src/cmd/compile/internal/importer/testdata/a.go15
-rw-r--r--src/cmd/compile/internal/importer/testdata/b.go12
-rw-r--r--src/cmd/compile/internal/importer/testdata/exports.go89
-rw-r--r--src/cmd/compile/internal/importer/testdata/issue15920.go12
-rw-r--r--src/cmd/compile/internal/importer/testdata/issue20046.go10
-rw-r--r--src/cmd/compile/internal/importer/testdata/issue25301.go18
-rw-r--r--src/cmd/compile/internal/importer/testdata/issue25596.go14
-rw-r--r--src/cmd/compile/internal/importer/testdata/p.go14
-rw-r--r--src/cmd/compile/internal/importer/testdata/versions/test.go29
-rw-r--r--src/cmd/compile/internal/types2/api.go426
-rw-r--r--src/cmd/compile/internal/types2/api_test.go1741
-rw-r--r--src/cmd/compile/internal/types2/assignments.go359
-rw-r--r--src/cmd/compile/internal/types2/builtins.go777
-rw-r--r--src/cmd/compile/internal/types2/builtins_test.go219
-rw-r--r--src/cmd/compile/internal/types2/call.go808
-rw-r--r--src/cmd/compile/internal/types2/check.go449
-rw-r--r--src/cmd/compile/internal/types2/check_test.go269
-rw-r--r--src/cmd/compile/internal/types2/conversions.go164
-rw-r--r--src/cmd/compile/internal/types2/decl.go981
-rw-r--r--src/cmd/compile/internal/types2/errors.go159
-rw-r--r--src/cmd/compile/internal/types2/errors_test.go26
-rw-r--r--src/cmd/compile/internal/types2/example_test.go324
-rw-r--r--src/cmd/compile/internal/types2/examples/functions.go2216
-rw-r--r--src/cmd/compile/internal/types2/examples/methods.go297
-rw-r--r--src/cmd/compile/internal/types2/examples/types.go2261
-rw-r--r--src/cmd/compile/internal/types2/expr.go1906
-rw-r--r--src/cmd/compile/internal/types2/exprstring.go287
-rw-r--r--src/cmd/compile/internal/types2/exprstring_test.go97
-rw-r--r--src/cmd/compile/internal/types2/fixedbugs/issue39634.go292
-rw-r--r--src/cmd/compile/internal/types2/fixedbugs/issue39664.go216
-rw-r--r--src/cmd/compile/internal/types2/fixedbugs/issue39680.go228
-rw-r--r--src/cmd/compile/internal/types2/fixedbugs/issue39693.go215
-rw-r--r--src/cmd/compile/internal/types2/fixedbugs/issue39699.go230
-rw-r--r--src/cmd/compile/internal/types2/fixedbugs/issue39711.go212
-rw-r--r--src/cmd/compile/internal/types2/fixedbugs/issue39723.go210
-rw-r--r--src/cmd/compile/internal/types2/fixedbugs/issue39725.go217
-rw-r--r--src/cmd/compile/internal/types2/fixedbugs/issue39754.go221
-rw-r--r--src/cmd/compile/internal/types2/fixedbugs/issue39755.go224
-rw-r--r--src/cmd/compile/internal/types2/fixedbugs/issue39768.go221
-rw-r--r--src/cmd/compile/internal/types2/fixedbugs/issue39938.go251
-rw-r--r--src/cmd/compile/internal/types2/fixedbugs/issue39948.go210
-rw-r--r--src/cmd/compile/internal/types2/fixedbugs/issue39976.go217
-rw-r--r--src/cmd/compile/internal/types2/fixedbugs/issue39982.go237
-rw-r--r--src/cmd/compile/internal/types2/fixedbugs/issue40038.go216
-rw-r--r--src/cmd/compile/internal/types2/fixedbugs/issue40056.go216
-rw-r--r--src/cmd/compile/internal/types2/fixedbugs/issue40057.go218
-rw-r--r--src/cmd/compile/internal/types2/fixedbugs/issue40301.go213
-rw-r--r--src/cmd/compile/internal/types2/fixedbugs/issue40684.go216
-rw-r--r--src/cmd/compile/internal/types2/fixedbugs/issue41124.go292
-rw-r--r--src/cmd/compile/internal/types2/gccgosizes.go41
-rw-r--r--src/cmd/compile/internal/types2/hilbert_test.go220
-rw-r--r--src/cmd/compile/internal/types2/importer_test.go36
-rw-r--r--src/cmd/compile/internal/types2/infer.go359
-rw-r--r--src/cmd/compile/internal/types2/initorder.go298
-rw-r--r--src/cmd/compile/internal/types2/issues_test.go533
-rw-r--r--src/cmd/compile/internal/types2/labels.go260
-rw-r--r--src/cmd/compile/internal/types2/lookup.go493
-rw-r--r--src/cmd/compile/internal/types2/methodset.go261
-rw-r--r--src/cmd/compile/internal/types2/object.go492
-rw-r--r--src/cmd/compile/internal/types2/object_test.go89
-rw-r--r--src/cmd/compile/internal/types2/objset.go32
-rw-r--r--src/cmd/compile/internal/types2/operand.go315
-rw-r--r--src/cmd/compile/internal/types2/package.go65
-rw-r--r--src/cmd/compile/internal/types2/pos.go364
-rw-r--r--src/cmd/compile/internal/types2/predicates.go413
-rw-r--r--src/cmd/compile/internal/types2/resolver.go714
-rw-r--r--src/cmd/compile/internal/types2/resolver_test.go223
-rw-r--r--src/cmd/compile/internal/types2/return.go180
-rw-r--r--src/cmd/compile/internal/types2/sanitize.go149
-rw-r--r--src/cmd/compile/internal/types2/scope.go217
-rw-r--r--src/cmd/compile/internal/types2/selection.go144
-rw-r--r--src/cmd/compile/internal/types2/self_test.go97
-rw-r--r--src/cmd/compile/internal/types2/sizes.go266
-rw-r--r--src/cmd/compile/internal/types2/sizes_test.go108
-rw-r--r--src/cmd/compile/internal/types2/stdlib_test.go320
-rw-r--r--src/cmd/compile/internal/types2/stmt.go919
-rw-r--r--src/cmd/compile/internal/types2/subst.go554
-rw-r--r--src/cmd/compile/internal/types2/type.go1061
-rw-r--r--src/cmd/compile/internal/types2/typestring.go480
-rw-r--r--src/cmd/compile/internal/types2/typestring_test.go221
-rw-r--r--src/cmd/compile/internal/types2/typexpr.go1280
-rw-r--r--src/cmd/compile/internal/types2/unify.go454
-rw-r--r--src/cmd/compile/internal/types2/universe.go282
-rw-r--r--src/cmd/compile/internal/types2/walk.go322
90 files changed, 24300 insertions, 3 deletions
diff --git a/src/cmd/compile/fmtmap_test.go b/src/cmd/compile/fmtmap_test.go
index a3d09576a7..bcc82dda2f 100644
--- a/src/cmd/compile/fmtmap_test.go
+++ b/src/cmd/compile/fmtmap_test.go
@@ -42,6 +42,10 @@ var knownFormats = map[string]string{
"*cmd/compile/internal/ssa.Value %s": "",
"*cmd/compile/internal/ssa.Value %v": "",
"*cmd/compile/internal/ssa.sparseTreeMapEntry %v": "",
+ "*cmd/compile/internal/syntax.CallExpr %s": "",
+ "*cmd/compile/internal/syntax.CallExpr %v": "",
+ "*cmd/compile/internal/syntax.FuncLit %s": "",
+ "*cmd/compile/internal/syntax.IndexExpr %s": "",
"*cmd/compile/internal/types.Field %p": "",
"*cmd/compile/internal/types.Field %v": "",
"*cmd/compile/internal/types.Sym %0S": "",
@@ -58,6 +62,25 @@ var knownFormats = map[string]string{
"*cmd/compile/internal/types.Type %p": "",
"*cmd/compile/internal/types.Type %s": "",
"*cmd/compile/internal/types.Type %v": "",
+ "*cmd/compile/internal/types2.Basic %s": "",
+ "*cmd/compile/internal/types2.Chan %s": "",
+ "*cmd/compile/internal/types2.Func %s": "",
+ "*cmd/compile/internal/types2.Initializer %s": "",
+ "*cmd/compile/internal/types2.Interface %s": "",
+ "*cmd/compile/internal/types2.MethodSet %s": "",
+ "*cmd/compile/internal/types2.Named %s": "",
+ "*cmd/compile/internal/types2.Named %v": "",
+ "*cmd/compile/internal/types2.Package %s": "",
+ "*cmd/compile/internal/types2.Package %v": "",
+ "*cmd/compile/internal/types2.Scope %p": "",
+ "*cmd/compile/internal/types2.Selection %s": "",
+ "*cmd/compile/internal/types2.Signature %s": "",
+ "*cmd/compile/internal/types2.TypeName %s": "",
+ "*cmd/compile/internal/types2.TypeName %v": "",
+ "*cmd/compile/internal/types2.TypeParam %s": "",
+ "*cmd/compile/internal/types2.Var %s": "",
+ "*cmd/compile/internal/types2.operand %s": "",
+ "*cmd/compile/internal/types2.substMap %s": "",
"*cmd/internal/obj.Addr %v": "",
"*cmd/internal/obj.LSym %v": "",
"*math/big.Float %f": "",
@@ -67,6 +90,8 @@ var knownFormats = map[string]string{
"[16]byte %x": "",
"[]*cmd/compile/internal/ssa.Block %v": "",
"[]*cmd/compile/internal/ssa.Value %v": "",
+ "[]*cmd/compile/internal/types2.Func %v": "",
+ "[]*cmd/compile/internal/types2.TypeName %s": "",
"[][]string %q": "",
"[]byte %s": "",
"[]byte %x": "",
@@ -75,6 +100,8 @@ var knownFormats = map[string]string{
"[]cmd/compile/internal/ssa.posetNode %v": "",
"[]cmd/compile/internal/ssa.posetUndo %v": "",
"[]cmd/compile/internal/syntax.token %s": "",
+ "[]cmd/compile/internal/types2.Type %s": "",
+ "[]int %v": "",
"[]string %v": "",
"[]uint32 %v": "",
"bool %v": "",
@@ -100,6 +127,7 @@ var knownFormats = map[string]string{
"cmd/compile/internal/gc.fmtMode %d": "",
"cmd/compile/internal/gc.initKind %d": "",
"cmd/compile/internal/gc.itag %v": "",
+ "cmd/compile/internal/importer.itag %v": "",
"cmd/compile/internal/ssa.BranchPrediction %d": "",
"cmd/compile/internal/ssa.Edge %v": "",
"cmd/compile/internal/ssa.GCNode %v": "",
@@ -122,9 +150,13 @@ var knownFormats = map[string]string{
"cmd/compile/internal/ssa.regMask %d": "",
"cmd/compile/internal/ssa.register %d": "",
"cmd/compile/internal/ssa.relation %s": "",
+ "cmd/compile/internal/syntax.ChanDir %d": "",
+ "cmd/compile/internal/syntax.Decl %T": "",
"cmd/compile/internal/syntax.Error %q": "",
"cmd/compile/internal/syntax.Error %v": "",
"cmd/compile/internal/syntax.Expr %#v": "",
+ "cmd/compile/internal/syntax.Expr %T": "",
+ "cmd/compile/internal/syntax.Expr %s": "",
"cmd/compile/internal/syntax.LitKind %d": "",
"cmd/compile/internal/syntax.Node %T": "",
"cmd/compile/internal/syntax.Operator %s": "",
@@ -136,12 +168,22 @@ var knownFormats = map[string]string{
"cmd/compile/internal/types.EType %d": "",
"cmd/compile/internal/types.EType %s": "",
"cmd/compile/internal/types.EType %v": "",
+ "cmd/compile/internal/types2.Object %T": "",
+ "cmd/compile/internal/types2.Object %p": "",
+ "cmd/compile/internal/types2.Object %s": "",
+ "cmd/compile/internal/types2.Object %v": "",
+ "cmd/compile/internal/types2.Type %T": "",
+ "cmd/compile/internal/types2.Type %s": "",
+ "cmd/compile/internal/types2.Type %v": "",
+ "cmd/compile/internal/types2.color %s": "",
"cmd/internal/obj.ABI %v": "",
+ "error %s": "",
"error %v": "",
"float64 %.2f": "",
"float64 %.3f": "",
"float64 %.6g": "",
"float64 %g": "",
+ "go/constant.Value %s": "",
"int %#x": "",
"int %-12d": "",
"int %-6d": "",
@@ -174,9 +216,10 @@ var knownFormats = map[string]string{
"interface{} %q": "",
"interface{} %s": "",
"interface{} %v": "",
- "map[*cmd/compile/internal/gc.Node]*cmd/compile/internal/ssa.Value %v": "",
- "map[*cmd/compile/internal/gc.Node][]*cmd/compile/internal/gc.Node %v": "",
- "map[cmd/compile/internal/ssa.ID]uint32 %v": "",
+ "map[*cmd/compile/internal/gc.Node]*cmd/compile/internal/ssa.Value %v": "",
+ "map[*cmd/compile/internal/gc.Node][]*cmd/compile/internal/gc.Node %v": "",
+ "map[*cmd/compile/internal/types2.TypeParam]cmd/compile/internal/types2.Type %s": "",
+ "map[cmd/compile/internal/ssa.ID]uint32 %v": "",
"map[int64]uint32 %v": "",
"math/big.Accuracy %s": "",
"reflect.Type %s": "",
@@ -186,6 +229,7 @@ var knownFormats = map[string]string{
"string %-*s": "",
"string %-16s": "",
"string %-6s": "",
+ "string %T": "",
"string %q": "",
"string %s": "",
"string %v": "",
@@ -205,6 +249,7 @@ var knownFormats = map[string]string{
"uint64 %08x": "",
"uint64 %b": "",
"uint64 %d": "",
+ "uint64 %v": "",
"uint64 %x": "",
"uint8 %#x": "",
"uint8 %d": "",
diff --git a/src/cmd/compile/internal/importer/exportdata.go b/src/cmd/compile/internal/importer/exportdata.go
new file mode 100644
index 0000000000..3925a64314
--- /dev/null
+++ b/src/cmd/compile/internal/importer/exportdata.go
@@ -0,0 +1,92 @@
+// UNREVIEWED
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements FindExportData.
+
+package importer
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "strconv"
+ "strings"
+)
+
+func readGopackHeader(r *bufio.Reader) (name string, size int, err error) {
+ // See $GOROOT/include/ar.h.
+ hdr := make([]byte, 16+12+6+6+8+10+2)
+ _, err = io.ReadFull(r, hdr)
+ if err != nil {
+ return
+ }
+ // leave for debugging
+ if false {
+ fmt.Printf("header: %s", hdr)
+ }
+ s := strings.TrimSpace(string(hdr[16+12+6+6+8:][:10]))
+ size, err = strconv.Atoi(s)
+ if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' {
+ err = fmt.Errorf("invalid archive header")
+ return
+ }
+ name = strings.TrimSpace(string(hdr[:16]))
+ return
+}
+
+// FindExportData positions the reader r at the beginning of the
+// export data section of an underlying GC-created object/archive
+// file by reading from it. The reader must be positioned at the
+// start of the file before calling this function. The hdr result
+// is the string before the export data, either "$$" or "$$B".
+//
+func FindExportData(r *bufio.Reader) (hdr string, err error) {
+ // Read first line to make sure this is an object file.
+ line, err := r.ReadSlice('\n')
+ if err != nil {
+ err = fmt.Errorf("can't find export data (%v)", err)
+ return
+ }
+
+ if string(line) == "!<arch>\n" {
+ // Archive file. Scan to __.PKGDEF.
+ var name string
+ if name, _, err = readGopackHeader(r); err != nil {
+ return
+ }
+
+ // First entry should be __.PKGDEF.
+ if name != "__.PKGDEF" {
+ err = fmt.Errorf("go archive is missing __.PKGDEF")
+ return
+ }
+
+ // Read first line of __.PKGDEF data, so that line
+ // is once again the first line of the input.
+ if line, err = r.ReadSlice('\n'); err != nil {
+ err = fmt.Errorf("can't find export data (%v)", err)
+ return
+ }
+ }
+
+ // Now at __.PKGDEF in archive or still at beginning of file.
+ // Either way, line should begin with "go object ".
+ if !strings.HasPrefix(string(line), "go object ") {
+ err = fmt.Errorf("not a Go object file")
+ return
+ }
+
+ // Skip over object header to export data.
+ // Begins after first line starting with $$.
+ for line[0] != '$' {
+ if line, err = r.ReadSlice('\n'); err != nil {
+ err = fmt.Errorf("can't find export data (%v)", err)
+ return
+ }
+ }
+ hdr = string(line)
+
+ return
+}
diff --git a/src/cmd/compile/internal/importer/gcimporter.go b/src/cmd/compile/internal/importer/gcimporter.go
new file mode 100644
index 0000000000..feb18cf2c9
--- /dev/null
+++ b/src/cmd/compile/internal/importer/gcimporter.go
@@ -0,0 +1,175 @@
+// UNREVIEWED
+// Copyright 2011 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 importer implements Import for gc-generated object files.
+package importer
+
+import (
+ "bufio"
+ "cmd/compile/internal/types2"
+ "fmt"
+ "go/build"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+// debugging/development support
+const debug = false
+
+var pkgExts = [...]string{".a", ".o"}
+
+// FindPkg returns the filename and unique package id for an import
+// path based on package information provided by build.Import (using
+// the build.Default build.Context). A relative srcDir is interpreted
+// relative to the current working directory.
+// If no file was found, an empty filename is returned.
+//
+func FindPkg(path, srcDir string) (filename, id string) {
+ if path == "" {
+ return
+ }
+
+ var noext string
+ switch {
+ default:
+ // "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x"
+ // Don't require the source files to be present.
+ if abs, err := filepath.Abs(srcDir); err == nil { // see issue 14282
+ srcDir = abs
+ }
+ bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary)
+ if bp.PkgObj == "" {
+ id = path // make sure we have an id to print in error message
+ return
+ }
+ noext = strings.TrimSuffix(bp.PkgObj, ".a")
+ id = bp.ImportPath
+
+ case build.IsLocalImport(path):
+ // "./x" -> "/this/directory/x.ext", "/this/directory/x"
+ noext = filepath.Join(srcDir, path)
+ id = noext
+
+ case filepath.IsAbs(path):
+ // for completeness only - go/build.Import
+ // does not support absolute imports
+ // "/x" -> "/x.ext", "/x"
+ noext = path
+ id = path
+ }
+
+ if false { // for debugging
+ if path != id {
+ fmt.Printf("%s -> %s\n", path, id)
+ }
+ }
+
+ // try extensions
+ for _, ext := range pkgExts {
+ filename = noext + ext
+ if f, err := os.Stat(filename); err == nil && !f.IsDir() {
+ return
+ }
+ }
+
+ filename = "" // not found
+ return
+}
+
+// Import imports a gc-generated package given its import path and srcDir, adds
+// the corresponding package object to the packages map, and returns the object.
+// The packages map must contain all packages already imported.
+//
+func Import(packages map[string]*types2.Package, path, srcDir string, lookup func(path string) (io.ReadCloser, error)) (pkg *types2.Package, err error) {
+ var rc io.ReadCloser
+ var id string
+ if lookup != nil {
+ // With custom lookup specified, assume that caller has
+ // converted path to a canonical import path for use in the map.
+ if path == "unsafe" {
+ return types2.Unsafe, nil
+ }
+ id = path
+
+ // No need to re-import if the package was imported completely before.
+ if pkg = packages[id]; pkg != nil && pkg.Complete() {
+ return
+ }
+ f, err := lookup(path)
+ if err != nil {
+ return nil, err
+ }
+ rc = f
+ } else {
+ var filename string
+ filename, id = FindPkg(path, srcDir)
+ if filename == "" {
+ if path == "unsafe" {
+ return types2.Unsafe, nil
+ }
+ return nil, fmt.Errorf("can't find import: %q", id)
+ }
+
+ // no need to re-import if the package was imported completely before
+ if pkg = packages[id]; pkg != nil && pkg.Complete() {
+ return
+ }
+
+ // open file
+ f, err := os.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ defer func() {
+ if err != nil {
+ // add file name to error
+ err = fmt.Errorf("%s: %v", filename, err)
+ }
+ }()
+ rc = f
+ }
+ defer rc.Close()
+
+ var hdr string
+ buf := bufio.NewReader(rc)
+ if hdr, err = FindExportData(buf); err != nil {
+ return
+ }
+
+ switch hdr {
+ case "$$\n":
+ err = fmt.Errorf("import %q: old textual export format no longer supported (recompile library)", path)
+
+ case "$$B\n":
+ var data []byte
+ data, err = ioutil.ReadAll(buf)
+ if err != nil {
+ break
+ }
+
+ // The indexed export format starts with an 'i'; the older
+ // binary export format starts with a 'c', 'd', or 'v'
+ // (from "version"). Select appropriate importer.
+ if len(data) > 0 && data[0] == 'i' {
+ _, pkg, err = iImportData(packages, data[1:], id)
+ } else {
+ err = fmt.Errorf("import %q: old binary export format no longer supported (recompile library)", path)
+ }
+
+ default:
+ err = fmt.Errorf("import %q: unknown export data header: %q", path, hdr)
+ }
+
+ return
+}
+
+type byPath []*types2.Package
+
+func (a byPath) Len() int { return len(a) }
+func (a byPath) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a byPath) Less(i, j int) bool { return a[i].Path() < a[j].Path() }
diff --git a/src/cmd/compile/internal/importer/gcimporter_test.go b/src/cmd/compile/internal/importer/gcimporter_test.go
new file mode 100644
index 0000000000..a275524484
--- /dev/null
+++ b/src/cmd/compile/internal/importer/gcimporter_test.go
@@ -0,0 +1,612 @@
+// UNREVIEWED
+// Copyright 2011 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 importer
+
+import (
+ "bytes"
+ "cmd/compile/internal/types2"
+ "fmt"
+ "internal/testenv"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+ "time"
+)
+
+// skipSpecialPlatforms causes the test to be skipped for platforms where
+// builders (build.golang.org) don't have access to compiled packages for
+// import.
+func skipSpecialPlatforms(t *testing.T) {
+ switch platform := runtime.GOOS + "-" + runtime.GOARCH; platform {
+ case "darwin-arm64":
+ t.Skipf("no compiled packages available for import on %s", platform)
+ }
+}
+
+// compile runs the compiler on filename, with dirname as the working directory,
+// and writes the output file to outdirname.
+func compile(t *testing.T, dirname, filename, outdirname string) string {
+ // filename must end with ".go"
+ if !strings.HasSuffix(filename, ".go") {
+ t.Fatalf("filename doesn't end in .go: %s", filename)
+ }
+ basename := filepath.Base(filename)
+ outname := filepath.Join(outdirname, basename[:len(basename)-2]+"o")
+ cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-o", outname, filename)
+ cmd.Dir = dirname
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Logf("%s", out)
+ t.Fatalf("go tool compile %s failed: %s", filename, err)
+ }
+ return outname
+}
+
+func testPath(t *testing.T, path, srcDir string) *types2.Package {
+ t0 := time.Now()
+ pkg, err := Import(make(map[string]*types2.Package), path, srcDir, nil)
+ if err != nil {
+ t.Errorf("testPath(%s): %s", path, err)
+ return nil
+ }
+ t.Logf("testPath(%s): %v", path, time.Since(t0))
+ return pkg
+}
+
+const maxTime = 30 * time.Second
+
+func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
+ dirname := filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir)
+ list, err := ioutil.ReadDir(dirname)
+ if err != nil {
+ t.Fatalf("testDir(%s): %s", dirname, err)
+ }
+ for _, f := range list {
+ if time.Now().After(endTime) {
+ t.Log("testing time used up")
+ return
+ }
+ switch {
+ case !f.IsDir():
+ // try extensions
+ for _, ext := range pkgExts {
+ if strings.HasSuffix(f.Name(), ext) {
+ name := f.Name()[0 : len(f.Name())-len(ext)] // remove extension
+ if testPath(t, filepath.Join(dir, name), dir) != nil {
+ nimports++
+ }
+ }
+ }
+ case f.IsDir():
+ nimports += testDir(t, filepath.Join(dir, f.Name()), endTime)
+ }
+ }
+ return
+}
+
+func mktmpdir(t *testing.T) string {
+ tmpdir, err := ioutil.TempDir("", "gcimporter_test")
+ if err != nil {
+ t.Fatal("mktmpdir:", err)
+ }
+ if err := os.Mkdir(filepath.Join(tmpdir, "testdata"), 0700); err != nil {
+ os.RemoveAll(tmpdir)
+ t.Fatal("mktmpdir:", err)
+ }
+ return tmpdir
+}
+
+func TestImportTestdata(t *testing.T) {
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ tmpdir := mktmpdir(t)
+ defer os.RemoveAll(tmpdir)
+
+ compile(t, "testdata", "exports.go", filepath.Join(tmpdir, "testdata"))
+
+ if pkg := testPath(t, "./testdata/exports", tmpdir); pkg != nil {
+ // The package's Imports list must include all packages
+ // explicitly imported by exports.go, plus all packages
+ // referenced indirectly via exported objects in exports.go.
+ // With the textual export format, the list may also include
+ // additional packages that are not strictly required for
+ // import processing alone (they are exported to err "on
+ // the safe side").
+ // TODO(gri) update the want list to be precise, now that
+ // the textual export data is gone.
+ got := fmt.Sprint(pkg.Imports())
+ for _, want := range []string{"go/ast", "go/token"} {
+ if !strings.Contains(got, want) {
+ t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want)
+ }
+ }
+ }
+}
+
+func TestVersionHandling(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ const dir = "./testdata/versions"
+ list, err := ioutil.ReadDir(dir)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ tmpdir := mktmpdir(t)
+ defer os.RemoveAll(tmpdir)
+ corruptdir := filepath.Join(tmpdir, "testdata", "versions")
+ if err := os.Mkdir(corruptdir, 0700); err != nil {
+ t.Fatal(err)
+ }
+
+ for _, f := range list {
+ name := f.Name()
+ if !strings.HasSuffix(name, ".a") {
+ continue // not a package file
+ }
+ if strings.Contains(name, "corrupted") {
+ continue // don't process a leftover corrupted file
+ }
+ pkgpath := "./" + name[:len(name)-2]
+
+ if testing.Verbose() {
+ t.Logf("importing %s", name)
+ }
+
+ // test that export data can be imported
+ _, err := Import(make(map[string]*types2.Package), pkgpath, dir, nil)
+ if err != nil {
+ // ok to fail if it fails with a no longer supported error for select files
+ if strings.Contains(err.Error(), "no longer supported") {
+ switch name {
+ case "test_go1.7_0.a", "test_go1.7_1.a",
+ "test_go1.8_4.a", "test_go1.8_5.a",
+ "test_go1.11_6b.a", "test_go1.11_999b.a":
+ continue
+ }
+ // fall through
+ }
+ // ok to fail if it fails with a newer version error for select files
+ if strings.Contains(err.Error(), "newer version") {
+ switch name {
+ case "test_go1.11_999i.a":
+ continue
+ }
+ // fall through
+ }
+ t.Errorf("import %q failed: %v", pkgpath, err)
+ continue
+ }
+
+ // create file with corrupted export data
+ // 1) read file
+ data, err := ioutil.ReadFile(filepath.Join(dir, name))
+ if err != nil {
+ t.Fatal(err)
+ }
+ // 2) find export data
+ i := bytes.Index(data, []byte("\n$$B\n")) + 5
+ j := bytes.Index(data[i:], []byte("\n$$\n")) + i
+ if i < 0 || j < 0 || i > j {
+ t.Fatalf("export data section not found (i = %d, j = %d)", i, j)
+ }
+ // 3) corrupt the data (increment every 7th byte)
+ for k := j - 13; k >= i; k -= 7 {
+ data[k]++
+ }
+ // 4) write the file
+ pkgpath += "_corrupted"
+ filename := filepath.Join(corruptdir, pkgpath) + ".a"
+ ioutil.WriteFile(filename, data, 0666)
+
+ // test that importing the corrupted file results in an error
+ _, err = Import(make(map[string]*types2.Package), pkgpath, corruptdir, nil)
+ if err == nil {
+ t.Errorf("import corrupted %q succeeded", pkgpath)
+ } else if msg := err.Error(); !strings.Contains(msg, "version skew") {
+ t.Errorf("import %q error incorrect (%s)", pkgpath, msg)
+ }
+ }
+}
+
+func TestImportStdLib(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ dt := maxTime
+ if testing.Short() && testenv.Builder() == "" {
+ dt = 10 * time.Millisecond
+ }
+ nimports := testDir(t, "", time.Now().Add(dt)) // installed packages
+ t.Logf("tested %d imports", nimports)
+}
+
+var importedObjectTests = []struct {
+ name string
+ want string
+}{
+ // non-interfaces
+ {"crypto.Hash", "type Hash uint"},
+ {"go/ast.ObjKind", "type ObjKind int"},
+ {"go/types.Qualifier", "type Qualifier func(*Package) string"},
+ {"go/types.Comparable", "func Comparable(T Type) bool"},
+ {"math.Pi", "const Pi untyped float"},
+ {"math.Sin", "func Sin(x float64) float64"},
+ {"go/ast.NotNilFilter", "func NotNilFilter(_ string, v reflect.Value) bool"},
+ {"go/internal/gcimporter.FindPkg", "func FindPkg(path string, srcDir string) (filename string, id string)"},
+
+ // interfaces
+ {"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key interface{}) interface{}}"},
+ {"crypto.Decrypter", "type Decrypter interface{Decrypt(rand io.Reader, msg []byte, opts DecrypterOpts) (plaintext []byte, err error); Public() PublicKey}"},
+ {"encoding.BinaryMarshaler", "type BinaryMarshaler interface{MarshalBinary() (data []byte, err error)}"},
+ {"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 has grown much larger - excluded for now
+ // {"go/types.Type", "type Type interface{String() string; Underlying() Type}"},
+}
+
+func TestImportedTypes(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ for _, test := range importedObjectTests {
+ s := strings.Split(test.name, ".")
+ if len(s) != 2 {
+ t.Fatal("inconsistent test data")
+ }
+ importPath := s[0]
+ objName := s[1]
+
+ pkg, err := Import(make(map[string]*types2.Package), importPath, ".", nil)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+
+ obj := pkg.Scope().Lookup(objName)
+ if obj == nil {
+ t.Errorf("%s: object not found", test.name)
+ continue
+ }
+
+ got := types2.ObjectString(obj, types2.RelativeTo(pkg))
+ if got != test.want {
+ t.Errorf("%s: got %q; want %q", test.name, got, test.want)
+ }
+
+ if named, _ := obj.Type().(*types2.Named); named != nil {
+ verifyInterfaceMethodRecvs(t, named, 0)
+ }
+ }
+}
+
+// verifyInterfaceMethodRecvs verifies that method receiver types
+// are named if the methods belong to a named interface type.
+func verifyInterfaceMethodRecvs(t *testing.T, named *types2.Named, level int) {
+ // avoid endless recursion in case of an embedding bug that lead to a cycle
+ if level > 10 {
+ t.Errorf("%s: embeds itself", named)
+ return
+ }
+
+ iface, _ := named.Underlying().(*types2.Interface)
+ if iface == nil {
+ return // not an interface
+ }
+
+ // check explicitly declared methods
+ for i := 0; i < iface.NumExplicitMethods(); i++ {
+ m := iface.ExplicitMethod(i)
+ recv := m.Type().(*types2.Signature).Recv()
+ if recv == nil {
+ t.Errorf("%s: missing receiver type", m)
+ continue
+ }
+ if recv.Type() != named {
+ t.Errorf("%s: got recv type %s; want %s", m, recv.Type(), named)
+ }
+ }
+
+ // check embedded interfaces (if they are named, too)
+ for i := 0; i < iface.NumEmbeddeds(); i++ {
+ // embedding of interfaces cannot have cycles; recursion will terminate
+ if etype, _ := iface.EmbeddedType(i).(*types2.Named); etype != nil {
+ verifyInterfaceMethodRecvs(t, etype, level+1)
+ }
+ }
+}
+
+func TestIssue5815(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ pkg := importPkg(t, "strings", ".")
+
+ scope := pkg.Scope()
+ for _, name := range scope.Names() {
+ obj := scope.Lookup(name)
+ if obj.Pkg() == nil {
+ t.Errorf("no pkg for %s", obj)
+ }
+ if tname, _ := obj.(*types2.TypeName); tname != nil {
+ named := tname.Type().(*types2.Named)
+ for i := 0; i < named.NumMethods(); i++ {
+ m := named.Method(i)
+ if m.Pkg() == nil {
+ t.Errorf("no pkg for %s", m)
+ }
+ }
+ }
+ }
+}
+
+// Smoke test to ensure that imported methods get the correct package.
+func TestCorrectMethodPackage(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ imports := make(map[string]*types2.Package)
+ _, err := Import(imports, "net/http", ".", nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ mutex := imports["sync"].Scope().Lookup("Mutex").(*types2.TypeName).Type()
+ mset := types2.NewMethodSet(types2.NewPointer(mutex)) // methods of *sync.Mutex
+ sel := mset.Lookup(nil, "Lock")
+ lock := sel.Obj().(*types2.Func)
+ if got, want := lock.Pkg().Path(), "sync"; got != want {
+ t.Errorf("got package path %q; want %q", got, want)
+ }
+}
+
+func TestIssue13566(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ // On windows, we have to set the -D option for the compiler to avoid having a drive
+ // letter and an illegal ':' in the import path - just skip it (see also issue #3483).
+ if runtime.GOOS == "windows" {
+ t.Skip("avoid dealing with relative paths/drive letters on windows")
+ }
+
+ tmpdir := mktmpdir(t)
+ defer os.RemoveAll(tmpdir)
+ testoutdir := filepath.Join(tmpdir, "testdata")
+
+ // b.go needs to be compiled from the output directory so that the compiler can
+ // find the compiled package a. We pass the full path to compile() so that we
+ // don't have to copy the file to that directory.
+ bpath, err := filepath.Abs(filepath.Join("testdata", "b.go"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ compile(t, "testdata", "a.go", testoutdir)
+ compile(t, testoutdir, bpath, testoutdir)
+
+ // import must succeed (test for issue at hand)
+ pkg := importPkg(t, "./testdata/b", tmpdir)
+
+ // make sure all indirectly imported packages have names
+ for _, imp := range pkg.Imports() {
+ if imp.Name() == "" {
+ t.Errorf("no name for %s package", imp.Path())
+ }
+ }
+}
+
+func TestIssue13898(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ // import go/internal/gcimporter which imports go/types partially
+ imports := make(map[string]*types2.Package)
+ _, err := Import(imports, "go/internal/gcimporter", ".", nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // look for go/types package
+ var goTypesPkg *types2.Package
+ for path, pkg := range imports {
+ if path == "go/types" {
+ goTypesPkg = pkg
+ break
+ }
+ }
+ if goTypesPkg == nil {
+ t.Fatal("go/types not found")
+ }
+
+ // look for go/types2.Object type
+ obj := lookupObj(t, goTypesPkg.Scope(), "Object")
+ typ, ok := obj.Type().(*types2.Named)
+ if !ok {
+ t.Fatalf("go/types2.Object type is %v; wanted named type", typ)
+ }
+
+ // lookup go/types2.Object.Pkg method
+ m, index, indirect := types2.LookupFieldOrMethod(typ, false, nil, "Pkg")
+ if m == nil {
+ t.Fatalf("go/types2.Object.Pkg not found (index = %v, indirect = %v)", index, indirect)
+ }
+
+ // the method must belong to go/types
+ if m.Pkg().Path() != "go/types" {
+ t.Fatalf("found %v; want go/types", m.Pkg())
+ }
+}
+
+func TestIssue15517(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ // On windows, we have to set the -D option for the compiler to avoid having a drive
+ // letter and an illegal ':' in the import path - just skip it (see also issue #3483).
+ if runtime.GOOS == "windows" {
+ t.Skip("avoid dealing with relative paths/drive letters on windows")
+ }
+
+ tmpdir := mktmpdir(t)
+ defer os.RemoveAll(tmpdir)
+
+ compile(t, "testdata", "p.go", filepath.Join(tmpdir, "testdata"))
+
+ // Multiple imports of p must succeed without redeclaration errors.
+ // We use an import path that's not cleaned up so that the eventual
+ // file path for the package is different from the package path; this
+ // will expose the error if it is present.
+ //
+ // (Issue: Both the textual and the binary importer used the file path
+ // of the package to be imported as key into the shared packages map.
+ // However, the binary importer then used the package path to identify
+ // the imported package to mark it as complete; effectively marking the
+ // wrong package as complete. By using an "unclean" package path, the
+ // file and package path are different, exposing the problem if present.
+ // The same issue occurs with vendoring.)
+ imports := make(map[string]*types2.Package)
+ for i := 0; i < 3; i++ {
+ if _, err := Import(imports, "./././testdata/p", tmpdir, nil); err != nil {
+ t.Fatal(err)
+ }
+ }
+}
+
+func TestIssue15920(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ // On windows, we have to set the -D option for the compiler to avoid having a drive
+ // letter and an illegal ':' in the import path - just skip it (see also issue #3483).
+ if runtime.GOOS == "windows" {
+ t.Skip("avoid dealing with relative paths/drive letters on windows")
+ }
+
+ compileAndImportPkg(t, "issue15920")
+}
+
+func TestIssue20046(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ // On windows, we have to set the -D option for the compiler to avoid having a drive
+ // letter and an illegal ':' in the import path - just skip it (see also issue #3483).
+ if runtime.GOOS == "windows" {
+ t.Skip("avoid dealing with relative paths/drive letters on windows")
+ }
+
+ // "./issue20046".V.M must exist
+ pkg := compileAndImportPkg(t, "issue20046")
+ obj := lookupObj(t, pkg.Scope(), "V")
+ if m, index, indirect := types2.LookupFieldOrMethod(obj.Type(), false, nil, "M"); m == nil {
+ t.Fatalf("V.M not found (index = %v, indirect = %v)", index, indirect)
+ }
+}
+func TestIssue25301(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ // On windows, we have to set the -D option for the compiler to avoid having a drive
+ // letter and an illegal ':' in the import path - just skip it (see also issue #3483).
+ if runtime.GOOS == "windows" {
+ t.Skip("avoid dealing with relative paths/drive letters on windows")
+ }
+
+ compileAndImportPkg(t, "issue25301")
+}
+
+func TestIssue25596(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ // On windows, we have to set the -D option for the compiler to avoid having a drive
+ // letter and an illegal ':' in the import path - just skip it (see also issue #3483).
+ if runtime.GOOS == "windows" {
+ t.Skip("avoid dealing with relative paths/drive letters on windows")
+ }
+
+ compileAndImportPkg(t, "issue25596")
+}
+
+func importPkg(t *testing.T, path, srcDir string) *types2.Package {
+ pkg, err := Import(make(map[string]*types2.Package), path, srcDir, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return pkg
+}
+
+func compileAndImportPkg(t *testing.T, name string) *types2.Package {
+ tmpdir := mktmpdir(t)
+ defer os.RemoveAll(tmpdir)
+ compile(t, "testdata", name+".go", filepath.Join(tmpdir, "testdata"))
+ return importPkg(t, "./testdata/"+name, tmpdir)
+}
+
+func lookupObj(t *testing.T, scope *types2.Scope, name string) types2.Object {
+ if obj := scope.Lookup(name); obj != nil {
+ return obj
+ }
+ t.Fatalf("%s not found", name)
+ return nil
+}
diff --git a/src/cmd/compile/internal/importer/iimport.go b/src/cmd/compile/internal/importer/iimport.go
new file mode 100644
index 0000000000..b9c1ccfb66
--- /dev/null
+++ b/src/cmd/compile/internal/importer/iimport.go
@@ -0,0 +1,616 @@
+// UNREVIEWED
+// Copyright 2018 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.
+
+// Indexed package import.
+// See cmd/compile/internal/gc/iexport.go for the export data format.
+
+package importer
+
+import (
+ "bytes"
+ "cmd/compile/internal/syntax"
+ "cmd/compile/internal/types2"
+ "encoding/binary"
+ "fmt"
+ "go/constant"
+ "go/token"
+ "io"
+ "sort"
+)
+
+type intReader struct {
+ *bytes.Reader
+ path string
+}
+
+func (r *intReader) int64() int64 {
+ i, err := binary.ReadVarint(r.Reader)
+ if err != nil {
+ errorf("import %q: read varint error: %v", r.path, err)
+ }
+ return i
+}
+
+func (r *intReader) uint64() uint64 {
+ i, err := binary.ReadUvarint(r.Reader)
+ if err != nil {
+ errorf("import %q: read varint error: %v", r.path, err)
+ }
+ return i
+}
+
+const predeclReserved = 32
+
+type itag uint64
+
+const (
+ // Types
+ definedType itag = iota
+ pointerType
+ sliceType
+ arrayType
+ chanType
+ mapType
+ signatureType
+ structType
+ interfaceType
+)
+
+// iImportData imports a package from the serialized package data
+// and returns the number of bytes consumed and a reference to the package.
+// If the export data version is not recognized or the format is otherwise
+// compromised, an error is returned.
+func iImportData(imports map[string]*types2.Package, data []byte, path string) (_ int, pkg *types2.Package, err error) {
+ const currentVersion = 1
+ version := int64(-1)
+ defer func() {
+ if e := recover(); e != nil {
+ if version > currentVersion {
+ err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e)
+ } else {
+ err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e)
+ }
+ }
+ }()
+
+ r := &intReader{bytes.NewReader(data), path}
+
+ version = int64(r.uint64())
+ switch version {
+ case currentVersion, 0:
+ default:
+ errorf("unknown iexport format version %d", version)
+ }
+
+ sLen := int64(r.uint64())
+ dLen := int64(r.uint64())
+
+ whence, _ := r.Seek(0, io.SeekCurrent)
+ stringData := data[whence : whence+sLen]
+ declData := data[whence+sLen : whence+sLen+dLen]
+ r.Seek(sLen+dLen, io.SeekCurrent)
+
+ p := iimporter{
+ ipath: path,
+ version: int(version),
+
+ stringData: stringData,
+ stringCache: make(map[uint64]string),
+ pkgCache: make(map[uint64]*types2.Package),
+
+ declData: declData,
+ pkgIndex: make(map[*types2.Package]map[string]uint64),
+ typCache: make(map[uint64]types2.Type),
+ }
+
+ for i, pt := range predeclared {
+ p.typCache[uint64(i)] = pt
+ }
+
+ pkgList := make([]*types2.Package, r.uint64())
+ for i := range pkgList {
+ pkgPathOff := r.uint64()
+ pkgPath := p.stringAt(pkgPathOff)
+ pkgName := p.stringAt(r.uint64())
+ _ = r.uint64() // package height; unused by go/types
+
+ if pkgPath == "" {
+ pkgPath = path
+ }
+ pkg := imports[pkgPath]
+ if pkg == nil {
+ pkg = types2.NewPackage(pkgPath, pkgName)
+ imports[pkgPath] = pkg
+ } else if pkg.Name() != pkgName {
+ errorf("conflicting names %s and %s for package %q", pkg.Name(), pkgName, path)
+ }
+
+ p.pkgCache[pkgPathOff] = pkg
+
+ nameIndex := make(map[string]uint64)
+ for nSyms := r.uint64(); nSyms > 0; nSyms-- {
+ name := p.stringAt(r.uint64())
+ nameIndex[name] = r.uint64()
+ }
+
+ p.pkgIndex[pkg] = nameIndex
+ pkgList[i] = pkg
+ }
+
+ localpkg := pkgList[0]
+
+ names := make([]string, 0, len(p.pkgIndex[localpkg]))
+ for name := range p.pkgIndex[localpkg] {
+ names = append(names, name)
+ }
+ sort.Strings(names)
+ for _, name := range names {
+ p.doDecl(localpkg, name)
+ }
+
+ for _, typ := range p.interfaceList {
+ typ.Complete()
+ }
+
+ // record all referenced packages as imports
+ list := append(([]*types2.Package)(nil), pkgList[1:]...)
+ sort.Sort(byPath(list))
+ localpkg.SetImports(list)
+
+ // package was imported completely and without errors
+ localpkg.MarkComplete()
+
+ consumed, _ := r.Seek(0, io.SeekCurrent)
+ return int(consumed), localpkg, nil
+}
+
+type iimporter struct {
+ ipath string
+ version int
+
+ stringData []byte
+ stringCache map[uint64]string
+ pkgCache map[uint64]*types2.Package
+
+ declData []byte
+ pkgIndex map[*types2.Package]map[string]uint64
+ typCache map[uint64]types2.Type
+
+ interfaceList []*types2.Interface
+}
+
+func (p *iimporter) doDecl(pkg *types2.Package, name string) {
+ // See if we've already imported this declaration.
+ if obj := pkg.Scope().Lookup(name); obj != nil {
+ return
+ }
+
+ off, ok := p.pkgIndex[pkg][name]
+ if !ok {
+ errorf("%v.%v not in index", pkg, name)
+ }
+
+ r := &importReader{p: p, currPkg: pkg}
+ r.declReader.Reset(p.declData[off:])
+
+ r.obj(name)
+}
+
+func (p *iimporter) stringAt(off uint64) string {
+ if s, ok := p.stringCache[off]; ok {
+ return s
+ }
+
+ slen, n := binary.Uvarint(p.stringData[off:])
+ if n <= 0 {
+ errorf("varint failed")
+ }
+ spos := off + uint64(n)
+ s := string(p.stringData[spos : spos+slen])
+ p.stringCache[off] = s
+ return s
+}
+
+func (p *iimporter) pkgAt(off uint64) *types2.Package {
+ if pkg, ok := p.pkgCache[off]; ok {
+ return pkg
+ }
+ path := p.stringAt(off)
+ errorf("missing package %q in %q", path, p.ipath)
+ return nil
+}
+
+func (p *iimporter) typAt(off uint64, base *types2.Named) types2.Type {
+ if t, ok := p.typCache[off]; ok && (base == nil || !isInterface(t)) {
+ return t
+ }
+
+ if off < predeclReserved {
+ errorf("predeclared type missing from cache: %v", off)
+ }
+
+ r := &importReader{p: p}
+ r.declReader.Reset(p.declData[off-predeclReserved:])
+ t := r.doType(base)
+
+ if base == nil || !isInterface(t) {
+ p.typCache[off] = t
+ }
+ return t
+}
+
+type importReader struct {
+ p *iimporter
+ declReader bytes.Reader
+ currPkg *types2.Package
+ prevFile string
+ prevLine int64
+ prevColumn int64
+}
+
+func (r *importReader) obj(name string) {
+ tag := r.byte()
+ pos := r.pos()
+
+ switch tag {
+ case 'A':
+ typ := r.typ()
+
+ r.declare(types2.NewTypeName(pos, r.currPkg, name, typ))
+
+ case 'C':
+ typ, val := r.value()
+
+ r.declare(types2.NewConst(pos, r.currPkg, name, typ, val))
+
+ case 'F':
+ sig := r.signature(nil)
+
+ r.declare(types2.NewFunc(pos, r.currPkg, name, sig))
+
+ case 'T':
+ // Types can be recursive. We need to setup a stub
+ // declaration before recursing.
+ obj := types2.NewTypeName(pos, r.currPkg, name, nil)
+ named := types2.NewNamed(obj, nil, nil)
+ r.declare(obj)
+
+ underlying := r.p.typAt(r.uint64(), named).Underlying()
+ named.SetUnderlying(underlying)
+
+ if !isInterface(underlying) {
+ for n := r.uint64(); n > 0; n-- {
+ mpos := r.pos()
+ mname := r.ident()
+ recv := r.param()
+ msig := r.signature(recv)
+
+ named.AddMethod(types2.NewFunc(mpos, r.currPkg, mname, msig))
+ }
+ }
+
+ case 'V':
+ typ := r.typ()
+
+ r.declare(types2.NewVar(pos, r.currPkg, name, typ))
+
+ default:
+ errorf("unexpected tag: %v", tag)
+ }
+}
+
+func (r *importReader) declare(obj types2.Object) {
+ obj.Pkg().Scope().Insert(obj)
+}
+
+func (r *importReader) value() (typ types2.Type, val constant.Value) {
+ typ = r.typ()
+
+ switch b := typ.Underlying().(*types2.Basic); b.Info() & types2.IsConstType {
+ case types2.IsBoolean:
+ val = constant.MakeBool(r.bool())
+
+ case types2.IsString:
+ val = constant.MakeString(r.string())
+
+ case types2.IsInteger:
+ val = r.mpint(b)
+
+ case types2.IsFloat:
+ val = r.mpfloat(b)
+
+ case types2.IsComplex:
+ re := r.mpfloat(b)
+ im := r.mpfloat(b)
+ val = constant.BinaryOp(re, token.ADD, constant.MakeImag(im))
+
+ default:
+ errorf("unexpected type %v", typ) // panics
+ panic("unreachable")
+ }
+
+ return
+}
+
+func intSize(b *types2.Basic) (signed bool, maxBytes uint) {
+ if (b.Info() & types2.IsUntyped) != 0 {
+ return true, 64
+ }
+
+ switch b.Kind() {
+ case types2.Float32, types2.Complex64:
+ return true, 3
+ case types2.Float64, types2.Complex128:
+ return true, 7
+ }
+
+ signed = (b.Info() & types2.IsUnsigned) == 0
+ switch b.Kind() {
+ case types2.Int8, types2.Uint8:
+ maxBytes = 1
+ case types2.Int16, types2.Uint16:
+ maxBytes = 2
+ case types2.Int32, types2.Uint32:
+ maxBytes = 4
+ default:
+ maxBytes = 8
+ }
+
+ return
+}
+
+func (r *importReader) mpint(b *types2.Basic) constant.Value {
+ signed, maxBytes := intSize(b)
+
+ maxSmall := 256 - maxBytes
+ if signed {
+ maxSmall = 256 - 2*maxBytes
+ }
+ if maxBytes == 1 {
+ maxSmall = 256
+ }
+
+ n, _ := r.declReader.ReadByte()
+ if uint(n) < maxSmall {
+ v := int64(n)
+ if signed {
+ v >>= 1
+ if n&1 != 0 {
+ v = ^v
+ }
+ }
+ return constant.MakeInt64(v)
+ }
+
+ v := -n
+ if signed {
+ v = -(n &^ 1) >> 1
+ }
+ if v < 1 || uint(v) > maxBytes {
+ errorf("weird decoding: %v, %v => %v", n, signed, v)
+ }
+
+ buf := make([]byte, v)
+ io.ReadFull(&r.declReader, buf)
+
+ // convert to little endian
+ // TODO(gri) go/constant should have a more direct conversion function
+ // (e.g., once it supports a big.Float based implementation)
+ for i, j := 0, len(buf)-1; i < j; i, j = i+1, j-1 {
+ buf[i], buf[j] = buf[j], buf[i]
+ }
+
+ x := constant.MakeFromBytes(buf)
+ if signed && n&1 != 0 {
+ x = constant.UnaryOp(token.SUB, x, 0)
+ }
+ return x
+}
+
+func (r *importReader) mpfloat(b *types2.Basic) constant.Value {
+ x := r.mpint(b)
+ if constant.Sign(x) == 0 {
+ return x
+ }
+
+ exp := r.int64()
+ switch {
+ case exp > 0:
+ x = constant.Shift(x, token.SHL, uint(exp))
+ case exp < 0:
+ d := constant.Shift(constant.MakeInt64(1), token.SHL, uint(-exp))
+ x = constant.BinaryOp(x, token.QUO, d)
+ }
+ return x
+}
+
+func (r *importReader) ident() string {
+ return r.string()
+}
+
+func (r *importReader) qualifiedIdent() (*types2.Package, string) {
+ name := r.string()
+ pkg := r.pkg()
+ return pkg, name
+}
+
+func (r *importReader) pos() syntax.Pos {
+ if r.p.version >= 1 {
+ r.posv1()
+ } else {
+ r.posv0()
+ }
+
+ if r.prevFile == "" && r.prevLine == 0 && r.prevColumn == 0 {
+ return syntax.Pos{}
+ }
+ // TODO(gri) fix this
+ // return r.p.fake.pos(r.prevFile, int(r.prevLine), int(r.prevColumn))
+ return syntax.Pos{}
+}
+
+func (r *importReader) posv0() {
+ delta := r.int64()
+ if delta != deltaNewFile {
+ r.prevLine += delta
+ } else if l := r.int64(); l == -1 {
+ r.prevLine += deltaNewFile
+ } else {
+ r.prevFile = r.string()
+ r.prevLine = l
+ }
+}
+
+func (r *importReader) posv1() {
+ delta := r.int64()
+ r.prevColumn += delta >> 1
+ if delta&1 != 0 {
+ delta = r.int64()
+ r.prevLine += delta >> 1
+ if delta&1 != 0 {
+ r.prevFile = r.string()
+ }
+ }
+}
+
+func (r *importReader) typ() types2.Type {
+ return r.p.typAt(r.uint64(), nil)
+}
+
+func isInterface(t types2.Type) bool {
+ _, ok := t.(*types2.Interface)
+ return ok
+}
+
+func (r *importReader) pkg() *types2.Package { return r.p.pkgAt(r.uint64()) }
+func (r *importReader) string() string { return r.p.stringAt(r.uint64()) }
+
+func (r *importReader) doType(base *types2.Named) types2.Type {
+ switch k := r.kind(); k {
+ default:
+ errorf("unexpected kind tag in %q: %v", r.p.ipath, k)
+ return nil
+
+ case definedType:
+ pkg, name := r.qualifiedIdent()
+ r.p.doDecl(pkg, name)
+ return pkg.Scope().Lookup(name).(*types2.TypeName).Type()
+ case pointerType:
+ return types2.NewPointer(r.typ())
+ case sliceType:
+ return types2.NewSlice(r.typ())
+ case arrayType:
+ n := r.uint64()
+ return types2.NewArray(r.typ(), int64(n))
+ case chanType:
+ dir := chanDir(int(r.uint64()))
+ return types2.NewChan(dir, r.typ())
+ case mapType:
+ return types2.NewMap(r.typ(), r.typ())
+ case signatureType:
+ r.currPkg = r.pkg()
+ return r.signature(nil)
+
+ case structType:
+ r.currPkg = r.pkg()
+
+ fields := make([]*types2.Var, r.uint64())
+ tags := make([]string, len(fields))
+ for i := range fields {
+ fpos := r.pos()
+ fname := r.ident()
+ ftyp := r.typ()
+ emb := r.bool()
+ tag := r.string()
+
+ fields[i] = types2.NewField(fpos, r.currPkg, fname, ftyp, emb)
+ tags[i] = tag
+ }
+ return types2.NewStruct(fields, tags)
+
+ case interfaceType:
+ r.currPkg = r.pkg()
+
+ embeddeds := make([]types2.Type, r.uint64())
+ for i := range embeddeds {
+ _ = r.pos()
+ embeddeds[i] = r.typ()
+ }
+
+ methods := make([]*types2.Func, r.uint64())
+ for i := range methods {
+ mpos := r.pos()
+ mname := r.ident()
+
+ // TODO(mdempsky): Matches bimport.go, but I
+ // don't agree with this.
+ var recv *types2.Var
+ if base != nil {
+ recv = types2.NewVar(syntax.Pos{}, r.currPkg, "", base)
+ }
+
+ msig := r.signature(recv)
+ methods[i] = types2.NewFunc(mpos, r.currPkg, mname, msig)
+ }
+
+ typ := types2.NewInterfaceType(methods, embeddeds)
+ r.p.interfaceList = append(r.p.interfaceList, typ)
+ return typ
+ }
+}
+
+func (r *importReader) kind() itag {
+ return itag(r.uint64())
+}
+
+func (r *importReader) signature(recv *types2.Var) *types2.Signature {
+ params := r.paramList()
+ results := r.paramList()
+ variadic := params.Len() > 0 && r.bool()
+ return types2.NewSignature(recv, params, results, variadic)
+}
+
+func (r *importReader) paramList() *types2.Tuple {
+ xs := make([]*types2.Var, r.uint64())
+ for i := range xs {
+ xs[i] = r.param()
+ }
+ return types2.NewTuple(xs...)
+}
+
+func (r *importReader) param() *types2.Var {
+ pos := r.pos()
+ name := r.ident()
+ typ := r.typ()
+ return types2.NewParam(pos, r.currPkg, name, typ)
+}
+
+func (r *importReader) bool() bool {
+ return r.uint64() != 0
+}
+
+func (r *importReader) int64() int64 {
+ n, err := binary.ReadVarint(&r.declReader)
+ if err != nil {
+ errorf("readVarint: %v", err)
+ }
+ return n
+}
+
+func (r *importReader) uint64() uint64 {
+ n, err := binary.ReadUvarint(&r.declReader)
+ if err != nil {
+ errorf("readUvarint: %v", err)
+ }
+ return n
+}
+
+func (r *importReader) byte() byte {
+ x, err := r.declReader.ReadByte()
+ if err != nil {
+ errorf("declReader.ReadByte: %v", err)
+ }
+ return x
+}
diff --git a/src/cmd/compile/internal/importer/support.go b/src/cmd/compile/internal/importer/support.go
new file mode 100644
index 0000000000..4f013f4a51
--- /dev/null
+++ b/src/cmd/compile/internal/importer/support.go
@@ -0,0 +1,144 @@
+// UNREVIEWED
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements support functionality for iimport.go.
+
+package importer
+
+import (
+ "cmd/compile/internal/types2"
+ "fmt"
+ "go/token"
+ "sync"
+)
+
+func errorf(format string, args ...interface{}) {
+ panic(fmt.Sprintf(format, args...))
+}
+
+const deltaNewFile = -64 // see cmd/compile/internal/gc/bexport.go
+
+// Synthesize a token.Pos
+type fakeFileSet struct {
+ fset *token.FileSet
+ files map[string]*token.File
+}
+
+func (s *fakeFileSet) pos(file string, line, column int) token.Pos {
+ // TODO(mdempsky): Make use of column.
+
+ // Since we don't know the set of needed file positions, we
+ // reserve maxlines positions per file.
+ const maxlines = 64 * 1024
+ f := s.files[file]
+ if f == nil {
+ f = s.fset.AddFile(file, -1, maxlines)
+ s.files[file] = f
+ // Allocate the fake linebreak indices on first use.
+ // TODO(adonovan): opt: save ~512KB using a more complex scheme?
+ fakeLinesOnce.Do(func() {
+ fakeLines = make([]int, maxlines)
+ for i := range fakeLines {
+ fakeLines[i] = i
+ }
+ })
+ f.SetLines(fakeLines)
+ }
+
+ if line > maxlines {
+ line = 1
+ }
+
+ // Treat the file as if it contained only newlines
+ // and column=1: use the line number as the offset.
+ return f.Pos(line - 1)
+}
+
+var (
+ fakeLines []int
+ fakeLinesOnce sync.Once
+)
+
+func chanDir(d int) types2.ChanDir {
+ // tag values must match the constants in cmd/compile/internal/gc/go.go
+ switch d {
+ case 1 /* Crecv */ :
+ return types2.RecvOnly
+ case 2 /* Csend */ :
+ return types2.SendOnly
+ case 3 /* Cboth */ :
+ return types2.SendRecv
+ default:
+ errorf("unexpected channel dir %d", d)
+ return 0
+ }
+}
+
+var predeclared = []types2.Type{
+ // basic types
+ types2.Typ[types2.Bool],
+ types2.Typ[types2.Int],
+ types2.Typ[types2.Int8],
+ types2.Typ[types2.Int16],
+ types2.Typ[types2.Int32],
+ types2.Typ[types2.Int64],
+ types2.Typ[types2.Uint],
+ types2.Typ[types2.Uint8],
+ types2.Typ[types2.Uint16],
+ types2.Typ[types2.Uint32],
+ types2.Typ[types2.Uint64],
+ types2.Typ[types2.Uintptr],
+ types2.Typ[types2.Float32],
+ types2.Typ[types2.Float64],
+ types2.Typ[types2.Complex64],
+ types2.Typ[types2.Complex128],
+ types2.Typ[types2.String],
+
+ // basic type aliases
+ types2.Universe.Lookup("byte").Type(),
+ types2.Universe.Lookup("rune").Type(),
+
+ // error
+ types2.Universe.Lookup("error").Type(),
+
+ // untyped types
+ types2.Typ[types2.UntypedBool],
+ types2.Typ[types2.UntypedInt],
+ types2.Typ[types2.UntypedRune],
+ types2.Typ[types2.UntypedFloat],
+ types2.Typ[types2.UntypedComplex],
+ types2.Typ[types2.UntypedString],
+ types2.Typ[types2.UntypedNil],
+
+ // package unsafe
+ types2.Typ[types2.UnsafePointer],
+
+ // invalid type
+ types2.Typ[types2.Invalid], // only appears in packages with errors
+
+ // used internally by gc; never used by this package or in .a files
+ anyType{},
+}
+
+type anyType struct{}
+
+func (t anyType) Underlying() types2.Type { return t }
+func (t anyType) Under() types2.Type { return t }
+func (t anyType) String() string { return "any" }
+
+// types2.aType is not exported for now so we need to implemented these here.
+func (anyType) Basic() *types2.Basic { return nil }
+func (anyType) Array() *types2.Array { return nil }
+func (anyType) Slice() *types2.Slice { return nil }
+func (anyType) Struct() *types2.Struct { return nil }
+func (anyType) Pointer() *types2.Pointer { return nil }
+func (anyType) Tuple() *types2.Tuple { return nil }
+func (anyType) Signature() *types2.Signature { return nil }
+func (anyType) Sum() *types2.Sum { return nil }
+func (anyType) Interface() *types2.Interface { return nil }
+func (anyType) Map() *types2.Map { return nil }
+func (anyType) Chan() *types2.Chan { return nil }
+func (anyType) Named() *types2.Named { return nil }
+func (anyType) TypeParam() *types2.TypeParam { return nil }
diff --git a/src/cmd/compile/internal/importer/testdata/a.go b/src/cmd/compile/internal/importer/testdata/a.go
new file mode 100644
index 0000000000..06dafee98c
--- /dev/null
+++ b/src/cmd/compile/internal/importer/testdata/a.go
@@ -0,0 +1,15 @@
+// UNREVIEWED
+// Copyright 2016 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.
+
+// Input for TestIssue13566
+
+package a
+
+import "encoding/json"
+
+type A struct {
+ a *A
+ json json.RawMessage
+}
diff --git a/src/cmd/compile/internal/importer/testdata/b.go b/src/cmd/compile/internal/importer/testdata/b.go
new file mode 100644
index 0000000000..a601dbccc5
--- /dev/null
+++ b/src/cmd/compile/internal/importer/testdata/b.go
@@ -0,0 +1,12 @@
+// UNREVIEWED
+// Copyright 2016 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.
+
+// Input for TestIssue13566
+
+package b
+
+import "./a"
+
+type A a.A
diff --git a/src/cmd/compile/internal/importer/testdata/exports.go b/src/cmd/compile/internal/importer/testdata/exports.go
new file mode 100644
index 0000000000..2a720fd2c1
--- /dev/null
+++ b/src/cmd/compile/internal/importer/testdata/exports.go
@@ -0,0 +1,89 @@
+// UNREVIEWED
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file is used to generate an object file which
+// serves as test file for gcimporter_test.go.
+
+package exports
+
+import "go/ast"
+
+// Issue 3682: Correctly read dotted identifiers from export data.
+const init1 = 0
+
+func init() {}
+
+const (
+ C0 int = 0
+ C1 = 3.14159265
+ C2 = 2.718281828i
+ C3 = -123.456e-789
+ C4 = +123.456e+789
+ C5 = 1234i
+ C6 = "foo\n"
+ C7 = `bar\n`
+)
+
+type (
+ T1 int
+ T2 [10]int
+ T3 []int
+ T4 *int
+ T5 chan int
+ T6a chan<- int
+ T6b chan (<-chan int)
+ T6c chan<- (chan int)
+ T7 <-chan *ast.File
+ T8 struct{}
+ T9 struct {
+ a int
+ b, c float32
+ d []string `go:"tag"`
+ }
+ T10 struct {
+ T8
+ T9
+ _ *T10
+ }
+ T11 map[int]string
+ T12 interface{}
+ T13 interface {
+ m1()
+ m2(int) float32
+ }
+ T14 interface {
+ T12
+ T13
+ m3(x ...struct{}) []T9
+ }
+ T15 func()
+ T16 func(int)
+ T17 func(x int)
+ T18 func() float32
+ T19 func() (x float32)
+ T20 func(...interface{})
+ T21 struct{ next *T21 }
+ T22 struct{ link *T23 }
+ T23 struct{ link *T22 }
+ T24 *T24
+ T25 *T26
+ T26 *T27
+ T27 *T25
+ T28 func(T28) T28
+)
+
+var (
+ V0 int
+ V1 = -991.0
+ V2 float32 = 1.2
+)
+
+func F1() {}
+func F2(x int) {}
+func F3() int { return 0 }
+func F4() float32 { return 0 }
+func F5(a, b, c int, u, v, w struct{ x, y T1 }, more ...interface{}) (p, q, r chan<- T10)
+
+func (p *T1) M1()
diff --git a/src/cmd/compile/internal/importer/testdata/issue15920.go b/src/cmd/compile/internal/importer/testdata/issue15920.go
new file mode 100644
index 0000000000..b402026162
--- /dev/null
+++ b/src/cmd/compile/internal/importer/testdata/issue15920.go
@@ -0,0 +1,12 @@
+// UNREVIEWED
+// Copyright 2016 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
+
+// The underlying type of Error is the underlying type of error.
+// Make sure we can import this again without problems.
+type Error error
+
+func F() Error { return nil }
diff --git a/src/cmd/compile/internal/importer/testdata/issue20046.go b/src/cmd/compile/internal/importer/testdata/issue20046.go
new file mode 100644
index 0000000000..e412f353ad
--- /dev/null
+++ b/src/cmd/compile/internal/importer/testdata/issue20046.go
@@ -0,0 +1,10 @@
+// UNREVIEWED
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+var V interface {
+ M()
+}
diff --git a/src/cmd/compile/internal/importer/testdata/issue25301.go b/src/cmd/compile/internal/importer/testdata/issue25301.go
new file mode 100644
index 0000000000..a9dc1d7f08
--- /dev/null
+++ b/src/cmd/compile/internal/importer/testdata/issue25301.go
@@ -0,0 +1,18 @@
+// UNREVIEWED
+// Copyright 2018 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 issue25301
+
+type (
+ A = interface {
+ M()
+ }
+ T interface {
+ A
+ }
+ S struct{}
+)
+
+func (S) M() { println("m") }
diff --git a/src/cmd/compile/internal/importer/testdata/issue25596.go b/src/cmd/compile/internal/importer/testdata/issue25596.go
new file mode 100644
index 0000000000..95bef42280
--- /dev/null
+++ b/src/cmd/compile/internal/importer/testdata/issue25596.go
@@ -0,0 +1,14 @@
+// UNREVIEWED
+// Copyright 2018 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 issue25596
+
+type E interface {
+ M() T
+}
+
+type T interface {
+ E
+}
diff --git a/src/cmd/compile/internal/importer/testdata/p.go b/src/cmd/compile/internal/importer/testdata/p.go
new file mode 100644
index 0000000000..34a20eaa14
--- /dev/null
+++ b/src/cmd/compile/internal/importer/testdata/p.go
@@ -0,0 +1,14 @@
+// UNREVIEWED
+// Copyright 2016 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.
+
+// Input for TestIssue15517
+
+package p
+
+const C = 0
+
+var V int
+
+func F() {}
diff --git a/src/cmd/compile/internal/importer/testdata/versions/test.go b/src/cmd/compile/internal/importer/testdata/versions/test.go
new file mode 100644
index 0000000000..2f8eb5ced0
--- /dev/null
+++ b/src/cmd/compile/internal/importer/testdata/versions/test.go
@@ -0,0 +1,29 @@
+// UNREVIEWED
+// Copyright 2016 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.
+
+// To create a test case for a new export format version,
+// build this package with the latest compiler and store
+// the resulting .a file appropriately named in the versions
+// directory. The VersionHandling test will pick it up.
+//
+// In the testdata/versions:
+//
+// go build -o test_go1.$X_$Y.a test.go
+//
+// with $X = Go version and $Y = export format version
+// (add 'b' or 'i' to distinguish between binary and
+// indexed format starting with 1.11 as long as both
+// formats are supported).
+//
+// Make sure this source is extended such that it exercises
+// whatever export format change has taken place.
+
+package test
+
+// Any release before and including Go 1.7 didn't encode
+// the package for a blank struct field.
+type BlankField struct {
+ _ int
+}
diff --git a/src/cmd/compile/internal/types2/api.go b/src/cmd/compile/internal/types2/api.go
new file mode 100644
index 0000000000..eff90d4cdf
--- /dev/null
+++ b/src/cmd/compile/internal/types2/api.go
@@ -0,0 +1,426 @@
+// UNREVIEWED
+// Copyright 2012 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 declares the data types and implements
+// the algorithms for type-checking of Go packages. Use
+// Config.Check to invoke the type checker for a package.
+// Alternatively, create a new type checker with NewChecker
+// and invoke it incrementally by calling Checker.Files.
+//
+// Type-checking consists of several interdependent phases:
+//
+// Name resolution maps each identifier (syntax.Name) in the program to the
+// language object (Object) it denotes.
+// Use Info.{Defs,Uses,Implicits} for the results of name resolution.
+//
+// Constant folding computes the exact constant value (constant.Value)
+// for every expression (syntax.Expr) that is a compile-time constant.
+// Use Info.Types[expr].Value for the results of constant folding.
+//
+// Type inference computes the type (Type) of every expression (syntax.Expr)
+// and checks for compliance with the language specification.
+// Use Info.Types[expr].Type for the results of type inference.
+//
+// For a tutorial, see https://golang.org/s/types-tutorial.
+//
+package types2
+
+import (
+ "bytes"
+ "cmd/compile/internal/syntax"
+ "fmt"
+ "go/constant"
+)
+
+// An Error describes a type-checking error; it implements the error interface.
+// A "soft" error is an error that still permits a valid interpretation of a
+// package (such as "unused variable"); "hard" errors may lead to unpredictable
+// behavior if ignored.
+type Error struct {
+ Pos syntax.Pos // error position
+ Msg string // default error message, user-friendly
+ Full string // full error message, for debugging (may contain internal details)
+ Soft bool // if set, error is "soft"
+}
+
+// Error returns an error string formatted as follows:
+// filename:line:column: message
+func (err Error) Error() string {
+ return fmt.Sprintf("%s: %s", err.Pos, err.Msg)
+}
+
+// FullError returns an error string like Error, buy it may contain
+// type-checker internal details such as subscript indices for type
+// parameters and more. Useful for debugging.
+func (err Error) FullError() string {
+ return fmt.Sprintf("%s: %s", err.Pos, err.Full)
+}
+
+// An Importer resolves import paths to Packages.
+//
+// CAUTION: This interface does not support the import of locally
+// vendored packages. See https://golang.org/s/go15vendor.
+// If possible, external implementations should implement ImporterFrom.
+type Importer interface {
+ // Import returns the imported package for the given import path.
+ // The semantics is like for ImporterFrom.ImportFrom except that
+ // dir and mode are ignored (since they are not present).
+ Import(path string) (*Package, error)
+}
+
+// ImportMode is reserved for future use.
+type ImportMode int
+
+// An ImporterFrom resolves import paths to packages; it
+// supports vendoring per https://golang.org/s/go15vendor.
+// Use go/importer to obtain an ImporterFrom implementation.
+type ImporterFrom interface {
+ // Importer is present for backward-compatibility. Calling
+ // Import(path) is the same as calling ImportFrom(path, "", 0);
+ // i.e., locally vendored packages may not be found.
+ // The types package does not call Import if an ImporterFrom
+ // is present.
+ Importer
+
+ // ImportFrom returns the imported package for the given import
+ // path when imported by a package file located in dir.
+ // If the import failed, besides returning an error, ImportFrom
+ // is encouraged to cache and return a package anyway, if one
+ // was created. This will reduce package inconsistencies and
+ // follow-on type checker errors due to the missing package.
+ // The mode value must be 0; it is reserved for future use.
+ // Two calls to ImportFrom with the same path and dir must
+ // return the same package.
+ ImportFrom(path, dir string, mode ImportMode) (*Package, error)
+}
+
+// A Config specifies the configuration for type checking.
+// The zero value for Config is a ready-to-use default configuration.
+type Config struct {
+ // If IgnoreFuncBodies is set, function bodies are not
+ // type-checked.
+ IgnoreFuncBodies bool
+
+ // If AcceptMethodTypeParams is set, methods may have type parameters.
+ AcceptMethodTypeParams bool
+
+ // If InferFromConstraints is set, constraint type inference is used
+ // if some function type arguments are missing.
+ InferFromConstraints bool
+
+ // If FakeImportC is set, `import "C"` (for packages requiring Cgo)
+ // declares an empty "C" package and errors are omitted for qualified
+ // identifiers referring to package C (which won't find an object).
+ // This feature is intended for the standard library cmd/api tool.
+ //
+ // Caution: Effects may be unpredictable due to follow-on errors.
+ // Do not use casually!
+ FakeImportC bool
+
+ // If go115UsesCgo is set, the type checker expects the
+ // _cgo_gotypes.go file generated by running cmd/cgo to be
+ // provided as a package source file. Qualified identifiers
+ // referring to package C will be resolved to cgo-provided
+ // declarations within _cgo_gotypes.go.
+ //
+ // It is an error to set both FakeImportC and go115UsesCgo.
+ go115UsesCgo bool
+
+ // If Trace is set, a debug trace is printed to stdout.
+ Trace bool
+
+ // If Error != nil, it is called with each error found
+ // during type checking; err has dynamic type Error.
+ // Secondary errors (for instance, to enumerate all types
+ // involved in an invalid recursive type declaration) have
+ // error strings that start with a '\t' character.
+ // If Error == nil, type-checking stops with the first
+ // error found.
+ Error func(err error)
+
+ // An importer is used to import packages referred to from
+ // import declarations.
+ // If the installed importer implements ImporterFrom, the type
+ // checker calls ImportFrom instead of Import.
+ // The type checker reports an error if an importer is needed
+ // but none was installed.
+ Importer Importer
+
+ // If Sizes != nil, it provides the sizing functions for package unsafe.
+ // Otherwise SizesFor("gc", "amd64") is used instead.
+ Sizes Sizes
+
+ // If DisableUnusedImportCheck is set, packages are not checked
+ // for unused imports.
+ DisableUnusedImportCheck bool
+}
+
+func srcimporter_setUsesCgo(conf *Config) {
+ conf.go115UsesCgo = true
+}
+
+// Info holds result type information for a type-checked package.
+// Only the information for which a map is provided is collected.
+// If the package has type errors, the collected information may
+// be incomplete.
+type Info struct {
+ // Types maps expressions to their types, and for constant
+ // expressions, also their values. Invalid expressions are
+ // omitted.
+ //
+ // For (possibly parenthesized) identifiers denoting built-in
+ // functions, the recorded signatures are call-site specific:
+ // if the call result is not a constant, the recorded type is
+ // an argument-specific signature. Otherwise, the recorded type
+ // is invalid.
+ //
+ // The Types map does not record the type of every identifier,
+ // only those that appear where an arbitrary expression is
+ // permitted. For instance, the identifier f in a selector
+ // expression x.f is found only in the Selections map, the
+ // identifier z in a variable declaration 'var z int' is found
+ // only in the Defs map, and identifiers denoting packages in
+ // qualified identifiers are collected in the Uses map.
+ Types map[syntax.Expr]TypeAndValue
+
+ // Inferred maps calls of parameterized functions that use
+ // type inference to the inferred type arguments and signature
+ // of the function called. The recorded "call" expression may be
+ // an *ast.CallExpr (as in f(x)), or an *ast.IndexExpr (s in f[T]).
+ Inferred map[syntax.Expr]Inferred
+
+ // Defs maps identifiers to the objects they define (including
+ // package names, dots "." of dot-imports, and blank "_" identifiers).
+ // For identifiers that do not denote objects (e.g., the package name
+ // in package clauses, or symbolic variables t in t := x.(type) of
+ // type switch headers), the corresponding objects are nil.
+ //
+ // For an embedded field, Defs returns the field *Var it defines.
+ //
+ // Invariant: Defs[id] == nil || Defs[id].Pos() == id.Pos()
+ Defs map[*syntax.Name]Object
+
+ // Uses maps identifiers to the objects they denote.
+ //
+ // For an embedded field, Uses returns the *TypeName it denotes.
+ //
+ // Invariant: Uses[id].Pos() != id.Pos()
+ Uses map[*syntax.Name]Object
+
+ // Implicits maps nodes to their implicitly declared objects, if any.
+ // The following node and object types may appear:
+ //
+ // node declared object
+ //
+ // *syntax.ImportDecl *PkgName for imports without renames
+ // *syntax.CaseClause type-specific *Var for each type switch case clause (incl. default)
+ // *syntax.Field anonymous parameter *Var (incl. unnamed results)
+ //
+ Implicits map[syntax.Node]Object
+
+ // Selections maps selector expressions (excluding qualified identifiers)
+ // to their corresponding selections.
+ Selections map[*syntax.SelectorExpr]*Selection
+
+ // Scopes maps syntax.Nodes to the scopes they define. Package scopes are not
+ // associated with a specific node but with all files belonging to a package.
+ // Thus, the package scope can be found in the type-checked Package object.
+ // Scopes nest, with the Universe scope being the outermost scope, enclosing
+ // the package scope, which contains (one or more) files scopes, which enclose
+ // function scopes which in turn enclose statement and function literal scopes.
+ // Note that even though package-level functions are declared in the package
+ // scope, the function scopes are embedded in the file scope of the file
+ // containing the function declaration.
+ //
+ // The following node types may appear in Scopes:
+ //
+ // *syntax.File
+ // *syntax.FuncType
+ // *syntax.BlockStmt
+ // *syntax.IfStmt
+ // *syntax.SwitchStmt
+ // *syntax.CaseClause
+ // *syntax.CommClause
+ // *syntax.ForStmt
+ //
+ Scopes map[syntax.Node]*Scope
+
+ // InitOrder is the list of package-level initializers in the order in which
+ // they must be executed. Initializers referring to variables related by an
+ // initialization dependency appear in topological order, the others appear
+ // in source order. Variables without an initialization expression do not
+ // appear in this list.
+ InitOrder []*Initializer
+}
+
+// TypeOf returns the type of expression e, or nil if not found.
+// Precondition: the Types, Uses and Defs maps are populated.
+//
+func (info *Info) TypeOf(e syntax.Expr) Type {
+ if t, ok := info.Types[e]; ok {
+ return t.Type
+ }
+ if id, _ := e.(*syntax.Name); id != nil {
+ if obj := info.ObjectOf(id); obj != nil {
+ return obj.Type()
+ }
+ }
+ return nil
+}
+
+// ObjectOf returns the object denoted by the specified id,
+// or nil if not found.
+//
+// If id is an embedded struct field, ObjectOf returns the field (*Var)
+// it defines, not the type (*TypeName) it uses.
+//
+// Precondition: the Uses and Defs maps are populated.
+//
+func (info *Info) ObjectOf(id *syntax.Name) Object {
+ if obj := info.Defs[id]; obj != nil {
+ return obj
+ }
+ return info.Uses[id]
+}
+
+// TypeAndValue reports the type and value (for constants)
+// of the corresponding expression.
+type TypeAndValue struct {
+ mode operandMode
+ Type Type
+ Value constant.Value
+}
+
+// IsVoid reports whether the corresponding expression
+// is a function call without results.
+func (tv TypeAndValue) IsVoid() bool {
+ return tv.mode == novalue
+}
+
+// IsType reports whether the corresponding expression specifies a type.
+func (tv TypeAndValue) IsType() bool {
+ return tv.mode == typexpr
+}
+
+// IsBuiltin reports whether the corresponding expression denotes
+// a (possibly parenthesized) built-in function.
+func (tv TypeAndValue) IsBuiltin() bool {
+ return tv.mode == builtin
+}
+
+// IsValue reports whether the corresponding expression is a value.
+// Builtins are not considered values. Constant values have a non-
+// nil Value.
+func (tv TypeAndValue) IsValue() bool {
+ switch tv.mode {
+ case constant_, variable, mapindex, value, commaok, commaerr:
+ return true
+ }
+ return false
+}
+
+// IsNil reports whether the corresponding expression denotes the
+// predeclared value nil.
+func (tv TypeAndValue) IsNil() bool {
+ return tv.mode == value && tv.Type == Typ[UntypedNil]
+}
+
+// Addressable reports whether the corresponding expression
+// is addressable (https://golang.org/ref/spec#Address_operators).
+func (tv TypeAndValue) Addressable() bool {
+ return tv.mode == variable
+}
+
+// Assignable reports whether the corresponding expression
+// is assignable to (provided a value of the right type).
+func (tv TypeAndValue) Assignable() bool {
+ return tv.mode == variable || tv.mode == mapindex
+}
+
+// HasOk reports whether the corresponding expression may be
+// used on the rhs of a comma-ok assignment.
+func (tv TypeAndValue) HasOk() bool {
+ return tv.mode == commaok || tv.mode == mapindex
+}
+
+// Inferred reports the inferred type arguments and signature
+// for a parameterized function call that uses type inference.
+type Inferred struct {
+ Targs []Type
+ Sig *Signature
+}
+
+// An Initializer describes a package-level variable, or a list of variables in case
+// of a multi-valued initialization expression, and the corresponding initialization
+// expression.
+type Initializer struct {
+ Lhs []*Var // var Lhs = Rhs
+ Rhs syntax.Expr
+}
+
+func (init *Initializer) String() string {
+ var buf bytes.Buffer
+ for i, lhs := range init.Lhs {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ buf.WriteString(lhs.Name())
+ }
+ buf.WriteString(" = ")
+ WriteExpr(&buf, init.Rhs)
+ return buf.String()
+}
+
+// Check type-checks a package and returns the resulting package object and
+// the first error if any. Additionally, if info != nil, Check populates each
+// of the non-nil maps in the Info struct.
+//
+// The package is marked as complete if no errors occurred, otherwise it is
+// incomplete. See Config.Error for controlling behavior in the presence of
+// errors.
+//
+// The package is specified by a list of *syntax.Files and corresponding
+// file set, and the package path the package is identified with.
+// The clean path must not be empty or dot (".").
+func (conf *Config) Check(path string, files []*syntax.File, info *Info) (*Package, error) {
+ pkg := NewPackage(path, "")
+ return pkg, NewChecker(conf, pkg, info).Files(files)
+}
+
+// AssertableTo reports whether a value of type V can be asserted to have type T.
+func AssertableTo(V *Interface, T Type) bool {
+ m, _ := (*Checker)(nil).assertableTo(V, T, false)
+ return m == nil
+}
+
+// AssignableTo reports whether a value of type V is assignable to a variable of type T.
+func AssignableTo(V, T Type) bool {
+ x := operand{mode: value, typ: V}
+ return x.assignableTo(nil, T, nil) // check not needed for non-constant x
+}
+
+// ConvertibleTo reports whether a value of type V is convertible to a value of type T.
+func ConvertibleTo(V, T Type) bool {
+ x := operand{mode: value, typ: V}
+ return x.convertibleTo(nil, T) // check not needed for non-constant x
+}
+
+// Implements reports whether type V implements interface T.
+func Implements(V Type, T *Interface) bool {
+ f, _ := MissingMethod(V, T, true)
+ return f == nil
+}
+
+// Identical reports whether x and y are identical types.
+// Receivers of Signature types are ignored.
+func Identical(x, y Type) bool {
+ return (*Checker)(nil).identical(x, y)
+}
+
+// IdenticalIgnoreTags reports whether x and y are identical types if tags are ignored.
+// Receivers of Signature types are ignored.
+func IdenticalIgnoreTags(x, y Type) bool {
+ return (*Checker)(nil).identicalIgnoreTags(x, y)
+}
diff --git a/src/cmd/compile/internal/types2/api_test.go b/src/cmd/compile/internal/types2/api_test.go
new file mode 100644
index 0000000000..403df3f941
--- /dev/null
+++ b/src/cmd/compile/internal/types2/api_test.go
@@ -0,0 +1,1741 @@
+// UNREVIEWED
+// Copyright 2013 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 types2_test
+
+import (
+ "bytes"
+ "cmd/compile/internal/syntax"
+ "fmt"
+ "internal/testenv"
+ "reflect"
+ "regexp"
+ "strings"
+ "testing"
+
+ . "cmd/compile/internal/types2"
+)
+
+func unimplemented() {
+ panic("unimplemented")
+}
+
+func parseSrc(path, src string) (*syntax.File, error) {
+ errh := func(error) {} // dummy error handler so that parsing continues in presence of errors
+ return syntax.Parse(syntax.NewFileBase(path), strings.NewReader(src), errh, nil, syntax.AllowGenerics)
+}
+
+func pkgFor(path, source string, info *Info) (*Package, error) {
+ f, err := parseSrc(path, source)
+ if err != nil {
+ return nil, err
+ }
+ conf := Config{Importer: defaultImporter()}
+ return conf.Check(f.PkgName.Value, []*syntax.File{f}, info)
+}
+
+func mustTypecheck(t *testing.T, path, source string, info *Info) string {
+ pkg, err := pkgFor(path, source, info)
+ if err != nil {
+ name := path
+ if pkg != nil {
+ name = "package " + pkg.Name()
+ }
+ t.Fatalf("%s: didn't type-check (%s)", name, err)
+ }
+ return pkg.Name()
+}
+
+func mayTypecheck(t *testing.T, path, source string, info *Info) (string, error) {
+ f, err := parseSrc(path, source)
+ if f == nil { // ignore errors unless f is nil
+ t.Fatalf("%s: unable to parse: %s", path, err)
+ }
+ conf := Config{
+ AcceptMethodTypeParams: true,
+ InferFromConstraints: true,
+ Error: func(err error) {},
+ Importer: defaultImporter(),
+ }
+ pkg, err := conf.Check(f.PkgName.Value, []*syntax.File{f}, info)
+ return pkg.Name(), err
+}
+
+func TestValuesInfo(t *testing.T) {
+ var tests = []struct {
+ src string
+ expr string // constant expression
+ typ string // constant type
+ val string // constant value
+ }{
+ {`package a0; const _ = false`, `false`, `untyped bool`, `false`},
+ {`package a1; const _ = 0`, `0`, `untyped int`, `0`},
+ {`package a2; const _ = 'A'`, `'A'`, `untyped rune`, `65`},
+ {`package a3; const _ = 0.`, `0.`, `untyped float`, `0`},
+ {`package a4; const _ = 0i`, `0i`, `untyped complex`, `(0 + 0i)`},
+ {`package a5; const _ = "foo"`, `"foo"`, `untyped string`, `"foo"`},
+
+ {`package b0; var _ = false`, `false`, `bool`, `false`},
+ {`package b1; var _ = 0`, `0`, `int`, `0`},
+ {`package b2; var _ = 'A'`, `'A'`, `rune`, `65`},
+ {`package b3; var _ = 0.`, `0.`, `float64`, `0`},
+ {`package b4; var _ = 0i`, `0i`, `complex128`, `(0 + 0i)`},
+ {`package b5; var _ = "foo"`, `"foo"`, `string`, `"foo"`},
+
+ {`package c0a; var _ = bool(false)`, `false`, `bool`, `false`},
+ {`package c0b; var _ = bool(false)`, `bool(false)`, `bool`, `false`},
+ {`package c0c; type T bool; var _ = T(false)`, `T(false)`, `c0c.T`, `false`},
+
+ {`package c1a; var _ = int(0)`, `0`, `int`, `0`},
+ {`package c1b; var _ = int(0)`, `int(0)`, `int`, `0`},
+ {`package c1c; type T int; var _ = T(0)`, `T(0)`, `c1c.T`, `0`},
+
+ {`package c2a; var _ = rune('A')`, `'A'`, `rune`, `65`},
+ {`package c2b; var _ = rune('A')`, `rune('A')`, `rune`, `65`},
+ {`package c2c; type T rune; var _ = T('A')`, `T('A')`, `c2c.T`, `65`},
+
+ {`package c3a; var _ = float32(0.)`, `0.`, `float32`, `0`},
+ {`package c3b; var _ = float32(0.)`, `float32(0.)`, `float32`, `0`},
+ {`package c3c; type T float32; var _ = T(0.)`, `T(0.)`, `c3c.T`, `0`},
+
+ {`package c4a; var _ = complex64(0i)`, `0i`, `complex64`, `(0 + 0i)`},
+ {`package c4b; var _ = complex64(0i)`, `complex64(0i)`, `complex64`, `(0 + 0i)`},
+ {`package c4c; type T complex64; var _ = T(0i)`, `T(0i)`, `c4c.T`, `(0 + 0i)`},
+
+ {`package c5a; var _ = string("foo")`, `"foo"`, `string`, `"foo"`},
+ {`package c5b; var _ = string("foo")`, `string("foo")`, `string`, `"foo"`},
+ {`package c5c; type T string; var _ = T("foo")`, `T("foo")`, `c5c.T`, `"foo"`},
+ {`package c5d; var _ = string(65)`, `65`, `untyped int`, `65`},
+ {`package c5e; var _ = string('A')`, `'A'`, `untyped rune`, `65`},
+ {`package c5f; type T string; var _ = T('A')`, `'A'`, `untyped rune`, `65`},
+ {`package c5g; var s uint; var _ = string(1 << s)`, `1 << s`, `untyped int`, ``},
+
+ {`package d0; var _ = []byte("foo")`, `"foo"`, `string`, `"foo"`},
+ {`package d1; var _ = []byte(string("foo"))`, `"foo"`, `string`, `"foo"`},
+ {`package d2; var _ = []byte(string("foo"))`, `string("foo")`, `string`, `"foo"`},
+ {`package d3; type T []byte; var _ = T("foo")`, `"foo"`, `string`, `"foo"`},
+
+ {`package e0; const _ = float32( 1e-200)`, `float32(1e-200)`, `float32`, `0`},
+ {`package e1; const _ = float32(-1e-200)`, `float32(-1e-200)`, `float32`, `0`},
+ {`package e2; const _ = float64( 1e-2000)`, `float64(1e-2000)`, `float64`, `0`},
+ {`package e3; const _ = float64(-1e-2000)`, `float64(-1e-2000)`, `float64`, `0`},
+ {`package e4; const _ = complex64( 1e-200)`, `complex64(1e-200)`, `complex64`, `(0 + 0i)`},
+ {`package e5; const _ = complex64(-1e-200)`, `complex64(-1e-200)`, `complex64`, `(0 + 0i)`},
+ {`package e6; const _ = complex128( 1e-2000)`, `complex128(1e-2000)`, `complex128`, `(0 + 0i)`},
+ {`package e7; const _ = complex128(-1e-2000)`, `complex128(-1e-2000)`, `complex128`, `(0 + 0i)`},
+
+ {`package f0 ; var _ float32 = 1e-200`, `1e-200`, `float32`, `0`},
+ {`package f1 ; var _ float32 = -1e-200`, `-1e-200`, `float32`, `0`},
+ {`package f2a; var _ float64 = 1e-2000`, `1e-2000`, `float64`, `0`},
+ {`package f3a; var _ float64 = -1e-2000`, `-1e-2000`, `float64`, `0`},
+ {`package f2b; var _ = 1e-2000`, `1e-2000`, `float64`, `0`},
+ {`package f3b; var _ = -1e-2000`, `-1e-2000`, `float64`, `0`},
+ {`package f4 ; var _ complex64 = 1e-200 `, `1e-200`, `complex64`, `(0 + 0i)`},
+ {`package f5 ; var _ complex64 = -1e-200 `, `-1e-200`, `complex64`, `(0 + 0i)`},
+ {`package f6a; var _ complex128 = 1e-2000i`, `1e-2000i`, `complex128`, `(0 + 0i)`},
+ {`package f7a; var _ complex128 = -1e-2000i`, `-1e-2000i`, `complex128`, `(0 + 0i)`},
+ {`package f6b; var _ = 1e-2000i`, `1e-2000i`, `complex128`, `(0 + 0i)`},
+ {`package f7b; var _ = -1e-2000i`, `-1e-2000i`, `complex128`, `(0 + 0i)`},
+
+ {`package g0; const (a = len([iota]int{}); b; c); const _ = c`, `c`, `int`, `2`}, // issue #22341
+ }
+
+ for _, test := range tests {
+ info := Info{
+ Types: make(map[syntax.Expr]TypeAndValue),
+ }
+ name := mustTypecheck(t, "ValuesInfo", test.src, &info)
+
+ // look for expression
+ var expr syntax.Expr
+ for e := range info.Types {
+ if ExprString(e) == test.expr {
+ expr = e
+ break
+ }
+ }
+ if expr == nil {
+ t.Errorf("package %s: no expression found for %s", name, test.expr)
+ continue
+ }
+ tv := info.Types[expr]
+
+ // check that type is correct
+ if got := tv.Type.String(); got != test.typ {
+ t.Errorf("package %s: got type %s; want %s", name, got, test.typ)
+ continue
+ }
+
+ // if we have a constant, check that value is correct
+ if tv.Value != nil {
+ if got := tv.Value.ExactString(); got != test.val {
+ t.Errorf("package %s: got value %s; want %s", name, got, test.val)
+ }
+ } else {
+ if test.val != "" {
+ t.Errorf("package %s: no constant found; want %s", name, test.val)
+ }
+ }
+ }
+}
+
+func TestTypesInfo(t *testing.T) {
+ var tests = []struct {
+ src string
+ expr string // expression
+ typ string // value type
+ }{
+ // single-valued expressions of untyped constants
+ {`package b0; var x interface{} = false`, `false`, `bool`},
+ {`package b1; var x interface{} = 0`, `0`, `int`},
+ {`package b2; var x interface{} = 0.`, `0.`, `float64`},
+ {`package b3; var x interface{} = 0i`, `0i`, `complex128`},
+ {`package b4; var x interface{} = "foo"`, `"foo"`, `string`},
+
+ // comma-ok expressions
+ {`package p0; var x interface{}; var _, _ = x.(int)`,
+ `x.(int)`,
+ `(int, bool)`,
+ },
+ {`package p1; var x interface{}; func _() { _, _ = x.(int) }`,
+ `x.(int)`,
+ `(int, bool)`,
+ },
+ {`package p2a; type mybool bool; var m map[string]complex128; var b mybool; func _() { _, b = m["foo"] }`,
+ `m["foo"]`,
+ `(complex128, p2a.mybool)`,
+ },
+ {`package p2b; var m map[string]complex128; var b bool; func _() { _, b = m["foo"] }`,
+ `m["foo"]`,
+ `(complex128, bool)`,
+ },
+ {`package p3; var c chan string; var _, _ = <-c`,
+ `<-c`,
+ `(string, bool)`,
+ },
+
+ // issue 6796
+ {`package issue6796_a; var x interface{}; var _, _ = (x.(int))`,
+ `x.(int)`,
+ `(int, bool)`,
+ },
+ {`package issue6796_b; var c chan string; var _, _ = (<-c)`,
+ `(<-c)`,
+ `(string, bool)`,
+ },
+ {`package issue6796_c; var c chan string; var _, _ = (<-c)`,
+ `<-c`,
+ `(string, bool)`,
+ },
+ {`package issue6796_d; var c chan string; var _, _ = ((<-c))`,
+ `(<-c)`,
+ `(string, bool)`,
+ },
+ {`package issue6796_e; func f(c chan string) { _, _ = ((<-c)) }`,
+ `(<-c)`,
+ `(string, bool)`,
+ },
+
+ // issue 7060
+ {`package issue7060_a; var ( m map[int]string; x, ok = m[0] )`,
+ `m[0]`,
+ `(string, bool)`,
+ },
+ {`package issue7060_b; var ( m map[int]string; x, ok interface{} = m[0] )`,
+ `m[0]`,
+ `(string, bool)`,
+ },
+ {`package issue7060_c; func f(x interface{}, ok bool, m map[int]string) { x, ok = m[0] }`,
+ `m[0]`,
+ `(string, bool)`,
+ },
+ {`package issue7060_d; var ( ch chan string; x, ok = <-ch )`,
+ `<-ch`,
+ `(string, bool)`,
+ },
+ {`package issue7060_e; var ( ch chan string; x, ok interface{} = <-ch )`,
+ `<-ch`,
+ `(string, bool)`,
+ },
+ {`package issue7060_f; func f(x interface{}, ok bool, ch chan string) { x, ok = <-ch }`,
+ `<-ch`,
+ `(string, bool)`,
+ },
+
+ // issue 28277
+ {`package issue28277_a; func f(...int)`,
+ `...int`,
+ `[]int`,
+ },
+ {`package issue28277_b; func f(a, b int, c ...[]struct{})`,
+ `...[]struct{}`,
+ `[][]struct{}`,
+ },
+
+ // tests for broken code that doesn't parse or type-check
+ {`package x0; func _() { var x struct {f string}; x.f := 0 }`, `x.f`, `string`},
+ {`package x1; func _() { var z string; type x struct {f string}; y := &x{q: z}}`, `z`, `string`},
+ {`package x2; func _() { var a, b string; type x struct {f string}; z := &x{f: a, f: b,}}`, `b`, `string`},
+ {`package x3; var x = panic("");`, `panic`, `func(interface{})`},
+ {`package x4; func _() { panic("") }`, `panic`, `func(interface{})`},
+ {`package x5; func _() { var x map[string][...]int; x = map[string][...]int{"": {1,2,3}} }`, `x`, `map[string][-1]int`},
+
+ // parameterized functions
+ {`package p0; func f[T any](T); var _ = f[int]`, `f`, `func[T₁ any](T₁)`},
+ {`package p1; func f[T any](T); var _ = f[int]`, `f[int]`, `func(int)`},
+ {`package p2; func f[T any](T); var _ = f(42)`, `f`, `func[T₁ any](T₁)`},
+ {`package p2; func f[T any](T); var _ = f(42)`, `f(42)`, `()`},
+
+ // type parameters
+ {`package t0; type t[] int; var _ t`, `t`, `t0.t`}, // t[] is a syntax error that is ignored in this test in favor of t
+ {`package t1; type t[P any] int; var _ t[int]`, `t`, `t1.t[P₁ any]`},
+ {`package t2; type t[P interface{}] int; var _ t[int]`, `t`, `t2.t[P₁ interface{}]`},
+ {`package t3; type t[P, Q interface{}] int; var _ t[int, int]`, `t`, `t3.t[P₁, Q₂ interface{}]`},
+ {`package t4; type t[P, Q interface{ m() }] int; var _ t[int, int]`, `t`, `t4.t[P₁, Q₂ interface{m()}]`},
+
+ // instantiated types must be sanitized
+ {`package g0; type t[P any] int; var x struct{ f t[int] }; var _ = x.f`, `x.f`, `g0.t[int]`},
+ }
+
+ for _, test := range tests {
+ info := Info{Types: make(map[syntax.Expr]TypeAndValue)}
+ name, _ := mayTypecheck(t, "TypesInfo", test.src, &info)
+
+ // look for expression type
+ var typ Type
+ for e, tv := range info.Types {
+ if ExprString(e) == test.expr {
+ typ = tv.Type
+ break
+ }
+ }
+ if typ == nil {
+ t.Errorf("package %s: no type found for %s", name, test.expr)
+ continue
+ }
+
+ // check that type is correct
+ if got := typ.String(); got != test.typ {
+ t.Errorf("package %s: got %s; want %s", name, got, test.typ)
+ }
+ }
+}
+
+func TestInferredInfo(t *testing.T) {
+ var tests = []struct {
+ src string
+ fun string
+ targs []string
+ sig string
+ }{
+ {`package p0; func f[T any](T); func _() { f(42) }`,
+ `f`,
+ []string{`int`},
+ `func(int)`,
+ },
+ {`package p1; func f[T any](T) T; func _() { f('@') }`,
+ `f`,
+ []string{`rune`},
+ `func(rune) rune`,
+ },
+ {`package p2; func f[T any](...T) T; func _() { f(0i) }`,
+ `f`,
+ []string{`complex128`},
+ `func(...complex128) complex128`,
+ },
+ {`package p3; func f[A, B, C any](A, *B, []C); func _() { f(1.2, new(string), []byte{}) }`,
+ `f`,
+ []string{`float64`, `string`, `byte`},
+ `func(float64, *string, []byte)`,
+ },
+ {`package p4; func f[A, B any](A, *B, ...[]B); func _() { f(1.2, new(byte)) }`,
+ `f`,
+ []string{`float64`, `byte`},
+ `func(float64, *byte, ...[]byte)`,
+ },
+
+ // we don't know how to translate these but we can type-check them
+ {`package q0; type T struct{}; func (T) m[P any](P); func _(x T) { x.m(42) }`,
+ `x.m`,
+ []string{`int`},
+ `func(int)`,
+ },
+ {`package q1; type T struct{}; func (T) m[P any](P) P; func _(x T) { x.m(42) }`,
+ `x.m`,
+ []string{`int`},
+ `func(int) int`,
+ },
+ {`package q2; type T struct{}; func (T) m[P any](...P) P; func _(x T) { x.m(42) }`,
+ `x.m`,
+ []string{`int`},
+ `func(...int) int`,
+ },
+ {`package q3; type T struct{}; func (T) m[A, B, C any](A, *B, []C); func _(x T) { x.m(1.2, new(string), []byte{}) }`,
+ `x.m`,
+ []string{`float64`, `string`, `byte`},
+ `func(float64, *string, []byte)`,
+ },
+ {`package q4; type T struct{}; func (T) m[A, B any](A, *B, ...[]B); func _(x T) { x.m(1.2, new(byte)) }`,
+ `x.m`,
+ []string{`float64`, `byte`},
+ `func(float64, *byte, ...[]byte)`,
+ },
+
+ {`package r0; type T[P any] struct{}; func (_ T[P]) m[Q any](Q); func _[P any](x T[P]) { x.m(42) }`,
+ `x.m`,
+ []string{`int`},
+ `func(int)`,
+ },
+ // TODO(gri) record method type parameters in syntax.FuncType so we can check this
+ // {`package r1; type T interface{ m[P any](P) }; func _(x T) { x.m(4.2) }`,
+ // `x.m`,
+ // []string{`float64`},
+ // `func(float64)`,
+ // },
+
+ {`package s1; func f[T any, P interface{type *T}](x T); func _(x string) { f(x) }`,
+ `f`,
+ []string{`string`, `*string`},
+ `func(x string)`,
+ },
+ {`package s2; func f[T any, P interface{type *T}](x []T); func _(x []int) { f(x) }`,
+ `f`,
+ []string{`int`, `*int`},
+ `func(x []int)`,
+ },
+ {`package s3; type C[T any] interface{type chan<- T}; func f[T any, P C[T]](x []T); func _(x []int) { f(x) }`,
+ `f`,
+ []string{`int`, `chan<- int`},
+ `func(x []int)`,
+ },
+ {`package s4; type C[T any] interface{type chan<- T}; func f[T any, P C[T], Q C[[]*P]](x []T); func _(x []int) { f(x) }`,
+ `f`,
+ []string{`int`, `chan<- int`, `chan<- []*chan<- int`},
+ `func(x []int)`,
+ },
+
+ {`package t1; func f[T any, P interface{type *T}]() T; func _() { _ = f[string] }`,
+ `f`,
+ []string{`string`, `*string`},
+ `func() string`,
+ },
+ {`package t2; type C[T any] interface{type chan<- T}; func f[T any, P C[T]]() []T; func _() { _ = f[int] }`,
+ `f`,
+ []string{`int`, `chan<- int`},
+ `func() []int`,
+ },
+ {`package t3; type C[T any] interface{type chan<- T}; func f[T any, P C[T], Q C[[]*P]]() []T; func _() { _ = f[int] }`,
+ `f`,
+ []string{`int`, `chan<- int`, `chan<- []*chan<- int`},
+ `func() []int`,
+ },
+ }
+
+ for _, test := range tests {
+ info := Info{Inferred: make(map[syntax.Expr]Inferred)}
+ name, err := mayTypecheck(t, "InferredInfo", test.src, &info)
+ if err != nil {
+ t.Errorf("package %s: %v", name, err)
+ continue
+ }
+
+ // look for inferred type arguments and signature
+ var targs []Type
+ var sig *Signature
+ for call, inf := range info.Inferred {
+ var fun syntax.Expr
+ switch x := call.(type) {
+ case *syntax.CallExpr:
+ fun = x.Fun
+ case *syntax.IndexExpr:
+ fun = x.X
+ default:
+ panic(fmt.Sprintf("unexpected call expression type %T", call))
+ }
+ if ExprString(fun) == test.fun {
+ targs = inf.Targs
+ sig = inf.Sig
+ break
+ }
+ }
+ if targs == nil {
+ t.Errorf("package %s: no inferred information found for %s", name, test.fun)
+ continue
+ }
+
+ // check that type arguments are correct
+ if len(targs) != len(test.targs) {
+ t.Errorf("package %s: got %d type arguments; want %d", name, len(targs), len(test.targs))
+ continue
+ }
+ for i, targ := range targs {
+ if got := targ.String(); got != test.targs[i] {
+ t.Errorf("package %s, %d. type argument: got %s; want %s", name, i, got, test.targs[i])
+ continue
+ }
+ }
+
+ // check that signature is correct
+ if got := sig.String(); got != test.sig {
+ t.Errorf("package %s: got %s; want %s", name, got, test.sig)
+ }
+ }
+}
+
+func TestDefsInfo(t *testing.T) {
+ var tests = []struct {
+ src string
+ obj string
+ want string
+ }{
+ {`package p0; const x = 42`, `x`, `const p0.x untyped int`},
+ {`package p1; const x int = 42`, `x`, `const p1.x int`},
+ {`package p2; var x int`, `x`, `var p2.x int`},
+ {`package p3; type x int`, `x`, `type p3.x int`},
+ {`package p4; func f()`, `f`, `func p4.f()`},
+
+ // generic types must be sanitized
+ // (need to use sufficiently nested types to provoke unexpanded types)
+ {`package g0; type t[P any] P; const x = t[int](42)`, `x`, `const g0.x g0.t[int]`},
+ {`package g1; type t[P any] P; var x = t[int](42)`, `x`, `var g1.x g1.t[int]`},
+ {`package g2; type t[P any] P; type x struct{ f t[int] }`, `x`, `type g2.x struct{f g2.t[int]}`},
+ {`package g3; type t[P any] P; func f(x struct{ f t[string] }); var g = f`, `g`, `var g3.g func(x struct{f g3.t[string]})`},
+ }
+
+ for _, test := range tests {
+ info := Info{
+ Defs: make(map[*syntax.Name]Object),
+ }
+ name := mustTypecheck(t, "DefsInfo", test.src, &info)
+
+ // find object
+ var def Object
+ for id, obj := range info.Defs {
+ if id.Value == test.obj {
+ def = obj
+ break
+ }
+ }
+ if def == nil {
+ t.Errorf("package %s: %s not found", name, test.obj)
+ continue
+ }
+
+ if got := def.String(); got != test.want {
+ t.Errorf("package %s: got %s; want %s", name, got, test.want)
+ }
+ }
+}
+
+func TestUsesInfo(t *testing.T) {
+ var tests = []struct {
+ src string
+ obj string
+ want string
+ }{
+ {`package p0; func _() { _ = x }; const x = 42`, `x`, `const p0.x untyped int`},
+ {`package p1; func _() { _ = x }; const x int = 42`, `x`, `const p1.x int`},
+ {`package p2; func _() { _ = x }; var x int`, `x`, `var p2.x int`},
+ {`package p3; func _() { type _ x }; type x int`, `x`, `type p3.x int`},
+ {`package p4; func _() { _ = f }; func f()`, `f`, `func p4.f()`},
+
+ // generic types must be sanitized
+ // (need to use sufficiently nested types to provoke unexpanded types)
+ {`package g0; func _() { _ = x }; type t[P any] P; const x = t[int](42)`, `x`, `const g0.x g0.t[int]`},
+ {`package g1; func _() { _ = x }; type t[P any] P; var x = t[int](42)`, `x`, `var g1.x g1.t[int]`},
+ {`package g2; func _() { type _ x }; type t[P any] P; type x struct{ f t[int] }`, `x`, `type g2.x struct{f g2.t[int]}`},
+ {`package g3; func _() { _ = f }; type t[P any] P; func f(x struct{ f t[string] })`, `f`, `func g3.f(x struct{f g3.t[string]})`},
+ }
+
+ for _, test := range tests {
+ info := Info{
+ Uses: make(map[*syntax.Name]Object),
+ }
+ name := mustTypecheck(t, "UsesInfo", test.src, &info)
+
+ // find object
+ var use Object
+ for id, obj := range info.Uses {
+ if id.Value == test.obj {
+ use = obj
+ break
+ }
+ }
+ if use == nil {
+ t.Errorf("package %s: %s not found", name, test.obj)
+ continue
+ }
+
+ if got := use.String(); got != test.want {
+ t.Errorf("package %s: got %s; want %s", name, got, test.want)
+ }
+ }
+}
+
+func TestImplicitsInfo(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ var tests = []struct {
+ src string
+ want string
+ }{
+ {`package p2; import . "fmt"; var _ = Println`, ""}, // no Implicits entry
+ {`package p0; import local "fmt"; var _ = local.Println`, ""}, // no Implicits entry
+ {`package p1; import "fmt"; var _ = fmt.Println`, "importSpec: package fmt"},
+
+ {`package p3; func f(x interface{}) { switch x.(type) { case int: } }`, ""}, // no Implicits entry
+ {`package p4; func f(x interface{}) { switch t := x.(type) { case int: _ = t } }`, "caseClause: var t int"},
+ {`package p5; func f(x interface{}) { switch t := x.(type) { case int, uint: _ = t } }`, "caseClause: var t interface{}"},
+ {`package p6; func f(x interface{}) { switch t := x.(type) { default: _ = t } }`, "caseClause: var t interface{}"},
+
+ {`package p7; func f(x int) {}`, ""}, // no Implicits entry
+ {`package p8; func f(int) {}`, "field: var int"},
+ {`package p9; func f() (complex64) { return 0 }`, "field: var complex64"},
+ {`package p10; type T struct{}; func (*T) f() {}`, "field: var *p10.T"},
+ }
+
+ for _, test := range tests {
+ info := Info{
+ Implicits: make(map[syntax.Node]Object),
+ }
+ name := mustTypecheck(t, "ImplicitsInfo", test.src, &info)
+
+ // the test cases expect at most one Implicits entry
+ if len(info.Implicits) > 1 {
+ t.Errorf("package %s: %d Implicits entries found", name, len(info.Implicits))
+ continue
+ }
+
+ // extract Implicits entry, if any
+ var got string
+ for n, obj := range info.Implicits {
+ switch x := n.(type) {
+ case *syntax.ImportDecl:
+ got = "importSpec"
+ case *syntax.CaseClause:
+ got = "caseClause"
+ case *syntax.Field:
+ got = "field"
+ default:
+ t.Fatalf("package %s: unexpected %T", name, x)
+ }
+ got += ": " + obj.String()
+ }
+
+ // verify entry
+ if got != test.want {
+ t.Errorf("package %s: got %q; want %q", name, got, test.want)
+ }
+ }
+}
+
+func predString(tv TypeAndValue) string {
+ var buf bytes.Buffer
+ pred := func(b bool, s string) {
+ if b {
+ if buf.Len() > 0 {
+ buf.WriteString(", ")
+ }
+ buf.WriteString(s)
+ }
+ }
+
+ pred(tv.IsVoid(), "void")
+ pred(tv.IsType(), "type")
+ pred(tv.IsBuiltin(), "builtin")
+ pred(tv.IsValue() && tv.Value != nil, "const")
+ pred(tv.IsValue() && tv.Value == nil, "value")
+ pred(tv.IsNil(), "nil")
+ pred(tv.Addressable(), "addressable")
+ pred(tv.Assignable(), "assignable")
+ pred(tv.HasOk(), "hasOk")
+
+ if buf.Len() == 0 {
+ return "invalid"
+ }
+ return buf.String()
+}
+
+func TestPredicatesInfo(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ var tests = []struct {
+ src string
+ expr string
+ pred string
+ }{
+ // void
+ {`package n0; func f() { f() }`, `f()`, `void`},
+
+ // types
+ {`package t0; type _ int`, `int`, `type`},
+ {`package t1; type _ []int`, `[]int`, `type`},
+ {`package t2; type _ func()`, `func()`, `type`},
+ {`package t3; type _ func(int)`, `int`, `type`},
+ {`package t3; type _ func(...int)`, `...int`, `type`},
+
+ // built-ins
+ {`package b0; var _ = len("")`, `len`, `builtin`},
+ {`package b1; var _ = (len)("")`, `(len)`, `builtin`},
+
+ // constants
+ {`package c0; var _ = 42`, `42`, `const`},
+ {`package c1; var _ = "foo" + "bar"`, `"foo" + "bar"`, `const`},
+ {`package c2; const (i = 1i; _ = i)`, `i`, `const`},
+
+ // values
+ {`package v0; var (a, b int; _ = a + b)`, `a + b`, `value`},
+ {`package v1; var _ = &[]int{1}`, `([]int literal)`, `value`},
+ {`package v2; var _ = func(){}`, `(func() literal)`, `value`},
+ {`package v4; func f() { _ = f }`, `f`, `value`},
+ {`package v3; var _ *int = nil`, `nil`, `value, nil`},
+ {`package v3; var _ *int = (nil)`, `(nil)`, `value, nil`},
+
+ // addressable (and thus assignable) operands
+ {`package a0; var (x int; _ = x)`, `x`, `value, addressable, assignable`},
+ {`package a1; var (p *int; _ = *p)`, `*p`, `value, addressable, assignable`},
+ {`package a2; var (s []int; _ = s[0])`, `s[0]`, `value, addressable, assignable`},
+ {`package a3; var (s struct{f int}; _ = s.f)`, `s.f`, `value, addressable, assignable`},
+ {`package a4; var (a [10]int; _ = a[0])`, `a[0]`, `value, addressable, assignable`},
+ {`package a5; func _(x int) { _ = x }`, `x`, `value, addressable, assignable`},
+ {`package a6; func _()(x int) { _ = x; return }`, `x`, `value, addressable, assignable`},
+ {`package a7; type T int; func (x T) _() { _ = x }`, `x`, `value, addressable, assignable`},
+ // composite literals are not addressable
+
+ // assignable but not addressable values
+ {`package s0; var (m map[int]int; _ = m[0])`, `m[0]`, `value, assignable, hasOk`},
+ {`package s1; var (m map[int]int; _, _ = m[0])`, `m[0]`, `value, assignable, hasOk`},
+
+ // hasOk expressions
+ {`package k0; var (ch chan int; _ = <-ch)`, `<-ch`, `value, hasOk`},
+ {`package k1; var (ch chan int; _, _ = <-ch)`, `<-ch`, `value, hasOk`},
+
+ // missing entries
+ // - package names are collected in the Uses map
+ // - identifiers being declared are collected in the Defs map
+ {`package m0; import "os"; func _() { _ = os.Stdout }`, `os`, `<missing>`},
+ {`package m1; import p "os"; func _() { _ = p.Stdout }`, `p`, `<missing>`},
+ {`package m2; const c = 0`, `c`, `<missing>`},
+ {`package m3; type T int`, `T`, `<missing>`},
+ {`package m4; var v int`, `v`, `<missing>`},
+ {`package m5; func f() {}`, `f`, `<missing>`},
+ {`package m6; func _(x int) {}`, `x`, `<missing>`},
+ {`package m6; func _()(x int) { return }`, `x`, `<missing>`},
+ {`package m6; type T int; func (x T) _() {}`, `x`, `<missing>`},
+ }
+
+ for _, test := range tests {
+ info := Info{Types: make(map[syntax.Expr]TypeAndValue)}
+ name := mustTypecheck(t, "PredicatesInfo", test.src, &info)
+
+ // look for expression predicates
+ got := "<missing>"
+ for e, tv := range info.Types {
+ //println(name, ExprString(e))
+ if ExprString(e) == test.expr {
+ got = predString(tv)
+ break
+ }
+ }
+
+ if got != test.pred {
+ t.Errorf("package %s: got %s; want %s", name, got, test.pred)
+ }
+ }
+}
+
+func TestScopesInfo(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ var tests = []struct {
+ src string
+ scopes []string // list of scope descriptors of the form kind:varlist
+ }{
+ {`package p0`, []string{
+ "file:",
+ }},
+ {`package p1; import ( "fmt"; m "math"; _ "os" ); var ( _ = fmt.Println; _ = m.Pi )`, []string{
+ "file:fmt m",
+ }},
+ {`package p2; func _() {}`, []string{
+ "file:", "func:",
+ }},
+ {`package p3; func _(x, y int) {}`, []string{
+ "file:", "func:x y",
+ }},
+ {`package p4; func _(x, y int) { x, z := 1, 2; _ = z }`, []string{
+ "file:", "func:x y z", // redeclaration of x
+ }},
+ {`package p5; func _(x, y int) (u, _ int) { return }`, []string{
+ "file:", "func:u x y",
+ }},
+ {`package p6; func _() { { var x int; _ = x } }`, []string{
+ "file:", "func:", "block:x",
+ }},
+ {`package p7; func _() { if true {} }`, []string{
+ "file:", "func:", "if:", "block:",
+ }},
+ {`package p8; func _() { if x := 0; x < 0 { y := x; _ = y } }`, []string{
+ "file:", "func:", "if:x", "block:y",
+ }},
+ {`package p9; func _() { switch x := 0; x {} }`, []string{
+ "file:", "func:", "switch:x",
+ }},
+ {`package p10; func _() { switch x := 0; x { case 1: y := x; _ = y; default: }}`, []string{
+ "file:", "func:", "switch:x", "case:y", "case:",
+ }},
+ {`package p11; func _(t interface{}) { switch t.(type) {} }`, []string{
+ "file:", "func:t", "switch:",
+ }},
+ {`package p12; func _(t interface{}) { switch t := t; t.(type) {} }`, []string{
+ "file:", "func:t", "switch:t",
+ }},
+ {`package p13; func _(t interface{}) { switch x := t.(type) { case int: _ = x } }`, []string{
+ "file:", "func:t", "switch:", "case:x", // x implicitly declared
+ }},
+ {`package p14; func _() { select{} }`, []string{
+ "file:", "func:",
+ }},
+ {`package p15; func _(c chan int) { select{ case <-c: } }`, []string{
+ "file:", "func:c", "select:",
+ }},
+ {`package p16; func _(c chan int) { select{ case i := <-c: x := i; _ = x} }`, []string{
+ "file:", "func:c", "select:i x",
+ }},
+ {`package p17; func _() { for{} }`, []string{
+ "file:", "func:", "for:", "block:",
+ }},
+ {`package p18; func _(n int) { for i := 0; i < n; i++ { _ = i } }`, []string{
+ "file:", "func:n", "for:i", "block:",
+ }},
+ {`package p19; func _(a []int) { for i := range a { _ = i} }`, []string{
+ "file:", "func:a", "for:i", "block:",
+ }},
+ {`package p20; var s int; func _(a []int) { for i, x := range a { s += x; _ = i } }`, []string{
+ "file:", "func:a", "for:i x", "block:",
+ }},
+ }
+
+ for _, test := range tests {
+ info := Info{Scopes: make(map[syntax.Node]*Scope)}
+ name := mustTypecheck(t, "ScopesInfo", test.src, &info)
+
+ // number of scopes must match
+ if len(info.Scopes) != len(test.scopes) {
+ t.Errorf("package %s: got %d scopes; want %d", name, len(info.Scopes), len(test.scopes))
+ }
+
+ // scope descriptions must match
+ for node, scope := range info.Scopes {
+ var kind string
+ switch node.(type) {
+ case *syntax.File:
+ kind = "file"
+ case *syntax.FuncType:
+ kind = "func"
+ case *syntax.BlockStmt:
+ kind = "block"
+ case *syntax.IfStmt:
+ kind = "if"
+ case *syntax.SwitchStmt:
+ kind = "switch"
+ case *syntax.SelectStmt:
+ kind = "select"
+ case *syntax.CaseClause:
+ kind = "case"
+ case *syntax.CommClause:
+ kind = "comm"
+ case *syntax.ForStmt:
+ kind = "for"
+ default:
+ kind = fmt.Sprintf("%T", node)
+ }
+
+ // look for matching scope description
+ desc := kind + ":" + strings.Join(scope.Names(), " ")
+ found := false
+ for _, d := range test.scopes {
+ if desc == d {
+ found = true
+ break
+ }
+ }
+ if !found {
+ t.Errorf("package %s: no matching scope found for %s", name, desc)
+ }
+ }
+ }
+}
+
+func TestInitOrderInfo(t *testing.T) {
+ var tests = []struct {
+ src string
+ inits []string
+ }{
+ {`package p0; var (x = 1; y = x)`, []string{
+ "x = 1", "y = x",
+ }},
+ {`package p1; var (a = 1; b = 2; c = 3)`, []string{
+ "a = 1", "b = 2", "c = 3",
+ }},
+ {`package p2; var (a, b, c = 1, 2, 3)`, []string{
+ "a = 1", "b = 2", "c = 3",
+ }},
+ {`package p3; var _ = f(); func f() int { return 1 }`, []string{
+ "_ = f()", // blank var
+ }},
+ {`package p4; var (a = 0; x = y; y = z; z = 0)`, []string{
+ "a = 0", "z = 0", "y = z", "x = y",
+ }},
+ {`package p5; var (a, _ = m[0]; m map[int]string)`, []string{
+ "a, _ = m[0]", // blank var
+ }},
+ {`package p6; var a, b = f(); func f() (_, _ int) { return z, z }; var z = 0`, []string{
+ "z = 0", "a, b = f()",
+ }},
+ {`package p7; var (a = func() int { return b }(); b = 1)`, []string{
+ "b = 1", "a = (func() int literal)()",
+ }},
+ {`package p8; var (a, b = func() (_, _ int) { return c, c }(); c = 1)`, []string{
+ "c = 1", "a, b = (func() (_, _ int) literal)()",
+ }},
+ {`package p9; type T struct{}; func (T) m() int { _ = y; return 0 }; var x, y = T.m, 1`, []string{
+ "y = 1", "x = T.m",
+ }},
+ {`package p10; var (d = c + b; a = 0; b = 0; c = 0)`, []string{
+ "a = 0", "b = 0", "c = 0", "d = c + b",
+ }},
+ {`package p11; var (a = e + c; b = d + c; c = 0; d = 0; e = 0)`, []string{
+ "c = 0", "d = 0", "b = d + c", "e = 0", "a = e + c",
+ }},
+ // emit an initializer for n:1 initializations only once (not for each node
+ // on the lhs which may appear in different order in the dependency graph)
+ {`package p12; var (a = x; b = 0; x, y = m[0]; m map[int]int)`, []string{
+ "b = 0", "x, y = m[0]", "a = x",
+ }},
+ // test case from spec section on package initialization
+ {`package p12
+
+ var (
+ a = c + b
+ b = f()
+ c = f()
+ d = 3
+ )
+
+ func f() int {
+ d++
+ return d
+ }`, []string{
+ "d = 3", "b = f()", "c = f()", "a = c + b",
+ }},
+ // test case for issue 7131
+ {`package main
+
+ var counter int
+ func next() int { counter++; return counter }
+
+ var _ = makeOrder()
+ func makeOrder() []int { return []int{f, b, d, e, c, a} }
+
+ var a = next()
+ var b, c = next(), next()
+ var d, e, f = next(), next(), next()
+ `, []string{
+ "a = next()", "b = next()", "c = next()", "d = next()", "e = next()", "f = next()", "_ = makeOrder()",
+ }},
+ // test case for issue 10709
+ {`package p13
+
+ var (
+ v = t.m()
+ t = makeT(0)
+ )
+
+ type T struct{}
+
+ func (T) m() int { return 0 }
+
+ func makeT(n int) T {
+ if n > 0 {
+ return makeT(n-1)
+ }
+ return T{}
+ }`, []string{
+ "t = makeT(0)", "v = t.m()",
+ }},
+ // test case for issue 10709: same as test before, but variable decls swapped
+ {`package p14
+
+ var (
+ t = makeT(0)
+ v = t.m()
+ )
+
+ type T struct{}
+
+ func (T) m() int { return 0 }
+
+ func makeT(n int) T {
+ if n > 0 {
+ return makeT(n-1)
+ }
+ return T{}
+ }`, []string{
+ "t = makeT(0)", "v = t.m()",
+ }},
+ // another candidate possibly causing problems with issue 10709
+ {`package p15
+
+ var y1 = f1()
+
+ func f1() int { return g1() }
+ func g1() int { f1(); return x1 }
+
+ var x1 = 0
+
+ var y2 = f2()
+
+ func f2() int { return g2() }
+ func g2() int { return x2 }
+
+ var x2 = 0`, []string{
+ "x1 = 0", "y1 = f1()", "x2 = 0", "y2 = f2()",
+ }},
+ }
+
+ for _, test := range tests {
+ info := Info{}
+ name := mustTypecheck(t, "InitOrderInfo", test.src, &info)
+
+ // number of initializers must match
+ if len(info.InitOrder) != len(test.inits) {
+ t.Errorf("package %s: got %d initializers; want %d", name, len(info.InitOrder), len(test.inits))
+ continue
+ }
+
+ // initializers must match
+ for i, want := range test.inits {
+ got := info.InitOrder[i].String()
+ if got != want {
+ t.Errorf("package %s, init %d: got %s; want %s", name, i, got, want)
+ continue
+ }
+ }
+ }
+}
+
+func TestMultiFileInitOrder(t *testing.T) {
+ mustParse := func(src string) *syntax.File {
+ f, err := parseSrc("main", src)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return f
+ }
+
+ fileA := mustParse(`package main; var a = 1`)
+ fileB := mustParse(`package main; var b = 2`)
+
+ // The initialization order must not depend on the parse
+ // order of the files, only on the presentation order to
+ // the type-checker.
+ for _, test := range []struct {
+ files []*syntax.File
+ want string
+ }{
+ {[]*syntax.File{fileA, fileB}, "[a = 1 b = 2]"},
+ {[]*syntax.File{fileB, fileA}, "[b = 2 a = 1]"},
+ } {
+ var info Info
+ if _, err := new(Config).Check("main", test.files, &info); err != nil {
+ t.Fatal(err)
+ }
+ if got := fmt.Sprint(info.InitOrder); got != test.want {
+ t.Fatalf("got %s; want %s", got, test.want)
+ }
+ }
+}
+
+func TestFiles(t *testing.T) {
+ var sources = []string{
+ "package p; type T struct{}; func (T) m1() {}",
+ "package p; func (T) m2() {}; var x interface{ m1(); m2() } = T{}",
+ "package p; func (T) m3() {}; var y interface{ m1(); m2(); m3() } = T{}",
+ "package p",
+ }
+
+ var conf Config
+ pkg := NewPackage("p", "p")
+ var info Info
+ check := NewChecker(&conf, pkg, &info)
+
+ for i, src := range sources {
+ filename := fmt.Sprintf("sources%d", i)
+ f, err := parseSrc(filename, src)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := check.Files([]*syntax.File{f}); err != nil {
+ t.Error(err)
+ }
+ }
+
+ // check InitOrder is [x y]
+ var vars []string
+ for _, init := range info.InitOrder {
+ for _, v := range init.Lhs {
+ vars = append(vars, v.Name())
+ }
+ }
+ if got, want := fmt.Sprint(vars), "[x y]"; got != want {
+ t.Errorf("InitOrder == %s, want %s", got, want)
+ }
+}
+
+type testImporter map[string]*Package
+
+func (m testImporter) Import(path string) (*Package, error) {
+ if pkg := m[path]; pkg != nil {
+ return pkg, nil
+ }
+ return nil, fmt.Errorf("package %q not found", path)
+}
+
+func TestSelection(t *testing.T) {
+ t.Skip("requires fixes around source positions")
+
+ selections := make(map[*syntax.SelectorExpr]*Selection)
+
+ imports := make(testImporter)
+ conf := Config{Importer: imports}
+ makePkg := func(path, src string) {
+ f, err := parseSrc(path+".go", src)
+ if err != nil {
+ t.Fatal(err)
+ }
+ pkg, err := conf.Check(path, []*syntax.File{f}, &Info{Selections: selections})
+ if err != nil {
+ t.Fatal(err)
+ }
+ imports[path] = pkg
+ }
+
+ const libSrc = `
+package lib
+type T float64
+const C T = 3
+var V T
+func F() {}
+func (T) M() {}
+`
+ const mainSrc = `
+package main
+import "lib"
+
+type A struct {
+ *B
+ C
+}
+
+type B struct {
+ b int
+}
+
+func (B) f(int)
+
+type C struct {
+ c int
+}
+
+func (C) g()
+func (*C) h()
+
+func main() {
+ // qualified identifiers
+ var _ lib.T
+ _ = lib.C
+ _ = lib.F
+ _ = lib.V
+ _ = lib.T.M
+
+ // fields
+ _ = A{}.B
+ _ = new(A).B
+
+ _ = A{}.C
+ _ = new(A).C
+
+ _ = A{}.b
+ _ = new(A).b
+
+ _ = A{}.c
+ _ = new(A).c
+
+ // methods
+ _ = A{}.f
+ _ = new(A).f
+ _ = A{}.g
+ _ = new(A).g
+ _ = new(A).h
+
+ _ = B{}.f
+ _ = new(B).f
+
+ _ = C{}.g
+ _ = new(C).g
+ _ = new(C).h
+
+ // method expressions
+ _ = A.f
+ _ = (*A).f
+ _ = B.f
+ _ = (*B).f
+}`
+
+ wantOut := map[string][2]string{
+ "lib.T.M": {"method expr (lib.T) M(lib.T)", ".[0]"},
+
+ "A{}.B": {"field (main.A) B *main.B", ".[0]"},
+ "new(A).B": {"field (*main.A) B *main.B", "->[0]"},
+ "A{}.C": {"field (main.A) C main.C", ".[1]"},
+ "new(A).C": {"field (*main.A) C main.C", "->[1]"},
+ "A{}.b": {"field (main.A) b int", "->[0 0]"},
+ "new(A).b": {"field (*main.A) b int", "->[0 0]"},
+ "A{}.c": {"field (main.A) c int", ".[1 0]"},
+ "new(A).c": {"field (*main.A) c int", "->[1 0]"},
+
+ "A{}.f": {"method (main.A) f(int)", "->[0 0]"},
+ "new(A).f": {"method (*main.A) f(int)", "->[0 0]"},
+ "A{}.g": {"method (main.A) g()", ".[1 0]"},
+ "new(A).g": {"method (*main.A) g()", "->[1 0]"},
+ "new(A).h": {"method (*main.A) h()", "->[1 1]"}, // TODO(gri) should this report .[1 1] ?
+ "B{}.f": {"method (main.B) f(int)", ".[0]"},
+ "new(B).f": {"method (*main.B) f(int)", "->[0]"},
+ "C{}.g": {"method (main.C) g()", ".[0]"},
+ "new(C).g": {"method (*main.C) g()", "->[0]"},
+ "new(C).h": {"method (*main.C) h()", "->[1]"}, // TODO(gri) should this report .[1] ?
+
+ "A.f": {"method expr (main.A) f(main.A, int)", "->[0 0]"},
+ "(*A).f": {"method expr (*main.A) f(*main.A, int)", "->[0 0]"},
+ "B.f": {"method expr (main.B) f(main.B, int)", ".[0]"},
+ "(*B).f": {"method expr (*main.B) f(*main.B, int)", "->[0]"},
+ }
+
+ makePkg("lib", libSrc)
+ makePkg("main", mainSrc)
+
+ for e, sel := range selections {
+ _ = sel.String() // assertion: must not panic
+
+ unimplemented()
+ _ = e
+ // start := fset.Position(e.Pos()).Offset
+ // end := fset.Position(e.End()).Offset
+ // syntax := mainSrc[start:end] // (all SelectorExprs are in main, not lib)
+
+ direct := "."
+ if sel.Indirect() {
+ direct = "->"
+ }
+ got := [2]string{
+ sel.String(),
+ fmt.Sprintf("%s%v", direct, sel.Index()),
+ }
+ unimplemented()
+ _ = got
+ // want := wantOut[syntax]
+ // if want != got {
+ // t.Errorf("%s: got %q; want %q", syntax, got, want)
+ // }
+ // delete(wantOut, syntax)
+
+ // We must explicitly assert properties of the
+ // Signature's receiver since it doesn't participate
+ // in Identical() or String().
+ sig, _ := sel.Type().(*Signature)
+ if sel.Kind() == MethodVal {
+ got := sig.Recv().Type()
+ want := sel.Recv()
+ if !Identical(got, want) {
+ unimplemented()
+ // t.Errorf("%s: Recv() = %s, want %s", syntax, got, want)
+ }
+ } else if sig != nil && sig.Recv() != nil {
+ t.Errorf("%s: signature has receiver %s", sig, sig.Recv().Type())
+ }
+ }
+ // Assert that all wantOut entries were used exactly once.
+ for syntax := range wantOut {
+ t.Errorf("no syntax.Selection found with syntax %q", syntax)
+ }
+}
+
+func TestIssue8518(t *testing.T) {
+ imports := make(testImporter)
+ conf := Config{
+ Error: func(err error) { t.Log(err) }, // don't exit after first error
+ Importer: imports,
+ }
+ makePkg := func(path, src string) {
+ f, err := parseSrc(path, src)
+ if err != nil {
+ t.Fatal(err)
+ }
+ pkg, _ := conf.Check(path, []*syntax.File{f}, nil) // errors logged via conf.Error
+ imports[path] = pkg
+ }
+
+ const libSrc = `
+package a
+import "missing"
+const C1 = foo
+const C2 = missing.C
+`
+
+ const mainSrc = `
+package main
+import "a"
+var _ = a.C1
+var _ = a.C2
+`
+
+ makePkg("a", libSrc)
+ makePkg("main", mainSrc) // don't crash when type-checking this package
+}
+
+func TestLookupFieldOrMethod(t *testing.T) {
+ // Test cases assume a lookup of the form a.f or x.f, where a stands for an
+ // addressable value, and x for a non-addressable value (even though a variable
+ // for ease of test case writing).
+ var tests = []struct {
+ src string
+ found bool
+ index []int
+ indirect bool
+ }{
+ // field lookups
+ {"var x T; type T struct{}", false, nil, false},
+ {"var x T; type T struct{ f int }", true, []int{0}, false},
+ {"var x T; type T struct{ a, b, f, c int }", true, []int{2}, false},
+
+ // method lookups
+ {"var a T; type T struct{}; func (T) f() {}", true, []int{0}, false},
+ {"var a *T; type T struct{}; func (T) f() {}", true, []int{0}, true},
+ {"var a T; type T struct{}; func (*T) f() {}", true, []int{0}, false},
+ {"var a *T; type T struct{}; func (*T) f() {}", true, []int{0}, true}, // TODO(gri) should this report indirect = false?
+
+ // collisions
+ {"type ( E1 struct{ f int }; E2 struct{ f int }; x struct{ E1; *E2 })", false, []int{1, 0}, false},
+ {"type ( E1 struct{ f int }; E2 struct{}; x struct{ E1; *E2 }); func (E2) f() {}", false, []int{1, 0}, false},
+
+ // outside methodset
+ // (*T).f method exists, but value of type T is not addressable
+ {"var x T; type T struct{}; func (*T) f() {}", false, nil, true},
+ }
+
+ for _, test := range tests {
+ pkg, err := pkgFor("test", "package p;"+test.src, nil)
+ if err != nil {
+ t.Errorf("%s: incorrect test case: %s", test.src, err)
+ continue
+ }
+
+ obj := pkg.Scope().Lookup("a")
+ if obj == nil {
+ if obj = pkg.Scope().Lookup("x"); obj == nil {
+ t.Errorf("%s: incorrect test case - no object a or x", test.src)
+ continue
+ }
+ }
+
+ f, index, indirect := LookupFieldOrMethod(obj.Type(), obj.Name() == "a", pkg, "f")
+ if (f != nil) != test.found {
+ if f == nil {
+ t.Errorf("%s: got no object; want one", test.src)
+ } else {
+ t.Errorf("%s: got object = %v; want none", test.src, f)
+ }
+ }
+ if !sameSlice(index, test.index) {
+ t.Errorf("%s: got index = %v; want %v", test.src, index, test.index)
+ }
+ if indirect != test.indirect {
+ t.Errorf("%s: got indirect = %v; want %v", test.src, indirect, test.indirect)
+ }
+ }
+}
+
+func sameSlice(a, b []int) bool {
+ if len(a) != len(b) {
+ return false
+ }
+ for i, x := range a {
+ if x != b[i] {
+ return false
+ }
+ }
+ return true
+}
+
+// TestScopeLookupParent ensures that (*Scope).LookupParent returns
+// the correct result at various positions within the source.
+func TestScopeLookupParent(t *testing.T) {
+ imports := make(testImporter)
+ conf := Config{Importer: imports}
+ var info Info
+ makePkg := func(path, src string) {
+ f, err := parseSrc(path, src)
+ if err != nil {
+ t.Fatal(err)
+ }
+ imports[path], err = conf.Check(path, []*syntax.File{f}, &info)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ makePkg("lib", "package lib; var X int")
+ // Each /*name=kind:line*/ comment makes the test look up the
+ // name at that point and checks that it resolves to a decl of
+ // the specified kind and line number. "undef" means undefined.
+ mainSrc := `
+/*lib=pkgname:5*/ /*X=var:1*/ /*Pi=const:8*/ /*T=typename:9*/ /*Y=var:10*/ /*F=func:12*/
+package main
+
+import "lib"
+import . "lib"
+
+const Pi = 3.1415
+type T struct{}
+var Y, _ = lib.X, X
+
+func F(){
+ const pi, e = 3.1415, /*pi=undef*/ 2.71828 /*pi=const:13*/ /*e=const:13*/
+ type /*t=undef*/ t /*t=typename:14*/ *t
+ print(Y) /*Y=var:10*/
+ x, Y := Y, /*x=undef*/ /*Y=var:10*/ Pi /*x=var:16*/ /*Y=var:16*/ ; _ = x; _ = Y
+ var F = /*F=func:12*/ F /*F=var:17*/ ; _ = F
+
+ var a []int
+ for i, x := range /*i=undef*/ /*x=var:16*/ a /*i=var:20*/ /*x=var:20*/ { _ = i; _ = x }
+
+ var i interface{}
+ switch y := i.(type) { /*y=undef*/
+ case /*y=undef*/ int /*y=var:23*/ :
+ case float32, /*y=undef*/ float64 /*y=var:23*/ :
+ default /*y=var:23*/:
+ println(y)
+ }
+ /*y=undef*/
+
+ switch int := i.(type) {
+ case /*int=typename:0*/ int /*int=var:31*/ :
+ println(int)
+ default /*int=var:31*/ :
+ }
+}
+/*main=undef*/
+`
+
+ info.Uses = make(map[*syntax.Name]Object)
+ makePkg("main", mainSrc)
+ mainScope := imports["main"].Scope()
+
+ rx := regexp.MustCompile(`^/\*(\w*)=([\w:]*)\*/$`)
+
+ base := syntax.NewFileBase("main")
+ syntax.CommentsDo(strings.NewReader(mainSrc), func(line, col uint, text string) {
+ pos := syntax.MakePos(base, line, col)
+
+ // Syntax errors are not comments.
+ if text[0] != '/' {
+ t.Errorf("%s: %s", pos, text)
+ return
+ }
+
+ // Parse the assertion in the comment.
+ m := rx.FindStringSubmatch(text)
+ if m == nil {
+ t.Errorf("%s: bad comment: %s", pos, text)
+ return
+ }
+ name, want := m[1], m[2]
+
+ // Look up the name in the innermost enclosing scope.
+ inner := mainScope.Innermost(pos)
+ if inner == nil {
+ t.Errorf("%s: at %s: can't find innermost scope", pos, text)
+ return
+ }
+ got := "undef"
+ if _, obj := inner.LookupParent(name, pos); obj != nil {
+ kind := strings.ToLower(strings.TrimPrefix(reflect.TypeOf(obj).String(), "*types2."))
+ got = fmt.Sprintf("%s:%d", kind, obj.Pos().Line())
+ }
+ if got != want {
+ t.Errorf("%s: at %s: %s resolved to %s, want %s", pos, text, name, got, want)
+ }
+ })
+
+ // Check that for each referring identifier,
+ // a lookup of its name on the innermost
+ // enclosing scope returns the correct object.
+
+ for id, wantObj := range info.Uses {
+ inner := mainScope.Innermost(id.Pos())
+ if inner == nil {
+ t.Errorf("%s: can't find innermost scope enclosing %q", id.Pos(), id.Value)
+ continue
+ }
+
+ // Exclude selectors and qualified identifiers---lexical
+ // refs only. (Ideally, we'd see if the AST parent is a
+ // SelectorExpr, but that requires PathEnclosingInterval
+ // from golang.org/x/tools/go/ast/astutil.)
+ if id.Value == "X" {
+ continue
+ }
+
+ _, gotObj := inner.LookupParent(id.Value, id.Pos())
+ if gotObj != wantObj {
+ t.Errorf("%s: got %v, want %v", id.Pos(), gotObj, wantObj)
+ continue
+ }
+ }
+}
+
+func TestIdentical_issue15173(t *testing.T) {
+ // Identical should allow nil arguments and be symmetric.
+ for _, test := range []struct {
+ x, y Type
+ want bool
+ }{
+ {Typ[Int], Typ[Int], true},
+ {Typ[Int], nil, false},
+ {nil, Typ[Int], false},
+ {nil, nil, true},
+ } {
+ if got := Identical(test.x, test.y); got != test.want {
+ t.Errorf("Identical(%v, %v) = %t", test.x, test.y, got)
+ }
+ }
+}
+
+func TestIssue15305(t *testing.T) {
+ const src = "package p; func f() int16; var _ = f(undef)"
+ f, err := parseSrc("issue15305.go", src)
+ if err != nil {
+ t.Fatal(err)
+ }
+ conf := Config{
+ Error: func(err error) {}, // allow errors
+ }
+ info := &Info{
+ Types: make(map[syntax.Expr]TypeAndValue),
+ }
+ conf.Check("p", []*syntax.File{f}, info) // ignore result
+ for e, tv := range info.Types {
+ if _, ok := e.(*syntax.CallExpr); ok {
+ if tv.Type != Typ[Int16] {
+ t.Errorf("CallExpr has type %v, want int16", tv.Type)
+ }
+ return
+ }
+ }
+ t.Errorf("CallExpr has no type")
+}
+
+// TestCompositeLitTypes verifies that Info.Types registers the correct
+// types for composite literal expressions and composite literal type
+// expressions.
+func TestCompositeLitTypes(t *testing.T) {
+ for _, test := range []struct {
+ lit, typ string
+ }{
+ {`[16]byte{}`, `[16]byte`},
+ {`[...]byte{}`, `[0]byte`}, // test for issue #14092
+ {`[...]int{1, 2, 3}`, `[3]int`}, // test for issue #14092
+ {`[...]int{90: 0, 98: 1, 2}`, `[100]int`}, // test for issue #14092
+ {`[]int{}`, `[]int`},
+ {`map[string]bool{"foo": true}`, `map[string]bool`},
+ {`struct{}{}`, `struct{}`},
+ {`struct{x, y int; z complex128}{}`, `struct{x int; y int; z complex128}`},
+ } {
+ f, err := parseSrc(test.lit, "package p; var _ = "+test.lit)
+ if err != nil {
+ t.Fatalf("%s: %v", test.lit, err)
+ }
+
+ info := &Info{
+ Types: make(map[syntax.Expr]TypeAndValue),
+ }
+ if _, err = new(Config).Check("p", []*syntax.File{f}, info); err != nil {
+ t.Fatalf("%s: %v", test.lit, err)
+ }
+
+ cmptype := func(x syntax.Expr, want string) {
+ tv, ok := info.Types[x]
+ if !ok {
+ t.Errorf("%s: no Types entry found", test.lit)
+ return
+ }
+ if tv.Type == nil {
+ t.Errorf("%s: type is nil", test.lit)
+ return
+ }
+ if got := tv.Type.String(); got != want {
+ t.Errorf("%s: got %v, want %s", test.lit, got, want)
+ }
+ }
+
+ // test type of composite literal expression
+ rhs := f.DeclList[0].(*syntax.VarDecl).Values
+ cmptype(rhs, test.typ)
+
+ // test type of composite literal type expression
+ cmptype(rhs.(*syntax.CompositeLit).Type, test.typ)
+ }
+}
+
+// TestObjectParents verifies that objects have parent scopes or not
+// as specified by the Object interface.
+func TestObjectParents(t *testing.T) {
+ const src = `
+package p
+
+const C = 0
+
+type T1 struct {
+ a, b int
+ T2
+}
+
+type T2 interface {
+ im1()
+ im2()
+}
+
+func (T1) m1() {}
+func (*T1) m2() {}
+
+func f(x int) { y := x; print(y) }
+`
+
+ f, err := parseSrc("src", src)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ info := &Info{
+ Defs: make(map[*syntax.Name]Object),
+ }
+ if _, err = new(Config).Check("p", []*syntax.File{f}, info); err != nil {
+ t.Fatal(err)
+ }
+
+ for ident, obj := range info.Defs {
+ if obj == nil {
+ // only package names and implicit vars have a nil object
+ // (in this test we only need to handle the package name)
+ if ident.Value != "p" {
+ t.Errorf("%v has nil object", ident)
+ }
+ continue
+ }
+
+ // struct fields, type-associated and interface methods
+ // have no parent scope
+ wantParent := true
+ switch obj := obj.(type) {
+ case *Var:
+ if obj.IsField() {
+ wantParent = false
+ }
+ case *Func:
+ if obj.Type().(*Signature).Recv() != nil { // method
+ wantParent = false
+ }
+ }
+
+ gotParent := obj.Parent() != nil
+ switch {
+ case gotParent && !wantParent:
+ t.Errorf("%v: want no parent, got %s", ident, obj.Parent())
+ case !gotParent && wantParent:
+ t.Errorf("%v: no parent found", ident)
+ }
+ }
+}
+
+// TestFailedImport tests that we don't get follow-on errors
+// elsewhere in a package due to failing to import a package.
+func TestFailedImport(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ const src = `
+package p
+
+import foo "go/types/thisdirectorymustnotexistotherwisethistestmayfail/foo" // should only see an error here
+
+const c = foo.C
+type T = foo.T
+var v T = c
+func f(x T) T { return foo.F(x) }
+`
+ f, err := parseSrc("src", src)
+ if err != nil {
+ t.Fatal(err)
+ }
+ files := []*syntax.File{f}
+
+ // type-check using all possible importers
+ for _, compiler := range []string{"gc", "gccgo", "source"} {
+ errcount := 0
+ conf := Config{
+ Error: func(err error) {
+ // we should only see the import error
+ if errcount > 0 || !strings.Contains(err.Error(), "could not import") {
+ t.Errorf("for %s importer, got unexpected error: %v", compiler, err)
+ }
+ errcount++
+ },
+ //Importer: importer.For(compiler, nil),
+ }
+
+ info := &Info{
+ Uses: make(map[*syntax.Name]Object),
+ }
+ pkg, _ := conf.Check("p", files, info)
+ if pkg == nil {
+ t.Errorf("for %s importer, type-checking failed to return a package", compiler)
+ continue
+ }
+
+ imports := pkg.Imports()
+ if len(imports) != 1 {
+ t.Errorf("for %s importer, got %d imports, want 1", compiler, len(imports))
+ continue
+ }
+ imp := imports[0]
+ if imp.Name() != "foo" {
+ t.Errorf(`for %s importer, got %q, want "foo"`, compiler, imp.Name())
+ continue
+ }
+
+ // verify that all uses of foo refer to the imported package foo (imp)
+ for ident, obj := range info.Uses {
+ if ident.Value == "foo" {
+ if obj, ok := obj.(*PkgName); ok {
+ if obj.Imported() != imp {
+ t.Errorf("%s resolved to %v; want %v", ident.Value, obj.Imported(), imp)
+ }
+ } else {
+ t.Errorf("%s resolved to %v; want package name", ident.Value, obj)
+ }
+ }
+ }
+ }
+}
diff --git a/src/cmd/compile/internal/types2/assignments.go b/src/cmd/compile/internal/types2/assignments.go
new file mode 100644
index 0000000000..93d3255686
--- /dev/null
+++ b/src/cmd/compile/internal/types2/assignments.go
@@ -0,0 +1,359 @@
+// UNREVIEWED
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements initialization and assignment checks.
+
+package types2
+
+import "cmd/compile/internal/syntax"
+
+// assignment reports whether x can be assigned to a variable of type T,
+// if necessary by attempting to convert untyped values to the appropriate
+// type. context describes the context in which the assignment takes place.
+// Use T == nil to indicate assignment to an untyped blank identifier.
+// x.mode is set to invalid if the assignment failed.
+func (check *Checker) assignment(x *operand, T Type, context string) {
+ check.singleValue(x)
+
+ switch x.mode {
+ case invalid:
+ return // error reported before
+ case constant_, variable, mapindex, value, commaok, commaerr:
+ // ok
+ default:
+ // we may get here because of other problems (issue #39634, crash 12)
+ check.errorf(x, "cannot assign %s to %s in %s", x, T, context)
+ return
+ }
+
+ if isUntyped(x.typ) {
+ target := T
+ // spec: "If an untyped constant is assigned to a variable of interface
+ // type or the blank identifier, the constant is first converted to type
+ // bool, rune, int, float64, complex128 or string respectively, depending
+ // on whether the value is a boolean, rune, integer, floating-point, complex,
+ // or string constant."
+ if T == nil || IsInterface(T) {
+ if T == nil && x.typ == Typ[UntypedNil] {
+ check.errorf(x, "use of untyped nil in %s", context)
+ x.mode = invalid
+ return
+ }
+ target = Default(x.typ)
+ }
+ check.convertUntyped(x, target)
+ if x.mode == invalid {
+ return
+ }
+ }
+ // x.typ is typed
+
+ // A generic (non-instantiated) function value cannot be assigned to a variable.
+ if sig := x.typ.Signature(); sig != nil && len(sig.tparams) > 0 {
+ check.errorf(x, "cannot use generic function %s without instantiation in %s", x, context)
+ }
+
+ // spec: "If a left-hand side is the blank identifier, any typed or
+ // non-constant value except for the predeclared identifier nil may
+ // be assigned to it."
+ if T == nil {
+ return
+ }
+
+ if reason := ""; !x.assignableTo(check, T, &reason) {
+ if reason != "" {
+ check.errorf(x, "cannot use %s as %s value in %s: %s", x, T, context, reason)
+ } else {
+ check.errorf(x, "cannot use %s as %s value in %s", x, T, context)
+ }
+ x.mode = invalid
+ }
+}
+
+func (check *Checker) initConst(lhs *Const, x *operand) {
+ if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] {
+ if lhs.typ == nil {
+ lhs.typ = Typ[Invalid]
+ }
+ return
+ }
+
+ // rhs must be a constant
+ if x.mode != constant_ {
+ check.errorf(x, "%s is not constant", x)
+ if lhs.typ == nil {
+ lhs.typ = Typ[Invalid]
+ }
+ return
+ }
+ assert(isConstType(x.typ))
+
+ // If the lhs doesn't have a type yet, use the type of x.
+ if lhs.typ == nil {
+ lhs.typ = x.typ
+ }
+
+ check.assignment(x, lhs.typ, "constant declaration")
+ if x.mode == invalid {
+ return
+ }
+
+ lhs.val = x.val
+}
+
+func (check *Checker) initVar(lhs *Var, x *operand, context string) Type {
+ if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] {
+ if lhs.typ == nil {
+ lhs.typ = Typ[Invalid]
+ }
+ return nil
+ }
+
+ // If the lhs doesn't have a type yet, use the type of x.
+ if lhs.typ == nil {
+ typ := x.typ
+ if isUntyped(typ) {
+ // convert untyped types to default types
+ if typ == Typ[UntypedNil] {
+ check.errorf(x, "use of untyped nil in %s", context)
+ lhs.typ = Typ[Invalid]
+ return nil
+ }
+ typ = Default(typ)
+ }
+ lhs.typ = typ
+ }
+
+ check.assignment(x, lhs.typ, context)
+ if x.mode == invalid {
+ return nil
+ }
+
+ return x.typ
+}
+
+func (check *Checker) assignVar(lhs syntax.Expr, x *operand) Type {
+ if x.mode == invalid || x.typ == Typ[Invalid] {
+ check.useLHS(lhs)
+ return nil
+ }
+
+ // Determine if the lhs is a (possibly parenthesized) identifier.
+ ident, _ := unparen(lhs).(*syntax.Name)
+
+ // Don't evaluate lhs if it is the blank identifier.
+ if ident != nil && ident.Value == "_" {
+ check.recordDef(ident, nil)
+ check.assignment(x, nil, "assignment to _ identifier")
+ if x.mode == invalid {
+ return nil
+ }
+ return x.typ
+ }
+
+ // If the lhs is an identifier denoting a variable v, this assignment
+ // is not a 'use' of v. Remember current value of v.used and restore
+ // after evaluating the lhs via check.expr.
+ var v *Var
+ var v_used bool
+ if ident != nil {
+ if obj := check.lookup(ident.Value); obj != nil {
+ // It's ok to mark non-local variables, but ignore variables
+ // from other packages to avoid potential race conditions with
+ // dot-imported variables.
+ if w, _ := obj.(*Var); w != nil && w.pkg == check.pkg {
+ v = w
+ v_used = v.used
+ }
+ }
+ }
+
+ var z operand
+ check.expr(&z, lhs)
+ if v != nil {
+ v.used = v_used // restore v.used
+ }
+
+ if z.mode == invalid || z.typ == Typ[Invalid] {
+ return nil
+ }
+
+ // spec: "Each left-hand side operand must be addressable, a map index
+ // expression, or the blank identifier. Operands may be parenthesized."
+ switch z.mode {
+ case invalid:
+ return nil
+ case variable, mapindex:
+ // ok
+ default:
+ if sel, ok := z.expr.(*syntax.SelectorExpr); ok {
+ var op operand
+ check.expr(&op, sel.X)
+ if op.mode == mapindex {
+ check.errorf(&z, "cannot assign to struct field %s in map", ExprString(z.expr))
+ return nil
+ }
+ }
+ check.errorf(&z, "cannot assign to %s", &z)
+ return nil
+ }
+
+ check.assignment(x, z.typ, "assignment")
+ if x.mode == invalid {
+ return nil
+ }
+
+ return x.typ
+}
+
+// If returnPos is valid, initVars is called to type-check the assignment of
+// return expressions, and returnPos is the position of the return statement.
+func (check *Checker) initVars(lhs []*Var, orig_rhs []syntax.Expr, returnPos syntax.Pos) {
+ rhs, commaOk := check.exprList(orig_rhs, len(lhs) == 2 && !returnPos.IsKnown())
+
+ if len(lhs) != len(rhs) {
+ // invalidate lhs
+ for _, obj := range lhs {
+ if obj.typ == nil {
+ obj.typ = Typ[Invalid]
+ }
+ }
+ // don't report an error if we already reported one
+ for _, x := range rhs {
+ if x.mode == invalid {
+ return
+ }
+ }
+ if returnPos.IsKnown() {
+ check.errorf(returnPos, "wrong number of return values (want %d, got %d)", len(lhs), len(rhs))
+ return
+ }
+ check.errorf(rhs[0], "cannot initialize %d variables with %d values", len(lhs), len(rhs))
+ return
+ }
+
+ context := "assignment"
+ if returnPos.IsKnown() {
+ context = "return statement"
+ }
+
+ if commaOk {
+ var a [2]Type
+ for i := range a {
+ a[i] = check.initVar(lhs[i], rhs[i], context)
+ }
+ check.recordCommaOkTypes(orig_rhs[0], a)
+ return
+ }
+
+ for i, lhs := range lhs {
+ check.initVar(lhs, rhs[i], context)
+ }
+}
+
+func (check *Checker) assignVars(lhs, orig_rhs []syntax.Expr) {
+ rhs, commaOk := check.exprList(orig_rhs, len(lhs) == 2)
+
+ if len(lhs) != len(rhs) {
+ check.useLHS(lhs...)
+ // don't report an error if we already reported one
+ for _, x := range rhs {
+ if x.mode == invalid {
+ return
+ }
+ }
+ check.errorf(rhs[0], "cannot assign %d values to %d variables", len(rhs), len(lhs))
+ return
+ }
+
+ if commaOk {
+ var a [2]Type
+ for i := range a {
+ a[i] = check.assignVar(lhs[i], rhs[i])
+ }
+ check.recordCommaOkTypes(orig_rhs[0], a)
+ return
+ }
+
+ for i, lhs := range lhs {
+ check.assignVar(lhs, rhs[i])
+ }
+}
+
+// unpack unpacks a *syntax.ListExpr into a list of syntax.Expr.
+// Helper introduced for the go/types -> types2 port.
+// TODO(gri) Should find a more efficient solution that doesn't
+// require introduction of a new slice for simple
+// expressions.
+func unpackExpr(x syntax.Expr) []syntax.Expr {
+ if x, _ := x.(*syntax.ListExpr); x != nil {
+ return x.ElemList
+ }
+ if x != nil {
+ return []syntax.Expr{x}
+ }
+ return nil
+}
+
+func (check *Checker) shortVarDecl(pos syntax.Pos, lhs, rhs []syntax.Expr) {
+ top := len(check.delayed)
+ scope := check.scope
+
+ // collect lhs variables
+ var newVars []*Var
+ var lhsVars = make([]*Var, len(lhs))
+ for i, lhs := range lhs {
+ var obj *Var
+ if ident, _ := lhs.(*syntax.Name); ident != nil {
+ // Use the correct obj if the ident is redeclared. The
+ // variable's scope starts after the declaration; so we
+ // must use Scope.Lookup here and call Scope.Insert
+ // (via check.declare) later.
+ name := ident.Value
+ if alt := scope.Lookup(name); alt != nil {
+ // redeclared object must be a variable
+ if alt, _ := alt.(*Var); alt != nil {
+ obj = alt
+ } else {
+ check.errorf(lhs, "cannot assign to %s", lhs)
+ }
+ check.recordUse(ident, alt)
+ } else {
+ // declare new variable, possibly a blank (_) variable
+ obj = NewVar(ident.Pos(), check.pkg, name, nil)
+ if name != "_" {
+ newVars = append(newVars, obj)
+ }
+ check.recordDef(ident, obj)
+ }
+ } else {
+ check.useLHS(lhs)
+ check.errorf(lhs, "cannot declare %s", lhs)
+ }
+ if obj == nil {
+ obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable
+ }
+ lhsVars[i] = obj
+ }
+
+ check.initVars(lhsVars, rhs, nopos)
+
+ // process function literals in rhs expressions before scope changes
+ check.processDelayed(top)
+
+ // declare new variables
+ if len(newVars) > 0 {
+ // spec: "The scope of a constant or variable identifier declared inside
+ // a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl
+ // for short variable declarations) and ends at the end of the innermost
+ // containing block."
+ scopePos := endPos(rhs[len(rhs)-1])
+ for _, obj := range newVars {
+ check.declare(scope, nil, obj, scopePos) // recordObject already called
+ }
+ } else {
+ check.softErrorf(pos, "no new variables on left side of :=")
+ }
+}
diff --git a/src/cmd/compile/internal/types2/builtins.go b/src/cmd/compile/internal/types2/builtins.go
new file mode 100644
index 0000000000..c1706fd873
--- /dev/null
+++ b/src/cmd/compile/internal/types2/builtins.go
@@ -0,0 +1,777 @@
+// UNREVIEWED
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements typechecking of builtin function calls.
+
+package types2
+
+import (
+ "cmd/compile/internal/syntax"
+ "go/constant"
+ "go/token"
+)
+
+// builtin type-checks a call to the built-in specified by id and
+// reports whether the call is valid, with *x holding the result;
+// but x.expr is not set. If the call is invalid, the result is
+// false, and *x is undefined.
+//
+func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (_ bool) {
+ // append is the only built-in that permits the use of ... for the last argument
+ bin := predeclaredFuncs[id]
+ if call.HasDots && id != _Append {
+ //check.invalidOpf(call.Ellipsis, "invalid use of ... with built-in %s", bin.name)
+ check.invalidOpf(call, "invalid use of ... with built-in %s", bin.name)
+ check.use(call.ArgList...)
+ return
+ }
+
+ // For len(x) and cap(x) we need to know if x contains any function calls or
+ // receive operations. Save/restore current setting and set hasCallOrRecv to
+ // false for the evaluation of x so that we can check it afterwards.
+ // Note: We must do this _before_ calling exprList because exprList evaluates
+ // all arguments.
+ if id == _Len || id == _Cap {
+ defer func(b bool) {
+ check.hasCallOrRecv = b
+ }(check.hasCallOrRecv)
+ check.hasCallOrRecv = false
+ }
+
+ // determine actual arguments
+ var arg func(*operand, int) // TODO(gri) remove use of arg getter in favor of using xlist directly
+ nargs := len(call.ArgList)
+ switch id {
+ default:
+ // make argument getter
+ xlist, _ := check.exprList(call.ArgList, false)
+ arg = func(x *operand, i int) { *x = *xlist[i]; x.typ = expand(x.typ) }
+ nargs = len(xlist)
+ // evaluate first argument, if present
+ if nargs > 0 {
+ arg(x, 0)
+ if x.mode == invalid {
+ return
+ }
+ }
+ case _Make, _New, _Offsetof, _Trace:
+ // arguments require special handling
+ }
+
+ // check argument count
+ {
+ msg := ""
+ if nargs < bin.nargs {
+ msg = "not enough"
+ } else if !bin.variadic && nargs > bin.nargs {
+ msg = "too many"
+ }
+ if msg != "" {
+ check.invalidOpf(call, "%s arguments for %s (expected %d, found %d)", msg, call, bin.nargs, nargs)
+ return
+ }
+ }
+
+ switch id {
+ case _Append:
+ // append(s S, x ...T) S, where T is the element type of S
+ // spec: "The variadic function append appends zero or more values x to s of type
+ // S, which must be a slice type, and returns the resulting slice, also of type S.
+ // The values x are passed to a parameter of type ...T where T is the element type
+ // of S and the respective parameter passing rules apply."
+ S := x.typ
+ var T Type
+ if s := S.Slice(); s != nil {
+ T = s.elem
+ } else {
+ check.invalidArgf(x, "%s is not a slice", x)
+ return
+ }
+
+ // remember arguments that have been evaluated already
+ alist := []operand{*x}
+
+ // spec: "As a special case, append also accepts a first argument assignable
+ // to type []byte with a second argument of string type followed by ... .
+ // This form appends the bytes of the string.
+ if nargs == 2 && call.HasDots && x.assignableTo(check, NewSlice(universeByte), nil) {
+ arg(x, 1)
+ if x.mode == invalid {
+ return
+ }
+ if isString(x.typ) {
+ if check.Types != nil {
+ sig := makeSig(S, S, x.typ)
+ sig.variadic = true
+ check.recordBuiltinType(call.Fun, sig)
+ }
+ x.mode = value
+ x.typ = S
+ break
+ }
+ alist = append(alist, *x)
+ // fallthrough
+ }
+
+ // check general case by creating custom signature
+ sig := makeSig(S, S, NewSlice(T)) // []T required for variadic signature
+ sig.variadic = true
+ var xlist []*operand
+ // convert []operand to []*operand
+ for i := range alist {
+ xlist = append(xlist, &alist[i])
+ }
+ for i := len(alist); i < nargs; i++ {
+ var x operand
+ arg(&x, i)
+ xlist = append(xlist, &x)
+ }
+ check.arguments(call, sig, xlist) // discard result (we know the result type)
+ // ok to continue even if check.arguments reported errors
+
+ x.mode = value
+ x.typ = S
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, sig)
+ }
+
+ case _Cap, _Len:
+ // cap(x)
+ // len(x)
+ mode := invalid
+ var typ Type
+ var val constant.Value
+ switch typ = implicitArrayDeref(optype(x.typ.Under())); t := typ.(type) {
+ case *Basic:
+ if isString(t) && id == _Len {
+ if x.mode == constant_ {
+ mode = constant_
+ val = constant.MakeInt64(int64(len(constant.StringVal(x.val))))
+ } else {
+ mode = value
+ }
+ }
+
+ case *Array:
+ mode = value
+ // spec: "The expressions len(s) and cap(s) are constants
+ // if the type of s is an array or pointer to an array and
+ // the expression s does not contain channel receives or
+ // function calls; in this case s is not evaluated."
+ if !check.hasCallOrRecv {
+ mode = constant_
+ if t.len >= 0 {
+ val = constant.MakeInt64(t.len)
+ } else {
+ val = constant.MakeUnknown()
+ }
+ }
+
+ case *Slice, *Chan:
+ mode = value
+
+ case *Map:
+ if id == _Len {
+ mode = value
+ }
+
+ case *Sum:
+ if t.is(func(t Type) bool {
+ switch t := t.Under().(type) {
+ case *Basic:
+ if isString(t) && id == _Len {
+ return true
+ }
+ case *Array, *Slice, *Chan:
+ return true
+ case *Map:
+ if id == _Len {
+ return true
+ }
+ }
+ return false
+ }) {
+ mode = value
+ }
+ }
+
+ if mode == invalid && typ != Typ[Invalid] {
+ check.invalidArgf(x, "%s for %s", x, bin.name)
+ return
+ }
+
+ x.mode = mode
+ x.typ = Typ[Int]
+ x.val = val
+ if check.Types != nil && mode != constant_ {
+ check.recordBuiltinType(call.Fun, makeSig(x.typ, typ))
+ }
+
+ case _Close:
+ // close(c)
+ c := x.typ.Chan()
+ if c == nil {
+ check.invalidArgf(x, "%s is not a channel", x)
+ return
+ }
+ if c.dir == RecvOnly {
+ check.invalidArgf(x, "%s must not be a receive-only channel", x)
+ return
+ }
+
+ x.mode = novalue
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(nil, c))
+ }
+
+ case _Complex:
+ // complex(x, y floatT) complexT
+ var y operand
+ arg(&y, 1)
+ if y.mode == invalid {
+ return
+ }
+
+ // convert or check untyped arguments
+ d := 0
+ if isUntyped(x.typ) {
+ d |= 1
+ }
+ if isUntyped(y.typ) {
+ d |= 2
+ }
+ switch d {
+ case 0:
+ // x and y are typed => nothing to do
+ case 1:
+ // only x is untyped => convert to type of y
+ check.convertUntyped(x, y.typ)
+ case 2:
+ // only y is untyped => convert to type of x
+ check.convertUntyped(&y, x.typ)
+ case 3:
+ // x and y are untyped =>
+ // 1) if both are constants, convert them to untyped
+ // floating-point numbers if possible,
+ // 2) if one of them is not constant (possible because
+ // it contains a shift that is yet untyped), convert
+ // both of them to float64 since they must have the
+ // same type to succeed (this will result in an error
+ // because shifts of floats are not permitted)
+ if x.mode == constant_ && y.mode == constant_ {
+ toFloat := func(x *operand) {
+ if isNumeric(x.typ) && constant.Sign(constant.Imag(x.val)) == 0 {
+ x.typ = Typ[UntypedFloat]
+ }
+ }
+ toFloat(x)
+ toFloat(&y)
+ } else {
+ check.convertUntyped(x, Typ[Float64])
+ check.convertUntyped(&y, Typ[Float64])
+ // x and y should be invalid now, but be conservative
+ // and check below
+ }
+ }
+ if x.mode == invalid || y.mode == invalid {
+ return
+ }
+
+ // both argument types must be identical
+ if !check.identical(x.typ, y.typ) {
+ check.invalidArgf(x, "mismatched types %s and %s", x.typ, y.typ)
+ return
+ }
+
+ // the argument types must be of floating-point type
+ f := func(x Type) Type {
+ if t := x.Basic(); t != nil {
+ switch t.kind {
+ case Float32:
+ return Typ[Complex64]
+ case Float64:
+ return Typ[Complex128]
+ case UntypedFloat:
+ return Typ[UntypedComplex]
+ }
+ }
+ return nil
+ }
+ resTyp := check.applyTypeFunc(f, x.typ)
+ if resTyp == nil {
+ check.invalidArgf(x, "arguments have type %s, expected floating-point", x.typ)
+ return
+ }
+
+ // if both arguments are constants, the result is a constant
+ if x.mode == constant_ && y.mode == constant_ {
+ x.val = constant.BinaryOp(constant.ToFloat(x.val), token.ADD, constant.MakeImag(constant.ToFloat(y.val)))
+ } else {
+ x.mode = value
+ }
+
+ if check.Types != nil && x.mode != constant_ {
+ check.recordBuiltinType(call.Fun, makeSig(resTyp, x.typ, x.typ))
+ }
+
+ x.typ = resTyp
+
+ case _Copy:
+ // copy(x, y []T) int
+ var dst Type
+ if t := x.typ.Slice(); t != nil {
+ dst = t.elem
+ }
+
+ var y operand
+ arg(&y, 1)
+ if y.mode == invalid {
+ return
+ }
+ var src Type
+ switch t := optype(y.typ.Under()).(type) {
+ case *Basic:
+ if isString(y.typ) {
+ src = universeByte
+ }
+ case *Slice:
+ src = t.elem
+ }
+
+ if dst == nil || src == nil {
+ check.invalidArgf(x, "copy expects slice arguments; found %s and %s", x, &y)
+ return
+ }
+
+ if !check.identical(dst, src) {
+ check.invalidArgf(x, "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src)
+ return
+ }
+
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(Typ[Int], x.typ, y.typ))
+ }
+ x.mode = value
+ x.typ = Typ[Int]
+
+ case _Delete:
+ // delete(m, k)
+ m := x.typ.Map()
+ if m == nil {
+ check.invalidArgf(x, "%s is not a map", x)
+ return
+ }
+ arg(x, 1) // k
+ if x.mode == invalid {
+ return
+ }
+
+ if !x.assignableTo(check, m.key, nil) {
+ check.invalidArgf(x, "%s is not assignable to %s", x, m.key)
+ return
+ }
+
+ x.mode = novalue
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(nil, m, m.key))
+ }
+
+ case _Imag, _Real:
+ // imag(complexT) floatT
+ // real(complexT) floatT
+
+ // convert or check untyped argument
+ if isUntyped(x.typ) {
+ if x.mode == constant_ {
+ // an untyped constant number can always be considered
+ // as a complex constant
+ if isNumeric(x.typ) {
+ x.typ = Typ[UntypedComplex]
+ }
+ } else {
+ // an untyped non-constant argument may appear if
+ // it contains a (yet untyped non-constant) shift
+ // expression: convert it to complex128 which will
+ // result in an error (shift of complex value)
+ check.convertUntyped(x, Typ[Complex128])
+ // x should be invalid now, but be conservative and check
+ if x.mode == invalid {
+ return
+ }
+ }
+ }
+
+ // the argument must be of complex type
+ f := func(x Type) Type {
+ if t := x.Basic(); t != nil {
+ switch t.kind {
+ case Complex64:
+ return Typ[Float32]
+ case Complex128:
+ return Typ[Float64]
+ case UntypedComplex:
+ return Typ[UntypedFloat]
+ }
+ }
+ return nil
+ }
+ resTyp := check.applyTypeFunc(f, x.typ)
+ if resTyp == nil {
+ check.invalidArgf(x, "argument has type %s, expected complex type", x.typ)
+ return
+ }
+
+ // if the argument is a constant, the result is a constant
+ if x.mode == constant_ {
+ if id == _Real {
+ x.val = constant.Real(x.val)
+ } else {
+ x.val = constant.Imag(x.val)
+ }
+ } else {
+ x.mode = value
+ }
+
+ if check.Types != nil && x.mode != constant_ {
+ check.recordBuiltinType(call.Fun, makeSig(resTyp, x.typ))
+ }
+
+ x.typ = resTyp
+
+ case _Make:
+ // make(T, n)
+ // make(T, n, m)
+ // (no argument evaluated yet)
+ arg0 := call.ArgList[0]
+ T := check.varType(arg0)
+ if T == Typ[Invalid] {
+ return
+ }
+
+ min, max := -1, 10
+ var valid func(t Type) bool
+ valid = func(t Type) bool {
+ var m int
+ switch t := optype(t.Under()).(type) {
+ case *Slice:
+ m = 2
+ case *Map, *Chan:
+ m = 1
+ case *Sum:
+ return t.is(valid)
+ default:
+ return false
+ }
+ if m > min {
+ min = m
+ }
+ if m+1 < max {
+ max = m + 1
+ }
+ return true
+ }
+
+ if !valid(T) {
+ check.invalidArgf(arg0, "cannot make %s; type must be slice, map, or channel", arg0)
+ return
+ }
+ if nargs < min || max < nargs {
+ if min == max {
+ check.errorf(call, "%v expects %d arguments; found %d", call, min, nargs)
+ } else {
+ check.errorf(call, "%v expects %d or %d arguments; found %d", call, min, max, nargs)
+ }
+ return
+ }
+
+ types := []Type{T}
+ var sizes []int64 // constant integer arguments, if any
+ for _, arg := range call.ArgList[1:] {
+ typ, size := check.index(arg, -1) // ok to continue with typ == Typ[Invalid]
+ types = append(types, typ)
+ if size >= 0 {
+ sizes = append(sizes, size)
+ }
+ }
+ if len(sizes) == 2 && sizes[0] > sizes[1] {
+ check.invalidArgf(call.ArgList[1], "length and capacity swapped")
+ // safe to continue
+ }
+ x.mode = value
+ x.typ = T
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(x.typ, types...))
+ }
+
+ case _New:
+ // new(T)
+ // (no argument evaluated yet)
+ T := check.varType(call.ArgList[0])
+ if T == Typ[Invalid] {
+ return
+ }
+
+ x.mode = value
+ x.typ = &Pointer{base: T}
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(x.typ, T))
+ }
+
+ case _Panic:
+ // panic(x)
+ // record panic call if inside a function with result parameters
+ // (for use in Checker.isTerminating)
+ if check.sig != nil && check.sig.results.Len() > 0 {
+ // function has result parameters
+ p := check.isPanic
+ if p == nil {
+ // allocate lazily
+ p = make(map[*syntax.CallExpr]bool)
+ check.isPanic = p
+ }
+ p[call] = true
+ }
+
+ check.assignment(x, &emptyInterface, "argument to panic")
+ if x.mode == invalid {
+ return
+ }
+
+ x.mode = novalue
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(nil, &emptyInterface))
+ }
+
+ case _Print, _Println:
+ // print(x, y, ...)
+ // println(x, y, ...)
+ var params []Type
+ if nargs > 0 {
+ params = make([]Type, nargs)
+ for i := 0; i < nargs; i++ {
+ if i > 0 {
+ arg(x, i) // first argument already evaluated
+ }
+ check.assignment(x, nil, "argument to "+predeclaredFuncs[id].name)
+ if x.mode == invalid {
+ // TODO(gri) "use" all arguments?
+ return
+ }
+ params[i] = x.typ
+ }
+ }
+
+ x.mode = novalue
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(nil, params...))
+ }
+
+ case _Recover:
+ // recover() interface{}
+ x.mode = value
+ x.typ = &emptyInterface
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(x.typ))
+ }
+
+ case _Alignof:
+ // unsafe.Alignof(x T) uintptr
+ if x.typ.TypeParam() != nil {
+ check.invalidOpf(call, "unsafe.Alignof undefined for %s", x)
+ return
+ }
+ check.assignment(x, nil, "argument to unsafe.Alignof")
+ if x.mode == invalid {
+ return
+ }
+
+ x.mode = constant_
+ x.val = constant.MakeInt64(check.conf.alignof(x.typ))
+ x.typ = Typ[Uintptr]
+ // result is constant - no need to record signature
+
+ case _Offsetof:
+ // unsafe.Offsetof(x T) uintptr, where x must be a selector
+ // (no argument evaluated yet)
+ arg0 := call.ArgList[0]
+ selx, _ := unparen(arg0).(*syntax.SelectorExpr)
+ if selx == nil {
+ check.invalidArgf(arg0, "%s is not a selector expression", arg0)
+ check.use(arg0)
+ return
+ }
+
+ check.expr(x, selx.X)
+ if x.mode == invalid {
+ return
+ }
+
+ base := derefStructPtr(x.typ)
+ sel := selx.Sel.Value
+ obj, index, indirect := check.lookupFieldOrMethod(base, false, check.pkg, sel)
+ switch obj.(type) {
+ case nil:
+ check.invalidArgf(x, "%s has no single field %s", base, sel)
+ return
+ case *Func:
+ // TODO(gri) Using derefStructPtr may result in methods being found
+ // that don't actually exist. An error either way, but the error
+ // message is confusing. See: https://play.golang.org/p/al75v23kUy ,
+ // but go/types reports: "invalid argument: x.m is a method value".
+ check.invalidArgf(arg0, "%s is a method value", arg0)
+ return
+ }
+ if indirect {
+ check.invalidArgf(x, "field %s is embedded via a pointer in %s", sel, base)
+ return
+ }
+
+ // TODO(gri) Should we pass x.typ instead of base (and indirect report if derefStructPtr indirected)?
+ check.recordSelection(selx, FieldVal, base, obj, index, false)
+
+ offs := check.conf.offsetof(base, index)
+ x.mode = constant_
+ x.val = constant.MakeInt64(offs)
+ x.typ = Typ[Uintptr]
+ // result is constant - no need to record signature
+
+ case _Sizeof:
+ // unsafe.Sizeof(x T) uintptr
+ if x.typ.TypeParam() != nil {
+ check.invalidOpf(call, "unsafe.Sizeof undefined for %s", x)
+ return
+ }
+ check.assignment(x, nil, "argument to unsafe.Sizeof")
+ if x.mode == invalid {
+ return
+ }
+
+ x.mode = constant_
+ x.val = constant.MakeInt64(check.conf.sizeof(x.typ))
+ x.typ = Typ[Uintptr]
+ // result is constant - no need to record signature
+
+ case _Assert:
+ // assert(pred) causes a typechecker error if pred is false.
+ // The result of assert is the value of pred if there is no error.
+ // Note: assert is only available in self-test mode.
+ if x.mode != constant_ || !isBoolean(x.typ) {
+ check.invalidArgf(x, "%s is not a boolean constant", x)
+ return
+ }
+ if x.val.Kind() != constant.Bool {
+ check.errorf(x, "internal error: value of %s should be a boolean constant", x)
+ return
+ }
+ if !constant.BoolVal(x.val) {
+ check.errorf(call, "%v failed", call)
+ // compile-time assertion failure - safe to continue
+ }
+ // result is constant - no need to record signature
+
+ case _Trace:
+ // trace(x, y, z, ...) dumps the positions, expressions, and
+ // values of its arguments. The result of trace is the value
+ // of the first argument.
+ // Note: trace is only available in self-test mode.
+ // (no argument evaluated yet)
+ if nargs == 0 {
+ check.dump("%v: trace() without arguments", posFor(call))
+ x.mode = novalue
+ break
+ }
+ var t operand
+ x1 := x
+ for _, arg := range call.ArgList {
+ check.rawExpr(x1, arg, nil) // permit trace for types, e.g.: new(trace(T))
+ check.dump("%v: %s", posFor(x1), x1)
+ x1 = &t // use incoming x only for first argument
+ }
+ // trace is only available in test mode - no need to record signature
+
+ default:
+ unreachable()
+ }
+
+ return true
+}
+
+// applyTypeFunc applies f to x. If x is a type parameter,
+// the result is a type parameter constrained by an new
+// interface bound. The type bounds for that interface
+// are computed by applying f to each of the type bounds
+// of x. If any of these applications of f return nil,
+// applyTypeFunc returns nil.
+// If x is not a type parameter, the result is f(x).
+func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type {
+ if tp := x.TypeParam(); tp != nil {
+ // Test if t satisfies the requirements for the argument
+ // type and collect possible result types at the same time.
+ var rtypes []Type
+ if !tp.Bound().is(func(x Type) bool {
+ if r := f(x); r != nil {
+ rtypes = append(rtypes, r)
+ return true
+ }
+ return false
+ }) {
+ return nil
+ }
+
+ // TODO(gri) Would it be ok to return just the one type
+ // if len(rtypes) == 1? What about top-level
+ // uses of real() where the result is used to
+ // define type and initialize a variable?
+
+ // construct a suitable new type parameter
+ tpar := NewTypeName(nopos, nil /* = Universe pkg */, "<type parameter>", nil)
+ ptyp := check.NewTypeParam(tp.ptr, tpar, 0, &emptyInterface) // assigns type to tpar as a side-effect
+ tsum := NewSum(rtypes)
+ ptyp.bound = &Interface{types: tsum, allMethods: markComplete, allTypes: tsum}
+
+ return ptyp
+ }
+
+ return f(x)
+}
+
+// makeSig makes a signature for the given argument and result types.
+// Default types are used for untyped arguments, and res may be nil.
+func makeSig(res Type, args ...Type) *Signature {
+ list := make([]*Var, len(args))
+ for i, param := range args {
+ list[i] = NewVar(nopos, nil, "", Default(param))
+ }
+ params := NewTuple(list...)
+ var result *Tuple
+ if res != nil {
+ assert(!isUntyped(res))
+ result = NewTuple(NewVar(nopos, nil, "", res))
+ }
+ return &Signature{params: params, results: result}
+}
+
+// implicitArrayDeref returns A if typ is of the form *A and A is an array;
+// otherwise it returns typ.
+//
+func implicitArrayDeref(typ Type) Type {
+ if p, ok := typ.(*Pointer); ok {
+ if a := p.base.Array(); a != nil {
+ return a
+ }
+ }
+ return typ
+}
+
+// unparen returns e with any enclosing parentheses stripped.
+func unparen(e syntax.Expr) syntax.Expr {
+ for {
+ p, ok := e.(*syntax.ParenExpr)
+ if !ok {
+ return e
+ }
+ e = p.X
+ }
+}
diff --git a/src/cmd/compile/internal/types2/builtins_test.go b/src/cmd/compile/internal/types2/builtins_test.go
new file mode 100644
index 0000000000..9f737bc9bb
--- /dev/null
+++ b/src/cmd/compile/internal/types2/builtins_test.go
@@ -0,0 +1,219 @@
+// UNREVIEWED
+// Copyright 2013 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 types2_test
+
+import (
+ "cmd/compile/internal/syntax"
+ "fmt"
+ "testing"
+
+ . "cmd/compile/internal/types2"
+)
+
+var builtinCalls = []struct {
+ name, src, sig string
+}{
+ {"append", `var s []int; _ = append(s)`, `func([]int, ...int) []int`},
+ {"append", `var s []int; _ = append(s, 0)`, `func([]int, ...int) []int`},
+ {"append", `var s []int; _ = (append)(s, 0)`, `func([]int, ...int) []int`},
+ {"append", `var s []byte; _ = ((append))(s, 0)`, `func([]byte, ...byte) []byte`},
+ {"append", `var s []byte; _ = append(s, "foo"...)`, `func([]byte, string...) []byte`},
+ {"append", `type T []byte; var s T; var str string; _ = append(s, str...)`, `func(p.T, string...) p.T`},
+ {"append", `type T []byte; type U string; var s T; var str U; _ = append(s, str...)`, `func(p.T, p.U...) p.T`},
+
+ {"cap", `var s [10]int; _ = cap(s)`, `invalid type`}, // constant
+ {"cap", `var s [10]int; _ = cap(&s)`, `invalid type`}, // constant
+ {"cap", `var s []int64; _ = cap(s)`, `func([]int64) int`},
+ {"cap", `var c chan<-bool; _ = cap(c)`, `func(chan<- bool) int`},
+
+ {"len", `_ = len("foo")`, `invalid type`}, // constant
+ {"len", `var s string; _ = len(s)`, `func(string) int`},
+ {"len", `var s [10]int; _ = len(s)`, `invalid type`}, // constant
+ {"len", `var s [10]int; _ = len(&s)`, `invalid type`}, // constant
+ {"len", `var s []int64; _ = len(s)`, `func([]int64) int`},
+ {"len", `var c chan<-bool; _ = len(c)`, `func(chan<- bool) int`},
+ {"len", `var m map[string]float32; _ = len(m)`, `func(map[string]float32) int`},
+
+ {"close", `var c chan int; close(c)`, `func(chan int)`},
+ {"close", `var c chan<- chan string; close(c)`, `func(chan<- chan string)`},
+
+ {"complex", `_ = complex(1, 0)`, `invalid type`}, // constant
+ {"complex", `var re float32; _ = complex(re, 1.0)`, `func(float32, float32) complex64`},
+ {"complex", `var im float64; _ = complex(1, im)`, `func(float64, float64) complex128`},
+ {"complex", `type F32 float32; var re, im F32; _ = complex(re, im)`, `func(p.F32, p.F32) complex64`},
+ {"complex", `type F64 float64; var re, im F64; _ = complex(re, im)`, `func(p.F64, p.F64) complex128`},
+
+ {"copy", `var src, dst []byte; copy(dst, src)`, `func([]byte, []byte) int`},
+ {"copy", `type T [][]int; var src, dst T; _ = copy(dst, src)`, `func(p.T, p.T) int`},
+ {"copy", `var src string; var dst []byte; copy(dst, src)`, `func([]byte, string) int`},
+ {"copy", `type T string; type U []byte; var src T; var dst U; copy(dst, src)`, `func(p.U, p.T) int`},
+ {"copy", `var dst []byte; copy(dst, "hello")`, `func([]byte, string) int`},
+
+ {"delete", `var m map[string]bool; delete(m, "foo")`, `func(map[string]bool, string)`},
+ {"delete", `type (K string; V int); var m map[K]V; delete(m, "foo")`, `func(map[p.K]p.V, p.K)`},
+
+ {"imag", `_ = imag(1i)`, `invalid type`}, // constant
+ {"imag", `var c complex64; _ = imag(c)`, `func(complex64) float32`},
+ {"imag", `var c complex128; _ = imag(c)`, `func(complex128) float64`},
+ {"imag", `type C64 complex64; var c C64; _ = imag(c)`, `func(p.C64) float32`},
+ {"imag", `type C128 complex128; var c C128; _ = imag(c)`, `func(p.C128) float64`},
+
+ {"real", `_ = real(1i)`, `invalid type`}, // constant
+ {"real", `var c complex64; _ = real(c)`, `func(complex64) float32`},
+ {"real", `var c complex128; _ = real(c)`, `func(complex128) float64`},
+ {"real", `type C64 complex64; var c C64; _ = real(c)`, `func(p.C64) float32`},
+ {"real", `type C128 complex128; var c C128; _ = real(c)`, `func(p.C128) float64`},
+
+ {"make", `_ = make([]int, 10)`, `func([]int, int) []int`},
+ {"make", `type T []byte; _ = make(T, 10, 20)`, `func(p.T, int, int) p.T`},
+
+ // issue #37349
+ {"make", ` _ = make([]int, 0 )`, `func([]int, int) []int`},
+ {"make", `var l int; _ = make([]int, l )`, `func([]int, int) []int`},
+ {"make", ` _ = make([]int, 0, 0)`, `func([]int, int, int) []int`},
+ {"make", `var l int; _ = make([]int, l, 0)`, `func([]int, int, int) []int`},
+ {"make", `var c int; _ = make([]int, 0, c)`, `func([]int, int, int) []int`},
+ {"make", `var l, c int; _ = make([]int, l, c)`, `func([]int, int, int) []int`},
+
+ // issue #37393
+ {"make", ` _ = make([]int , 0 )`, `func([]int, int) []int`},
+ {"make", `var l byte ; _ = make([]int8 , l )`, `func([]int8, byte) []int8`},
+ {"make", ` _ = make([]int16 , 0, 0)`, `func([]int16, int, int) []int16`},
+ {"make", `var l int16; _ = make([]string , l, 0)`, `func([]string, int16, int) []string`},
+ {"make", `var c int32; _ = make([]float64 , 0, c)`, `func([]float64, int, int32) []float64`},
+ {"make", `var l, c uint ; _ = make([]complex128, l, c)`, `func([]complex128, uint, uint) []complex128`},
+
+ {"new", `_ = new(int)`, `func(int) *int`},
+ {"new", `type T struct{}; _ = new(T)`, `func(p.T) *p.T`},
+
+ {"panic", `panic(0)`, `func(interface{})`},
+ {"panic", `panic("foo")`, `func(interface{})`},
+
+ {"print", `print()`, `func()`},
+ {"print", `print(0)`, `func(int)`},
+ {"print", `print(1, 2.0, "foo", true)`, `func(int, float64, string, bool)`},
+
+ {"println", `println()`, `func()`},
+ {"println", `println(0)`, `func(int)`},
+ {"println", `println(1, 2.0, "foo", true)`, `func(int, float64, string, bool)`},
+
+ {"recover", `recover()`, `func() interface{}`},
+ {"recover", `_ = recover()`, `func() interface{}`},
+
+ {"Alignof", `_ = unsafe.Alignof(0)`, `invalid type`}, // constant
+ {"Alignof", `var x struct{}; _ = unsafe.Alignof(x)`, `invalid type`}, // constant
+
+ {"Offsetof", `var x struct{f bool}; _ = unsafe.Offsetof(x.f)`, `invalid type`}, // constant
+ {"Offsetof", `var x struct{_ int; f bool}; _ = unsafe.Offsetof((&x).f)`, `invalid type`}, // constant
+
+ {"Sizeof", `_ = unsafe.Sizeof(0)`, `invalid type`}, // constant
+ {"Sizeof", `var x struct{}; _ = unsafe.Sizeof(x)`, `invalid type`}, // constant
+
+ {"assert", `assert(true)`, `invalid type`}, // constant
+ {"assert", `type B bool; const pred B = 1 < 2; assert(pred)`, `invalid type`}, // constant
+
+ // no tests for trace since it produces output as a side-effect
+}
+
+func TestBuiltinSignatures(t *testing.T) {
+ DefPredeclaredTestFuncs()
+
+ seen := map[string]bool{"trace": true} // no test for trace built-in; add it manually
+ for _, call := range builtinCalls {
+ testBuiltinSignature(t, call.name, call.src, call.sig)
+ seen[call.name] = true
+ }
+
+ // make sure we didn't miss one
+ for _, name := range Universe.Names() {
+ if _, ok := Universe.Lookup(name).(*Builtin); ok && !seen[name] {
+ t.Errorf("missing test for %s", name)
+ }
+ }
+ for _, name := range Unsafe.Scope().Names() {
+ if _, ok := Unsafe.Scope().Lookup(name).(*Builtin); ok && !seen[name] {
+ t.Errorf("missing test for unsafe.%s", name)
+ }
+ }
+}
+
+func testBuiltinSignature(t *testing.T, name, src0, want string) {
+ src := fmt.Sprintf(`package p; import "unsafe"; type _ unsafe.Pointer /* use unsafe */; func _() { %s }`, src0)
+ f, err := parseSrc("", src)
+ if err != nil {
+ t.Errorf("%s: %s", src0, err)
+ return
+ }
+
+ conf := Config{Importer: defaultImporter()}
+ uses := make(map[*syntax.Name]Object)
+ types := make(map[syntax.Expr]TypeAndValue)
+ _, err = conf.Check(f.PkgName.Value, []*syntax.File{f}, &Info{Uses: uses, Types: types})
+ if err != nil {
+ t.Errorf("%s: %s", src0, err)
+ return
+ }
+
+ // find called function
+ n := 0
+ var fun syntax.Expr
+ for x := range types {
+ if call, _ := x.(*syntax.CallExpr); call != nil {
+ fun = call.Fun
+ n++
+ }
+ }
+ if n != 1 {
+ t.Errorf("%s: got %d CallExprs; want 1", src0, n)
+ return
+ }
+
+ // check recorded types for fun and descendents (may be parenthesized)
+ for {
+ // the recorded type for the built-in must match the wanted signature
+ typ := types[fun].Type
+ if typ == nil {
+ t.Errorf("%s: no type recorded for %s", src0, ExprString(fun))
+ return
+ }
+ if got := typ.String(); got != want {
+ t.Errorf("%s: got type %s; want %s", src0, got, want)
+ return
+ }
+
+ // called function must be a (possibly parenthesized, qualified)
+ // identifier denoting the expected built-in
+ switch p := fun.(type) {
+ case *syntax.Name:
+ obj := uses[p]
+ if obj == nil {
+ t.Errorf("%s: no object found for %s", src0, p.Value)
+ return
+ }
+ bin, _ := obj.(*Builtin)
+ if bin == nil {
+ t.Errorf("%s: %s does not denote a built-in", src0, p.Value)
+ return
+ }
+ if bin.Name() != name {
+ t.Errorf("%s: got built-in %s; want %s", src0, bin.Name(), name)
+ return
+ }
+ return // we're done
+
+ case *syntax.ParenExpr:
+ fun = p.X // unpack
+
+ case *syntax.SelectorExpr:
+ // built-in from package unsafe - ignore details
+ return // we're done
+
+ default:
+ t.Errorf("%s: invalid function call", src0)
+ return
+ }
+ }
+}
diff --git a/src/cmd/compile/internal/types2/call.go b/src/cmd/compile/internal/types2/call.go
new file mode 100644
index 0000000000..a29096322a
--- /dev/null
+++ b/src/cmd/compile/internal/types2/call.go
@@ -0,0 +1,808 @@
+// UNREVIEWED
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements typechecking of call and selector expressions.
+
+package types2
+
+import (
+ "cmd/compile/internal/syntax"
+ "strings"
+ "unicode"
+)
+
+// funcInst type-checks a function instantiaton inst and returns the result in x.
+// The operand x must be the evaluation of inst.X and its type must be a signature.
+func (check *Checker) funcInst(x *operand, inst *syntax.IndexExpr) {
+ args, ok := check.exprOrTypeList(unpackExpr(inst.Index))
+ if ok && len(args) > 0 && args[0].mode != typexpr {
+ check.errorf(args[0], "%s is not a type", args[0])
+ ok = false
+ }
+ if !ok {
+ x.mode = invalid
+ x.expr = inst
+ return
+ }
+
+ // check number of type arguments
+ n := len(args)
+ sig := x.typ.(*Signature)
+ if !check.conf.InferFromConstraints && n != len(sig.tparams) || n > len(sig.tparams) {
+ check.errorf(args[n-1], "got %d type arguments but want %d", n, len(sig.tparams))
+ x.mode = invalid
+ x.expr = inst
+ return
+ }
+
+ // collect types
+ targs := make([]Type, n)
+ poslist := make([]syntax.Pos, n)
+ for i, a := range args {
+ if a.mode != typexpr {
+ // error was reported earlier
+ x.mode = invalid
+ x.expr = inst
+ return
+ }
+ targs[i] = a.typ
+ poslist[i] = a.Pos()
+ }
+
+ // if we don't have enough type arguments, use constraint type inference
+ var inferred bool
+ if n < len(sig.tparams) {
+ var failed int
+ targs, failed = check.inferB(sig.tparams, targs)
+ if targs == nil {
+ // error was already reported
+ x.mode = invalid
+ x.expr = inst
+ return
+ }
+ if failed >= 0 {
+ // at least one type argument couldn't be inferred
+ assert(targs[failed] == nil)
+ tpar := sig.tparams[failed]
+ check.errorf(inst, "cannot infer %s (%s) (%s)", tpar.name, tpar.pos, targs)
+ x.mode = invalid
+ x.expr = inst
+ return
+ }
+ // all type arguments were inferred sucessfully
+ if debug {
+ for _, targ := range targs {
+ assert(targ != nil)
+ }
+ }
+ //check.dump("### inferred targs = %s", targs)
+ n = len(targs)
+ inferred = true
+ }
+ assert(n == len(sig.tparams))
+
+ // instantiate function signature
+ for i, typ := range targs {
+ // some positions may be missing if types are inferred
+ var pos syntax.Pos
+ if i < len(poslist) {
+ pos = poslist[i]
+ }
+ check.ordinaryType(pos, typ)
+ }
+ res := check.instantiate(x.Pos(), sig, targs, poslist).(*Signature)
+ assert(res.tparams == nil) // signature is not generic anymore
+ if inferred {
+ check.recordInferred(inst, targs, res)
+ }
+ x.typ = res
+ x.mode = value
+ x.expr = inst
+}
+
+func (check *Checker) call(x *operand, call *syntax.CallExpr) exprKind {
+ check.exprOrType(x, call.Fun)
+
+ switch x.mode {
+ case invalid:
+ check.use(call.ArgList...)
+ x.expr = call
+ return statement
+
+ case typexpr:
+ // conversion
+ T := x.typ
+ x.mode = invalid
+ switch n := len(call.ArgList); n {
+ case 0:
+ check.errorf(call, "missing argument in conversion to %s", T)
+ case 1:
+ check.expr(x, call.ArgList[0])
+ if x.mode != invalid {
+ if t := T.Interface(); t != nil {
+ check.completeInterface(nopos, t)
+ if t.IsConstraint() {
+ check.errorf(call, "cannot use interface %s in conversion (contains type list or is comparable)", T)
+ break
+ }
+ }
+ check.conversion(x, T)
+ }
+ default:
+ check.use(call.ArgList...)
+ check.errorf(call.ArgList[n-1], "too many arguments in conversion to %s", T)
+ }
+ x.expr = call
+ return conversion
+
+ case builtin:
+ id := x.id
+ if !check.builtin(x, call, id) {
+ x.mode = invalid
+ }
+ x.expr = call
+ // a non-constant result implies a function call
+ if x.mode != invalid && x.mode != constant_ {
+ check.hasCallOrRecv = true
+ }
+ return predeclaredFuncs[id].kind
+
+ default:
+ // function/method call
+ cgocall := x.mode == cgofunc
+
+ sig := x.typ.Signature()
+ if sig == nil {
+ check.invalidOpf(x, "cannot call non-function %s", x)
+ x.mode = invalid
+ x.expr = call
+ return statement
+ }
+
+ // evaluate arguments
+ args, ok := check.exprOrTypeList(call.ArgList)
+ if !ok {
+ x.mode = invalid
+ x.expr = call
+ return expression
+ }
+
+ sig = check.arguments(call, sig, args)
+
+ // determine result
+ switch sig.results.Len() {
+ case 0:
+ x.mode = novalue
+ case 1:
+ if cgocall {
+ x.mode = commaerr
+ } else {
+ x.mode = value
+ }
+ x.typ = sig.results.vars[0].typ // unpack tuple
+ default:
+ x.mode = value
+ x.typ = sig.results
+ }
+ x.expr = call
+ check.hasCallOrRecv = true
+
+ // if type inference failed, a parametrized result must be invalidated
+ // (operands cannot have a parametrized type)
+ if x.mode == value && len(sig.tparams) > 0 && isParameterized(sig.tparams, x.typ) {
+ x.mode = invalid
+ }
+
+ return statement
+ }
+}
+
+// exprOrTypeList returns a list of operands and reports an error if the
+// list contains a mix of values and types (ignoring invalid operands).
+// TODO(gri) Now we can split this into exprList and typeList.
+func (check *Checker) exprOrTypeList(elist []syntax.Expr) (xlist []*operand, ok bool) {
+ ok = true
+
+ switch len(elist) {
+ case 0:
+ // nothing to do
+
+ case 1:
+ // single (possibly comma-ok) value or type, or function returning multiple values
+ e := elist[0]
+ var x operand
+ check.multiExprOrType(&x, e)
+ if t, ok := x.typ.(*Tuple); ok && x.mode != invalid && x.mode != typexpr {
+ // multiple values
+ xlist = make([]*operand, t.Len())
+ for i, v := range t.vars {
+ xlist[i] = &operand{mode: value, expr: e, typ: v.typ}
+ }
+ break
+ }
+
+ check.instantiatedOperand(&x)
+
+ // exactly one (possibly invalid or comma-ok) value or type
+ xlist = []*operand{&x}
+
+ default:
+ // multiple (possibly invalid) values or types
+ xlist = make([]*operand, len(elist))
+ ntypes := 0
+ for i, e := range elist {
+ var x operand
+ check.exprOrType(&x, e)
+ xlist[i] = &x
+ switch x.mode {
+ case invalid:
+ ntypes = len(xlist) // make 'if' condition fail below (no additional error in this case)
+ case typexpr:
+ ntypes++
+ check.instantiatedOperand(&x)
+ }
+ }
+ if 0 < ntypes && ntypes < len(xlist) {
+ check.errorf(xlist[0], "mix of value and type expressions")
+ ok = false
+ }
+ }
+
+ return
+}
+
+func (check *Checker) exprList(elist []syntax.Expr, allowCommaOk bool) (xlist []*operand, commaOk bool) {
+ switch len(elist) {
+ case 0:
+ // nothing to do
+
+ case 1:
+ // single (possibly comma-ok) value, or function returning multiple values
+ e := elist[0]
+ var x operand
+ check.multiExpr(&x, e)
+ if t, ok := x.typ.(*Tuple); ok && x.mode != invalid {
+ // multiple values
+ xlist = make([]*operand, t.Len())
+ for i, v := range t.vars {
+ xlist[i] = &operand{mode: value, expr: e, typ: v.typ}
+ }
+ break
+ }
+
+ // exactly one (possibly invalid or comma-ok) value
+ xlist = []*operand{&x}
+ if allowCommaOk && (x.mode == mapindex || x.mode == commaok || x.mode == commaerr) {
+ x.mode = value
+ xlist = append(xlist, &operand{mode: value, expr: e, typ: Typ[UntypedBool]})
+ commaOk = true
+ }
+
+ default:
+ // multiple (possibly invalid) values
+ xlist = make([]*operand, len(elist))
+ for i, e := range elist {
+ var x operand
+ check.expr(&x, e)
+ xlist[i] = &x
+ }
+ }
+
+ return
+}
+
+func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, args []*operand) (rsig *Signature) {
+ rsig = sig
+
+ // TODO(gri) try to eliminate this extra verification loop
+ for _, a := range args {
+ switch a.mode {
+ case typexpr:
+ check.errorf(a, "%s used as value", a)
+ return
+ case invalid:
+ return
+ }
+ }
+
+ // Function call argument/parameter count requirements
+ //
+ // | standard call | dotdotdot call |
+ // --------------+------------------+----------------+
+ // standard func | nargs == npars | invalid |
+ // --------------+------------------+----------------+
+ // variadic func | nargs >= npars-1 | nargs == npars |
+ // --------------+------------------+----------------+
+
+ nargs := len(args)
+ npars := sig.params.Len()
+ ddd := call.HasDots
+
+ // set up parameters
+ sig_params := sig.params // adjusted for variadic functions (may be nil for empty parameter lists!)
+ adjusted := false // indicates if sig_params is different from t.params
+ if sig.variadic {
+ if ddd {
+ // variadic_func(a, b, c...)
+ if len(call.ArgList) == 1 && nargs > 1 {
+ // f()... is not permitted if f() is multi-valued
+ //check.errorf(call.Ellipsis, "cannot use ... with %d-valued %s", nargs, call.ArgList[0])
+ check.errorf(call, "cannot use ... with %d-valued %s", nargs, call.ArgList[0])
+ return
+ }
+ } else {
+ // variadic_func(a, b, c)
+ if nargs >= npars-1 {
+ // Create custom parameters for arguments: keep
+ // the first npars-1 parameters and add one for
+ // each argument mapping to the ... parameter.
+ vars := make([]*Var, npars-1) // npars > 0 for variadic functions
+ copy(vars, sig.params.vars)
+ last := sig.params.vars[npars-1]
+ typ := last.typ.(*Slice).elem
+ for len(vars) < nargs {
+ vars = append(vars, NewParam(last.pos, last.pkg, last.name, typ))
+ }
+ sig_params = NewTuple(vars...) // possibly nil!
+ adjusted = true
+ npars = nargs
+ } else {
+ // nargs < npars-1
+ npars-- // for correct error message below
+ }
+ }
+ } else {
+ if ddd {
+ // standard_func(a, b, c...)
+ //check.errorf(call.Ellipsis, "cannot use ... in call to non-variadic %s", call.Fun)
+ check.errorf(call, "cannot use ... in call to non-variadic %s", call.Fun)
+ return
+ }
+ // standard_func(a, b, c)
+ }
+
+ // check argument count
+ switch {
+ case nargs < npars:
+ check.errorf(call, "not enough arguments in call to %s", call.Fun)
+ return
+ case nargs > npars:
+ check.errorf(args[npars], "too many arguments in call to %s", call.Fun) // report at first extra argument
+ return
+ }
+
+ // infer type arguments and instantiate signature if necessary
+ if len(sig.tparams) > 0 {
+ // TODO(gri) provide position information for targs so we can feed
+ // it to the instantiate call for better error reporting
+ targs, failed := check.infer(sig.tparams, sig_params, args)
+ if targs == nil {
+ return // error already reported
+ }
+ if failed >= 0 {
+ // Some type arguments couldn't be inferred. Use
+ // bounds type inference to try to make progress.
+ if check.conf.InferFromConstraints {
+ targs, failed = check.inferB(sig.tparams, targs)
+ if targs == nil {
+ return // error already reported
+ }
+ }
+ if failed >= 0 {
+ // at least one type argument couldn't be inferred
+ assert(targs[failed] == nil)
+ tpar := sig.tparams[failed]
+ // TODO(gri) here we'd like to use the position of the call's ')'
+ check.errorf(call.Pos(), "cannot infer %s (%s) (%s)", tpar.name, tpar.pos, targs)
+ return
+ }
+ }
+ // all type arguments were inferred sucessfully
+ if debug {
+ for _, targ := range targs {
+ assert(targ != nil)
+ }
+ }
+ //check.dump("### inferred targs = %s", targs)
+
+ // compute result signature
+ rsig = check.instantiate(call.Pos(), sig, targs, nil).(*Signature)
+ assert(rsig.tparams == nil) // signature is not generic anymore
+ check.recordInferred(call, targs, rsig)
+
+ // Optimization: Only if the parameter list was adjusted do we
+ // need to compute it from the adjusted list; otherwise we can
+ // simply use the result signature's parameter list.
+ if adjusted {
+ sig_params = check.subst(call.Pos(), sig_params, makeSubstMap(sig.tparams, targs)).(*Tuple)
+ } else {
+ sig_params = rsig.params
+ }
+ }
+
+ // check arguments
+ for i, a := range args {
+ check.assignment(a, sig_params.vars[i].typ, "argument")
+ }
+
+ return
+}
+
+var cgoPrefixes = [...]string{
+ "_Ciconst_",
+ "_Cfconst_",
+ "_Csconst_",
+ "_Ctype_",
+ "_Cvar_", // actually a pointer to the var
+ "_Cfpvar_fp_",
+ "_Cfunc_",
+ "_Cmacro_", // function to evaluate the expanded expression
+}
+
+func (check *Checker) selector(x *operand, e *syntax.SelectorExpr) {
+ // these must be declared before the "goto Error" statements
+ var (
+ obj Object
+ index []int
+ indirect bool
+ )
+
+ sel := e.Sel.Value
+ // If the identifier refers to a package, handle everything here
+ // so we don't need a "package" mode for operands: package names
+ // can only appear in qualified identifiers which are mapped to
+ // selector expressions.
+ if ident, ok := e.X.(*syntax.Name); ok {
+ obj := check.lookup(ident.Value)
+ if pname, _ := obj.(*PkgName); pname != nil {
+ assert(pname.pkg == check.pkg)
+ check.recordUse(ident, pname)
+ pname.used = true
+ pkg := pname.imported
+
+ var exp Object
+ funcMode := value
+ if pkg.cgo {
+ // cgo special cases C.malloc: it's
+ // rewritten to _CMalloc and does not
+ // support two-result calls.
+ if sel == "malloc" {
+ sel = "_CMalloc"
+ } else {
+ funcMode = cgofunc
+ }
+ for _, prefix := range cgoPrefixes {
+ // cgo objects are part of the current package (in file
+ // _cgo_gotypes.go). Use regular lookup.
+ _, exp = check.scope.LookupParent(prefix+sel, check.pos)
+ if exp != nil {
+ break
+ }
+ }
+ if exp == nil {
+ check.errorf(e.Sel, "%s not declared by package C", sel)
+ goto Error
+ }
+ check.objDecl(exp, nil)
+ } else {
+ exp = pkg.scope.Lookup(sel)
+ if exp == nil {
+ if !pkg.fake {
+ check.errorf(e.Sel, "%s not declared by package %s", sel, pkg.name)
+ }
+ goto Error
+ }
+ if !exp.Exported() {
+ check.errorf(e.Sel, "%s not exported by package %s", sel, pkg.name)
+ // ok to continue
+ }
+ }
+ check.recordUse(e.Sel, exp)
+
+ // Simplified version of the code for *syntax.Names:
+ // - imported objects are always fully initialized
+ switch exp := exp.(type) {
+ case *Const:
+ assert(exp.Val() != nil)
+ x.mode = constant_
+ x.typ = exp.typ
+ x.val = exp.val
+ case *TypeName:
+ x.mode = typexpr
+ x.typ = exp.typ
+ case *Var:
+ x.mode = variable
+ x.typ = exp.typ
+ if pkg.cgo && strings.HasPrefix(exp.name, "_Cvar_") {
+ x.typ = x.typ.(*Pointer).base
+ }
+ case *Func:
+ x.mode = funcMode
+ x.typ = exp.typ
+ if pkg.cgo && strings.HasPrefix(exp.name, "_Cmacro_") {
+ x.mode = value
+ x.typ = x.typ.(*Signature).results.vars[0].typ
+ }
+ case *Builtin:
+ x.mode = builtin
+ x.typ = exp.typ
+ x.id = exp.id
+ default:
+ check.dump("%v: unexpected object %v", posFor(e.Sel), exp)
+ unreachable()
+ }
+ x.expr = e
+ return
+ }
+ }
+
+ check.exprOrType(x, e.X)
+ if x.mode == invalid {
+ goto Error
+ }
+
+ check.instantiatedOperand(x)
+
+ obj, index, indirect = check.lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel)
+ if obj == nil {
+ switch {
+ case index != nil:
+ // TODO(gri) should provide actual type where the conflict happens
+ check.errorf(e.Sel, "ambiguous selector %s.%s", x.expr, sel)
+ case indirect:
+ check.errorf(e.Sel, "cannot call pointer method %s on %s", sel, x.typ)
+ default:
+ var why string
+ if tpar := x.typ.TypeParam(); tpar != nil {
+ // Type parameter bounds don't specify fields, so don't mention "field".
+ switch obj := tpar.Bound().obj.(type) {
+ case nil:
+ why = check.sprintf("type bound for %s has no method %s", x.typ, sel)
+ case *TypeName:
+ why = check.sprintf("interface %s has no method %s", obj.name, sel)
+ }
+ } else {
+ why = check.sprintf("type %s has no field or method %s", x.typ, sel)
+ }
+
+ // Check if capitalization of sel matters and provide better error message in that case.
+ if len(sel) > 0 {
+ var changeCase string
+ if r := rune(sel[0]); unicode.IsUpper(r) {
+ changeCase = string(unicode.ToLower(r)) + sel[1:]
+ } else {
+ changeCase = string(unicode.ToUpper(r)) + sel[1:]
+ }
+ if obj, _, _ = check.lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, changeCase); obj != nil {
+ why += ", but does have " + changeCase
+ }
+ }
+
+ check.errorf(e.Sel, "%s.%s undefined (%s)", x.expr, sel, why)
+
+ }
+ goto Error
+ }
+
+ // methods may not have a fully set up signature yet
+ if m, _ := obj.(*Func); m != nil {
+ // check.dump("### found method %s", m)
+ check.objDecl(m, nil)
+ // If m has a parameterized receiver type, infer the type parameter
+ // values from the actual receiver provided and then substitute the
+ // type parameters in the signature accordingly.
+ // TODO(gri) factor this code out
+ sig := m.typ.(*Signature)
+ if len(sig.rparams) > 0 {
+ //check.dump("### recv typ = %s", x.typ)
+ //check.dump("### method = %s rparams = %s tparams = %s", m, sig.rparams, sig.tparams)
+ // The method may have a pointer receiver, but the actually provided receiver
+ // may be a (hopefully addressable) non-pointer value, or vice versa. Here we
+ // only care about inferring receiver type parameters; to make the inference
+ // work, match up pointer-ness of receiver and argument.
+ arg := x
+ if ptrRecv := isPointer(sig.recv.typ); ptrRecv != isPointer(arg.typ) {
+ copy := *arg
+ if ptrRecv {
+ copy.typ = NewPointer(arg.typ)
+ } else {
+ copy.typ = arg.typ.(*Pointer).base
+ }
+ arg = &copy
+ }
+ targs, failed := check.infer(sig.rparams, NewTuple(sig.recv), []*operand{arg})
+ //check.dump("### inferred targs = %s", targs)
+ if failed >= 0 {
+ // We may reach here if there were other errors (see issue #40056).
+ // check.infer will report a follow-up error.
+ // TODO(gri) avoid the follow-up error or provide better explanation.
+ goto Error
+ }
+ // Don't modify m. Instead - for now - make a copy of m and use that instead.
+ // (If we modify m, some tests will fail; possibly because the m is in use.)
+ // TODO(gri) investigate and provide a correct explanation here
+ copy := *m
+ copy.typ = check.subst(e.Pos(), m.typ, makeSubstMap(sig.rparams, targs))
+ obj = &copy
+ }
+ // TODO(gri) we also need to do substitution for parameterized interface methods
+ // (this breaks code in testdata/linalg.go2 at the moment)
+ // 12/20/2019: Is this TODO still correct?
+ }
+
+ if x.mode == typexpr {
+ // method expression
+ m, _ := obj.(*Func)
+ if m == nil {
+ // TODO(gri) should check if capitalization of sel matters and provide better error message in that case
+ check.errorf(e.Sel, "%s.%s undefined (type %s has no method %s)", x.expr, sel, x.typ, sel)
+ goto Error
+ }
+
+ check.recordSelection(e, MethodExpr, x.typ, m, index, indirect)
+
+ // the receiver type becomes the type of the first function
+ // argument of the method expression's function type
+ var params []*Var
+ sig := m.typ.(*Signature)
+ if sig.params != nil {
+ params = sig.params.vars
+ }
+ x.mode = value
+ x.typ = &Signature{
+ tparams: sig.tparams,
+ params: NewTuple(append([]*Var{NewVar(nopos, check.pkg, "_", x.typ)}, params...)...),
+ results: sig.results,
+ variadic: sig.variadic,
+ }
+
+ check.addDeclDep(m)
+
+ } else {
+ // regular selector
+ switch obj := obj.(type) {
+ case *Var:
+ check.recordSelection(e, FieldVal, x.typ, obj, index, indirect)
+ if x.mode == variable || indirect {
+ x.mode = variable
+ } else {
+ x.mode = value
+ }
+ x.typ = obj.typ
+
+ case *Func:
+ // TODO(gri) If we needed to take into account the receiver's
+ // addressability, should we report the type &(x.typ) instead?
+ check.recordSelection(e, MethodVal, x.typ, obj, index, indirect)
+
+ // TODO(gri) The verification pass below is disabled for now because
+ // method sets don't match method lookup in some cases.
+ // For instance, if we made a copy above when creating a
+ // custom method for a parameterized received type, the
+ // method set method doesn't match (no copy there). There
+ /// may be other situations.
+ disabled := true
+ if !disabled && debug {
+ // Verify that LookupFieldOrMethod and MethodSet.Lookup agree.
+ // TODO(gri) This only works because we call LookupFieldOrMethod
+ // _before_ calling NewMethodSet: LookupFieldOrMethod completes
+ // any incomplete interfaces so they are available to NewMethodSet
+ // (which assumes that interfaces have been completed already).
+ typ := x.typ
+ if x.mode == variable {
+ // If typ is not an (unnamed) pointer or an interface,
+ // use *typ instead, because the method set of *typ
+ // includes the methods of typ.
+ // Variables are addressable, so we can always take their
+ // address.
+ if _, ok := typ.(*Pointer); !ok && !IsInterface(typ) {
+ typ = &Pointer{base: typ}
+ }
+ }
+ // If we created a synthetic pointer type above, we will throw
+ // away the method set computed here after use.
+ // TODO(gri) Method set computation should probably always compute
+ // both, the value and the pointer receiver method set and represent
+ // them in a single structure.
+ // TODO(gri) Consider also using a method set cache for the lifetime
+ // of checker once we rely on MethodSet lookup instead of individual
+ // lookup.
+ mset := NewMethodSet(typ)
+ if m := mset.Lookup(check.pkg, sel); m == nil || m.obj != obj {
+ check.dump("%v: (%s).%v -> %s", posFor(e), typ, obj.name, m)
+ check.dump("%s\n", mset)
+ // Caution: MethodSets are supposed to be used externally
+ // only (after all interface types were completed). It's
+ // now possible that we get here incorrectly. Not urgent
+ // to fix since we only run this code in debug mode.
+ // TODO(gri) fix this eventually.
+ panic("method sets and lookup don't agree")
+ }
+ }
+
+ x.mode = value
+
+ // remove receiver
+ sig := *obj.typ.(*Signature)
+ sig.recv = nil
+ x.typ = &sig
+
+ check.addDeclDep(obj)
+
+ default:
+ unreachable()
+ }
+ }
+
+ // everything went well
+ x.expr = e
+ return
+
+Error:
+ x.mode = invalid
+ x.expr = e
+}
+
+// use type-checks each argument.
+// Useful to make sure expressions are evaluated
+// (and variables are "used") in the presence of other errors.
+// The arguments may be nil.
+// TODO(gri) make this accept a []syntax.Expr and use an unpack function when we have a ListExpr?
+func (check *Checker) use(arg ...syntax.Expr) {
+ var x operand
+ for _, e := range arg {
+ // Certain AST fields may legally be nil (e.g., the ast.SliceExpr.High field).
+ if e == nil {
+ continue
+ }
+ if l, _ := e.(*syntax.ListExpr); l != nil {
+ check.use(l.ElemList...)
+ continue
+ }
+ check.rawExpr(&x, e, nil)
+ }
+}
+
+// useLHS is like use, but doesn't "use" top-level identifiers.
+// It should be called instead of use if the arguments are
+// expressions on the lhs of an assignment.
+// The arguments must not be nil.
+func (check *Checker) useLHS(arg ...syntax.Expr) {
+ var x operand
+ for _, e := range arg {
+ // If the lhs is an identifier denoting a variable v, this assignment
+ // is not a 'use' of v. Remember current value of v.used and restore
+ // after evaluating the lhs via check.rawExpr.
+ var v *Var
+ var v_used bool
+ if ident, _ := unparen(e).(*syntax.Name); ident != nil {
+ // never type-check the blank name on the lhs
+ if ident.Value == "_" {
+ continue
+ }
+ if _, obj := check.scope.LookupParent(ident.Value, nopos); obj != nil {
+ // It's ok to mark non-local variables, but ignore variables
+ // from other packages to avoid potential race conditions with
+ // dot-imported variables.
+ if w, _ := obj.(*Var); w != nil && w.pkg == check.pkg {
+ v = w
+ v_used = v.used
+ }
+ }
+ }
+ check.rawExpr(&x, e, nil)
+ if v != nil {
+ v.used = v_used // restore v.used
+ }
+ }
+}
+
+// instantiatedOperand reports an error of x is an uninstantiated (generic) type and sets x.typ to Typ[Invalid].
+func (check *Checker) instantiatedOperand(x *operand) {
+ if x.mode == typexpr && isGeneric(x.typ) {
+ check.errorf(x, "cannot use generic type %s without instantiation", x.typ)
+ x.typ = Typ[Invalid]
+ }
+}
diff --git a/src/cmd/compile/internal/types2/check.go b/src/cmd/compile/internal/types2/check.go
new file mode 100644
index 0000000000..4504586545
--- /dev/null
+++ b/src/cmd/compile/internal/types2/check.go
@@ -0,0 +1,449 @@
+// UNREVIEWED
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements the Check function, which drives type-checking.
+
+package types2
+
+import (
+ "cmd/compile/internal/syntax"
+ "errors"
+ "fmt"
+ "go/constant"
+)
+
+var nopos syntax.Pos
+
+// debugging/development support
+const debug = true // leave on during development
+
+// If forceStrict is set, the type-checker enforces additional
+// rules not specified by the Go 1 spec, but which will
+// catch guaranteed run-time errors if the respective
+// code is executed. In other words, programs passing in
+// strict mode are Go 1 compliant, but not all Go 1 programs
+// will pass in strict mode. The additional rules are:
+//
+// - A type assertion x.(T) where T is an interface type
+// is invalid if any (statically known) method that exists
+// for both x and T have different signatures.
+//
+const forceStrict = false
+
+// If methodTypeParamsOk is set, type parameters are
+// permitted in method declarations (in interfaces, too).
+// Generalization and experimental feature.
+const methodTypeParamsOk = true
+
+// exprInfo stores information about an untyped expression.
+type exprInfo struct {
+ isLhs bool // expression is lhs operand of a shift with delayed type-check
+ mode operandMode
+ typ *Basic
+ val constant.Value // constant value; or nil (if not a constant)
+}
+
+// A context represents the context within which an object is type-checked.
+type context struct {
+ decl *declInfo // package-level declaration whose init expression/function body is checked
+ scope *Scope // top-most scope for lookups
+ pos syntax.Pos // if valid, identifiers are looked up as if at position pos (used by Eval)
+ iota constant.Value // value of iota in a constant declaration; nil otherwise
+ sig *Signature // function signature if inside a function; nil otherwise
+ isPanic map[*syntax.CallExpr]bool // set of panic call expressions (used for termination check)
+ hasLabel bool // set if a function makes use of labels (only ~1% of functions); unused outside functions
+ hasCallOrRecv bool // set if an expression contains a function call or channel receive operation
+}
+
+// lookup looks up name in the current context and returns the matching object, or nil.
+func (ctxt *context) lookup(name string) Object {
+ _, obj := ctxt.scope.LookupParent(name, ctxt.pos)
+ return obj
+}
+
+// An importKey identifies an imported package by import path and source directory
+// (directory containing the file containing the import). In practice, the directory
+// may always be the same, or may not matter. Given an (import path, directory), an
+// importer must always return the same package (but given two different import paths,
+// an importer may still return the same package by mapping them to the same package
+// paths).
+type importKey struct {
+ path, dir string
+}
+
+// A Checker maintains the state of the type checker.
+// It must be created with NewChecker.
+type Checker struct {
+ // package information
+ // (initialized by NewChecker, valid for the life-time of checker)
+ conf *Config
+ pkg *Package
+ *Info
+ nextId uint64 // unique Id for type parameters (first valid Id is 1)
+ objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info
+ impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package
+ posMap map[*Interface][]syntax.Pos // maps interface types to lists of embedded interface positions
+ typMap map[string]*Named // maps an instantiated named type hash to a *Named type
+ pkgCnt map[string]int // counts number of imported packages with a given name (for better error messages)
+
+ // information collected during type-checking of a set of package files
+ // (initialized by Files, valid only for the duration of check.Files;
+ // maps and lists are allocated on demand)
+ files []*syntax.File // package files
+ unusedDotImports map[*Scope]map[*Package]syntax.Pos // positions of unused dot-imported packages for each file scope
+
+ firstErr error // first error encountered
+ methods map[*TypeName][]*Func // maps package scope type names to associated non-blank (non-interface) methods
+ untyped map[syntax.Expr]exprInfo // map of expressions without final type
+ delayed []func() // stack of delayed action segments; segments are processed in FIFO order
+ finals []func() // list of final actions; processed at the end of type-checking the current set of files
+ objPath []Object // path of object dependencies during type inference (for cycle reporting)
+
+ // context within which the current object is type-checked
+ // (valid only for the duration of type-checking a specific object)
+ context
+
+ // debugging
+ indent int // indentation for tracing
+}
+
+// addUnusedImport adds the position of a dot-imported package
+// pkg to the map of dot imports for the given file scope.
+func (check *Checker) addUnusedDotImport(scope *Scope, pkg *Package, pos syntax.Pos) {
+ mm := check.unusedDotImports
+ if mm == nil {
+ mm = make(map[*Scope]map[*Package]syntax.Pos)
+ check.unusedDotImports = mm
+ }
+ m := mm[scope]
+ if m == nil {
+ m = make(map[*Package]syntax.Pos)
+ mm[scope] = m
+ }
+ m[pkg] = pos
+}
+
+// addDeclDep adds the dependency edge (check.decl -> to) if check.decl exists
+func (check *Checker) addDeclDep(to Object) {
+ from := check.decl
+ if from == nil {
+ return // not in a package-level init expression
+ }
+ if _, found := check.objMap[to]; !found {
+ return // to is not a package-level object
+ }
+ from.addDep(to)
+}
+
+func (check *Checker) rememberUntyped(e syntax.Expr, lhs bool, mode operandMode, typ *Basic, val constant.Value) {
+ m := check.untyped
+ if m == nil {
+ m = make(map[syntax.Expr]exprInfo)
+ check.untyped = m
+ }
+ m[e] = exprInfo{lhs, mode, typ, val}
+}
+
+// later pushes f on to the stack of actions that will be processed later;
+// either at the end of the current statement, or in case of a local constant
+// or variable declaration, before the constant or variable is in scope
+// (so that f still sees the scope before any new declarations).
+func (check *Checker) later(f func()) {
+ check.delayed = append(check.delayed, f)
+}
+
+// atEnd adds f to the list of actions processed at the end
+// of type-checking, before initialization order computation.
+// Actions added by atEnd are processed after any actions
+// added by later.
+func (check *Checker) atEnd(f func()) {
+ check.finals = append(check.finals, f)
+}
+
+// push pushes obj onto the object path and returns its index in the path.
+func (check *Checker) push(obj Object) int {
+ check.objPath = append(check.objPath, obj)
+ return len(check.objPath) - 1
+}
+
+// pop pops and returns the topmost object from the object path.
+func (check *Checker) pop() Object {
+ i := len(check.objPath) - 1
+ obj := check.objPath[i]
+ check.objPath[i] = nil
+ check.objPath = check.objPath[:i]
+ return obj
+}
+
+// NewChecker returns a new Checker instance for a given package.
+// Package files may be added incrementally via checker.Files.
+func NewChecker(conf *Config, pkg *Package, info *Info) *Checker {
+ // make sure we have a configuration
+ if conf == nil {
+ conf = new(Config)
+ }
+
+ // make sure we have an info struct
+ if info == nil {
+ info = new(Info)
+ }
+
+ return &Checker{
+ conf: conf,
+ pkg: pkg,
+ Info: info,
+ nextId: 1,
+ objMap: make(map[Object]*declInfo),
+ impMap: make(map[importKey]*Package),
+ posMap: make(map[*Interface][]syntax.Pos),
+ typMap: make(map[string]*Named),
+ pkgCnt: make(map[string]int),
+ }
+}
+
+// initFiles initializes the files-specific portion of checker.
+// The provided files must all belong to the same package.
+func (check *Checker) initFiles(files []*syntax.File) {
+ // start with a clean slate (check.Files may be called multiple times)
+ check.files = nil
+ check.unusedDotImports = nil
+
+ check.firstErr = nil
+ check.methods = nil
+ check.untyped = nil
+ check.delayed = nil
+ check.finals = nil
+
+ // determine package name and collect valid files
+ pkg := check.pkg
+ for _, file := range files {
+ switch name := file.PkgName.Value; pkg.name {
+ case "":
+ if name != "_" {
+ pkg.name = name
+ } else {
+ check.errorf(file.PkgName, "invalid package name _")
+ }
+ fallthrough
+
+ case name:
+ check.files = append(check.files, file)
+
+ default:
+ check.errorf(file, "package %s; expected %s", name, pkg.name)
+ // ignore this file
+ }
+ }
+}
+
+// A bailout panic is used for early termination.
+type bailout struct{}
+
+func (check *Checker) handleBailout(err *error) {
+ switch p := recover().(type) {
+ case nil, bailout:
+ // normal return or early exit
+ *err = check.firstErr
+ default:
+ // re-panic
+ panic(p)
+ }
+}
+
+// Files checks the provided files as part of the checker's package.
+func (check *Checker) Files(files []*syntax.File) error { return check.checkFiles(files) }
+
+var errBadCgo = errors.New("cannot use FakeImportC and go115UsesCgo together")
+
+func (check *Checker) checkFiles(files []*syntax.File) (err error) {
+ if check.conf.FakeImportC && check.conf.go115UsesCgo {
+ return errBadCgo
+ }
+
+ defer check.handleBailout(&err)
+
+ print := func(msg string) {
+ if check.conf.Trace {
+ fmt.Println(msg)
+ }
+ }
+
+ print("== initFiles ==")
+ check.initFiles(files)
+
+ print("== collectObjects ==")
+ check.collectObjects()
+
+ print("== packageObjects ==")
+ check.packageObjects()
+
+ print("== processDelayed ==")
+ check.processDelayed(0) // incl. all functions
+ check.processFinals()
+
+ print("== initOrder ==")
+ check.initOrder()
+
+ if !check.conf.DisableUnusedImportCheck {
+ print("== unusedImports ==")
+ check.unusedImports()
+ }
+
+ print("== recordUntyped ==")
+ check.recordUntyped()
+
+ if check.Info != nil {
+ print("== sanitizeInfo ==")
+ sanitizeInfo(check.Info)
+ }
+
+ check.pkg.complete = true
+ return
+}
+
+// processDelayed processes all delayed actions pushed after top.
+func (check *Checker) processDelayed(top int) {
+ // If each delayed action pushes a new action, the
+ // stack will continue to grow during this loop.
+ // However, it is only processing functions (which
+ // are processed in a delayed fashion) that may
+ // add more actions (such as nested functions), so
+ // this is a sufficiently bounded process.
+ for i := top; i < len(check.delayed); i++ {
+ check.delayed[i]() // may append to check.delayed
+ }
+ assert(top <= len(check.delayed)) // stack must not have shrunk
+ check.delayed = check.delayed[:top]
+}
+
+func (check *Checker) processFinals() {
+ n := len(check.finals)
+ for _, f := range check.finals {
+ f() // must not append to check.finals
+ }
+ if len(check.finals) != n {
+ panic("internal error: final action list grew")
+ }
+}
+
+func (check *Checker) recordUntyped() {
+ if !debug && check.Types == nil {
+ return // nothing to do
+ }
+
+ for x, info := range check.untyped {
+ if debug && isTyped(info.typ) {
+ check.dump("%v: %s (type %s) is typed", posFor(x), x, info.typ)
+ unreachable()
+ }
+ check.recordTypeAndValue(x, info.mode, info.typ, info.val)
+ }
+}
+
+func (check *Checker) recordTypeAndValue(x syntax.Expr, mode operandMode, typ Type, val constant.Value) {
+ assert(x != nil)
+ assert(typ != nil)
+ if mode == invalid {
+ return // omit
+ }
+ if mode == constant_ {
+ assert(val != nil)
+ assert(typ == Typ[Invalid] || isConstType(typ))
+ }
+ if m := check.Types; m != nil {
+ m[x] = TypeAndValue{mode, typ, val}
+ }
+}
+
+func (check *Checker) recordBuiltinType(f syntax.Expr, sig *Signature) {
+ // f must be a (possibly parenthesized) identifier denoting a built-in
+ // (built-ins in package unsafe always produce a constant result and
+ // we don't record their signatures, so we don't see qualified idents
+ // here): record the signature for f and possible children.
+ for {
+ check.recordTypeAndValue(f, builtin, sig, nil)
+ switch p := f.(type) {
+ case *syntax.Name:
+ return // we're done
+ case *syntax.ParenExpr:
+ f = p.X
+ default:
+ unreachable()
+ }
+ }
+}
+
+func (check *Checker) recordCommaOkTypes(x syntax.Expr, a [2]Type) {
+ assert(x != nil)
+ if a[0] == nil || a[1] == nil {
+ return
+ }
+ assert(isTyped(a[0]) && isTyped(a[1]) && (isBoolean(a[1]) || a[1] == universeError))
+ if m := check.Types; m != nil {
+ for {
+ tv := m[x]
+ assert(tv.Type != nil) // should have been recorded already
+ pos := x.Pos()
+ tv.Type = NewTuple(
+ NewVar(pos, check.pkg, "", a[0]),
+ NewVar(pos, check.pkg, "", a[1]),
+ )
+ m[x] = tv
+ // if x is a parenthesized expression (p.X), update p.X
+ p, _ := x.(*syntax.ParenExpr)
+ if p == nil {
+ break
+ }
+ x = p.X
+ }
+ }
+}
+
+func (check *Checker) recordInferred(call syntax.Expr, targs []Type, sig *Signature) {
+ assert(call != nil)
+ assert(sig != nil)
+ if m := check.Inferred; m != nil {
+ m[call] = Inferred{targs, sig}
+ }
+}
+
+func (check *Checker) recordDef(id *syntax.Name, obj Object) {
+ assert(id != nil)
+ if m := check.Defs; m != nil {
+ m[id] = obj
+ }
+}
+
+func (check *Checker) recordUse(id *syntax.Name, obj Object) {
+ assert(id != nil)
+ assert(obj != nil)
+ if m := check.Uses; m != nil {
+ m[id] = obj
+ }
+}
+
+func (check *Checker) recordImplicit(node syntax.Node, obj Object) {
+ assert(node != nil)
+ assert(obj != nil)
+ if m := check.Implicits; m != nil {
+ m[node] = obj
+ }
+}
+
+func (check *Checker) recordSelection(x *syntax.SelectorExpr, kind SelectionKind, recv Type, obj Object, index []int, indirect bool) {
+ assert(obj != nil && (recv == nil || len(index) > 0))
+ check.recordUse(x.Sel, obj)
+ if m := check.Selections; m != nil {
+ m[x] = &Selection{kind, recv, obj, index, indirect}
+ }
+}
+
+func (check *Checker) recordScope(node syntax.Node, scope *Scope) {
+ assert(node != nil)
+ assert(scope != nil)
+ if m := check.Scopes; m != nil {
+ m[node] = scope
+ }
+}
diff --git a/src/cmd/compile/internal/types2/check_test.go b/src/cmd/compile/internal/types2/check_test.go
new file mode 100644
index 0000000000..4dac76ea80
--- /dev/null
+++ b/src/cmd/compile/internal/types2/check_test.go
@@ -0,0 +1,269 @@
+// UNREVIEWED
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements a typechecker test harness. The packages specified
+// in tests are typechecked. Error messages reported by the typechecker are
+// compared against the error messages expected in the test files.
+//
+// Expected errors are indicated in the test files by putting a comment
+// of the form /* ERROR "rx" */ immediately following an offending token.
+// The harness will verify that an error matching the regular expression
+// rx is reported at that source position. Consecutive comments may be
+// used to indicate multiple errors for the same token position.
+//
+// For instance, the following test file indicates that a "not declared"
+// error should be reported for the undeclared variable x:
+//
+// package p
+// func f() {
+// _ = x /* ERROR "not declared" */ + 1
+// }
+
+// TODO(gri) Also collect strict mode errors of the form /* STRICT ... */
+// and test against strict mode.
+
+package types2_test
+
+import (
+ "cmd/compile/internal/syntax"
+ "flag"
+ "fmt"
+ "internal/testenv"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "regexp"
+ "strings"
+ "testing"
+
+ . "cmd/compile/internal/types2"
+)
+
+var (
+ haltOnError = flag.Bool("halt", false, "halt on error")
+ listErrors = flag.Bool("errlist", false, "list errors")
+ testFiles = flag.String("files", "", "space-separated list of test files")
+)
+
+func parseFiles(t *testing.T, filenames []string) ([]*syntax.File, []error) {
+ var files []*syntax.File
+ var errlist []error
+ errh := func(err error) { errlist = append(errlist, err) }
+ for _, filename := range filenames {
+ file, err := syntax.ParseFile(filename, errh, nil, syntax.AllowGenerics)
+ if file == nil {
+ t.Fatalf("%s: %s", filename, err)
+ }
+ files = append(files, file)
+ }
+ return files, errlist
+}
+
+func unpackError(err error) syntax.Error {
+ switch err := err.(type) {
+ case syntax.Error:
+ return err
+ case Error:
+ return syntax.Error{Pos: err.Pos, Msg: err.Msg}
+ default:
+ return syntax.Error{Msg: err.Error()}
+ }
+}
+
+func delta(x, y uint) uint {
+ switch {
+ case x < y:
+ return y - x
+ case x > y:
+ return x - y
+ default:
+ return 0
+ }
+}
+
+func checkFiles(t *testing.T, sources []string, colDelta uint, trace bool) {
+ // parse files and collect parser errors
+ files, errlist := parseFiles(t, sources)
+
+ pkgName := "<no package>"
+ if len(files) > 0 {
+ pkgName = files[0].PkgName.Value
+ }
+
+ if *listErrors && len(errlist) > 0 {
+ t.Errorf("--- %s:", pkgName)
+ for _, err := range errlist {
+ t.Error(err)
+ }
+ }
+
+ // typecheck and collect typechecker errors
+ var conf Config
+ conf.AcceptMethodTypeParams = true
+ conf.InferFromConstraints = true
+ // special case for importC.src
+ if len(sources) == 1 && strings.HasSuffix(sources[0], "importC.src") {
+ conf.FakeImportC = true
+ }
+ conf.Trace = trace
+ conf.Importer = defaultImporter()
+ conf.Error = func(err error) {
+ if *haltOnError {
+ defer panic(err)
+ }
+ if *listErrors {
+ t.Error(err)
+ return
+ }
+ // Ignore secondary error messages starting with "\t";
+ // they are clarifying messages for a primary error.
+ if !strings.Contains(err.Error(), ": \t") {
+ errlist = append(errlist, err)
+ }
+ }
+ conf.Check(pkgName, files, nil)
+
+ if *listErrors {
+ return
+ }
+
+ // collect expected errors
+ errmap := make(map[string]map[uint][]syntax.Error)
+ for _, filename := range sources {
+ f, err := os.Open(filename)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ if m := syntax.ErrorMap(f); len(m) > 0 {
+ errmap[filename] = m
+ }
+ f.Close()
+ }
+
+ // match against found errors
+ for _, err := range errlist {
+ got := unpackError(err)
+
+ // find list of errors for the respective error line
+ filename := got.Pos.Base().Filename()
+ filemap := errmap[filename]
+ var line uint
+ var list []syntax.Error
+ if filemap != nil {
+ line = got.Pos.Line()
+ list = filemap[line]
+ }
+ // list may be nil
+
+ // one of errors in list should match the current error
+ index := -1 // list index of matching message, if any
+ for i, want := range list {
+ rx, err := regexp.Compile(want.Msg)
+ if err != nil {
+ t.Errorf("%s:%d:%d: %v", filename, line, want.Pos.Col(), err)
+ continue
+ }
+ if rx.MatchString(got.Msg) {
+ index = i
+ break
+ }
+ }
+ if index < 0 {
+ t.Errorf("%s: no error expected: %q", got.Pos, got.Msg)
+ continue
+ }
+
+ // column position must be within expected colDelta
+ want := list[index]
+ if delta(got.Pos.Col(), want.Pos.Col()) > colDelta {
+ t.Errorf("%s: got col = %d; want %d", got.Pos, got.Pos.Col(), want.Pos.Col())
+ }
+
+ // eliminate from list
+ if n := len(list) - 1; n > 0 {
+ // not the last entry - swap in last element and shorten list by 1
+ list[index] = list[n]
+ filemap[line] = list[:n]
+ } else {
+ // last entry - remove list from filemap
+ delete(filemap, line)
+ }
+
+ // if filemap is empty, eliminate from errmap
+ if len(filemap) == 0 {
+ delete(errmap, filename)
+ }
+ }
+
+ // there should be no expected errors left
+ if len(errmap) > 0 {
+ t.Errorf("--- %s: unreported errors:", pkgName)
+ for filename, filemap := range errmap {
+ for line, list := range filemap {
+ for _, err := range list {
+ t.Errorf("%s:%d:%d: %s", filename, line, err.Pos.Col(), err.Msg)
+ }
+ }
+ }
+ }
+}
+
+// TestCheck is for manual testing of selected input files, provided with -files.
+func TestCheck(t *testing.T) {
+ if *testFiles == "" {
+ return
+ }
+ testenv.MustHaveGoBuild(t)
+ DefPredeclaredTestFuncs()
+ checkFiles(t, strings.Split(*testFiles, " "), 0, testing.Verbose())
+}
+
+// TODO(gri) Enable once we have added the testdata tests.
+// func TestTestdata(t *testing.T) { DefPredeclaredTestFuncs(); testDir(t, 75, "testdata") } // TODO(gri) narrow column tolerance
+func TestExamples(t *testing.T) { testDir(t, 0, "examples") }
+func TestFixedbugs(t *testing.T) { testDir(t, 0, "fixedbugs") }
+
+func testDir(t *testing.T, colDelta uint, dir string) {
+ testenv.MustHaveGoBuild(t)
+
+ fis, err := ioutil.ReadDir(dir)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ for count, fi := range fis {
+ path := filepath.Join(dir, fi.Name())
+
+ // if fi is a directory, its files make up a single package
+ if fi.IsDir() {
+ if testing.Verbose() {
+ fmt.Printf("%3d %s\n", count, path)
+ }
+ fis, err := ioutil.ReadDir(path)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ files := make([]string, len(fis))
+ for i, fi := range fis {
+ // if fi is a directory, checkFiles below will complain
+ files[i] = filepath.Join(path, fi.Name())
+ if testing.Verbose() {
+ fmt.Printf("\t%s\n", files[i])
+ }
+ }
+ checkFiles(t, files, colDelta, false)
+ continue
+ }
+
+ // otherwise, fi is a stand-alone file
+ if testing.Verbose() {
+ fmt.Printf("%3d %s\n", count, path)
+ }
+ checkFiles(t, []string{path}, colDelta, false)
+ }
+}
diff --git a/src/cmd/compile/internal/types2/conversions.go b/src/cmd/compile/internal/types2/conversions.go
new file mode 100644
index 0000000000..9ff548593f
--- /dev/null
+++ b/src/cmd/compile/internal/types2/conversions.go
@@ -0,0 +1,164 @@
+// UNREVIEWED
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements typechecking of conversions.
+
+package types2
+
+import "go/constant"
+
+// Conversion type-checks the conversion T(x).
+// The result is in x.
+func (check *Checker) conversion(x *operand, T Type) {
+ constArg := x.mode == constant_
+
+ var ok bool
+ switch {
+ case constArg && isConstType(T):
+ // constant conversion
+ switch t := T.Basic(); {
+ case representableConst(x.val, check, t, &x.val):
+ ok = true
+ case isInteger(x.typ) && isString(t):
+ codepoint := int64(-1)
+ if i, ok := constant.Int64Val(x.val); ok {
+ codepoint = i
+ }
+ // If codepoint < 0 the absolute value is too large (or unknown) for
+ // conversion. This is the same as converting any other out-of-range
+ // value - let string(codepoint) do the work.
+ x.val = constant.MakeString(string(rune(codepoint)))
+ ok = true
+ }
+ case x.convertibleTo(check, T):
+ // non-constant conversion
+ x.mode = value
+ ok = true
+ }
+
+ if !ok {
+ check.errorf(x, "cannot convert %s to %s", x, T)
+ x.mode = invalid
+ return
+ }
+
+ // The conversion argument types are final. For untyped values the
+ // conversion provides the type, per the spec: "A constant may be
+ // given a type explicitly by a constant declaration or conversion,...".
+ if isUntyped(x.typ) {
+ final := T
+ // - For conversions to interfaces, use the argument's default type.
+ // - For conversions of untyped constants to non-constant types, also
+ // use the default type (e.g., []byte("foo") should report string
+ // not []byte as type for the constant "foo").
+ // - Keep untyped nil for untyped nil arguments.
+ // - For integer to string conversions, keep the argument type.
+ // (See also the TODO below.)
+ if IsInterface(T) || constArg && !isConstType(T) {
+ final = Default(x.typ)
+ } else if isInteger(x.typ) && isString(T) {
+ final = x.typ
+ }
+ check.updateExprType(x.expr, final, true)
+ }
+
+ x.typ = T
+}
+
+// TODO(gri) convertibleTo checks if T(x) is valid. It assumes that the type
+// of x is fully known, but that's not the case for say string(1<<s + 1.0):
+// Here, the type of 1<<s + 1.0 will be UntypedFloat which will lead to the
+// (correct!) refusal of the conversion. But the reported error is essentially
+// "cannot convert untyped float value to string", yet the correct error (per
+// the spec) is that we cannot shift a floating-point value: 1 in 1<<s should
+// be converted to UntypedFloat because of the addition of 1.0. Fixing this
+// is tricky because we'd have to run updateExprType on the argument first.
+// (Issue #21982.)
+
+// convertibleTo reports whether T(x) is valid.
+// The check parameter may be nil if convertibleTo is invoked through an
+// exported API call, i.e., when all methods have been type-checked.
+func (x *operand) convertibleTo(check *Checker, T Type) bool {
+ // "x is assignable to T"
+ if x.assignableTo(check, T, nil) {
+ return true
+ }
+
+ // "x's type and T have identical underlying types if tags are ignored"
+ V := x.typ
+ Vu := V.Under()
+ Tu := T.Under()
+ if check.identicalIgnoreTags(Vu, Tu) {
+ return true
+ }
+
+ // "x's type and T are unnamed pointer types and their pointer base types
+ // have identical underlying types if tags are ignored"
+ if V, ok := V.(*Pointer); ok {
+ if T, ok := T.(*Pointer); ok {
+ if check.identicalIgnoreTags(V.base.Under(), T.base.Under()) {
+ return true
+ }
+ }
+ }
+
+ // "x's type and T are both integer or floating point types"
+ if isIntegerOrFloat(V) && isIntegerOrFloat(T) {
+ return true
+ }
+
+ // "x's type and T are both complex types"
+ if isComplex(V) && isComplex(T) {
+ return true
+ }
+
+ // "x is an integer or a slice of bytes or runes and T is a string type"
+ if (isInteger(V) || isBytesOrRunes(Vu)) && isString(T) {
+ return true
+ }
+
+ // "x is a string and T is a slice of bytes or runes"
+ if isString(V) && isBytesOrRunes(Tu) {
+ return true
+ }
+
+ // package unsafe:
+ // "any pointer or value of underlying type uintptr can be converted into a unsafe.Pointer"
+ if (isPointer(Vu) || isUintptr(Vu)) && isUnsafePointer(T) {
+ return true
+ }
+ // "and vice versa"
+ if isUnsafePointer(V) && (isPointer(Tu) || isUintptr(Tu)) {
+ return true
+ }
+
+ return false
+}
+
+func isUintptr(typ Type) bool {
+ t := typ.Basic()
+ return t != nil && t.kind == Uintptr
+}
+
+func isUnsafePointer(typ Type) bool {
+ // TODO(gri): Is this typ.Basic() instead of typ.(*Basic) correct?
+ // (The former calls typ.Under(), while the latter doesn't.)
+ // The spec does not say so, but gc claims it is. See also
+ // issue 6326.
+ t := typ.Basic()
+ return t != nil && t.kind == UnsafePointer
+}
+
+func isPointer(typ Type) bool {
+ return typ.Pointer() != nil
+}
+
+func isBytesOrRunes(typ Type) bool {
+ if s := typ.Slice(); s != nil {
+ t := s.elem.Basic()
+ return t != nil && (t.kind == Byte || t.kind == Rune)
+ }
+ return false
+}
diff --git a/src/cmd/compile/internal/types2/decl.go b/src/cmd/compile/internal/types2/decl.go
new file mode 100644
index 0000000000..ef8dc7a245
--- /dev/null
+++ b/src/cmd/compile/internal/types2/decl.go
@@ -0,0 +1,981 @@
+// UNREVIEWED
+// Copyright 2014 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 types2
+
+import (
+ "cmd/compile/internal/syntax"
+ "fmt"
+ "go/constant"
+)
+
+func (check *Checker) reportAltDecl(obj Object) {
+ if pos := obj.Pos(); pos.IsKnown() {
+ // We use "other" rather than "previous" here because
+ // the first declaration seen may not be textually
+ // earlier in the source.
+ check.errorf(pos, "\tother declaration of %s", obj.Name()) // secondary error, \t indented
+ }
+}
+
+func (check *Checker) declare(scope *Scope, id *syntax.Name, obj Object, pos syntax.Pos) {
+ // spec: "The blank identifier, represented by the underscore
+ // character _, may be used in a declaration like any other
+ // identifier but the declaration does not introduce a new
+ // binding."
+ if obj.Name() != "_" {
+ if alt := scope.Insert(obj); alt != nil {
+ check.errorf(obj.Pos(), "%s redeclared in this block", obj.Name())
+ check.reportAltDecl(alt)
+ return
+ }
+ obj.setScopePos(pos)
+ }
+ if id != nil {
+ check.recordDef(id, obj)
+ }
+}
+
+// pathString returns a string of the form a->b-> ... ->g for a path [a, b, ... g].
+func pathString(path []Object) string {
+ var s string
+ for i, p := range path {
+ if i > 0 {
+ s += "->"
+ }
+ s += p.Name()
+ }
+ return s
+}
+
+// objDecl type-checks the declaration of obj in its respective (file) context.
+// For the meaning of def, see Checker.definedType, in typexpr.go.
+func (check *Checker) objDecl(obj Object, def *Named) {
+ if check.conf.Trace && obj.Type() == nil {
+ if check.indent == 0 {
+ fmt.Println() // empty line between top-level objects for readability
+ }
+ check.trace(obj.Pos(), "-- checking %s (%s, objPath = %s)", obj, obj.color(), pathString(check.objPath))
+ check.indent++
+ defer func() {
+ check.indent--
+ check.trace(obj.Pos(), "=> %s (%s)", obj, obj.color())
+ }()
+ }
+
+ // Checking the declaration of obj means inferring its type
+ // (and possibly its value, for constants).
+ // An object's type (and thus the object) may be in one of
+ // three states which are expressed by colors:
+ //
+ // - an object whose type is not yet known is painted white (initial color)
+ // - an object whose type is in the process of being inferred is painted grey
+ // - an object whose type is fully inferred is painted black
+ //
+ // During type inference, an object's color changes from white to grey
+ // to black (pre-declared objects are painted black from the start).
+ // A black object (i.e., its type) can only depend on (refer to) other black
+ // ones. White and grey objects may depend on white and black objects.
+ // A dependency on a grey object indicates a cycle which may or may not be
+ // valid.
+ //
+ // When objects turn grey, they are pushed on the object path (a stack);
+ // they are popped again when they turn black. Thus, if a grey object (a
+ // cycle) is encountered, it is on the object path, and all the objects
+ // it depends on are the remaining objects on that path. Color encoding
+ // is such that the color value of a grey object indicates the index of
+ // that object in the object path.
+
+ // During type-checking, white objects may be assigned a type without
+ // traversing through objDecl; e.g., when initializing constants and
+ // variables. Update the colors of those objects here (rather than
+ // everywhere where we set the type) to satisfy the color invariants.
+ if obj.color() == white && obj.Type() != nil {
+ obj.setColor(black)
+ return
+ }
+
+ switch obj.color() {
+ case white:
+ assert(obj.Type() == nil)
+ // All color values other than white and black are considered grey.
+ // Because black and white are < grey, all values >= grey are grey.
+ // Use those values to encode the object's index into the object path.
+ obj.setColor(grey + color(check.push(obj)))
+ defer func() {
+ check.pop().setColor(black)
+ }()
+
+ case black:
+ assert(obj.Type() != nil)
+ return
+
+ default:
+ // Color values other than white or black are considered grey.
+ fallthrough
+
+ case grey:
+ // We have a cycle.
+ // In the existing code, this is marked by a non-nil type
+ // for the object except for constants and variables whose
+ // type may be non-nil (known), or nil if it depends on the
+ // not-yet known initialization value.
+ // In the former case, set the type to Typ[Invalid] because
+ // we have an initialization cycle. The cycle error will be
+ // reported later, when determining initialization order.
+ // TODO(gri) Report cycle here and simplify initialization
+ // order code.
+ switch obj := obj.(type) {
+ case *Const:
+ if check.cycle(obj) || obj.typ == nil {
+ obj.typ = Typ[Invalid]
+ }
+
+ case *Var:
+ if check.cycle(obj) || obj.typ == nil {
+ obj.typ = Typ[Invalid]
+ }
+
+ case *TypeName:
+ if check.cycle(obj) {
+ // break cycle
+ // (without this, calling underlying()
+ // below may lead to an endless loop
+ // if we have a cycle for a defined
+ // (*Named) type)
+ obj.typ = Typ[Invalid]
+ }
+
+ case *Func:
+ if check.cycle(obj) {
+ // Don't set obj.typ to Typ[Invalid] here
+ // because plenty of code type-asserts that
+ // functions have a *Signature type. Grey
+ // functions have their type set to an empty
+ // signature which makes it impossible to
+ // initialize a variable with the function.
+ }
+
+ default:
+ unreachable()
+ }
+ assert(obj.Type() != nil)
+ return
+ }
+
+ d := check.objMap[obj]
+ if d == nil {
+ check.dump("%v: %s should have been declared", obj.Pos(), obj)
+ unreachable()
+ }
+
+ // save/restore current context and setup object context
+ defer func(ctxt context) {
+ check.context = ctxt
+ }(check.context)
+ check.context = context{
+ scope: d.file,
+ }
+
+ // Const and var declarations must not have initialization
+ // cycles. We track them by remembering the current declaration
+ // in check.decl. Initialization expressions depending on other
+ // consts, vars, or functions, add dependencies to the current
+ // check.decl.
+ switch obj := obj.(type) {
+ case *Const:
+ check.decl = d // new package-level const decl
+ check.constDecl(obj, d.vtyp, d.init)
+ case *Var:
+ check.decl = d // new package-level var decl
+ check.varDecl(obj, d.lhs, d.vtyp, d.init)
+ case *TypeName:
+ // invalid recursive types are detected via path
+ check.typeDecl(obj, d.tdecl, def)
+ check.collectMethods(obj) // methods can only be added to top-level types
+ case *Func:
+ // functions may be recursive - no need to track dependencies
+ check.funcDecl(obj, d)
+ default:
+ unreachable()
+ }
+}
+
+// cycle checks if the cycle starting with obj is valid and
+// reports an error if it is not.
+func (check *Checker) cycle(obj Object) (isCycle bool) {
+ // The object map contains the package scope objects and the non-interface methods.
+ if debug {
+ info := check.objMap[obj]
+ inObjMap := info != nil && (info.fdecl == nil || info.fdecl.Recv == nil) // exclude methods
+ isPkgObj := obj.Parent() == check.pkg.scope
+ if isPkgObj != inObjMap {
+ check.dump("%v: inconsistent object map for %s (isPkgObj = %v, inObjMap = %v)", obj.Pos(), obj, isPkgObj, inObjMap)
+ unreachable()
+ }
+ }
+
+ // Count cycle objects.
+ assert(obj.color() >= grey)
+ start := obj.color() - grey // index of obj in objPath
+ cycle := check.objPath[start:]
+ nval := 0 // number of (constant or variable) values in the cycle
+ ndef := 0 // number of type definitions in the cycle
+ for _, obj := range cycle {
+ switch obj := obj.(type) {
+ case *Const, *Var:
+ nval++
+ case *TypeName:
+ // Determine if the type name is an alias or not. For
+ // package-level objects, use the object map which
+ // provides syntactic information (which doesn't rely
+ // on the order in which the objects are set up). For
+ // local objects, we can rely on the order, so use
+ // the object's predicate.
+ // TODO(gri) It would be less fragile to always access
+ // the syntactic information. We should consider storing
+ // this information explicitly in the object.
+ var alias bool
+ if d := check.objMap[obj]; d != nil {
+ alias = d.tdecl.Alias // package-level object
+ } else {
+ alias = obj.IsAlias() // function local object
+ }
+ if !alias {
+ ndef++
+ }
+ case *Func:
+ // ignored for now
+ default:
+ unreachable()
+ }
+ }
+
+ if check.conf.Trace {
+ check.trace(obj.Pos(), "## cycle detected: objPath = %s->%s (len = %d)", pathString(cycle), obj.Name(), len(cycle))
+ check.trace(obj.Pos(), "## cycle contains: %d values, %d type definitions", nval, ndef)
+ defer func() {
+ if isCycle {
+ check.trace(obj.Pos(), "=> error: cycle is invalid")
+ }
+ }()
+ }
+
+ // A cycle involving only constants and variables is invalid but we
+ // ignore them here because they are reported via the initialization
+ // cycle check.
+ if nval == len(cycle) {
+ return false
+ }
+
+ // A cycle involving only types (and possibly functions) must have at least
+ // one type definition to be permitted: If there is no type definition, we
+ // have a sequence of alias type names which will expand ad infinitum.
+ if nval == 0 && ndef > 0 {
+ return false // cycle is permitted
+ }
+
+ check.cycleError(cycle)
+
+ return true
+}
+
+type typeInfo uint
+
+// validType verifies that the given type does not "expand" infinitely
+// producing a cycle in the type graph. Cycles are detected by marking
+// defined types.
+// (Cycles involving alias types, as in "type A = [10]A" are detected
+// earlier, via the objDecl cycle detection mechanism.)
+func (check *Checker) validType(typ Type, path []Object) typeInfo {
+ const (
+ unknown typeInfo = iota
+ marked
+ valid
+ invalid
+ )
+
+ switch t := typ.(type) {
+ case *Array:
+ return check.validType(t.elem, path)
+
+ case *Struct:
+ for _, f := range t.fields {
+ if check.validType(f.typ, path) == invalid {
+ return invalid
+ }
+ }
+
+ case *Interface:
+ for _, etyp := range t.embeddeds {
+ if check.validType(etyp, path) == invalid {
+ return invalid
+ }
+ }
+
+ case *Named:
+ // don't touch the type if it is from a different package or the Universe scope
+ // (doing so would lead to a race condition - was issue #35049)
+ if t.obj.pkg != check.pkg {
+ return valid
+ }
+
+ // don't report a 2nd error if we already know the type is invalid
+ // (e.g., if a cycle was detected earlier, via Checker.underlying).
+ if t.underlying == Typ[Invalid] {
+ t.info = invalid
+ return invalid
+ }
+
+ switch t.info {
+ case unknown:
+ t.info = marked
+ t.info = check.validType(t.orig, append(path, t.obj)) // only types of current package added to path
+ case marked:
+ // cycle detected
+ for i, tn := range path {
+ if t.obj.pkg != check.pkg {
+ panic("internal error: type cycle via package-external type")
+ }
+ if tn == t.obj {
+ check.cycleError(path[i:])
+ t.info = invalid
+ return t.info
+ }
+ }
+ panic("internal error: cycle start not found")
+ }
+ return t.info
+
+ case *instance:
+ return check.validType(t.expand(), path)
+ }
+
+ return valid
+}
+
+// cycleError reports a declaration cycle starting with
+// the object in cycle that is "first" in the source.
+func (check *Checker) cycleError(cycle []Object) {
+ // TODO(gri) Should we start with the last (rather than the first) object in the cycle
+ // since that is the earliest point in the source where we start seeing the
+ // cycle? That would be more consistent with other error messages.
+ i := firstInSrc(cycle)
+ obj := cycle[i]
+ check.errorf(obj.Pos(), "illegal cycle in declaration of %s", obj.Name())
+ for range cycle {
+ check.errorf(obj.Pos(), "\t%s refers to", obj.Name()) // secondary error, \t indented
+ i++
+ if i >= len(cycle) {
+ i = 0
+ }
+ obj = cycle[i]
+ }
+ check.errorf(obj.Pos(), "\t%s", obj.Name())
+}
+
+// TODO(gri) This functionality should probably be with the Pos implementation.
+func cmpPos(p, q syntax.Pos) int {
+ // TODO(gri) is RelFilename correct here?
+ pname := p.RelFilename()
+ qname := q.RelFilename()
+ switch {
+ case pname < qname:
+ return -1
+ case pname > qname:
+ return +1
+ }
+
+ pline := p.Line()
+ qline := q.Line()
+ switch {
+ case pline < qline:
+ return -1
+ case pline > qline:
+ return +1
+ }
+
+ pcol := p.Col()
+ qcol := q.Col()
+ switch {
+ case pcol < qcol:
+ return -1
+ case pcol > qcol:
+ return +1
+ }
+
+ return 0
+}
+
+// firstInSrc reports the index of the object with the "smallest"
+// source position in path. path must not be empty.
+func firstInSrc(path []Object) int {
+ fst, pos := 0, path[0].Pos()
+ for i, t := range path[1:] {
+ if cmpPos(t.Pos(), pos) < 0 {
+ fst, pos = i+1, t.Pos()
+ }
+ }
+ return fst
+}
+
+func (check *Checker) constDecl(obj *Const, typ, init syntax.Expr) {
+ assert(obj.typ == nil)
+
+ // use the correct value of iota
+ defer func(iota constant.Value) { check.iota = iota }(check.iota)
+ check.iota = obj.val
+
+ // provide valid constant value under all circumstances
+ obj.val = constant.MakeUnknown()
+
+ // determine type, if any
+ if typ != nil {
+ t := check.typ(typ)
+ if !isConstType(t) {
+ // don't report an error if the type is an invalid C (defined) type
+ // (issue #22090)
+ if t.Under() != Typ[Invalid] {
+ check.errorf(typ, "invalid constant type %s", t)
+ }
+ obj.typ = Typ[Invalid]
+ return
+ }
+ obj.typ = t
+ }
+
+ // check initialization
+ var x operand
+ if init != nil {
+ check.expr(&x, init)
+ }
+ check.initConst(obj, &x)
+}
+
+func (check *Checker) varDecl(obj *Var, lhs []*Var, typ, init syntax.Expr) {
+ assert(obj.typ == nil)
+
+ // determine type, if any
+ if typ != nil {
+ obj.typ = check.varType(typ)
+ // We cannot spread the type to all lhs variables if there
+ // are more than one since that would mark them as checked
+ // (see Checker.objDecl) and the assignment of init exprs,
+ // if any, would not be checked.
+ //
+ // TODO(gri) If we have no init expr, we should distribute
+ // a given type otherwise we need to re-evalate the type
+ // expr for each lhs variable, leading to duplicate work.
+ }
+
+ // check initialization
+ if init == nil {
+ if typ == nil {
+ // error reported before by arityMatch
+ obj.typ = Typ[Invalid]
+ }
+ return
+ }
+
+ if lhs == nil || len(lhs) == 1 {
+ assert(lhs == nil || lhs[0] == obj)
+ var x operand
+ check.expr(&x, init)
+ check.initVar(obj, &x, "variable declaration")
+ return
+ }
+
+ if debug {
+ // obj must be one of lhs
+ found := false
+ for _, lhs := range lhs {
+ if obj == lhs {
+ found = true
+ break
+ }
+ }
+ if !found {
+ panic("inconsistent lhs")
+ }
+ }
+
+ // We have multiple variables on the lhs and one init expr.
+ // Make sure all variables have been given the same type if
+ // one was specified, otherwise they assume the type of the
+ // init expression values (was issue #15755).
+ if typ != nil {
+ for _, lhs := range lhs {
+ lhs.typ = obj.typ
+ }
+ }
+
+ check.initVars(lhs, []syntax.Expr{init}, nopos)
+}
+
+// Under returns the expanded underlying type of n0; possibly by following
+// forward chains of named types. If an underlying type is found, resolve
+// the chain by setting the underlying type for each defined type in the
+// chain before returning it. If no underlying type is found or a cycle
+// is detected, the result is Typ[Invalid]. If a cycle is detected and
+// n0.check != nil, the cycle is reported.
+func (n0 *Named) Under() Type {
+ u := n0.underlying
+ if u == nil {
+ return Typ[Invalid]
+ }
+
+ // If the underlying type of a defined type is not a defined
+ // type, then that is the desired underlying type.
+ n := u.Named()
+ if n == nil {
+ return u // common case
+ }
+
+ // Otherwise, follow the forward chain.
+ seen := map[*Named]int{n0: 0}
+ path := []Object{n0.obj}
+ for {
+ u = n.underlying
+ if u == nil {
+ u = Typ[Invalid]
+ break
+ }
+ n1 := u.Named()
+ if n1 == nil {
+ break // end of chain
+ }
+
+ seen[n] = len(seen)
+ path = append(path, n.obj)
+ n = n1
+
+ if i, ok := seen[n]; ok {
+ // cycle
+ if n0.check != nil {
+ n0.check.cycleError(path[i:])
+ }
+ u = Typ[Invalid]
+ break
+ }
+ }
+
+ for n := range seen {
+ // We should never have to update the underlying type of an imported type;
+ // those underlying types should have been resolved during the import.
+ // Also, doing so would lead to a race condition (was issue #31749).
+ // Do this check always, not just in debug more (it's cheap).
+ if n0.check != nil && n.obj.pkg != n0.check.pkg {
+ panic("internal error: imported type with unresolved underlying type")
+ }
+ n.underlying = u
+ }
+
+ return u
+}
+
+func (n *Named) setUnderlying(typ Type) {
+ if n != nil {
+ n.underlying = typ
+ }
+}
+
+func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named) {
+ assert(obj.typ == nil)
+
+ check.later(func() {
+ check.validType(obj.typ, nil)
+ })
+
+ alias := tdecl.Alias
+ if alias && tdecl.TParamList != nil {
+ // The parser will ensure this but we may still get an invalid AST.
+ // Complain and continue as regular type definition.
+ check.errorf(tdecl, "generic type cannot be alias")
+ alias = false
+ }
+
+ if alias {
+ // type alias declaration
+
+ obj.typ = Typ[Invalid]
+ obj.typ = check.anyType(tdecl.Type)
+
+ } else {
+ // defined type declaration
+
+ named := &Named{check: check, obj: obj}
+ def.setUnderlying(named)
+ obj.typ = named // make sure recursive type declarations terminate
+
+ if tdecl.TParamList != nil {
+ check.openScope(tdecl, "type parameters")
+ defer check.closeScope()
+ named.tparams = check.collectTypeParams(tdecl.TParamList)
+ }
+
+ // determine underlying type of named
+ named.orig = check.definedType(tdecl.Type, named)
+
+ // The underlying type of named may be itself a named type that is
+ // incomplete:
+ //
+ // type (
+ // A B
+ // B *C
+ // C A
+ // )
+ //
+ // The type of C is the (named) type of A which is incomplete,
+ // and which has as its underlying type the named type B.
+ // Determine the (final, unnamed) underlying type by resolving
+ // any forward chain.
+ // TODO(gri) Investigate if we can just use named.origin here
+ // and rely on lazy computation of the underlying type.
+ named.underlying = named.Under()
+ }
+
+}
+
+func (check *Checker) collectTypeParams(list []*syntax.Field) (tparams []*TypeName) {
+ // Type parameter lists should not be empty. The parser will
+ // complain but we still may get an incorrect AST: ignore it.
+ if len(list) == 0 {
+ return
+ }
+
+ // Declare type parameters up-front, with empty interface as type bound.
+ // The scope of type parameters starts at the beginning of the type parameter
+ // list (so we can have mutually recursive parameterized interfaces).
+ for _, f := range list {
+ tparams = check.declareTypeParam(tparams, f.Name)
+ }
+
+ var bound Type
+ for i, j := 0, 0; i < len(list); i = j {
+ f := list[i]
+ ftype := f.Type
+
+ // determine the range of type parameters list[i:j] with identical type bound
+ // (declared as in (type a, b, c B))
+ j = i + 1
+ for j < len(list) && list[j].Type == ftype {
+ j++
+ }
+
+ // this should never be the case, but be careful
+ if ftype == nil {
+ continue
+ }
+
+ // If the type bound expects exactly one type argument, permit leaving
+ // it away and use the corresponding type parameter as implicit argument.
+ // This allows us to write (type p b(p), q b(q), r b(r)) as (type p, q, r b).
+ // Enabled if enableImplicitTParam is set.
+ const enableImplicitTParam = false
+
+ // The predeclared identifier "any" is visible only as a constraint
+ // in a type parameter list. Look for it before general constraint
+ // resolution.
+ if tident, _ := f.Type.(*syntax.Name); tident != nil && tident.Value == "any" && check.lookup("any") == nil {
+ bound = universeAny
+ } else if enableImplicitTParam {
+ bound = check.anyType(f.Type)
+ } else {
+ bound = check.typ(f.Type)
+ }
+
+ // type bound must be an interface
+ // TODO(gri) We should delay the interface check because
+ // we may not have a complete interface yet:
+ // type C(type T C) interface {}
+ // (issue #39724).
+ if _, ok := bound.Under().(*Interface); ok {
+ if enableImplicitTParam && isGeneric(bound) {
+ base := bound.(*Named) // only a *Named type can be generic
+ if j-i != 1 || len(base.tparams) != 1 {
+ // TODO(gri) make this error message better
+ check.errorf(ftype, "cannot use generic type %s without instantiation (more than one type parameter)", bound)
+ bound = Typ[Invalid]
+ continue
+ }
+ // We have exactly one type parameter.
+ // "Manually" instantiate the bound with each type
+ // parameter the bound applies to.
+ // TODO(gri) this code (in more general form) is also in
+ // checker.typInternal for the *ast.CallExpr case. Factor?
+ typ := new(instance)
+ typ.check = check
+ typ.pos = ftype.Pos()
+ typ.base = base
+ typ.targs = []Type{tparams[i].typ}
+ typ.poslist = []syntax.Pos{f.Name.Pos()}
+ // Make sure we check instantiation works at least once
+ // and that the resulting type is valid.
+ check.atEnd(func() {
+ check.validType(typ.expand(), nil)
+ })
+ // update bound and recorded type
+ bound = typ
+ check.recordTypeAndValue(ftype, typexpr, typ, nil)
+ }
+ // set the type bounds
+ for i < j {
+ tparams[i].typ.(*TypeParam).bound = bound
+ i++
+ }
+ } else if bound != Typ[Invalid] {
+ check.errorf(f.Type, "%s is not an interface", bound)
+ }
+ }
+
+ return
+}
+
+func (check *Checker) declareTypeParam(tparams []*TypeName, name *syntax.Name) []*TypeName {
+ var ptr bool
+ nstr := name.Value
+ if len(nstr) > 0 && nstr[0] == '*' {
+ ptr = true
+ nstr = nstr[1:]
+ }
+ tpar := NewTypeName(name.Pos(), check.pkg, nstr, nil)
+ check.NewTypeParam(ptr, tpar, len(tparams), &emptyInterface) // assigns type to tpar as a side-effect
+ check.declare(check.scope, name, tpar, check.scope.pos) // TODO(gri) check scope position
+ tparams = append(tparams, tpar)
+
+ if check.conf.Trace {
+ check.trace(name.Pos(), "type param = %v", tparams[len(tparams)-1])
+ }
+
+ return tparams
+}
+
+func (check *Checker) collectMethods(obj *TypeName) {
+ // get associated methods
+ // (Checker.collectObjects only collects methods with non-blank names;
+ // Checker.resolveBaseTypeName ensures that obj is not an alias name
+ // if it has attached methods.)
+ methods := check.methods[obj]
+ if methods == nil {
+ return
+ }
+ delete(check.methods, obj)
+ assert(!check.objMap[obj].tdecl.Alias) // don't use TypeName.IsAlias (requires fully set up object)
+
+ // use an objset to check for name conflicts
+ var mset objset
+
+ // spec: "If the base type is a struct type, the non-blank method
+ // and field names must be distinct."
+ base := obj.typ.Named() // shouldn't fail but be conservative
+ if base != nil {
+ if t, _ := base.underlying.(*Struct); t != nil {
+ for _, fld := range t.fields {
+ if fld.name != "_" {
+ assert(mset.insert(fld) == nil)
+ }
+ }
+ }
+
+ // Checker.Files may be called multiple times; additional package files
+ // may add methods to already type-checked types. Add pre-existing methods
+ // so that we can detect redeclarations.
+ for _, m := range base.methods {
+ assert(m.name != "_")
+ assert(mset.insert(m) == nil)
+ }
+ }
+
+ // add valid methods
+ for _, m := range methods {
+ // spec: "For a base type, the non-blank names of methods bound
+ // to it must be unique."
+ assert(m.name != "_")
+ if alt := mset.insert(m); alt != nil {
+ switch alt.(type) {
+ case *Var:
+ check.errorf(m.pos, "field and method with the same name %s", m.name)
+ case *Func:
+ check.errorf(m.pos, "method %s already declared for %s", m.name, obj)
+ default:
+ unreachable()
+ }
+ check.reportAltDecl(alt)
+ continue
+ }
+
+ if base != nil {
+ base.methods = append(base.methods, m)
+ }
+ }
+}
+
+func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
+ assert(obj.typ == nil)
+
+ // func declarations cannot use iota
+ assert(check.iota == nil)
+
+ sig := new(Signature)
+ obj.typ = sig // guard against cycles
+
+ // Avoid cycle error when referring to method while type-checking the signature.
+ // This avoids a nuisance in the best case (non-parameterized receiver type) and
+ // since the method is not a type, we get an error. If we have a parameterized
+ // receiver type, instantiating the receiver type leads to the instantiation of
+ // its methods, and we don't want a cycle error in that case.
+ // TODO(gri) review if this is correct and/or whether we still need this?
+ saved := obj.color_
+ obj.color_ = black
+ fdecl := decl.fdecl
+ check.funcType(sig, fdecl.Recv, fdecl.TParamList, fdecl.Type)
+ obj.color_ = saved
+
+ // function body must be type-checked after global declarations
+ // (functions implemented elsewhere have no body)
+ if !check.conf.IgnoreFuncBodies && fdecl.Body != nil {
+ check.later(func() {
+ check.funcBody(decl, obj.name, sig, fdecl.Body, nil)
+ })
+ }
+}
+
+func (check *Checker) declStmt(list []syntax.Decl) {
+ pkg := check.pkg
+
+ first := -1 // index of first ConstDecl in the current group, or -1
+ var last *syntax.ConstDecl // last ConstDecl with init expressions, or nil
+ for index, decl := range list {
+ if _, ok := decl.(*syntax.ConstDecl); !ok {
+ first = -1 // we're not in a constant declaration
+ }
+
+ switch s := decl.(type) {
+ case *syntax.ConstDecl:
+ top := len(check.delayed)
+
+ // iota is the index of the current constDecl within the group
+ if first < 0 || list[index-1].(*syntax.ConstDecl).Group != s.Group {
+ first = index
+ last = nil
+ }
+ iota := constant.MakeInt64(int64(index - first))
+
+ // determine which initialization expressions to use
+ inherited := true
+ switch {
+ case s.Type != nil || s.Values != nil:
+ last = s
+ inherited = false
+ case last == nil:
+ last = new(syntax.ConstDecl) // make sure last exists
+ inherited = false
+ }
+
+ // declare all constants
+ lhs := make([]*Const, len(s.NameList))
+ values := unpackExpr(last.Values)
+ for i, name := range s.NameList {
+ obj := NewConst(name.Pos(), pkg, name.Value, nil, iota)
+ lhs[i] = obj
+
+ var init syntax.Expr
+ if i < len(values) {
+ init = values[i]
+ }
+
+ check.constDecl(obj, last.Type, init)
+ }
+
+ // Constants must always have init values.
+ check.arity(s.Pos(), s.NameList, values, true, inherited)
+
+ // process function literals in init expressions before scope changes
+ check.processDelayed(top)
+
+ // spec: "The scope of a constant or variable identifier declared
+ // inside a function begins at the end of the ConstSpec or VarSpec
+ // (ShortVarDecl for short variable declarations) and ends at the
+ // end of the innermost containing block."
+ scopePos := endPos(s)
+ for i, name := range s.NameList {
+ check.declare(check.scope, name, lhs[i], scopePos)
+ }
+
+ case *syntax.VarDecl:
+ top := len(check.delayed)
+
+ lhs0 := make([]*Var, len(s.NameList))
+ for i, name := range s.NameList {
+ lhs0[i] = NewVar(name.Pos(), pkg, name.Value, nil)
+ }
+
+ // initialize all variables
+ values := unpackExpr(s.Values)
+ for i, obj := range lhs0 {
+ var lhs []*Var
+ var init syntax.Expr
+ switch len(values) {
+ case len(s.NameList):
+ // lhs and rhs match
+ init = values[i]
+ case 1:
+ // rhs is expected to be a multi-valued expression
+ lhs = lhs0
+ init = values[0]
+ default:
+ if i < len(values) {
+ init = values[i]
+ }
+ }
+ check.varDecl(obj, lhs, s.Type, init)
+ if len(values) == 1 {
+ // If we have a single lhs variable we are done either way.
+ // If we have a single rhs expression, it must be a multi-
+ // valued expression, in which case handling the first lhs
+ // variable will cause all lhs variables to have a type
+ // assigned, and we are done as well.
+ if debug {
+ for _, obj := range lhs0 {
+ assert(obj.typ != nil)
+ }
+ }
+ break
+ }
+ }
+
+ // If we have no type, we must have values.
+ if s.Type == nil || values != nil {
+ check.arity(s.Pos(), s.NameList, values, false, false)
+ }
+
+ // process function literals in init expressions before scope changes
+ check.processDelayed(top)
+
+ // declare all variables
+ // (only at this point are the variable scopes (parents) set)
+ scopePos := endPos(s) // see constant declarations
+ for i, name := range s.NameList {
+ // see constant declarations
+ check.declare(check.scope, name, lhs0[i], scopePos)
+ }
+
+ case *syntax.TypeDecl:
+ obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Value, nil)
+ // spec: "The scope of a type identifier declared inside a function
+ // begins at the identifier in the TypeSpec and ends at the end of
+ // the innermost containing block."
+ scopePos := s.Name.Pos()
+ check.declare(check.scope, s.Name, obj, scopePos)
+ // mark and unmark type before calling typeDecl; its type is still nil (see Checker.objDecl)
+ obj.setColor(grey + color(check.push(obj)))
+ check.typeDecl(obj, s, nil)
+ check.pop().setColor(black)
+
+ default:
+ check.invalidASTf(s, "unknown syntax.Decl node %T", s)
+ }
+ }
+}
diff --git a/src/cmd/compile/internal/types2/errors.go b/src/cmd/compile/internal/types2/errors.go
new file mode 100644
index 0000000000..5211439f89
--- /dev/null
+++ b/src/cmd/compile/internal/types2/errors.go
@@ -0,0 +1,159 @@
+// UNREVIEWED
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements various error reporters.
+
+package types2
+
+import (
+ "cmd/compile/internal/syntax"
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+func unimplemented() {
+ panic("unimplemented")
+}
+
+func assert(p bool) {
+ if !p {
+ panic("assertion failed")
+ }
+}
+
+func unreachable() {
+ panic("unreachable")
+}
+
+func (check *Checker) qualifier(pkg *Package) string {
+ // Qualify the package unless it's the package being type-checked.
+ if pkg != check.pkg {
+ // If the same package name was used by multiple packages, display the full path.
+ if check.pkgCnt[pkg.name] > 1 {
+ return strconv.Quote(pkg.path)
+ }
+ return pkg.name
+ }
+ return ""
+}
+
+func (check *Checker) sprintf(format string, args ...interface{}) string {
+ for i, arg := range args {
+ switch a := arg.(type) {
+ case nil:
+ arg = "<nil>"
+ case operand:
+ panic("internal error: should always pass *operand")
+ case *operand:
+ arg = operandString(a, check.qualifier)
+ case syntax.Pos:
+ arg = a.String()
+ case syntax.Expr:
+ arg = ExprString(a)
+ case Object:
+ arg = ObjectString(a, check.qualifier)
+ case Type:
+ arg = TypeString(a, check.qualifier)
+ }
+ args[i] = arg
+ }
+ return fmt.Sprintf(format, args...)
+}
+
+func (check *Checker) trace(pos syntax.Pos, format string, args ...interface{}) {
+ fmt.Printf("%s:\t%s%s\n",
+ pos,
+ strings.Repeat(". ", check.indent),
+ check.sprintf(format, args...),
+ )
+}
+
+// dump is only needed for debugging
+func (check *Checker) dump(format string, args ...interface{}) {
+ fmt.Println(check.sprintf(format, args...))
+}
+
+func (check *Checker) err(pos syntax.Pos, msg string, soft bool) {
+ // Cheap trick: Don't report errors with messages containing
+ // "invalid operand" or "invalid type" as those tend to be
+ // follow-on errors which don't add useful information. Only
+ // exclude them if these strings are not at the beginning,
+ // and only if we have at least one error already reported.
+ if check.firstErr != nil && (strings.Index(msg, "invalid operand") > 0 || strings.Index(msg, "invalid type") > 0) {
+ return
+ }
+
+ err := Error{pos, stripAnnotations(msg), msg, soft}
+ if check.firstErr == nil {
+ check.firstErr = err
+ }
+
+ if check.conf.Trace {
+ check.trace(pos, "ERROR: %s", msg)
+ }
+
+ f := check.conf.Error
+ if f == nil {
+ panic(bailout{}) // report only first error
+ }
+ f(err)
+}
+
+type poser interface {
+ Pos() syntax.Pos
+}
+
+func (check *Checker) error(at poser, msg string) {
+ check.err(posFor(at), msg, false)
+}
+
+func (check *Checker) errorf(at poser, format string, args ...interface{}) {
+ check.err(posFor(at), check.sprintf(format, args...), false)
+}
+
+func (check *Checker) softErrorf(at poser, format string, args ...interface{}) {
+ check.err(posFor(at), check.sprintf(format, args...), true)
+}
+
+func (check *Checker) invalidASTf(at poser, format string, args ...interface{}) {
+ check.errorf(at, "invalid AST: "+format, args...)
+}
+
+func (check *Checker) invalidArgf(at poser, format string, args ...interface{}) {
+ check.errorf(at, "invalid argument: "+format, args...)
+}
+
+func (check *Checker) invalidOpf(at poser, format string, args ...interface{}) {
+ check.errorf(at, "invalid operation: "+format, args...)
+}
+
+// posFor reports the left (= start) position of at.
+func posFor(at poser) syntax.Pos {
+ switch x := at.(type) {
+ case *operand:
+ if x.expr != nil {
+ return startPos(x.expr)
+ }
+ case syntax.Node:
+ return startPos(x)
+ }
+ return at.Pos()
+}
+
+// stripAnnotations removes internal (type) annotations from s.
+func stripAnnotations(s string) string {
+ var b strings.Builder
+ for _, r := range s {
+ // strip #'s and subscript digits
+ if r != instanceMarker && !('₀' <= r && r < '₀'+10) { // '₀' == U+2080
+ b.WriteRune(r)
+ }
+ }
+ if b.Len() < len(s) {
+ return b.String()
+ }
+ return s
+}
diff --git a/src/cmd/compile/internal/types2/errors_test.go b/src/cmd/compile/internal/types2/errors_test.go
new file mode 100644
index 0000000000..51ae5fdb73
--- /dev/null
+++ b/src/cmd/compile/internal/types2/errors_test.go
@@ -0,0 +1,26 @@
+// UNREVIEWED
+// 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 types2
+
+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/src/cmd/compile/internal/types2/example_test.go b/src/cmd/compile/internal/types2/example_test.go
new file mode 100644
index 0000000000..dcdeaca0c0
--- /dev/null
+++ b/src/cmd/compile/internal/types2/example_test.go
@@ -0,0 +1,324 @@
+// UNREVIEWED
+// Copyright 2015 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.
+
+// Only run where builders (build.golang.org) have
+// access to compiled packages for import.
+//
+// +build !arm,!arm64
+
+package types2_test
+
+// This file shows examples of basic usage of the go/types API.
+//
+// To locate a Go package, use (*go/build.Context).Import.
+// To load, parse, and type-check a complete Go program
+// from source, use golang.org/x/tools/go/loader.
+
+import (
+ "bytes"
+ "cmd/compile/internal/syntax"
+ "cmd/compile/internal/types2"
+ "fmt"
+ "log"
+ "regexp"
+ "sort"
+ "strings"
+)
+
+// ExampleScope prints the tree of Scopes of a package created from a
+// set of parsed files.
+func ExampleScope() {
+ // Parse the source files for a package.
+ var files []*syntax.File
+ for _, file := range []struct{ name, input string }{
+ {"main.go", `
+package main
+import "fmt"
+func main() {
+ freezing := FToC(-18)
+ fmt.Println(freezing, Boiling) }
+`},
+ {"celsius.go", `
+package main
+import "fmt"
+type Celsius float64
+func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) }
+func FToC(f float64) Celsius { return Celsius(f - 32 / 9 * 5) }
+const Boiling Celsius = 100
+func Unused() { {}; {{ var x int; _ = x }} } // make sure empty block scopes get printed
+`},
+ } {
+ f, err := parseSrc(file.name, file.input)
+ if err != nil {
+ log.Fatal(err)
+ }
+ files = append(files, f)
+ }
+
+ // Type-check a package consisting of these files.
+ // Type information for the imported "fmt" package
+ // comes from $GOROOT/pkg/$GOOS_$GOOARCH/fmt.a.
+ conf := types2.Config{Importer: defaultImporter()}
+ pkg, err := conf.Check("temperature", files, nil)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Print the tree of scopes.
+ // For determinism, we redact addresses.
+ var buf bytes.Buffer
+ pkg.Scope().WriteTo(&buf, 0, true)
+ rx := regexp.MustCompile(` 0x[a-fA-F0-9]*`)
+ fmt.Println(rx.ReplaceAllString(buf.String(), ""))
+
+ // Output:
+ // package "temperature" scope {
+ // . const temperature.Boiling temperature.Celsius
+ // . type temperature.Celsius float64
+ // . func temperature.FToC(f float64) temperature.Celsius
+ // . func temperature.Unused()
+ // . func temperature.main()
+ // . main.go scope {
+ // . . package fmt
+ // . . function scope {
+ // . . . var freezing temperature.Celsius
+ // . . }
+ // . }
+ // . celsius.go scope {
+ // . . package fmt
+ // . . function scope {
+ // . . . var c temperature.Celsius
+ // . . }
+ // . . function scope {
+ // . . . var f float64
+ // . . }
+ // . . function scope {
+ // . . . block scope {
+ // . . . }
+ // . . . block scope {
+ // . . . . block scope {
+ // . . . . . var x int
+ // . . . . }
+ // . . . }
+ // . . }
+ // . }
+ // }
+}
+
+// ExampleMethodSet prints the method sets of various types.
+func ExampleMethodSet() {
+ // Parse a single source file.
+ const input = `
+package temperature
+import "fmt"
+type Celsius float64
+func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) }
+func (c *Celsius) SetF(f float64) { *c = Celsius(f - 32 / 9 * 5) }
+
+type S struct { I; m int }
+type I interface { m() byte }
+`
+ f, err := parseSrc("celsius.go", input)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Type-check a package consisting of this file.
+ // Type information for the imported packages
+ // comes from $GOROOT/pkg/$GOOS_$GOOARCH/fmt.a.
+ conf := types2.Config{Importer: defaultImporter()}
+ pkg, err := conf.Check("temperature", []*syntax.File{f}, nil)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Print the method sets of Celsius and *Celsius.
+ celsius := pkg.Scope().Lookup("Celsius").Type()
+ for _, t := range []types2.Type{celsius, types2.NewPointer(celsius)} {
+ fmt.Printf("Method set of %s:\n", t)
+ mset := types2.NewMethodSet(t)
+ for i := 0; i < mset.Len(); i++ {
+ fmt.Println(mset.At(i))
+ }
+ fmt.Println()
+ }
+
+ // Print the method set of S.
+ styp := pkg.Scope().Lookup("S").Type()
+ fmt.Printf("Method set of %s:\n", styp)
+ fmt.Println(types2.NewMethodSet(styp))
+
+ // Output:
+ // Method set of temperature.Celsius:
+ // method (temperature.Celsius) String() string
+ //
+ // Method set of *temperature.Celsius:
+ // method (*temperature.Celsius) SetF(f float64)
+ // method (*temperature.Celsius) String() string
+ //
+ // Method set of temperature.S:
+ // MethodSet {}
+}
+
+// ExampleInfo prints various facts recorded by the type checker in a
+// types2.Info struct: definitions of and references to each named object,
+// and the type, value, and mode of every expression in the package.
+func ExampleInfo() {
+ // Parse a single source file.
+ const input = `
+package fib
+
+type S string
+
+var a, b, c = len(b), S(c), "hello"
+
+func fib(x int) int {
+ if x < 2 {
+ return x
+ }
+ return fib(x-1) - fib(x-2)
+}`
+ f, err := parseSrc("fib.go", input)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Type-check the package.
+ // We create an empty map for each kind of input
+ // we're interested in, and Check populates them.
+ info := types2.Info{
+ Types: make(map[syntax.Expr]types2.TypeAndValue),
+ Defs: make(map[*syntax.Name]types2.Object),
+ Uses: make(map[*syntax.Name]types2.Object),
+ }
+ var conf types2.Config
+ pkg, err := conf.Check("fib", []*syntax.File{f}, &info)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Print package-level variables in initialization order.
+ fmt.Printf("InitOrder: %v\n\n", info.InitOrder)
+
+ // For each named object, print the line and
+ // column of its definition and each of its uses.
+ fmt.Println("Defs and Uses of each named object:")
+ usesByObj := make(map[types2.Object][]string)
+ for id, obj := range info.Uses {
+ posn := id.Pos()
+ lineCol := fmt.Sprintf("%d:%d", posn.Line(), posn.Col())
+ usesByObj[obj] = append(usesByObj[obj], lineCol)
+ }
+ var items []string
+ for obj, uses := range usesByObj {
+ sort.Strings(uses)
+ item := fmt.Sprintf("%s:\n defined at %s\n used at %s",
+ types2.ObjectString(obj, types2.RelativeTo(pkg)),
+ obj.Pos(),
+ strings.Join(uses, ", "))
+ items = append(items, item)
+ }
+ sort.Strings(items) // sort by line:col, in effect
+ fmt.Println(strings.Join(items, "\n"))
+ fmt.Println()
+
+ // TODO(gri) Enable once positions are updated/verified
+ // fmt.Println("Types and Values of each expression:")
+ // items = nil
+ // for expr, tv := range info.Types {
+ // var buf bytes.Buffer
+ // posn := expr.Pos()
+ // tvstr := tv.Type.String()
+ // if tv.Value != nil {
+ // tvstr += " = " + tv.Value.String()
+ // }
+ // // line:col | expr | mode : type = value
+ // fmt.Fprintf(&buf, "%2d:%2d | %-19s | %-7s : %s",
+ // posn.Line(), posn.Col(), types2.ExprString(expr),
+ // mode(tv), tvstr)
+ // items = append(items, buf.String())
+ // }
+ // sort.Strings(items)
+ // fmt.Println(strings.Join(items, "\n"))
+
+ // Output:
+ // InitOrder: [c = "hello" b = S(c) a = len(b)]
+ //
+ // Defs and Uses of each named object:
+ // builtin len:
+ // defined at <unknown position>
+ // used at 6:15
+ // func fib(x int) int:
+ // defined at fib.go:8:6
+ // used at 12:20, 12:9
+ // type S string:
+ // defined at fib.go:4:6
+ // used at 6:23
+ // type int:
+ // defined at <unknown position>
+ // used at 8:12, 8:17
+ // type string:
+ // defined at <unknown position>
+ // used at 4:8
+ // var b S:
+ // defined at fib.go:6:8
+ // used at 6:19
+ // var c string:
+ // defined at fib.go:6:11
+ // used at 6:25
+ // var x int:
+ // defined at fib.go:8:10
+ // used at 10:10, 12:13, 12:24, 9:5
+
+ // TODO(gri) Enable once positions are updated/verified
+ // Types and Values of each expression:
+ // 4: 8 | string | type : string
+ // 6:15 | len | builtin : func(string) int
+ // 6:15 | len(b) | value : int
+ // 6:19 | b | var : fib.S
+ // 6:23 | S | type : fib.S
+ // 6:23 | S(c) | value : fib.S
+ // 6:25 | c | var : string
+ // 6:29 | "hello" | value : string = "hello"
+ // 8:12 | int | type : int
+ // 8:17 | int | type : int
+ // 9: 5 | x | var : int
+ // 9: 5 | x < 2 | value : untyped bool
+ // 9: 9 | 2 | value : int = 2
+ // 10:10 | x | var : int
+ // 12: 9 | fib | value : func(x int) int
+ // 12: 9 | fib(x - 1) | value : int
+ // 12: 9 | fib(x - 1) - fib(x - 2) | value : int
+ // 12:13 | x | var : int
+ // 12:13 | x - 1 | value : int
+ // 12:15 | 1 | value : int = 1
+ // 12:20 | fib | value : func(x int) int
+ // 12:20 | fib(x - 2) | value : int
+ // 12:24 | x | var : int
+ // 12:24 | x - 2 | value : int
+ // 12:26 | 2 | value : int = 2
+}
+
+func mode(tv types2.TypeAndValue) string {
+ switch {
+ case tv.IsVoid():
+ return "void"
+ case tv.IsType():
+ return "type"
+ case tv.IsBuiltin():
+ return "builtin"
+ case tv.IsNil():
+ return "nil"
+ case tv.Assignable():
+ if tv.Addressable() {
+ return "var"
+ }
+ return "mapindex"
+ case tv.IsValue():
+ return "value"
+ default:
+ return "unknown"
+ }
+}
diff --git a/src/cmd/compile/internal/types2/examples/functions.go2 b/src/cmd/compile/internal/types2/examples/functions.go2
new file mode 100644
index 0000000000..ab4c192c00
--- /dev/null
+++ b/src/cmd/compile/internal/types2/examples/functions.go2
@@ -0,0 +1,216 @@
+// UNREVIEWED
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file shows some examples of type-parameterized functions.
+
+package p
+
+// Reverse is a generic function that takes a []T argument and
+// reverses that slice in place.
+func Reverse[T any](list []T) {
+ i := 0
+ j := len(list)-1
+ for i < j {
+ list[i], list[j] = list[j], list[i]
+ i++
+ j--
+ }
+}
+
+func _() {
+ // Reverse can be called with an explicit type argument.
+ Reverse[int](nil)
+ Reverse[string]([]string{"foo", "bar"})
+ Reverse[struct{x, y int}]([]struct{x, y int}{{1, 2}, {2, 3}, {3, 4}})
+
+ // Since the type parameter is used for an incoming argument,
+ // it can be inferred from the provided argument's type.
+ Reverse([]string{"foo", "bar"})
+ Reverse([]struct{x, y int}{{1, 2}, {2, 3}, {3, 4}})
+
+ // But the incoming argument must have a type, even if it's a
+ // default type. An untyped nil won't work.
+ // Reverse(nil) // this won't type-check
+
+ // A typed nil will work, though.
+ Reverse([]int(nil))
+}
+
+// Certain functions, such as the built-in `new` could be written using
+// type parameters.
+func new[T any]() *T {
+ var x T
+ return &x
+}
+
+// When calling our own `new`, we need to pass the type parameter
+// explicitly since there is no (value) argument from which the
+// result type could be inferred. We don't try to infer the
+// result type from the assignment to keep things simple and
+// easy to understand.
+var _ = new[int]()
+var _ *float64 = new[float64]() // the result type is indeed *float64
+
+// A function may have multiple type parameters, of course.
+func foo[A, B, C any](a A, b []B, c *C) B {
+ // do something here
+ return b[0]
+}
+
+// As before, we can pass type parameters explicitly.
+var s = foo[int, string, float64](1, []string{"first"}, new[float64]())
+
+// Or we can use type inference.
+var _ float64 = foo(42, []float64{1.0}, &s)
+
+// Type inference works in a straight-forward manner even
+// for variadic functions.
+func variadic[A, B any](A, B, ...B) int
+
+// var _ = variadic(1) // ERROR not enough arguments
+var _ = variadic(1, 2.3)
+var _ = variadic(1, 2.3, 3.4, 4.5)
+var _ = variadic[int, float64](1, 2.3, 3.4, 4)
+
+// Type inference also works in recursive function calls where
+// the inferred type is the type parameter of the caller.
+func f1[T any](x T) {
+ f1(x)
+}
+
+func f2a[T any](x, y T) {
+ f2a(x, y)
+}
+
+func f2b[T any](x, y T) {
+ f2b(y, x)
+}
+
+func g2a[P, Q any](x P, y Q) {
+ g2a(x, y)
+}
+
+func g2b[P, Q any](x P, y Q) {
+ g2b(y, x)
+}
+
+// Here's an example of a recursive function call with variadic
+// arguments and type inference inferring the type parameter of
+// the caller (i.e., itself).
+func max[T interface{ type int }](x ...T) T {
+ var x0 T
+ if len(x) > 0 {
+ x0 = x[0]
+ }
+ if len(x) > 1 {
+ x1 := max(x[1:]...)
+ if x1 > x0 {
+ return x1
+ }
+ }
+ return x0
+}
+
+// When inferring channel types, the channel direction is ignored
+// for the purpose of type inference. Once the type has been in-
+// fered, the usual parameter passing rules are applied.
+// Thus even if a type can be inferred successfully, the function
+// call may not be valid.
+
+func fboth[T any](chan T)
+func frecv[T any](<-chan T)
+func fsend[T any](chan<- T)
+
+func _() {
+ var both chan int
+ var recv <-chan int
+ var send chan<-int
+
+ fboth(both)
+ fboth(recv /* ERROR cannot use */ )
+ fboth(send /* ERROR cannot use */ )
+
+ frecv(both)
+ frecv(recv)
+ frecv(send /* ERROR cannot use */ )
+
+ fsend(both)
+ fsend(recv /* ERROR cannot use */)
+ fsend(send)
+}
+
+func ffboth[T any](func(chan T))
+func ffrecv[T any](func(<-chan T))
+func ffsend[T any](func(chan<- T))
+
+func _() {
+ var both func(chan int)
+ var recv func(<-chan int)
+ var send func(chan<- int)
+
+ ffboth(both)
+ ffboth(recv /* ERROR cannot use */ )
+ ffboth(send /* ERROR cannot use */ )
+
+ ffrecv(both /* ERROR cannot use */ )
+ ffrecv(recv)
+ ffrecv(send /* ERROR cannot use */ )
+
+ ffsend(both /* ERROR cannot use */ )
+ ffsend(recv /* ERROR cannot use */ )
+ ffsend(send)
+}
+
+// When inferring elements of unnamed composite parameter types,
+// if the arguments are defined types, use their underlying types.
+// Even though the matching types are not exactly structurally the
+// same (one is a type literal, the other a named type), because
+// assignment is permitted, parameter passing is permitted as well,
+// so type inference should be able to handle these cases well.
+
+func g1[T any]([]T)
+func g2[T any]([]T, T)
+func g3[T any](*T, ...T)
+
+func _() {
+ type intSlize []int
+ g1([]int{})
+ g1(intSlize{})
+ g2(nil, 0)
+
+ type myString string
+ var s1 string
+ g3(nil, "1", myString("2"), "3")
+ g3(&s1, "1", myString /* ERROR does not match */ ("2"), "3")
+ _ = s1
+
+ type myStruct struct{x int}
+ var s2 myStruct
+ g3(nil, struct{x int}{}, myStruct{})
+ g3(&s2, struct{x int}{}, myStruct{})
+ g3(nil, myStruct{}, struct{x int}{})
+ g3(&s2, myStruct{}, struct{x int}{})
+}
+
+// Here's a realistic example.
+
+func append[T any](s []T, t ...T) []T
+
+func _() {
+ var f func()
+ type Funcs []func()
+ var funcs Funcs
+ _ = append(funcs, f)
+}
+
+// Generic type declarations cannot have empty type parameter lists
+// (that would indicate a slice type). Thus, generic functions cannot
+// have empty type parameter lists, either. This is a syntax error.
+
+func h[] /* ERROR empty type parameter list */ ()
+
+func _() {
+ h[] /* ERROR operand */ ()
+}
diff --git a/src/cmd/compile/internal/types2/examples/methods.go2 b/src/cmd/compile/internal/types2/examples/methods.go2
new file mode 100644
index 0000000000..52f835f80e
--- /dev/null
+++ b/src/cmd/compile/internal/types2/examples/methods.go2
@@ -0,0 +1,97 @@
+// UNREVIEWED
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file shows some examples of methods on type-parameterized types.
+
+package p
+
+// Parameterized types may have methods.
+type T1[A any] struct{ a A }
+
+// When declaring a method for a parameterized type, the "instantiated"
+// receiver type acts as an implicit declaration of the type parameters
+// for the receiver type. In the example below, method m1 on type T1 has
+// the receiver type T1[A] which declares the type parameter A for use
+// with this method. That is, within the method m1, A stands for the
+// actual type argument provided to an instantiated T1.
+func (t T1[A]) m1() A { return t.a }
+
+// For instance, if T1 is instantiated with the type int, the type
+// parameter A in m1 assumes that type (int) as well and we can write
+// code like this:
+var x T1[int]
+var _ int = x.m1()
+
+// Because the type parameter provided to a parameterized receiver type
+// is declared through that receiver declaration, it must be an identifier.
+// It cannot possibly be some other type because the receiver type is not
+// instantiated with concrete types, it is standing for the parameterized
+// receiver type.
+func (t T1[[ /* ERROR must be an identifier */ ]int]) m2() {}
+
+// Note that using what looks like a predeclared identifier, say int,
+// as type parameter in this situation is deceptive and considered bad
+// style. In m3 below, int is the name of the local receiver type parameter
+// and it shadows the predeclared identifier int which then cannot be used
+// anymore as expected.
+// This is no different from locally redelaring a predeclared identifier
+// and usually should be avoided. There are some notable exceptions; e.g.,
+// sometimes it makes sense to use the identifier "copy" which happens to
+// also be the name of a predeclared built-in function.
+func (t T1[int]) m3() { var _ int = 42 /* ERROR cannot convert 42 .* to int */ }
+
+// The names of the type parameters used in a parameterized receiver
+// type don't have to match the type parameter names in the the declaration
+// of the type used for the receiver. In our example, even though T1 is
+// declared with type parameter named A, methods using that receiver type
+// are free to use their own name for that type parameter. That is, the
+// name of type parameters is always local to the declaration where they
+// are introduced. In our example we can write a method m2 and use the
+// name X instead of A for the type parameter w/o any difference.
+func (t T1[X]) m4() X { return t.a }
+
+// If the receiver type is parameterized, type parameters must always be
+// provided: this simply follows from the general rule that a parameterized
+// type must be instantiated before it can be used. A method receiver
+// declaration using a parameterized receiver type is no exception. It is
+// simply that such receiver type expressions perform two tasks simultaneously:
+// they declare the (local) type parameters and then use them to instantiate
+// the receiver type. Forgetting to provide a type parameter leads to an error.
+func (t T1 /* ERROR generic type .* without instantiation */ ) m5() {}
+
+// However, sometimes we don't need the type parameter, and thus it is
+// inconvenient to have to choose a name. Since the receiver type expression
+// serves as a declaration for its type parameters, we are free to choose the
+// blank identifier:
+func (t T1[_]) m6() {}
+
+// Naturally, these rules apply to any number of type parameters on the receiver
+// type. Here are some more complex examples.
+type T2[A, B, C any] struct {
+ a A
+ b B
+ c C
+}
+
+// Naming of the type parameters is local and has no semantic impact:
+func (t T2[A, B, C]) m1() (A, B, C) { return t.a, t.b, t.c }
+func (t T2[C, B, A]) m2() (C, B, A) { return t.a, t.b, t.c }
+func (t T2[X, Y, Z]) m3() (X, Y, Z) { return t.a, t.b, t.c }
+
+// Type parameters may be left blank if they are not needed:
+func (t T2[A, _, C]) m4() (A, C) { return t.a, t.c }
+func (t T2[_, _, X]) m5() X { return t.c }
+func (t T2[_, _, _]) m6() {}
+
+// As usual, blank names may be used for any object which we don't care about
+// using later. For instance, we may write an unnamed method with a receiver
+// that cannot be accessed:
+func (_ T2[_, _, _]) _() int { return 42 }
+
+// Because a receiver parameter list is simply a parameter list, we can
+// leave the receiver argument away for receiver types.
+type T0 struct{}
+func (T0) _() {}
+func (T1[A]) _() {}
diff --git a/src/cmd/compile/internal/types2/examples/types.go2 b/src/cmd/compile/internal/types2/examples/types.go2
new file mode 100644
index 0000000000..be8d44e599
--- /dev/null
+++ b/src/cmd/compile/internal/types2/examples/types.go2
@@ -0,0 +1,261 @@
+// UNREVIEWED
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file shows some examples of generic types.
+
+package p
+
+// List is just what it says - a slice of E elements.
+type List[E any] []E
+
+// A generic (parameterized) type must always be instantiated
+// before it can be used to designate the type of a variable
+// (including a struct field, or function parameter); though
+// for the latter cases, the provided type may be another type
+// parameter. So:
+var _ List[byte] = []byte{}
+
+// A generic binary tree might be declared as follows.
+type Tree[E any] struct {
+ left, right *Tree[E]
+ payload E
+}
+
+// A simple instantiation of Tree:
+var root1 Tree[int]
+
+// The actual type parameter provided may be a generic type itself:
+var root2 Tree[List[int]]
+
+// A couple of more complex examples.
+// We don't need extra parentheses around the element type of the slices on
+// the right (unlike when we use ()'s rather than []'s for type parameters).
+var _ List[List[int]] = []List[int]{}
+var _ List[List[List[Tree[int]]]] = []List[List[Tree[int]]]{}
+
+// Type parameters act like type aliases when used in generic types
+// in the sense that we can "emulate" a specific type instantiation
+// with type aliases.
+type T1[P any] struct {
+ f P
+}
+
+type T2[P any] struct {
+ f struct {
+ g P
+ }
+}
+
+var x1 T1[struct{ g int }]
+var x2 T2[int]
+
+func _() {
+ // This assignment is invalid because the types of x1, x2 are T1(...)
+ // and T2(...) respectively, which are two different defined types.
+ x1 = x2 // ERROR assignment
+
+ // This assignment is valid because the types of x1.f and x2.f are
+ // both struct { g int }; the type parameters act like type aliases
+ // and their actual names don't come into play here.
+ x1.f = x2.f
+}
+
+// We can verify this behavior using type aliases instead:
+type T1a struct {
+ f A1
+}
+type A1 = struct { g int }
+
+type T2a struct {
+ f struct {
+ g A2
+ }
+}
+type A2 = int
+
+var x1a T1a
+var x2a T2a
+
+func _() {
+ x1a = x2a // ERROR assignment
+ x1a.f = x2a.f
+}
+
+// Another interesting corner case are generic types that don't use
+// their type arguments. For instance:
+type T[P any] struct{}
+
+var xint T[int]
+var xbool T[bool]
+
+// Are these two variables of the same type? After all, their underlying
+// types are identical. We consider them to be different because each type
+// instantiation creates a new named type, in this case T<int> and T<bool>
+// even if their underlying types are identical. This is sensible because
+// we might still have methods that have different signatures or behave
+// differently depending on the type arguments, and thus we can't possibly
+// consider such types identical. Consequently:
+func _() {
+ xint = xbool // ERROR assignment
+}
+
+// Generic types cannot be used without instantiation.
+var _ T // ERROR cannot use generic type T
+
+// In type context, generic (parameterized) types cannot be parenthesized before
+// being instantiated. See also NOTES entry from 12/4/2019.
+var _ (T /* ERROR cannot use generic type T */ )[ /* ERROR unexpected \[ */ int]
+
+// All types may be parameterized, including interfaces.
+type I1[T any] interface{
+ m1(T)
+}
+
+// Generic interfaces may be embedded as one would expect.
+type I2 interface {
+ I1(int) // method!
+ I1[string] // embedded I1
+}
+
+func _() {
+ var x I2
+ x.I1(0)
+ x.m1("foo")
+}
+
+type I0 interface {
+ m0()
+}
+
+type I3 interface {
+ I0
+ I1[bool]
+ m(string)
+}
+
+func _() {
+ var x I3
+ x.m0()
+ x.m1(true)
+ x.m("foo")
+}
+
+type _ struct {
+ ( /* ERROR cannot parenthesize */ int8)
+ ( /* ERROR cannot parenthesize */ *int16)
+ *( /* ERROR cannot parenthesize */ int32)
+ List[int]
+
+ int8 /* ERROR int8 redeclared */
+ * /* ERROR int16 redeclared */ int16
+ List /* ERROR List redeclared */ [int]
+}
+
+// It's possible to declare local types whose underlying types
+// are type parameters. As with ordinary type definitions, the
+// types underlying properties are "inherited" but the methods
+// are not.
+func _[T interface{ m(); type int }]() {
+ type L T
+ var x L
+
+ // m is not defined on L (it is not "inherited" from
+ // its underlying type).
+ x.m /* ERROR x.m undefined */ ()
+
+ // But the properties of T, such that as that it supports
+ // the operations of the types given by its type bound,
+ // are also the properties of L.
+ x++
+ _ = x - x
+
+ // On the other hand, if we define a local alias for T,
+ // that alias stands for T as expected.
+ type A = T
+ var y A
+ y.m()
+ _ = y < 0
+}
+
+// As a special case, an explicit type argument may be omitted
+// from a type parameter bound if the type bound expects exactly
+// one type argument. In that case, the type argument is the
+// respective type parameter to which the type bound applies.
+// Note: We may not permit this syntactic sugar at first.
+// Note: This is now disabled. All examples below are adjusted.
+type Adder[T any] interface {
+ Add(T) T
+}
+
+// We don't need to explicitly instantiate the Adder bound
+// if we have exactly one type parameter.
+func Sum[T Adder[T]](list []T) T {
+ var sum T
+ for _, x := range list {
+ sum = sum.Add(x)
+ }
+ return sum
+}
+
+// Valid and invalid variations.
+type B0 interface {}
+type B1[_ any] interface{}
+type B2[_, _ any] interface{}
+
+func _[T1 B0]()
+func _[T1 B1[T1]]()
+func _[T1 B2 /* ERROR cannot use generic type .* without instantiation */ ]()
+
+func _[T1, T2 B0]()
+func _[T1 B1[T1], T2 B1[T2]]()
+func _[T1, T2 B2 /* ERROR cannot use generic type .* without instantiation */ ]()
+
+func _[T1 B0, T2 B1[T2]]() // here B1 applies to T2
+
+// When the type argument is left away, the type bound is
+// instantiated for each type parameter with that type
+// parameter.
+// Note: We may not permit this syntactic sugar at first.
+func _[A Adder[A], B Adder[B], C Adder[A]]() {
+ var a A // A's type bound is Adder[A]
+ a = a.Add(a)
+ var b B // B's type bound is Adder[B]
+ b = b.Add(b)
+ var c C // C's type bound is Adder[A]
+ a = c.Add(a)
+}
+
+// The type of variables (incl. parameters and return values) cannot
+// be an interface with type constraints or be/embed comparable.
+type I interface {
+ type int
+}
+
+var (
+ _ interface /* ERROR contains type constraints */ {type int}
+ _ I /* ERROR contains type constraints */
+)
+
+func _(I /* ERROR contains type constraints */ )
+func _(x, y, z I /* ERROR contains type constraints */ )
+func _() I /* ERROR contains type constraints */
+
+func _() {
+ var _ I /* ERROR contains type constraints */
+}
+
+type C interface {
+ comparable
+}
+
+var _ comparable /* ERROR comparable */
+var _ C /* ERROR comparable */
+
+func _(_ comparable /* ERROR comparable */ , _ C /* ERROR comparable */ )
+
+func _() {
+ var _ comparable /* ERROR comparable */
+ var _ C /* ERROR comparable */
+} \ No newline at end of file
diff --git a/src/cmd/compile/internal/types2/expr.go b/src/cmd/compile/internal/types2/expr.go
new file mode 100644
index 0000000000..f83aa86f6e
--- /dev/null
+++ b/src/cmd/compile/internal/types2/expr.go
@@ -0,0 +1,1906 @@
+// UNREVIEWED
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements typechecking of expressions.
+
+package types2
+
+import (
+ "cmd/compile/internal/syntax"
+ "fmt"
+ "go/constant"
+ "go/token"
+ "math"
+)
+
+/*
+Basic algorithm:
+
+Expressions are checked recursively, top down. Expression checker functions
+are generally of the form:
+
+ func f(x *operand, e *syntax.Expr, ...)
+
+where e is the expression to be checked, and x is the result of the check.
+The check performed by f may fail in which case x.mode == invalid, and
+related error messages will have been issued by f.
+
+If a hint argument is present, it is the composite literal element type
+of an outer composite literal; it is used to type-check composite literal
+elements that have no explicit type specification in the source
+(e.g.: []T{{...}, {...}}, the hint is the type T in this case).
+
+All expressions are checked via rawExpr, which dispatches according
+to expression kind. Upon returning, rawExpr is recording the types and
+constant values for all expressions that have an untyped type (those types
+may change on the way up in the expression tree). Usually these are constants,
+but the results of comparisons or non-constant shifts of untyped constants
+may also be untyped, but not constant.
+
+Untyped expressions may eventually become fully typed (i.e., not untyped),
+typically when the value is assigned to a variable, or is used otherwise.
+The updateExprType method is used to record this final type and update
+the recorded types: the type-checked expression tree is again traversed down,
+and the new type is propagated as needed. Untyped constant expression values
+that become fully typed must now be representable by the full type (constant
+sub-expression trees are left alone except for their roots). This mechanism
+ensures that a client sees the actual (run-time) type an untyped value would
+have. It also permits type-checking of lhs shift operands "as if the shift
+were not present": when updateExprType visits an untyped lhs shift operand
+and assigns it it's final type, that type must be an integer type, and a
+constant lhs must be representable as an integer.
+
+When an expression gets its final type, either on the way out from rawExpr,
+on the way down in updateExprType, or at the end of the type checker run,
+the type (and constant value, if any) is recorded via Info.Types, if present.
+*/
+
+type opPredicates map[syntax.Operator]func(Type) bool
+
+var unaryOpPredicates = opPredicates{
+ syntax.Add: isNumeric,
+ syntax.Sub: isNumeric,
+ syntax.Xor: isInteger,
+ syntax.Not: isBoolean,
+}
+
+func (check *Checker) op(m opPredicates, x *operand, op syntax.Operator) bool {
+ if pred := m[op]; pred != nil {
+ if !pred(x.typ) {
+ check.invalidOpf(x, "operator %s not defined for %s", op, x)
+ return false
+ }
+ } else {
+ check.invalidASTf(x, "unknown operator %s", op)
+ return false
+ }
+ return true
+}
+
+func op2token(op syntax.Operator) token.Token {
+ switch op {
+ case syntax.Def: // :
+ unreachable()
+ case syntax.Not: // !
+ return token.NOT
+ case syntax.Recv: // <-
+ unreachable()
+
+ case syntax.OrOr: // ||
+ return token.LOR
+ case syntax.AndAnd: // &&
+ return token.LAND
+
+ case syntax.Eql: // ==
+ return token.EQL
+ case syntax.Neq: // !=
+ return token.NEQ
+ case syntax.Lss: // <
+ return token.LSS
+ case syntax.Leq: // <=
+ return token.LEQ
+ case syntax.Gtr: // >
+ return token.GTR
+ case syntax.Geq: // >=
+ return token.GEQ
+
+ case syntax.Add: // +
+ return token.ADD
+ case syntax.Sub: // -
+ return token.SUB
+ case syntax.Or: // |
+ return token.OR
+ case syntax.Xor: // ^
+ return token.XOR
+
+ case syntax.Mul: // *
+ return token.MUL
+ case syntax.Div: // /
+ return token.QUO
+ case syntax.Rem: // %
+ return token.REM
+ case syntax.And: // &
+ return token.AND
+ case syntax.AndNot: // &^
+ return token.AND_NOT
+ case syntax.Shl: // <<
+ return token.SHL
+ case syntax.Shr: // >>
+ return token.SHR
+ }
+
+ return token.ILLEGAL
+}
+
+// The unary expression e may be nil. It's passed in for better error messages only.
+func (check *Checker) unary(x *operand, e *syntax.Operation, op syntax.Operator) {
+ switch op {
+ case syntax.And:
+ // spec: "As an exception to the addressability
+ // requirement x may also be a composite literal."
+ if _, ok := unparen(x.expr).(*syntax.CompositeLit); !ok && x.mode != variable {
+ check.invalidOpf(x, "cannot take address of %s", x)
+ x.mode = invalid
+ return
+ }
+ x.mode = value
+ x.typ = &Pointer{base: x.typ}
+ return
+
+ case syntax.Recv:
+ typ := x.typ.Chan()
+ if typ == nil {
+ check.invalidOpf(x, "cannot receive from non-channel %s", x)
+ x.mode = invalid
+ return
+ }
+ if typ.dir == SendOnly {
+ check.invalidOpf(x, "cannot receive from send-only channel %s", x)
+ x.mode = invalid
+ return
+ }
+ x.mode = commaok
+ x.typ = typ.elem
+ check.hasCallOrRecv = true
+ return
+ }
+
+ if !check.op(unaryOpPredicates, x, op) {
+ x.mode = invalid
+ return
+ }
+
+ if x.mode == constant_ {
+ typ := x.typ.Basic()
+ var prec uint
+ if isUnsigned(typ) {
+ prec = uint(check.conf.sizeof(typ) * 8)
+ }
+ x.val = constant.UnaryOp(op2token(op), x.val, prec)
+ // Typed constants must be representable in
+ // their type after each constant operation.
+ if isTyped(typ) {
+ if e != nil {
+ x.expr = e // for better error message
+ }
+ check.representable(x, typ)
+ }
+ return
+ }
+
+ x.mode = value
+ // x.typ remains unchanged
+}
+
+func isShift(op syntax.Operator) bool {
+ return op == syntax.Shl || op == syntax.Shr
+}
+
+func isComparison(op syntax.Operator) bool {
+ // Note: tokens are not ordered well to make this much easier
+ switch op {
+ case syntax.Eql, syntax.Neq, syntax.Lss, syntax.Leq, syntax.Gtr, syntax.Geq:
+ return true
+ }
+ return false
+}
+
+func fitsFloat32(x constant.Value) bool {
+ f32, _ := constant.Float32Val(x)
+ f := float64(f32)
+ return !math.IsInf(f, 0)
+}
+
+func roundFloat32(x constant.Value) constant.Value {
+ f32, _ := constant.Float32Val(x)
+ f := float64(f32)
+ if !math.IsInf(f, 0) {
+ return constant.MakeFloat64(f)
+ }
+ return nil
+}
+
+func fitsFloat64(x constant.Value) bool {
+ f, _ := constant.Float64Val(x)
+ return !math.IsInf(f, 0)
+}
+
+func roundFloat64(x constant.Value) constant.Value {
+ f, _ := constant.Float64Val(x)
+ if !math.IsInf(f, 0) {
+ return constant.MakeFloat64(f)
+ }
+ return nil
+}
+
+// representableConst reports whether x can be represented as
+// value of the given basic type and for the configuration
+// provided (only needed for int/uint sizes).
+//
+// If rounded != nil, *rounded is set to the rounded value of x for
+// representable floating-point and complex values, and to an Int
+// value for integer values; it is left alone otherwise.
+// It is ok to provide the addressof the first argument for rounded.
+//
+// The check parameter may be nil if representableConst is invoked
+// (indirectly) through an exported API call (AssignableTo, ConvertibleTo)
+// because we don't need the Checker's config for those calls.
+func representableConst(x constant.Value, check *Checker, typ *Basic, rounded *constant.Value) bool {
+ if x.Kind() == constant.Unknown {
+ return true // avoid follow-up errors
+ }
+
+ var conf *Config
+ if check != nil {
+ conf = check.conf
+ }
+
+ switch {
+ case isInteger(typ):
+ x := constant.ToInt(x)
+ if x.Kind() != constant.Int {
+ return false
+ }
+ if rounded != nil {
+ *rounded = x
+ }
+ if x, ok := constant.Int64Val(x); ok {
+ switch typ.kind {
+ case Int:
+ var s = uint(conf.sizeof(typ)) * 8
+ return int64(-1)<<(s-1) <= x && x <= int64(1)<<(s-1)-1
+ case Int8:
+ const s = 8
+ return -1<<(s-1) <= x && x <= 1<<(s-1)-1
+ case Int16:
+ const s = 16
+ return -1<<(s-1) <= x && x <= 1<<(s-1)-1
+ case Int32:
+ const s = 32
+ return -1<<(s-1) <= x && x <= 1<<(s-1)-1
+ case Int64, UntypedInt:
+ return true
+ case Uint, Uintptr:
+ if s := uint(conf.sizeof(typ)) * 8; s < 64 {
+ return 0 <= x && x <= int64(1)<<s-1
+ }
+ return 0 <= x
+ case Uint8:
+ const s = 8
+ return 0 <= x && x <= 1<<s-1
+ case Uint16:
+ const s = 16
+ return 0 <= x && x <= 1<<s-1
+ case Uint32:
+ const s = 32
+ return 0 <= x && x <= 1<<s-1
+ case Uint64:
+ return 0 <= x
+ default:
+ unreachable()
+ }
+ }
+ // x does not fit into int64
+ switch n := constant.BitLen(x); typ.kind {
+ case Uint, Uintptr:
+ var s = uint(conf.sizeof(typ)) * 8
+ return constant.Sign(x) >= 0 && n <= int(s)
+ case Uint64:
+ return constant.Sign(x) >= 0 && n <= 64
+ case UntypedInt:
+ return true
+ }
+
+ case isFloat(typ):
+ x := constant.ToFloat(x)
+ if x.Kind() != constant.Float {
+ return false
+ }
+ switch typ.kind {
+ case Float32:
+ if rounded == nil {
+ return fitsFloat32(x)
+ }
+ r := roundFloat32(x)
+ if r != nil {
+ *rounded = r
+ return true
+ }
+ case Float64:
+ if rounded == nil {
+ return fitsFloat64(x)
+ }
+ r := roundFloat64(x)
+ if r != nil {
+ *rounded = r
+ return true
+ }
+ case UntypedFloat:
+ return true
+ default:
+ unreachable()
+ }
+
+ case isComplex(typ):
+ x := constant.ToComplex(x)
+ if x.Kind() != constant.Complex {
+ return false
+ }
+ switch typ.kind {
+ case Complex64:
+ if rounded == nil {
+ return fitsFloat32(constant.Real(x)) && fitsFloat32(constant.Imag(x))
+ }
+ re := roundFloat32(constant.Real(x))
+ im := roundFloat32(constant.Imag(x))
+ if re != nil && im != nil {
+ *rounded = constant.BinaryOp(re, token.ADD, constant.MakeImag(im))
+ return true
+ }
+ case Complex128:
+ if rounded == nil {
+ return fitsFloat64(constant.Real(x)) && fitsFloat64(constant.Imag(x))
+ }
+ re := roundFloat64(constant.Real(x))
+ im := roundFloat64(constant.Imag(x))
+ if re != nil && im != nil {
+ *rounded = constant.BinaryOp(re, token.ADD, constant.MakeImag(im))
+ return true
+ }
+ case UntypedComplex:
+ return true
+ default:
+ unreachable()
+ }
+
+ case isString(typ):
+ return x.Kind() == constant.String
+
+ case isBoolean(typ):
+ return x.Kind() == constant.Bool
+ }
+
+ return false
+}
+
+// representable checks that a constant operand is representable in the given basic type.
+func (check *Checker) representable(x *operand, typ *Basic) {
+ assert(x.mode == constant_)
+ if !representableConst(x.val, check, typ, &x.val) {
+ var msg string
+ if isNumeric(x.typ) && isNumeric(typ) {
+ // numeric conversion : error msg
+ //
+ // integer -> integer : overflows
+ // integer -> float : overflows (actually not possible)
+ // float -> integer : truncated
+ // float -> float : overflows
+ //
+ if !isInteger(x.typ) && isInteger(typ) {
+ msg = "%s truncated to %s"
+ } else {
+ msg = "%s overflows %s"
+ }
+ } else {
+ msg = "cannot convert %s to %s"
+ }
+ check.errorf(x, msg, x, typ)
+ x.mode = invalid
+ }
+}
+
+// updateExprType updates the type of x to typ and invokes itself
+// recursively for the operands of x, depending on expression kind.
+// If typ is still an untyped and not the final type, updateExprType
+// only updates the recorded untyped type for x and possibly its
+// operands. Otherwise (i.e., typ is not an untyped type anymore,
+// or it is the final type for x), the type and value are recorded.
+// Also, if x is a constant, it must be representable as a value of typ,
+// and if x is the (formerly untyped) lhs operand of a non-constant
+// shift, it must be an integer value.
+//
+func (check *Checker) updateExprType(x syntax.Expr, typ Type, final bool) {
+ old, found := check.untyped[x]
+ if !found {
+ return // nothing to do
+ }
+
+ // update operands of x if necessary
+ switch x := x.(type) {
+ case *syntax.BadExpr,
+ *syntax.FuncLit,
+ *syntax.CompositeLit,
+ *syntax.IndexExpr,
+ *syntax.SliceExpr,
+ *syntax.AssertExpr,
+ //*syntax.StarExpr,
+ *syntax.KeyValueExpr,
+ *syntax.ArrayType,
+ *syntax.StructType,
+ *syntax.FuncType,
+ *syntax.InterfaceType,
+ *syntax.MapType,
+ *syntax.ChanType:
+ // These expression are never untyped - nothing to do.
+ // The respective sub-expressions got their final types
+ // upon assignment or use.
+ if debug {
+ check.dump("%v: found old type(%s): %s (new: %s)", posFor(x), x, old.typ, typ)
+ unreachable()
+ }
+ return
+
+ case *syntax.CallExpr:
+ // Resulting in an untyped constant (e.g., built-in complex).
+ // The respective calls take care of calling updateExprType
+ // for the arguments if necessary.
+
+ case *syntax.Name, *syntax.BasicLit, *syntax.SelectorExpr:
+ // An identifier denoting a constant, a constant literal,
+ // or a qualified identifier (imported untyped constant).
+ // No operands to take care of.
+
+ case *syntax.ParenExpr:
+ check.updateExprType(x.X, typ, final)
+
+ // case *syntax.UnaryExpr:
+ // // If x is a constant, the operands were constants.
+ // // The operands don't need to be updated since they
+ // // never get "materialized" into a typed value. If
+ // // left in the untyped map, they will be processed
+ // // at the end of the type check.
+ // if old.val != nil {
+ // break
+ // }
+ // check.updateExprType(x.X, typ, final)
+
+ case *syntax.Operation:
+ if x.Y == nil {
+ // unary expression
+ if x.Op == syntax.Mul {
+ // see commented out code for StarExpr above
+ // TODO(gri) needs cleanup
+ if debug {
+ unimplemented()
+ }
+ return
+ }
+ // If x is a constant, the operands were constants.
+ // The operands don't need to be updated since they
+ // never get "materialized" into a typed value. If
+ // left in the untyped map, they will be processed
+ // at the end of the type check.
+ if old.val != nil {
+ break
+ }
+ check.updateExprType(x.X, typ, final)
+ break
+ }
+
+ // binary expression
+ if old.val != nil {
+ break // see comment for unary expressions
+ }
+ if isComparison(x.Op) {
+ // The result type is independent of operand types
+ // and the operand types must have final types.
+ } else if isShift(x.Op) {
+ // The result type depends only on lhs operand.
+ // The rhs type was updated when checking the shift.
+ check.updateExprType(x.X, typ, final)
+ } else {
+ // The operand types match the result type.
+ check.updateExprType(x.X, typ, final)
+ check.updateExprType(x.Y, typ, final)
+ }
+
+ default:
+ unreachable()
+ }
+
+ // If the new type is not final and still untyped, just
+ // update the recorded type.
+ if !final && isUntyped(typ) {
+ old.typ = typ.Basic()
+ check.untyped[x] = old
+ return
+ }
+
+ // Otherwise we have the final (typed or untyped type).
+ // Remove it from the map of yet untyped expressions.
+ delete(check.untyped, x)
+
+ if old.isLhs {
+ // If x is the lhs of a shift, its final type must be integer.
+ // We already know from the shift check that it is representable
+ // as an integer if it is a constant.
+ if !isInteger(typ) {
+ check.invalidOpf(x, "shifted operand %s (type %s) must be integer", x, typ)
+ return
+ }
+ // Even if we have an integer, if the value is a constant we
+ // still must check that it is representable as the specific
+ // int type requested (was issue #22969). Fall through here.
+ }
+ if old.val != nil {
+ // If x is a constant, it must be representable as a value of typ.
+ c := operand{old.mode, x, old.typ, old.val, 0}
+ check.convertUntyped(&c, typ)
+ if c.mode == invalid {
+ return
+ }
+ }
+
+ // Everything's fine, record final type and value for x.
+ check.recordTypeAndValue(x, old.mode, typ, old.val)
+}
+
+// updateExprVal updates the value of x to val.
+func (check *Checker) updateExprVal(x syntax.Expr, val constant.Value) {
+ if info, ok := check.untyped[x]; ok {
+ info.val = val
+ check.untyped[x] = info
+ }
+}
+
+// convertUntyped attempts to set the type of an untyped value to the target type.
+func (check *Checker) convertUntyped(x *operand, target Type) {
+ target = expand(target)
+ if x.mode == invalid || isTyped(x.typ) || target == Typ[Invalid] {
+ return
+ }
+
+ // TODO(gri) Sloppy code - clean up. This function is central
+ // to assignment and expression checking.
+
+ if isUntyped(target) {
+ // both x and target are untyped
+ xkind := x.typ.(*Basic).kind
+ tkind := target.(*Basic).kind
+ if isNumeric(x.typ) && isNumeric(target) {
+ if xkind < tkind {
+ x.typ = target
+ check.updateExprType(x.expr, target, false)
+ }
+ } else if xkind != tkind {
+ goto Error
+ }
+ return
+ }
+
+ // In case of a type parameter, conversion must succeed against
+ // all types enumerated by the type parameter bound.
+ // TODO(gri) We should not need this because we have the code
+ // for Sum types in convertUntypedInternal. But at least one
+ // test fails. Investigate.
+ if t := target.TypeParam(); t != nil {
+ types := t.Bound().allTypes
+ if types == nil {
+ goto Error
+ }
+
+ for _, t := range unpack(types) {
+ check.convertUntypedInternal(x, t)
+ if x.mode == invalid {
+ goto Error
+ }
+ }
+
+ // keep nil untyped (was bug #39755)
+ if x.isNil() {
+ target = Typ[UntypedNil]
+ }
+ x.typ = target
+ check.updateExprType(x.expr, target, true) // UntypedNils are final
+ return
+ }
+
+ check.convertUntypedInternal(x, target)
+ return
+
+Error:
+ // TODO(gri) better error message (explain cause)
+ check.errorf(x, "cannot convert %s to %s", x, target)
+ x.mode = invalid
+}
+
+// convertUntypedInternal should only be called by convertUntyped.
+func (check *Checker) convertUntypedInternal(x *operand, target Type) {
+ assert(isTyped(target))
+
+ // typed target
+ switch t := optype(target.Under()).(type) {
+ case *Basic:
+ if x.mode == constant_ {
+ check.representable(x, t)
+ if x.mode == invalid {
+ return
+ }
+ // expression value may have been rounded - update if needed
+ check.updateExprVal(x.expr, x.val)
+ } else {
+ // Non-constant untyped values may appear as the
+ // result of comparisons (untyped bool), intermediate
+ // (delayed-checked) rhs operands of shifts, and as
+ // the value nil.
+ switch x.typ.(*Basic).kind {
+ case UntypedBool:
+ if !isBoolean(target) {
+ goto Error
+ }
+ case UntypedInt, UntypedRune, UntypedFloat, UntypedComplex:
+ if !isNumeric(target) {
+ goto Error
+ }
+ case UntypedString:
+ // Non-constant untyped string values are not
+ // permitted by the spec and should not occur.
+ unreachable()
+ case UntypedNil:
+ // Unsafe.Pointer is a basic type that includes nil.
+ if !hasNil(target) {
+ goto Error
+ }
+ default:
+ goto Error
+ }
+ }
+ case *Sum:
+ t.is(func(t Type) bool {
+ check.convertUntypedInternal(x, t)
+ return x.mode != invalid
+ })
+ case *Interface:
+ // Update operand types to the default type rather then
+ // the target (interface) type: values must have concrete
+ // dynamic types. If the value is nil, keep it untyped
+ // (this is important for tools such as go vet which need
+ // the dynamic type for argument checking of say, print
+ // functions)
+ if x.isNil() {
+ target = Typ[UntypedNil]
+ } else {
+ // cannot assign untyped values to non-empty interfaces
+ check.completeInterface(nopos, t)
+ if !t.Empty() {
+ goto Error
+ }
+ target = Default(x.typ)
+ }
+ case *Pointer, *Signature, *Slice, *Map, *Chan:
+ if !x.isNil() {
+ goto Error
+ }
+ // keep nil untyped - see comment for interfaces, above
+ target = Typ[UntypedNil]
+ default:
+ goto Error
+ }
+
+ x.typ = target
+ check.updateExprType(x.expr, target, true) // UntypedNils are final
+ return
+
+Error:
+ check.errorf(x, "cannot convert %s to %s", x, target)
+ x.mode = invalid
+}
+
+func (check *Checker) comparison(x, y *operand, op syntax.Operator) {
+ // spec: "In any comparison, the first operand must be assignable
+ // to the type of the second operand, or vice versa."
+ err := ""
+ if x.assignableTo(check, y.typ, nil) || y.assignableTo(check, x.typ, nil) {
+ defined := false
+ switch op {
+ case syntax.Eql, syntax.Neq:
+ // spec: "The equality operators == and != apply to operands that are comparable."
+ defined = Comparable(x.typ) && Comparable(y.typ) || x.isNil() && hasNil(y.typ) || y.isNil() && hasNil(x.typ)
+ case syntax.Lss, syntax.Leq, syntax.Gtr, syntax.Geq:
+ // spec: The ordering operators <, <=, >, and >= apply to operands that are ordered."
+ defined = isOrdered(x.typ) && isOrdered(y.typ)
+ default:
+ unreachable()
+ }
+ if !defined {
+ typ := x.typ
+ if x.isNil() {
+ typ = y.typ
+ }
+ err = check.sprintf("operator %s not defined for %s", op, typ)
+ }
+ } else {
+ err = check.sprintf("mismatched types %s and %s", x.typ, y.typ)
+ }
+
+ if err != "" {
+ check.errorf(x, "cannot compare %s %s %s (%s)", x.expr, op, y.expr, err)
+ x.mode = invalid
+ return
+ }
+
+ if x.mode == constant_ && y.mode == constant_ {
+ x.val = constant.MakeBool(constant.Compare(x.val, op2token(op), y.val))
+ // The operands are never materialized; no need to update
+ // their types.
+ } else {
+ x.mode = value
+ // The operands have now their final types, which at run-
+ // time will be materialized. Update the expression trees.
+ // If the current types are untyped, the materialized type
+ // is the respective default type.
+ check.updateExprType(x.expr, Default(x.typ), true)
+ check.updateExprType(y.expr, Default(y.typ), true)
+ }
+
+ // spec: "Comparison operators compare two operands and yield
+ // an untyped boolean value."
+ x.typ = Typ[UntypedBool]
+}
+
+func (check *Checker) shift(x, y *operand, e *syntax.Operation, op syntax.Operator) {
+ untypedx := isUntyped(x.typ)
+
+ var xval constant.Value
+ if x.mode == constant_ {
+ xval = constant.ToInt(x.val)
+ }
+
+ if isInteger(x.typ) || untypedx && xval != nil && xval.Kind() == constant.Int {
+ // The lhs is of integer type or an untyped constant representable
+ // as an integer. Nothing to do.
+ } else {
+ // shift has no chance
+ check.invalidOpf(x, "shifted operand %s must be integer", x)
+ x.mode = invalid
+ return
+ }
+
+ // spec: "The right operand in a shift expression must have integer type
+ // or be an untyped constant representable by a value of type uint."
+ switch {
+ case isInteger(y.typ):
+ // nothing to do
+ case isUntyped(y.typ):
+ check.convertUntyped(y, Typ[Uint])
+ if y.mode == invalid {
+ x.mode = invalid
+ return
+ }
+ default:
+ check.invalidOpf(y, "shift count %s must be integer", y)
+ x.mode = invalid
+ return
+ }
+
+ var yval constant.Value
+ if y.mode == constant_ {
+ // rhs must be an integer value
+ // (Either it was of an integer type already, or it was
+ // untyped and successfully converted to a uint above.)
+ yval = constant.ToInt(y.val)
+ assert(yval.Kind() == constant.Int)
+ if constant.Sign(yval) < 0 {
+ check.invalidOpf(y, "negative shift count %s", y)
+ x.mode = invalid
+ return
+ }
+ }
+
+ if x.mode == constant_ {
+ if y.mode == constant_ {
+ // rhs must be within reasonable bounds in constant shifts
+ const shiftBound = 1023 - 1 + 52 // so we can express smallestFloat64
+ s, ok := constant.Uint64Val(yval)
+ if !ok || s > shiftBound {
+ check.invalidOpf(y, "invalid shift count %s", y)
+ x.mode = invalid
+ return
+ }
+ // The lhs is representable as an integer but may not be an integer
+ // (e.g., 2.0, an untyped float) - this can only happen for untyped
+ // non-integer numeric constants. Correct the type so that the shift
+ // result is of integer type.
+ if !isInteger(x.typ) {
+ x.typ = Typ[UntypedInt]
+ }
+ // x is a constant so xval != nil and it must be of Int kind.
+ x.val = constant.Shift(xval, op2token(op), uint(s))
+ // Typed constants must be representable in
+ // their type after each constant operation.
+ if isTyped(x.typ) {
+ if e != nil {
+ x.expr = e // for better error message
+ }
+ check.representable(x, x.typ.Basic())
+ }
+ return
+ }
+
+ // non-constant shift with constant lhs
+ if untypedx {
+ // spec: "If the left operand of a non-constant shift
+ // expression is an untyped constant, the type of the
+ // constant is what it would be if the shift expression
+ // were replaced by its left operand alone.".
+ //
+ // Delay operand checking until we know the final type
+ // by marking the lhs expression as lhs shift operand.
+ //
+ // Usually (in correct programs), the lhs expression
+ // is in the untyped map. However, it is possible to
+ // create incorrect programs where the same expression
+ // is evaluated twice (via a declaration cycle) such
+ // that the lhs expression type is determined in the
+ // first round and thus deleted from the map, and then
+ // not found in the second round (double insertion of
+ // the same expr node still just leads to one entry for
+ // that node, and it can only be deleted once).
+ // Be cautious and check for presence of entry.
+ // Example: var e, f = int(1<<""[f]) // issue 11347
+ if info, found := check.untyped[x.expr]; found {
+ info.isLhs = true
+ check.untyped[x.expr] = info
+ }
+ // keep x's type
+ x.mode = value
+ return
+ }
+ }
+
+ // non-constant shift - lhs must be an integer
+ if !isInteger(x.typ) {
+ check.invalidOpf(x, "shifted operand %s must be integer", x)
+ x.mode = invalid
+ return
+ }
+
+ x.mode = value
+}
+
+var binaryOpPredicates = opPredicates{
+ syntax.Add: isNumericOrString,
+ syntax.Sub: isNumeric,
+ syntax.Mul: isNumeric,
+ syntax.Div: isNumeric,
+ syntax.Rem: isInteger,
+
+ syntax.And: isInteger,
+ syntax.Or: isInteger,
+ syntax.Xor: isInteger,
+ syntax.AndNot: isInteger,
+
+ syntax.AndAnd: isBoolean,
+ syntax.OrOr: isBoolean,
+}
+
+// The binary expression e may be nil. It's passed in for better error messages only.
+func (check *Checker) binary(x *operand, e *syntax.Operation, lhs, rhs syntax.Expr, op syntax.Operator) {
+ var y operand
+
+ check.expr(x, lhs)
+ check.expr(&y, rhs)
+
+ if x.mode == invalid {
+ return
+ }
+ if y.mode == invalid {
+ x.mode = invalid
+ x.expr = y.expr
+ return
+ }
+
+ if isShift(op) {
+ check.shift(x, &y, e, op)
+ return
+ }
+
+ check.convertUntyped(x, y.typ)
+ if x.mode == invalid {
+ return
+ }
+ check.convertUntyped(&y, x.typ)
+ if y.mode == invalid {
+ x.mode = invalid
+ return
+ }
+
+ if isComparison(op) {
+ check.comparison(x, &y, op)
+ return
+ }
+
+ if !check.identical(x.typ, y.typ) {
+ // only report an error if we have valid types
+ // (otherwise we had an error reported elsewhere already)
+ if x.typ != Typ[Invalid] && y.typ != Typ[Invalid] {
+ check.invalidOpf(x, "mismatched types %s and %s", x.typ, y.typ)
+ }
+ x.mode = invalid
+ return
+ }
+
+ if !check.op(binaryOpPredicates, x, op) {
+ x.mode = invalid
+ return
+ }
+
+ if op == syntax.Div || op == syntax.Rem {
+ // check for zero divisor
+ if (x.mode == constant_ || isInteger(x.typ)) && y.mode == constant_ && constant.Sign(y.val) == 0 {
+ check.invalidOpf(&y, "division by zero")
+ x.mode = invalid
+ return
+ }
+
+ // check for divisor underflow in complex division (see issue 20227)
+ if x.mode == constant_ && y.mode == constant_ && isComplex(x.typ) {
+ re, im := constant.Real(y.val), constant.Imag(y.val)
+ re2, im2 := constant.BinaryOp(re, token.MUL, re), constant.BinaryOp(im, token.MUL, im)
+ if constant.Sign(re2) == 0 && constant.Sign(im2) == 0 {
+ check.invalidOpf(&y, "division by zero")
+ x.mode = invalid
+ return
+ }
+ }
+ }
+
+ if x.mode == constant_ && y.mode == constant_ {
+ xval := x.val
+ yval := y.val
+ typ := x.typ.Basic()
+ // force integer division of integer operands
+ tok := op2token(op)
+ if op == syntax.Div && isInteger(typ) {
+ tok = token.QUO_ASSIGN
+ }
+ x.val = constant.BinaryOp(xval, tok, yval)
+ // Typed constants must be representable in
+ // their type after each constant operation.
+ if isTyped(typ) {
+ if e != nil {
+ x.expr = e // for better error message
+ }
+ check.representable(x, typ)
+ }
+ return
+ }
+
+ x.mode = value
+ // x.typ is unchanged
+}
+
+// index checks an index expression for validity.
+// If max >= 0, it is the upper bound for index.
+// If the result typ is != Typ[Invalid], index is valid and typ is its (possibly named) integer type.
+// If the result val >= 0, index is valid and val is its constant int value.
+func (check *Checker) index(index syntax.Expr, max int64) (typ Type, val int64) {
+ typ = Typ[Invalid]
+ val = -1
+
+ var x operand
+ check.expr(&x, index)
+ if x.mode == invalid {
+ return
+ }
+
+ // an untyped constant must be representable as Int
+ check.convertUntyped(&x, Typ[Int])
+ if x.mode == invalid {
+ return
+ }
+
+ // the index must be of integer type
+ if !isInteger(x.typ) {
+ check.invalidArgf(&x, "index %s must be integer", &x)
+ return
+ }
+
+ if x.mode != constant_ {
+ return x.typ, -1
+ }
+
+ // a constant index i must be in bounds
+ if constant.Sign(x.val) < 0 {
+ check.invalidArgf(&x, "index %s must not be negative", &x)
+ return
+ }
+
+ v, valid := constant.Int64Val(constant.ToInt(x.val))
+ if !valid || max >= 0 && v >= max {
+ check.errorf(&x, "index %s is out of bounds", &x)
+ return
+ }
+
+ // 0 <= v [ && v < max ]
+ return Typ[Int], v
+}
+
+// indexElts checks the elements (elts) of an array or slice composite literal
+// against the literal's element type (typ), and the element indices against
+// the literal length if known (length >= 0). It returns the length of the
+// literal (maximum index value + 1).
+//
+func (check *Checker) indexedElts(elts []syntax.Expr, typ Type, length int64) int64 {
+ visited := make(map[int64]bool, len(elts))
+ var index, max int64
+ for _, e := range elts {
+ // determine and check index
+ validIndex := false
+ eval := e
+ if kv, _ := e.(*syntax.KeyValueExpr); kv != nil {
+ if typ, i := check.index(kv.Key, length); typ != Typ[Invalid] {
+ if i >= 0 {
+ index = i
+ validIndex = true
+ } else {
+ check.errorf(e, "index %s must be integer constant", kv.Key)
+ }
+ }
+ eval = kv.Value
+ } else if length >= 0 && index >= length {
+ check.errorf(e, "index %d is out of bounds (>= %d)", index, length)
+ } else {
+ validIndex = true
+ }
+
+ // if we have a valid index, check for duplicate entries
+ if validIndex {
+ if visited[index] {
+ check.errorf(e, "duplicate index %d in array or slice literal", index)
+ }
+ visited[index] = true
+ }
+ index++
+ if index > max {
+ max = index
+ }
+
+ // check element against composite literal element type
+ var x operand
+ check.exprWithHint(&x, eval, typ)
+ check.assignment(&x, typ, "array or slice literal")
+ }
+ return max
+}
+
+// exprKind describes the kind of an expression; the kind
+// determines if an expression is valid in 'statement context'.
+type exprKind int
+
+const (
+ conversion exprKind = iota
+ expression
+ statement
+)
+
+// rawExpr typechecks expression e and initializes x with the expression
+// value or type. If an error occurred, x.mode is set to invalid.
+// If hint != nil, it is the type of a composite literal element.
+//
+func (check *Checker) rawExpr(x *operand, e syntax.Expr, hint Type) exprKind {
+ if check.conf.Trace {
+ check.trace(e.Pos(), "expr %s", e)
+ check.indent++
+ defer func() {
+ check.indent--
+ check.trace(e.Pos(), "=> %s", x)
+ }()
+ }
+
+ kind := check.exprInternal(x, e, hint)
+
+ // convert x into a user-friendly set of values
+ // TODO(gri) this code can be simplified
+ var typ Type
+ var val constant.Value
+ switch x.mode {
+ case invalid:
+ typ = Typ[Invalid]
+ case novalue:
+ typ = (*Tuple)(nil)
+ case constant_:
+ typ = x.typ
+ val = x.val
+ default:
+ typ = x.typ
+ }
+ assert(x.expr != nil && typ != nil)
+
+ if isUntyped(typ) {
+ // delay type and value recording until we know the type
+ // or until the end of type checking
+ check.rememberUntyped(x.expr, false, x.mode, typ.(*Basic), val)
+ } else {
+ check.recordTypeAndValue(e, x.mode, typ, val)
+ }
+
+ return kind
+}
+
+// exprInternal contains the core of type checking of expressions.
+// Must only be called by rawExpr.
+//
+func (check *Checker) exprInternal(x *operand, e syntax.Expr, hint Type) exprKind {
+ // make sure x has a valid state in case of bailout
+ // (was issue 5770)
+ x.mode = invalid
+ x.typ = Typ[Invalid]
+
+ switch e := e.(type) {
+ case nil:
+ unreachable()
+
+ case *syntax.BadExpr:
+ goto Error // error was reported before
+
+ case *syntax.Name:
+ check.ident(x, e, nil, false)
+
+ case *syntax.DotsType:
+ // ellipses are handled explicitly where they are legal
+ // (array composite literals and parameter lists)
+ check.error(e, "invalid use of '...'")
+ goto Error
+
+ case *syntax.BasicLit:
+ x.setConst(e.Kind, e.Value)
+ if x.mode == invalid {
+ check.invalidASTf(e, "invalid literal %v", e.Value)
+ goto Error
+ }
+
+ case *syntax.FuncLit:
+ if sig, ok := check.typ(e.Type).(*Signature); ok {
+ // Anonymous functions are considered part of the
+ // init expression/func declaration which contains
+ // them: use existing package-level declaration info.
+ decl := check.decl // capture for use in closure below
+ iota := check.iota // capture for use in closure below (#22345)
+ // Don't type-check right away because the function may
+ // be part of a type definition to which the function
+ // body refers. Instead, type-check as soon as possible,
+ // but before the enclosing scope contents changes (#22992).
+ check.later(func() {
+ check.funcBody(decl, "<function literal>", sig, e.Body, iota)
+ })
+ x.mode = value
+ x.typ = sig
+ } else {
+ check.invalidASTf(e, "invalid function literal %s", e)
+ goto Error
+ }
+
+ case *syntax.CompositeLit:
+ var typ, base Type
+
+ switch {
+ case e.Type != nil:
+ // composite literal type present - use it
+ // [...]T array types may only appear with composite literals.
+ // Check for them here so we don't have to handle ... in general.
+ if atyp, _ := e.Type.(*syntax.ArrayType); atyp != nil && atyp.Len == nil {
+ // We have an "open" [...]T array type.
+ // Create a new ArrayType with unknown length (-1)
+ // and finish setting it up after analyzing the literal.
+ typ = &Array{len: -1, elem: check.varType(atyp.Elem)}
+ base = typ
+ break
+ }
+ typ = check.typ(e.Type)
+ base = typ
+
+ case hint != nil:
+ // no composite literal type present - use hint (element type of enclosing type)
+ typ = hint
+ base, _ = deref(typ.Under()) // *T implies &T{}
+
+ default:
+ // TODO(gri) provide better error messages depending on context
+ check.error(e, "missing type in composite literal")
+ goto Error
+ }
+
+ switch utyp := optype(base.Under()).(type) {
+ case *Struct:
+ if len(e.ElemList) == 0 {
+ break
+ }
+ fields := utyp.fields
+ if _, ok := e.ElemList[0].(*syntax.KeyValueExpr); ok {
+ // all elements must have keys
+ visited := make([]bool, len(fields))
+ for _, e := range e.ElemList {
+ kv, _ := e.(*syntax.KeyValueExpr)
+ if kv == nil {
+ check.error(e, "mixture of field:value and value elements in struct literal")
+ continue
+ }
+ key, _ := kv.Key.(*syntax.Name)
+ // do all possible checks early (before exiting due to errors)
+ // so we don't drop information on the floor
+ check.expr(x, kv.Value)
+ if key == nil {
+ check.errorf(kv, "invalid field name %s in struct literal", kv.Key)
+ continue
+ }
+ i := fieldIndex(utyp.fields, check.pkg, key.Value)
+ if i < 0 {
+ check.errorf(kv, "unknown field %s in struct literal", key.Value)
+ continue
+ }
+ fld := fields[i]
+ check.recordUse(key, fld)
+ etyp := fld.typ
+ check.assignment(x, etyp, "struct literal")
+ // 0 <= i < len(fields)
+ if visited[i] {
+ check.errorf(kv, "duplicate field name %s in struct literal", key.Value)
+ continue
+ }
+ visited[i] = true
+ }
+ } else {
+ // no element must have a key
+ for i, e := range e.ElemList {
+ if kv, _ := e.(*syntax.KeyValueExpr); kv != nil {
+ check.error(kv, "mixture of field:value and value elements in struct literal")
+ continue
+ }
+ check.expr(x, e)
+ if i >= len(fields) {
+ check.error(x, "too many values in struct literal")
+ break // cannot continue
+ }
+ // i < len(fields)
+ fld := fields[i]
+ if !fld.Exported() && fld.pkg != check.pkg {
+ check.errorf(x, "implicit assignment to unexported field %s in %s literal", fld.name, typ)
+ continue
+ }
+ etyp := fld.typ
+ check.assignment(x, etyp, "struct literal")
+ }
+ if len(e.ElemList) < len(fields) {
+ check.error(e.Rbrace, "too few values in struct literal")
+ // ok to continue
+ }
+ }
+
+ case *Array:
+ // Prevent crash if the array referred to is not yet set up. Was issue #18643.
+ // This is a stop-gap solution. Should use Checker.objPath to report entire
+ // path starting with earliest declaration in the source. TODO(gri) fix this.
+ if utyp.elem == nil {
+ check.error(e, "illegal cycle in type declaration")
+ goto Error
+ }
+ n := check.indexedElts(e.ElemList, utyp.elem, utyp.len)
+ // If we have an array of unknown length (usually [...]T arrays, but also
+ // arrays [n]T where n is invalid) set the length now that we know it and
+ // record the type for the array (usually done by check.typ which is not
+ // called for [...]T). We handle [...]T arrays and arrays with invalid
+ // length the same here because it makes sense to "guess" the length for
+ // the latter if we have a composite literal; e.g. for [n]int{1, 2, 3}
+ // where n is invalid for some reason, it seems fair to assume it should
+ // be 3 (see also Checked.arrayLength and issue #27346).
+ if utyp.len < 0 {
+ utyp.len = n
+ // e.Type is missing if we have a composite literal element
+ // that is itself a composite literal with omitted type. In
+ // that case there is nothing to record (there is no type in
+ // the source at that point).
+ if e.Type != nil {
+ check.recordTypeAndValue(e.Type, typexpr, utyp, nil)
+ }
+ }
+
+ case *Slice:
+ // Prevent crash if the slice referred to is not yet set up.
+ // See analogous comment for *Array.
+ if utyp.elem == nil {
+ check.error(e, "illegal cycle in type declaration")
+ goto Error
+ }
+ check.indexedElts(e.ElemList, utyp.elem, -1)
+
+ case *Map:
+ // Prevent crash if the map referred to is not yet set up.
+ // See analogous comment for *Array.
+ if utyp.key == nil || utyp.elem == nil {
+ check.error(e, "illegal cycle in type declaration")
+ goto Error
+ }
+ visited := make(map[interface{}][]Type, len(e.ElemList))
+ for _, e := range e.ElemList {
+ kv, _ := e.(*syntax.KeyValueExpr)
+ if kv == nil {
+ check.error(e, "missing key in map literal")
+ continue
+ }
+ check.exprWithHint(x, kv.Key, utyp.key)
+ check.assignment(x, utyp.key, "map literal")
+ if x.mode == invalid {
+ continue
+ }
+ if x.mode == constant_ {
+ duplicate := false
+ // if the key is of interface type, the type is also significant when checking for duplicates
+ xkey := keyVal(x.val)
+ if utyp.key.Interface() != nil {
+ for _, vtyp := range visited[xkey] {
+ if check.identical(vtyp, x.typ) {
+ duplicate = true
+ break
+ }
+ }
+ visited[xkey] = append(visited[xkey], x.typ)
+ } else {
+ _, duplicate = visited[xkey]
+ visited[xkey] = nil
+ }
+ if duplicate {
+ check.errorf(x, "duplicate key %s in map literal", x.val)
+ continue
+ }
+ }
+ check.exprWithHint(x, kv.Value, utyp.elem)
+ check.assignment(x, utyp.elem, "map literal")
+ }
+
+ default:
+ // when "using" all elements unpack KeyValueExpr
+ // explicitly because check.use doesn't accept them
+ for _, e := range e.ElemList {
+ if kv, _ := e.(*syntax.KeyValueExpr); kv != nil {
+ // Ideally, we should also "use" kv.Key but we can't know
+ // if it's an externally defined struct key or not. Going
+ // forward anyway can lead to other errors. Give up instead.
+ e = kv.Value
+ }
+ check.use(e)
+ }
+ // if utyp is invalid, an error was reported before
+ if utyp != Typ[Invalid] {
+ check.errorf(e, "invalid composite literal type %s", typ)
+ goto Error
+ }
+ }
+
+ x.mode = value
+ x.typ = typ
+
+ case *syntax.ParenExpr:
+ kind := check.rawExpr(x, e.X, nil)
+ x.expr = e
+ return kind
+
+ case *syntax.SelectorExpr:
+ check.selector(x, e)
+
+ case *syntax.IndexExpr:
+ check.exprOrType(x, e.X)
+ if x.mode == invalid {
+ check.use(e.Index)
+ goto Error
+ }
+
+ if x.mode == typexpr {
+ // type instantiation
+ x.mode = invalid
+ x.typ = check.varType(e)
+ if x.typ != Typ[Invalid] {
+ x.mode = typexpr
+ }
+ return expression
+ }
+
+ if x.mode == value {
+ if sig := x.typ.Signature(); sig != nil && len(sig.tparams) > 0 {
+ // function instantiation
+ check.funcInst(x, e)
+ return expression
+ }
+ }
+
+ // ordinary index expression
+ valid := false
+ length := int64(-1) // valid if >= 0
+ switch typ := optype(x.typ.Under()).(type) {
+ case *Basic:
+ if isString(typ) {
+ valid = true
+ if x.mode == constant_ {
+ length = int64(len(constant.StringVal(x.val)))
+ }
+ // an indexed string always yields a byte value
+ // (not a constant) even if the string and the
+ // index are constant
+ x.mode = value
+ x.typ = universeByte // use 'byte' name
+ }
+
+ case *Array:
+ valid = true
+ length = typ.len
+ if x.mode != variable {
+ x.mode = value
+ }
+ x.typ = typ.elem
+
+ case *Pointer:
+ if typ := typ.base.Array(); typ != nil {
+ valid = true
+ length = typ.len
+ x.mode = variable
+ x.typ = typ.elem
+ }
+
+ case *Slice:
+ valid = true
+ x.mode = variable
+ x.typ = typ.elem
+
+ case *Map:
+ var key operand
+ check.expr(&key, e.Index)
+ check.assignment(&key, typ.key, "map index")
+ if x.mode == invalid {
+ goto Error
+ }
+ x.mode = mapindex
+ x.typ = typ.elem
+ x.expr = e
+ return expression
+
+ case *Sum:
+ // A sum type can be indexed if all the sum's types
+ // support indexing and have the same element type.
+ var elem Type
+ if typ.is(func(t Type) bool {
+ var e Type
+ switch t := t.Under().(type) {
+ case *Basic:
+ if isString(t) {
+ e = universeByte
+ }
+ case *Array:
+ e = t.elem
+ case *Pointer:
+ if t := t.base.Array(); t != nil {
+ e = t.elem
+ }
+ case *Slice:
+ e = t.elem
+ case *Map:
+ e = t.elem
+ case *TypeParam:
+ check.errorf(x, "type of %s contains a type parameter - cannot index (implementation restriction)", x)
+ case *instance:
+ unimplemented()
+ }
+ if e != nil && (e == elem || elem == nil) {
+ elem = e
+ return true
+ }
+ return false
+ }) {
+ valid = true
+ x.mode = variable
+ x.typ = elem
+ }
+ }
+
+ if !valid {
+ check.invalidOpf(x, "cannot index %s", x)
+ goto Error
+ }
+
+ if e.Index == nil {
+ check.invalidASTf(e, "missing index for %s", x)
+ goto Error
+ }
+
+ index := e.Index
+ if l, _ := index.(*syntax.ListExpr); l != nil {
+ if n := len(l.ElemList); n <= 1 {
+ check.invalidASTf(e, "invalid use of ListExpr for index expression %s with %d indices", e, n)
+ goto Error
+ }
+ // len(l.ElemList) > 1
+ check.invalidOpf(l.ElemList[1], "more than one index")
+ index = l.ElemList[0] // continue with first index
+ }
+
+ // In pathological (invalid) cases (e.g.: type T1 [][[]T1{}[0][0]]T0)
+ // the element type may be accessed before it's set. Make sure we have
+ // a valid type.
+ if x.typ == nil {
+ x.typ = Typ[Invalid]
+ }
+
+ check.index(index, length)
+ // ok to continue
+
+ case *syntax.SliceExpr:
+ check.expr(x, e.X)
+ if x.mode == invalid {
+ check.use(e.Index[:]...)
+ goto Error
+ }
+
+ valid := false
+ length := int64(-1) // valid if >= 0
+ switch typ := optype(x.typ.Under()).(type) {
+ case *Basic:
+ if isString(typ) {
+ if e.Full {
+ check.invalidOpf(x, "3-index slice of string")
+ goto Error
+ }
+ valid = true
+ if x.mode == constant_ {
+ length = int64(len(constant.StringVal(x.val)))
+ }
+ // spec: "For untyped string operands the result
+ // is a non-constant value of type string."
+ if typ.kind == UntypedString {
+ x.typ = Typ[String]
+ }
+ }
+
+ case *Array:
+ valid = true
+ length = typ.len
+ if x.mode != variable {
+ check.invalidOpf(x, "cannot slice %s (value not addressable)", x)
+ goto Error
+ }
+ x.typ = &Slice{elem: typ.elem}
+
+ case *Pointer:
+ if typ := typ.base.Array(); typ != nil {
+ valid = true
+ length = typ.len
+ x.typ = &Slice{elem: typ.elem}
+ }
+
+ case *Slice:
+ valid = true
+ // x.typ doesn't change
+
+ case *Sum, *TypeParam:
+ check.errorf(x, "generic slice expressions not yet implemented")
+ goto Error
+ }
+
+ if !valid {
+ check.invalidOpf(x, "cannot slice %s", x)
+ goto Error
+ }
+
+ x.mode = value
+
+ // spec: "Only the first index may be omitted; it defaults to 0."
+ if e.Full && (e.Index[1] == nil || e.Index[2] == nil) {
+ check.invalidASTf(e, "2nd and 3rd index required in 3-index slice")
+ goto Error
+ }
+
+ // check indices
+ var ind [3]int64
+ for i, expr := range e.Index {
+ x := int64(-1)
+ switch {
+ case expr != nil:
+ // The "capacity" is only known statically for strings, arrays,
+ // and pointers to arrays, and it is the same as the length for
+ // those types.
+ max := int64(-1)
+ if length >= 0 {
+ max = length + 1
+ }
+ if _, v := check.index(expr, max); v >= 0 {
+ x = v
+ }
+ case i == 0:
+ // default is 0 for the first index
+ x = 0
+ case length >= 0:
+ // default is length (== capacity) otherwise
+ x = length
+ }
+ ind[i] = x
+ }
+
+ // constant indices must be in range
+ // (check.index already checks that existing indices >= 0)
+ L:
+ for i, x := range ind[:len(ind)-1] {
+ if x > 0 {
+ for _, y := range ind[i+1:] {
+ if y >= 0 && x > y {
+ check.errorf(e, "invalid slice indices: %d > %d", x, y)
+ break L // only report one error, ok to continue
+ }
+ }
+ }
+ }
+
+ case *syntax.AssertExpr:
+ check.expr(x, e.X)
+ if x.mode == invalid {
+ goto Error
+ }
+ var xtyp *Interface
+ var strict bool
+ switch t := optype(x.typ.Under()).(type) {
+ case *Interface:
+ xtyp = t
+ // Disabled for now. It is not clear what the right approach is
+ // here. Also, the implementation below is inconsistent because
+ // the underlying type of a type parameter is either itself or
+ // a sum type if the corresponding type bound contains a type list.
+ // case *TypeParam:
+ // xtyp = t.Bound()
+ // strict = true
+ default:
+ check.invalidOpf(x, "%s is not an interface type", x)
+ goto Error
+ }
+ // x.(type) expressions are encoded via TypeSwitchGuards
+ if e.Type == nil {
+ check.invalidASTf(e, "invalid use of AssertExpr")
+ goto Error
+ }
+ T := check.varType(e.Type)
+ if T == Typ[Invalid] {
+ goto Error
+ }
+ check.typeAssertion(posFor(x), x, xtyp, T, strict)
+ x.mode = commaok
+ x.typ = T
+
+ case *syntax.TypeSwitchGuard:
+ // x.(type) expressions are handled explicitly in type switches
+ check.invalidASTf(e, "use of .(type) outside type switch")
+ goto Error
+
+ case *syntax.CallExpr:
+ return check.call(x, e)
+
+ // case *syntax.UnaryExpr:
+ // check.expr(x, e.X)
+ // if x.mode == invalid {
+ // goto Error
+ // }
+ // check.unary(x, e, e.Op)
+ // if x.mode == invalid {
+ // goto Error
+ // }
+ // if e.Op == token.ARROW {
+ // x.expr = e
+ // return statement // receive operations may appear in statement context
+ // }
+
+ // case *syntax.BinaryExpr:
+ // check.binary(x, e, e.X, e.Y, e.Op)
+ // if x.mode == invalid {
+ // goto Error
+ // }
+
+ case *syntax.Operation:
+ if e.Y == nil {
+ // unary expression
+ if e.Op == syntax.Mul {
+ // pointer indirection
+ check.exprOrType(x, e.X)
+ switch x.mode {
+ case invalid:
+ goto Error
+ case typexpr:
+ x.typ = &Pointer{base: x.typ}
+ default:
+ if typ := x.typ.Pointer(); typ != nil {
+ x.mode = variable
+ x.typ = typ.base
+ } else {
+ check.invalidOpf(x, "cannot indirect %s", x)
+ goto Error
+ }
+ }
+ break
+ }
+
+ check.expr(x, e.X)
+ if x.mode == invalid {
+ goto Error
+ }
+ check.unary(x, e, e.Op)
+ if x.mode == invalid {
+ goto Error
+ }
+ if e.Op == syntax.Recv {
+ x.expr = e
+ return statement // receive operations may appear in statement context
+ }
+ break
+ }
+
+ // binary expression
+ check.binary(x, e, e.X, e.Y, e.Op)
+ if x.mode == invalid {
+ goto Error
+ }
+
+ case *syntax.KeyValueExpr:
+ // key:value expressions are handled in composite literals
+ check.invalidASTf(e, "no key:value expected")
+ goto Error
+
+ case *syntax.ArrayType, *syntax.SliceType, *syntax.StructType, *syntax.FuncType,
+ *syntax.InterfaceType, *syntax.MapType, *syntax.ChanType:
+ x.mode = typexpr
+ x.typ = check.typ(e)
+ // Note: rawExpr (caller of exprInternal) will call check.recordTypeAndValue
+ // even though check.typ has already called it. This is fine as both
+ // times the same expression and type are recorded. It is also not a
+ // performance issue because we only reach here for composite literal
+ // types, which are comparatively rare.
+
+ default:
+ panic(fmt.Sprintf("%s: unknown expression type %T", posFor(e), e))
+ }
+
+ // everything went well
+ x.expr = e
+ return expression
+
+Error:
+ x.mode = invalid
+ x.expr = e
+ return statement // avoid follow-up errors
+}
+
+func keyVal(x constant.Value) interface{} {
+ switch x.Kind() {
+ case constant.Bool:
+ return constant.BoolVal(x)
+ case constant.String:
+ return constant.StringVal(x)
+ case constant.Int:
+ if v, ok := constant.Int64Val(x); ok {
+ return v
+ }
+ if v, ok := constant.Uint64Val(x); ok {
+ return v
+ }
+ case constant.Float:
+ v, _ := constant.Float64Val(x)
+ return v
+ case constant.Complex:
+ r, _ := constant.Float64Val(constant.Real(x))
+ i, _ := constant.Float64Val(constant.Imag(x))
+ return complex(r, i)
+ }
+ return x
+}
+
+// typeAssertion checks that x.(T) is legal; xtyp must be the type of x.
+func (check *Checker) typeAssertion(pos syntax.Pos, x *operand, xtyp *Interface, T Type, strict bool) {
+ method, wrongType := check.assertableTo(xtyp, T, strict)
+ if method == nil {
+ return
+ }
+ var msg string
+ if wrongType != nil {
+ if check.identical(method.typ, wrongType.typ) {
+ msg = fmt.Sprintf("missing method %s (%s has pointer receiver)", method.name, method.name)
+ } else {
+ msg = fmt.Sprintf("wrong type for method %s (have %s, want %s)", method.name, wrongType.typ, method.typ)
+ }
+ } else {
+ msg = "missing method " + method.name
+ }
+ check.errorf(pos, "%s cannot have dynamic type %s (%s)", x, T, msg)
+}
+
+// expr typechecks expression e and initializes x with the expression value.
+// The result must be a single value.
+// If an error occurred, x.mode is set to invalid.
+//
+func (check *Checker) expr(x *operand, e syntax.Expr) {
+ check.rawExpr(x, e, nil)
+ check.exclude(x, 1<<novalue|1<<builtin|1<<typexpr)
+ check.singleValue(x)
+}
+
+// multiExpr is like expr but the result may also be a multi-value.
+func (check *Checker) multiExpr(x *operand, e syntax.Expr) {
+ check.rawExpr(x, e, nil)
+ check.exclude(x, 1<<novalue|1<<builtin|1<<typexpr)
+}
+
+// multiExprOrType is like multiExpr but the result may also be a type.
+func (check *Checker) multiExprOrType(x *operand, e syntax.Expr) {
+ check.rawExpr(x, e, nil)
+ check.exclude(x, 1<<novalue|1<<builtin)
+}
+
+// exprWithHint typechecks expression e and initializes x with the expression value;
+// hint is the type of a composite literal element.
+// If an error occurred, x.mode is set to invalid.
+//
+func (check *Checker) exprWithHint(x *operand, e syntax.Expr, hint Type) {
+ assert(hint != nil)
+ check.rawExpr(x, e, hint)
+ check.exclude(x, 1<<novalue|1<<builtin|1<<typexpr)
+ check.singleValue(x)
+}
+
+// exprOrType typechecks expression or type e and initializes x with the expression value or type.
+// If an error occurred, x.mode is set to invalid.
+//
+func (check *Checker) exprOrType(x *operand, e syntax.Expr) {
+ check.rawExpr(x, e, nil)
+ check.exclude(x, 1<<novalue)
+ check.singleValue(x)
+}
+
+// exclude reports an error if x.mode is in modeset and sets x.mode to invalid.
+// The modeset may contain any of 1<<novalue, 1<<builtin, 1<<typexpr.
+func (check *Checker) exclude(x *operand, modeset uint) {
+ if modeset&(1<<x.mode) != 0 {
+ var msg string
+ switch x.mode {
+ case novalue:
+ if modeset&(1<<typexpr) != 0 {
+ msg = "%s used as value"
+ } else {
+ msg = "%s used as value or type"
+ }
+ case builtin:
+ msg = "%s must be called"
+ case typexpr:
+ msg = "%s is not an expression"
+ default:
+ unreachable()
+ }
+ check.errorf(x, msg, x)
+ x.mode = invalid
+ }
+}
+
+// singleValue reports an error if x describes a tuple and sets x.mode to invalid.
+func (check *Checker) singleValue(x *operand) {
+ if x.mode == value {
+ // tuple types are never named - no need for underlying type below
+ if t, ok := x.typ.(*Tuple); ok {
+ assert(t.Len() != 1)
+ check.errorf(x, "%d-valued %s where single value is expected", t.Len(), x)
+ x.mode = invalid
+ }
+ }
+}
diff --git a/src/cmd/compile/internal/types2/exprstring.go b/src/cmd/compile/internal/types2/exprstring.go
new file mode 100644
index 0000000000..e187d050e6
--- /dev/null
+++ b/src/cmd/compile/internal/types2/exprstring.go
@@ -0,0 +1,287 @@
+// UNREVIEWED
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements printing of expressions.
+
+package types2
+
+import (
+ "bytes"
+ "cmd/compile/internal/syntax"
+)
+
+// ExprString returns the (possibly shortened) string representation for x.
+// Shortened representations are suitable for user interfaces but may not
+// necessarily follow Go syntax.
+func ExprString(x syntax.Expr) string {
+ var buf bytes.Buffer
+ WriteExpr(&buf, x)
+ return buf.String()
+}
+
+// WriteExpr writes the (possibly shortened) string representation for x to buf.
+// Shortened representations are suitable for user interfaces but may not
+// necessarily follow Go syntax.
+func WriteExpr(buf *bytes.Buffer, x syntax.Expr) {
+ // The AST preserves source-level parentheses so there is
+ // no need to introduce them here to correct for different
+ // operator precedences. (This assumes that the AST was
+ // generated by a Go parser.)
+
+ // TODO(gri): This assumption is not correct - we need to recreate
+ // parentheses in expressions.
+
+ switch x := x.(type) {
+ default:
+ buf.WriteString("(ast: bad expr)") // nil, syntax.BadExpr, syntax.KeyValueExpr
+
+ case *syntax.Name:
+ buf.WriteString(x.Value)
+
+ case *syntax.DotsType:
+ buf.WriteString("...")
+ if x.Elem != nil {
+ WriteExpr(buf, x.Elem)
+ }
+
+ case *syntax.BasicLit:
+ buf.WriteString(x.Value)
+
+ case *syntax.FuncLit:
+ buf.WriteByte('(')
+ WriteExpr(buf, x.Type)
+ buf.WriteString(" literal)") // shortened
+
+ case *syntax.CompositeLit:
+ buf.WriteByte('(')
+ WriteExpr(buf, x.Type)
+ buf.WriteString(" literal)") // shortened
+
+ case *syntax.ParenExpr:
+ buf.WriteByte('(')
+ WriteExpr(buf, x.X)
+ buf.WriteByte(')')
+
+ case *syntax.SelectorExpr:
+ WriteExpr(buf, x.X)
+ buf.WriteByte('.')
+ buf.WriteString(x.Sel.Value)
+
+ case *syntax.IndexExpr:
+ WriteExpr(buf, x.X)
+ buf.WriteByte('[')
+ WriteExpr(buf, x.Index) // x.Index may be a *ListExpr
+ buf.WriteByte(']')
+
+ case *syntax.SliceExpr:
+ WriteExpr(buf, x.X)
+ buf.WriteByte('[')
+ if x.Index[0] != nil {
+ WriteExpr(buf, x.Index[0])
+ }
+ buf.WriteByte(':')
+ if x.Index[1] != nil {
+ WriteExpr(buf, x.Index[1])
+ }
+ if x.Full {
+ buf.WriteByte(':')
+ if x.Index[2] != nil {
+ WriteExpr(buf, x.Index[2])
+ }
+ }
+ buf.WriteByte(']')
+
+ case *syntax.AssertExpr:
+ WriteExpr(buf, x.X)
+ buf.WriteString(".(")
+ WriteExpr(buf, x.Type)
+ buf.WriteByte(')')
+
+ case *syntax.CallExpr:
+ WriteExpr(buf, x.Fun)
+ buf.WriteByte('(')
+ writeExprList(buf, x.ArgList)
+ if x.HasDots {
+ buf.WriteString("...")
+ }
+ buf.WriteByte(')')
+
+ case *syntax.ListExpr:
+ writeExprList(buf, x.ElemList)
+
+ case *syntax.Operation:
+ // TODO(gri) This would be simpler if x.X == nil meant unary expression.
+ if x.Y == nil {
+ // unary expression
+ buf.WriteString(x.Op.String())
+ WriteExpr(buf, x.X)
+ } else {
+ // binary expression
+ WriteExpr(buf, x.X)
+ buf.WriteByte(' ')
+ buf.WriteString(x.Op.String())
+ buf.WriteByte(' ')
+ WriteExpr(buf, x.Y)
+ }
+
+ // case *ast.StarExpr:
+ // buf.WriteByte('*')
+ // WriteExpr(buf, x.X)
+
+ // case *ast.UnaryExpr:
+ // buf.WriteString(x.Op.String())
+ // WriteExpr(buf, x.X)
+
+ // case *ast.BinaryExpr:
+ // WriteExpr(buf, x.X)
+ // buf.WriteByte(' ')
+ // buf.WriteString(x.Op.String())
+ // buf.WriteByte(' ')
+ // WriteExpr(buf, x.Y)
+
+ case *syntax.ArrayType:
+ if x.Len == nil {
+ buf.WriteString("[...]")
+ } else {
+ buf.WriteByte('[')
+ WriteExpr(buf, x.Len)
+ buf.WriteByte(']')
+ }
+ WriteExpr(buf, x.Elem)
+
+ case *syntax.SliceType:
+ buf.WriteString("[]")
+ WriteExpr(buf, x.Elem)
+
+ case *syntax.StructType:
+ buf.WriteString("struct{")
+ writeFieldList(buf, x.FieldList, "; ", false)
+ buf.WriteByte('}')
+
+ case *syntax.FuncType:
+ buf.WriteString("func")
+ writeSigExpr(buf, x)
+
+ case *syntax.InterfaceType:
+ // separate type list types from method list
+ // TODO(gri) we can get rid of this extra code if writeExprList does the separation
+ var types []syntax.Expr
+ var methods []*syntax.Field
+ for _, f := range x.MethodList {
+ if f.Name != nil && f.Name.Value == "type" {
+ // type list type
+ types = append(types, f.Type)
+ } else {
+ // method or embedded interface
+ methods = append(methods, f)
+ }
+ }
+
+ buf.WriteString("interface{")
+ writeFieldList(buf, methods, "; ", true)
+ if len(types) > 0 {
+ if len(methods) > 0 {
+ buf.WriteString("; ")
+ }
+ buf.WriteString("type ")
+ writeExprList(buf, types)
+ }
+ buf.WriteByte('}')
+
+ case *syntax.MapType:
+ buf.WriteString("map[")
+ WriteExpr(buf, x.Key)
+ buf.WriteByte(']')
+ WriteExpr(buf, x.Value)
+
+ case *syntax.ChanType:
+ var s string
+ switch x.Dir {
+ case syntax.SendOnly:
+ s = "chan<- "
+ case syntax.RecvOnly:
+ s = "<-chan "
+ default:
+ s = "chan "
+ }
+ buf.WriteString(s)
+ if e, _ := x.Elem.(*syntax.ChanType); x.Dir != syntax.SendOnly && e != nil && e.Dir == syntax.RecvOnly {
+ // don't print chan (<-chan T) as chan <-chan T (but chan<- <-chan T is ok)
+ buf.WriteByte('(')
+ WriteExpr(buf, x.Elem)
+ buf.WriteByte(')')
+ } else {
+ WriteExpr(buf, x.Elem)
+ }
+ }
+}
+
+func writeSigExpr(buf *bytes.Buffer, sig *syntax.FuncType) {
+ buf.WriteByte('(')
+ writeFieldList(buf, sig.ParamList, ", ", false)
+ buf.WriteByte(')')
+
+ res := sig.ResultList
+ n := len(res)
+ if n == 0 {
+ // no result
+ return
+ }
+
+ buf.WriteByte(' ')
+ if n == 1 && res[0].Name == nil {
+ // single unnamed result
+ WriteExpr(buf, res[0].Type)
+ return
+ }
+
+ // multiple or named result(s)
+ buf.WriteByte('(')
+ writeFieldList(buf, res, ", ", false)
+ buf.WriteByte(')')
+}
+
+func writeFieldList(buf *bytes.Buffer, list []*syntax.Field, sep string, iface bool) {
+ for i := 0; i < len(list); {
+ f := list[i]
+ if i > 0 {
+ buf.WriteString(sep)
+ }
+
+ // if we don't have a name, we have an embedded type
+ if f.Name == nil {
+ WriteExpr(buf, f.Type)
+ i++
+ continue
+ }
+
+ // types of interface methods consist of signatures only
+ if sig, _ := f.Type.(*syntax.FuncType); sig != nil && iface {
+ buf.WriteString(f.Name.Value)
+ writeSigExpr(buf, sig)
+ i++
+ continue
+ }
+
+ // write the type only once for a sequence of fields with the same type
+ t := f.Type
+ buf.WriteString(f.Name.Value)
+ for i++; i < len(list) && list[i].Type == t; i++ {
+ buf.WriteString(", ")
+ buf.WriteString(list[i].Name.Value)
+ }
+ buf.WriteByte(' ')
+ WriteExpr(buf, t)
+ }
+}
+
+func writeExprList(buf *bytes.Buffer, list []syntax.Expr) {
+ for i, x := range list {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ WriteExpr(buf, x)
+ }
+}
diff --git a/src/cmd/compile/internal/types2/exprstring_test.go b/src/cmd/compile/internal/types2/exprstring_test.go
new file mode 100644
index 0000000000..d7b9d5b2ef
--- /dev/null
+++ b/src/cmd/compile/internal/types2/exprstring_test.go
@@ -0,0 +1,97 @@
+// UNREVIEWED
+// Copyright 2013 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 types2_test
+
+import (
+ "testing"
+
+ "cmd/compile/internal/syntax"
+ . "cmd/compile/internal/types2"
+)
+
+var testExprs = []testEntry{
+ // basic type literals
+ dup("x"),
+ dup("true"),
+ dup("42"),
+ dup("3.1415"),
+ dup("2.71828i"),
+ dup(`'a'`),
+ dup(`"foo"`),
+ dup("`bar`"),
+
+ // func and composite literals
+ {"func(){}", "(func() literal)"},
+ {"func(x int) complex128 {}", "(func(x int) complex128 literal)"},
+ {"[]int{1, 2, 3}", "([]int literal)"},
+
+ // non-type expressions
+ dup("(x)"),
+ dup("x.f"),
+ dup("a[i]"),
+
+ dup("s[:]"),
+ dup("s[i:]"),
+ dup("s[:j]"),
+ dup("s[i:j]"),
+ dup("s[:j:k]"),
+ dup("s[i:j:k]"),
+
+ dup("x.(T)"),
+
+ dup("x.([10]int)"),
+ dup("x.([...]int)"),
+
+ dup("x.(struct{})"),
+ dup("x.(struct{x int; y, z float32; E})"),
+
+ dup("x.(func())"),
+ dup("x.(func(x int))"),
+ dup("x.(func() int)"),
+ dup("x.(func(x, y int, z float32) (r int))"),
+ dup("x.(func(a, b, c int))"),
+ dup("x.(func(x ...T))"),
+
+ dup("x.(interface{})"),
+ dup("x.(interface{m(); n(x int); E})"),
+ dup("x.(interface{m(); n(x int) T; E; F})"),
+
+ dup("x.(map[K]V)"),
+
+ dup("x.(chan E)"),
+ dup("x.(<-chan E)"),
+ dup("x.(chan<- chan int)"),
+ dup("x.(chan<- <-chan int)"),
+ dup("x.(<-chan chan int)"),
+ dup("x.(chan (<-chan int))"),
+
+ dup("f()"),
+ dup("f(x)"),
+ dup("int(x)"),
+ dup("f(x, x + y)"),
+ dup("f(s...)"),
+ dup("f(a, s...)"),
+
+ dup("*x"),
+ dup("&x"),
+ dup("x + y"),
+ dup("x + y << (2 * s)"),
+}
+
+func TestExprString(t *testing.T) {
+ for _, test := range testExprs {
+ src := "package p; var _ = " + test.src
+ f, err := parseSrc("expr", src)
+ if err != nil {
+ t.Errorf("%s: %s", test.src, err)
+ continue
+ }
+ x := f.DeclList[0].(*syntax.VarDecl).Values
+ if got := ExprString(x); got != test.str {
+ t.Errorf("%s: got %s, want %s", test.src, got, test.str)
+ }
+ }
+}
diff --git a/src/cmd/compile/internal/types2/fixedbugs/issue39634.go2 b/src/cmd/compile/internal/types2/fixedbugs/issue39634.go2
new file mode 100644
index 0000000000..f37930d0e8
--- /dev/null
+++ b/src/cmd/compile/internal/types2/fixedbugs/issue39634.go2
@@ -0,0 +1,92 @@
+// UNREVIEWED
+// 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.
+
+// Examples adjusted to match new [T any] syntax for type parameters.
+// Also, previously permitted empty type parameter lists and instantiations
+// are now syntax errors.
+
+package p
+
+// crash 1
+type nt1[_ any]interface{g /* ERROR undeclared name */ }
+type ph1[e nt1[e],g(d /* ERROR undeclared name */ )]s /* ERROR undeclared name */
+func(*ph1[e,e /* ERROR redeclared */ ])h(d /* ERROR undeclared name */ )
+
+// crash 2
+// Disabled: empty []'s are now syntax errors. This example leads to too many follow-on errors.
+// type Numeric2 interface{t2 /* ERROR not a type */ }
+// func t2[T Numeric2](s[]T){0 /* ERROR not a type */ []{s /* ERROR cannot index */ [0][0]}}
+
+// crash 3
+type t3 *interface{ t3.p /* ERROR no field or method p */ }
+
+// crash 4
+type Numeric4 interface{t4 /* ERROR not a type */ }
+func t4[T Numeric4](s[]T){if( /* ERROR non-boolean */ 0){*s /* ERROR cannot indirect */ [0]}}
+
+// crash 7
+type foo7 interface { bar() }
+type x7[A any] struct{ foo7 }
+func main7() { var _ foo7 = x7[int]{} }
+
+// crash 8
+type foo8[A any] interface { type A }
+func bar8[A foo8[A]](a A) {}
+func main8() {}
+
+// crash 9
+type foo9[A any] interface { type foo9 /* ERROR interface contains type constraints */ [A] }
+func _() { var _ = new(foo9 /* ERROR interface contains type constraints */ [int]) }
+
+// crash 12
+var u /* ERROR cycle */ , i [func /* ERROR used as value */ /* ERROR used as value */ (u, c /* ERROR undeclared */ /* ERROR undeclared */ ) {}(0, len)]c /* ERROR undeclared */ /* ERROR undeclared */
+
+// crash 15
+func y15() { var a /* ERROR declared but not used */ interface{ p() } = G15[string]{} }
+type G15[X any] s /* ERROR undeclared name */
+func (G15 /* ERROR generic type .* without instantiation */ ) p()
+
+// crash 16
+type Foo16[T any] r16 /* ERROR not a type */
+func r16[T any]() Foo16[Foo16[T]]
+
+// crash 17
+type Y17 interface{ c() }
+type Z17 interface {
+ c() Y17
+ Y17 /* ERROR duplicate method */
+}
+func F17[T Z17](T)
+
+// crash 18
+type o18[T any] []func(_ o18[[]_ /* ERROR cannot use _ */ ])
+
+// crash 19
+type Z19 [][[]Z19{}[0][0]]c19 /* ERROR undeclared */
+
+// crash 20
+type Z20 /* ERROR illegal cycle */ interface{ Z20 }
+func F20[t Z20]() { F20(t /* ERROR invalid composite literal type */ {}) }
+
+// crash 21
+type Z21 /* ERROR illegal cycle */ interface{ Z21 }
+func F21[T Z21]() { ( /* ERROR not used */ F21[Z21]) }
+
+// crash 24
+type T24[P any] P
+func (r T24[P]) m() { T24 /* ERROR without instantiation */ .m() }
+
+// crash 25
+type T25[A any] int
+func (t T25[A]) m1() {}
+var x T25 /* ERROR without instantiation */ .m1
+
+// crash 26
+type T26 = interface{ F26[ /* ERROR cannot have type parameters */ Z any]() }
+func F26[Z any]() T26 { return F26 /* ERROR without instantiation */ /* ERROR missing method */ [] /* ERROR operand */ }
+
+// crash 27
+func e27[T any]() interface{ x27 /* ERROR not a type */ }
+func x27() { e27( /* ERROR cannot infer T */ ) } \ No newline at end of file
diff --git a/src/cmd/compile/internal/types2/fixedbugs/issue39664.go2 b/src/cmd/compile/internal/types2/fixedbugs/issue39664.go2
new file mode 100644
index 0000000000..cf566c3e24
--- /dev/null
+++ b/src/cmd/compile/internal/types2/fixedbugs/issue39664.go2
@@ -0,0 +1,16 @@
+// UNREVIEWED
+// 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 T[_ any] struct {}
+
+func (T /* ERROR instantiation */ ) m()
+
+func _() {
+ var x interface { m() }
+ x = T[int]{}
+ _ = x
+}
diff --git a/src/cmd/compile/internal/types2/fixedbugs/issue39680.go2 b/src/cmd/compile/internal/types2/fixedbugs/issue39680.go2
new file mode 100644
index 0000000000..3239c57d43
--- /dev/null
+++ b/src/cmd/compile/internal/types2/fixedbugs/issue39680.go2
@@ -0,0 +1,28 @@
+// UNREVIEWED
+// 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 "fmt"
+
+// Minimal test case.
+func _[T interface{type T}](x T) T{
+ return x
+}
+
+// Test case from issue.
+type constr[T any] interface {
+ type T
+}
+
+func Print[T constr[T]](s []T) {
+ for _, v := range s {
+ fmt.Print(v)
+ }
+}
+
+func f() {
+ Print([]string{"Hello, ", "playground\n"})
+}
diff --git a/src/cmd/compile/internal/types2/fixedbugs/issue39693.go2 b/src/cmd/compile/internal/types2/fixedbugs/issue39693.go2
new file mode 100644
index 0000000000..6f4d701185
--- /dev/null
+++ b/src/cmd/compile/internal/types2/fixedbugs/issue39693.go2
@@ -0,0 +1,15 @@
+// UNREVIEWED
+// 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 Number interface {
+ int /* ERROR int is not an interface */
+ float64 /* ERROR float64 is not an interface */
+}
+
+func Add[T Number](a, b T) T {
+ return a /* ERROR not defined */ + b
+}
diff --git a/src/cmd/compile/internal/types2/fixedbugs/issue39699.go2 b/src/cmd/compile/internal/types2/fixedbugs/issue39699.go2
new file mode 100644
index 0000000000..c8655efee5
--- /dev/null
+++ b/src/cmd/compile/internal/types2/fixedbugs/issue39699.go2
@@ -0,0 +1,30 @@
+// UNREVIEWED
+// 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 T0 interface{
+}
+
+type T1 interface{
+ type int
+}
+
+type T2 interface{
+ comparable
+}
+
+type T3 interface {
+ T0
+ T1
+ T2
+}
+
+func _() {
+ _ = T0(0)
+ _ = T1 /* ERROR cannot use interface T1 in conversion */ (1)
+ _ = T2 /* ERROR cannot use interface T2 in conversion */ (2)
+ _ = T3 /* ERROR cannot use interface T3 in conversion */ (3)
+}
diff --git a/src/cmd/compile/internal/types2/fixedbugs/issue39711.go2 b/src/cmd/compile/internal/types2/fixedbugs/issue39711.go2
new file mode 100644
index 0000000000..8edce78c10
--- /dev/null
+++ b/src/cmd/compile/internal/types2/fixedbugs/issue39711.go2
@@ -0,0 +1,12 @@
+// UNREVIEWED
+// 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
+
+// Do not report a duplicate type error for this type list.
+// (Check types after interfaces have been completed.)
+type _ interface {
+ type interface{ Error() string }, interface{ String() string }
+}
diff --git a/src/cmd/compile/internal/types2/fixedbugs/issue39723.go2 b/src/cmd/compile/internal/types2/fixedbugs/issue39723.go2
new file mode 100644
index 0000000000..8a4006ef84
--- /dev/null
+++ b/src/cmd/compile/internal/types2/fixedbugs/issue39723.go2
@@ -0,0 +1,10 @@
+// UNREVIEWED
+// 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
+
+// A constraint must be an interface; it cannot
+// be a type parameter, for instance.
+func _[A interface{ type interface{} }, B A /* ERROR not an interface */ ]()
diff --git a/src/cmd/compile/internal/types2/fixedbugs/issue39725.go2 b/src/cmd/compile/internal/types2/fixedbugs/issue39725.go2
new file mode 100644
index 0000000000..6de661a38e
--- /dev/null
+++ b/src/cmd/compile/internal/types2/fixedbugs/issue39725.go2
@@ -0,0 +1,17 @@
+// UNREVIEWED
+// 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[T1, T2 any](T1, T2, struct{a T1; b T2})
+func _() {
+ f1(42, string("foo"), struct /* ERROR does not match inferred type struct\{a int; b string\} */ {a, b int}{})
+}
+
+// simplified test case from issue
+func f2[T any](_ []T, _ func(T))
+func _() {
+ f2([]string{}, func /* ERROR does not match inferred type func\(string\) */ (f []byte) {})
+}
diff --git a/src/cmd/compile/internal/types2/fixedbugs/issue39754.go2 b/src/cmd/compile/internal/types2/fixedbugs/issue39754.go2
new file mode 100644
index 0000000000..36b774faaf
--- /dev/null
+++ b/src/cmd/compile/internal/types2/fixedbugs/issue39754.go2
@@ -0,0 +1,21 @@
+// UNREVIEWED
+// 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 Optional[T any] struct {}
+
+func (_ Optional[T]) Val() (T, bool)
+
+type Box[T any] interface {
+ Val() (T, bool)
+}
+
+func f[V interface{}, A, B Box[V]]() {}
+
+func _() {
+ f[int, Optional[int], Optional[int]]()
+ f[int, Optional[int], Optional /* ERROR does not satisfy Box */ [string]]()
+}
diff --git a/src/cmd/compile/internal/types2/fixedbugs/issue39755.go2 b/src/cmd/compile/internal/types2/fixedbugs/issue39755.go2
new file mode 100644
index 0000000000..93aea85215
--- /dev/null
+++ b/src/cmd/compile/internal/types2/fixedbugs/issue39755.go2
@@ -0,0 +1,24 @@
+// UNREVIEWED
+// 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 _[T interface{type map[string]int}](x T) {
+ _ = x == nil
+}
+
+// simplified test case from issue
+
+type PathParamsConstraint interface {
+ type map[string]string, []struct{key, value string}
+}
+
+type PathParams[T PathParamsConstraint] struct {
+ t T
+}
+
+func (pp *PathParams[T]) IsNil() bool {
+ return pp.t == nil // this must succeed
+}
diff --git a/src/cmd/compile/internal/types2/fixedbugs/issue39768.go2 b/src/cmd/compile/internal/types2/fixedbugs/issue39768.go2
new file mode 100644
index 0000000000..81b4a91f2c
--- /dev/null
+++ b/src/cmd/compile/internal/types2/fixedbugs/issue39768.go2
@@ -0,0 +1,21 @@
+// UNREVIEWED
+// 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 T[P any] P
+type A = T
+var x A[int]
+var _ A /* ERROR cannot use generic type */
+
+type B = T[int]
+var y B = x
+var _ B /* ERROR not a generic type */ [int]
+
+// test case from issue
+
+type Vector[T any] []T
+type VectorAlias = Vector
+var v Vector[int]
diff --git a/src/cmd/compile/internal/types2/fixedbugs/issue39938.go2 b/src/cmd/compile/internal/types2/fixedbugs/issue39938.go2
new file mode 100644
index 0000000000..19e69e6486
--- /dev/null
+++ b/src/cmd/compile/internal/types2/fixedbugs/issue39938.go2
@@ -0,0 +1,51 @@
+// UNREVIEWED
+// 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.
+
+// Check "infinite expansion" cycle errors across instantiated types.
+
+package p
+
+type E0[P any] P
+type E1[P any] *P
+type E2[P any] struct{ P }
+type E3[P any] struct{ *P }
+
+type T0 /* ERROR illegal cycle */ struct {
+ _ E0[T0]
+}
+
+type T0_ /* ERROR illegal cycle */ struct {
+ E0[T0_]
+}
+
+type T1 struct {
+ _ E1[T1]
+}
+
+type T2 /* ERROR illegal cycle */ struct {
+ _ E2[T2]
+}
+
+type T3 struct {
+ _ E3[T3]
+}
+
+// some more complex cases
+
+type T4 /* ERROR illegal cycle */ struct {
+ _ E0[E2[T4]]
+}
+
+type T5 struct {
+ _ E0[E2[E0[E1[E2[[10]T5]]]]]
+}
+
+type T6 /* ERROR illegal cycle */ struct {
+ _ E0[[10]E2[E0[E2[E2[T6]]]]]
+}
+
+type T7 struct {
+ _ E0[[]E2[E0[E2[E2[T6]]]]]
+}
diff --git a/src/cmd/compile/internal/types2/fixedbugs/issue39948.go2 b/src/cmd/compile/internal/types2/fixedbugs/issue39948.go2
new file mode 100644
index 0000000000..dede9c5797
--- /dev/null
+++ b/src/cmd/compile/internal/types2/fixedbugs/issue39948.go2
@@ -0,0 +1,10 @@
+// UNREVIEWED
+// 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 T[P any] interface{
+ P // ERROR P is a type parameter, not an interface
+}
diff --git a/src/cmd/compile/internal/types2/fixedbugs/issue39976.go2 b/src/cmd/compile/internal/types2/fixedbugs/issue39976.go2
new file mode 100644
index 0000000000..2ab9664f88
--- /dev/null
+++ b/src/cmd/compile/internal/types2/fixedbugs/issue39976.go2
@@ -0,0 +1,17 @@
+// UNREVIEWED
+// 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 policy[K, V any] interface{}
+type LRU[K, V any] struct{}
+
+func NewCache[K, V any](p policy[K, V])
+
+func _() {
+ var lru LRU[int, string]
+ NewCache[int, string](&lru)
+ NewCache(& /* ERROR does not match policy\[K, V\] \(cannot infer K and V\) */ lru)
+}
diff --git a/src/cmd/compile/internal/types2/fixedbugs/issue39982.go2 b/src/cmd/compile/internal/types2/fixedbugs/issue39982.go2
new file mode 100644
index 0000000000..3abdfcb1b0
--- /dev/null
+++ b/src/cmd/compile/internal/types2/fixedbugs/issue39982.go2
@@ -0,0 +1,37 @@
+// UNREVIEWED
+// 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 (
+ T[_ any] struct{}
+ S[_ any] struct {
+ data T[*T[int]]
+ }
+)
+
+func _() {
+ _ = S[int]{
+ data: T[*T[int]]{},
+ }
+}
+
+// full test case from issue
+
+type (
+ Element[TElem any] struct{}
+
+ entry[K comparable] struct{}
+
+ Cache[K comparable] struct {
+ data map[K]*Element[*entry[K]]
+ }
+)
+
+func _() {
+ _ = Cache[int]{
+ data: make(map[int](*Element[*entry[int]])),
+ }
+}
diff --git a/src/cmd/compile/internal/types2/fixedbugs/issue40038.go2 b/src/cmd/compile/internal/types2/fixedbugs/issue40038.go2
new file mode 100644
index 0000000000..fe3963aac2
--- /dev/null
+++ b/src/cmd/compile/internal/types2/fixedbugs/issue40038.go2
@@ -0,0 +1,16 @@
+// UNREVIEWED
+// 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 A[T any] int
+
+func (A[T]) m(A[T])
+
+func f[P interface{m(P)}]()
+
+func _() {
+ _ = f[A[int]]
+} \ No newline at end of file
diff --git a/src/cmd/compile/internal/types2/fixedbugs/issue40056.go2 b/src/cmd/compile/internal/types2/fixedbugs/issue40056.go2
new file mode 100644
index 0000000000..0c78c3f289
--- /dev/null
+++ b/src/cmd/compile/internal/types2/fixedbugs/issue40056.go2
@@ -0,0 +1,16 @@
+// UNREVIEWED
+// 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 _() {
+ NewS( /* ERROR cannot infer T */ ) .M()
+}
+
+type S struct {}
+
+func NewS[T any]() *S
+
+func (_ *S /* ERROR S is not a generic type */ [T]) M() \ No newline at end of file
diff --git a/src/cmd/compile/internal/types2/fixedbugs/issue40057.go2 b/src/cmd/compile/internal/types2/fixedbugs/issue40057.go2
new file mode 100644
index 0000000000..b2ff11e4bf
--- /dev/null
+++ b/src/cmd/compile/internal/types2/fixedbugs/issue40057.go2
@@ -0,0 +1,18 @@
+// UNREVIEWED
+// 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 _() {
+ var x interface{}
+ switch t := x.(type) {
+ case S /* ERROR cannot use generic type */ :
+ t.m()
+ }
+}
+
+type S[T any] struct {}
+
+func (_ S[T]) m()
diff --git a/src/cmd/compile/internal/types2/fixedbugs/issue40301.go2 b/src/cmd/compile/internal/types2/fixedbugs/issue40301.go2
new file mode 100644
index 0000000000..6a3dfc741e
--- /dev/null
+++ b/src/cmd/compile/internal/types2/fixedbugs/issue40301.go2
@@ -0,0 +1,13 @@
+// UNREVIEWED
+// 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 "unsafe"
+
+func _[T any](x T) {
+ _ = unsafe /* ERROR undefined */ .Alignof(x)
+ _ = unsafe /* ERROR undefined */ .Sizeof(x)
+}
diff --git a/src/cmd/compile/internal/types2/fixedbugs/issue40684.go2 b/src/cmd/compile/internal/types2/fixedbugs/issue40684.go2
new file mode 100644
index 0000000000..001c6d7b99
--- /dev/null
+++ b/src/cmd/compile/internal/types2/fixedbugs/issue40684.go2
@@ -0,0 +1,16 @@
+// UNREVIEWED
+// 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 T[_ any] int
+
+func f[_ any]()
+func g[_, _ any]()
+
+func _() {
+ _ = f[T /* ERROR without instantiation */ ]
+ _ = g[T /* ERROR without instantiation */ , T /* ERROR without instantiation */ ]
+} \ No newline at end of file
diff --git a/src/cmd/compile/internal/types2/fixedbugs/issue41124.go2 b/src/cmd/compile/internal/types2/fixedbugs/issue41124.go2
new file mode 100644
index 0000000000..3098f44948
--- /dev/null
+++ b/src/cmd/compile/internal/types2/fixedbugs/issue41124.go2
@@ -0,0 +1,92 @@
+// UNREVIEWED
+// 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
+
+// Test case from issue.
+
+type Nat interface {
+ type Zero, Succ
+}
+
+type Zero struct{}
+type Succ struct{
+ Nat // ERROR interface contains type constraints
+}
+
+// Struct tests.
+
+type I1 interface {
+ comparable
+}
+
+type I2 interface {
+ type int
+}
+
+type I3 interface {
+ I1
+ I2
+}
+
+type _ struct {
+ f I1 // ERROR interface is .* comparable
+}
+
+type _ struct {
+ comparable // ERROR interface is .* comparable
+}
+
+type _ struct{
+ I1 // ERROR interface is .* comparable
+}
+
+type _ struct{
+ I2 // ERROR interface contains type constraints
+}
+
+type _ struct{
+ I3 // ERROR interface contains type constraints
+}
+
+// General composite types.
+
+type (
+ _ [10]I1 // ERROR interface is .* comparable
+ _ [10]I2 // ERROR interface contains type constraints
+
+ _ []I1 // ERROR interface is .* comparable
+ _ []I2 // ERROR interface contains type constraints
+
+ _ *I3 // ERROR interface contains type constraints
+ _ map[I1 /* ERROR interface is .* comparable */ ]I2 // ERROR interface contains type constraints
+ _ chan I3 // ERROR interface contains type constraints
+ _ func(I1 /* ERROR interface is .* comparable */ )
+ _ func() I2 // ERROR interface contains type constraints
+)
+
+// Other cases.
+
+var _ = [...]I3 /* ERROR interface contains type constraints */ {}
+
+func _(x interface{}) {
+ _ = x.(I3 /* ERROR interface contains type constraints */ )
+}
+
+type T1[_ any] struct{}
+type T3[_, _, _ any] struct{}
+var _ T1[I2 /* ERROR interface contains type constraints */ ]
+var _ T3[int, I2 /* ERROR interface contains type constraints */ , float32]
+
+func f1[_ any]() int
+var _ = f1[I2 /* ERROR interface contains type constraints */ ]()
+func f3[_, _, _ any]() int
+var _ = f3[int, I2 /* ERROR interface contains type constraints */ , float32]()
+
+func _(x interface{}) {
+ switch x.(type) {
+ case I2 /* ERROR interface contains type constraints */ :
+ }
+}
diff --git a/src/cmd/compile/internal/types2/gccgosizes.go b/src/cmd/compile/internal/types2/gccgosizes.go
new file mode 100644
index 0000000000..d3c79745a2
--- /dev/null
+++ b/src/cmd/compile/internal/types2/gccgosizes.go
@@ -0,0 +1,41 @@
+// UNREVIEWED
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This is a copy of the file generated during the gccgo build process.
+// Last update 2019-01-22.
+
+package types2
+
+var gccgoArchSizes = map[string]*StdSizes{
+ "386": {4, 4},
+ "alpha": {8, 8},
+ "amd64": {8, 8},
+ "amd64p32": {4, 8},
+ "arm": {4, 8},
+ "armbe": {4, 8},
+ "arm64": {8, 8},
+ "arm64be": {8, 8},
+ "ia64": {8, 8},
+ "m68k": {4, 2},
+ "mips": {4, 8},
+ "mipsle": {4, 8},
+ "mips64": {8, 8},
+ "mips64le": {8, 8},
+ "mips64p32": {4, 8},
+ "mips64p32le": {4, 8},
+ "nios2": {4, 8},
+ "ppc": {4, 8},
+ "ppc64": {8, 8},
+ "ppc64le": {8, 8},
+ "riscv": {4, 8},
+ "riscv64": {8, 8},
+ "s390": {4, 8},
+ "s390x": {8, 8},
+ "sh": {4, 8},
+ "shbe": {4, 8},
+ "sparc": {4, 8},
+ "sparc64": {8, 8},
+ "wasm": {8, 8},
+}
diff --git a/src/cmd/compile/internal/types2/hilbert_test.go b/src/cmd/compile/internal/types2/hilbert_test.go
new file mode 100644
index 0000000000..ee0c4daea6
--- /dev/null
+++ b/src/cmd/compile/internal/types2/hilbert_test.go
@@ -0,0 +1,220 @@
+// UNREVIEWED
+// Copyright 2013 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 types2_test
+
+import (
+ "bytes"
+ "cmd/compile/internal/syntax"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "testing"
+
+ . "cmd/compile/internal/types2"
+)
+
+var (
+ H = flag.Int("H", 5, "Hilbert matrix size")
+ out = flag.String("out", "", "write generated program to out")
+)
+
+func TestHilbert(t *testing.T) {
+ // generate source
+ src := program(*H, *out)
+ if *out != "" {
+ ioutil.WriteFile(*out, src, 0666)
+ return
+ }
+
+ // parse source
+ // TODO(gri) get rid of []bytes to string conversion below
+ f, err := parseSrc("hilbert.go", string(src))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // type-check file
+ DefPredeclaredTestFuncs() // define assert built-in
+ conf := Config{Importer: defaultImporter()}
+ _, err = conf.Check(f.PkgName.Value, []*syntax.File{f}, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func program(n int, out string) []byte {
+ var g gen
+
+ g.p(`// Code generated by: go test -run=Hilbert -H=%d -out=%q. DO NOT EDIT.
+
+// +`+`build ignore
+
+// This program tests arbitrary precision constant arithmetic
+// by generating the constant elements of a Hilbert matrix H,
+// its inverse I, and the product P = H*I. The product should
+// be the identity matrix.
+package main
+
+func main() {
+ if !ok {
+ printProduct()
+ return
+ }
+ println("PASS")
+}
+
+`, n, out)
+ g.hilbert(n)
+ g.inverse(n)
+ g.product(n)
+ g.verify(n)
+ g.printProduct(n)
+ g.binomials(2*n - 1)
+ g.factorials(2*n - 1)
+
+ return g.Bytes()
+}
+
+type gen struct {
+ bytes.Buffer
+}
+
+func (g *gen) p(format string, args ...interface{}) {
+ fmt.Fprintf(&g.Buffer, format, args...)
+}
+
+func (g *gen) hilbert(n int) {
+ g.p(`// Hilbert matrix, n = %d
+const (
+`, n)
+ for i := 0; i < n; i++ {
+ g.p("\t")
+ for j := 0; j < n; j++ {
+ if j > 0 {
+ g.p(", ")
+ }
+ g.p("h%d_%d", i, j)
+ }
+ if i == 0 {
+ g.p(" = ")
+ for j := 0; j < n; j++ {
+ if j > 0 {
+ g.p(", ")
+ }
+ g.p("1.0/(iota + %d)", j+1)
+ }
+ }
+ g.p("\n")
+ }
+ g.p(")\n\n")
+}
+
+func (g *gen) inverse(n int) {
+ g.p(`// Inverse Hilbert matrix
+const (
+`)
+ for i := 0; i < n; i++ {
+ for j := 0; j < n; j++ {
+ s := "+"
+ if (i+j)&1 != 0 {
+ s = "-"
+ }
+ g.p("\ti%d_%d = %s%d * b%d_%d * b%d_%d * b%d_%d * b%d_%d\n",
+ i, j, s, i+j+1, n+i, n-j-1, n+j, n-i-1, i+j, i, i+j, i)
+ }
+ g.p("\n")
+ }
+ g.p(")\n\n")
+}
+
+func (g *gen) product(n int) {
+ g.p(`// Product matrix
+const (
+`)
+ for i := 0; i < n; i++ {
+ for j := 0; j < n; j++ {
+ g.p("\tp%d_%d = ", i, j)
+ for k := 0; k < n; k++ {
+ if k > 0 {
+ g.p(" + ")
+ }
+ g.p("h%d_%d*i%d_%d", i, k, k, j)
+ }
+ g.p("\n")
+ }
+ g.p("\n")
+ }
+ g.p(")\n\n")
+}
+
+func (g *gen) verify(n int) {
+ g.p(`// Verify that product is the identity matrix
+const ok =
+`)
+ for i := 0; i < n; i++ {
+ for j := 0; j < n; j++ {
+ if j == 0 {
+ g.p("\t")
+ } else {
+ g.p(" && ")
+ }
+ v := 0
+ if i == j {
+ v = 1
+ }
+ g.p("p%d_%d == %d", i, j, v)
+ }
+ g.p(" &&\n")
+ }
+ g.p("\ttrue\n\n")
+
+ // verify ok at type-check time
+ if *out == "" {
+ g.p("const _ = assert(ok)\n\n")
+ }
+}
+
+func (g *gen) printProduct(n int) {
+ g.p("func printProduct() {\n")
+ for i := 0; i < n; i++ {
+ g.p("\tprintln(")
+ for j := 0; j < n; j++ {
+ if j > 0 {
+ g.p(", ")
+ }
+ g.p("p%d_%d", i, j)
+ }
+ g.p(")\n")
+ }
+ g.p("}\n\n")
+}
+
+func (g *gen) binomials(n int) {
+ g.p(`// Binomials
+const (
+`)
+ for j := 0; j <= n; j++ {
+ if j > 0 {
+ g.p("\n")
+ }
+ for k := 0; k <= j; k++ {
+ g.p("\tb%d_%d = f%d / (f%d*f%d)\n", j, k, j, k, j-k)
+ }
+ }
+ g.p(")\n\n")
+}
+
+func (g *gen) factorials(n int) {
+ g.p(`// Factorials
+const (
+ f0 = 1
+ f1 = 1
+`)
+ for i := 2; i <= n; i++ {
+ g.p("\tf%d = f%d * %d\n", i, i-1, i)
+ }
+ g.p(")\n\n")
+}
diff --git a/src/cmd/compile/internal/types2/importer_test.go b/src/cmd/compile/internal/types2/importer_test.go
new file mode 100644
index 0000000000..90476c4269
--- /dev/null
+++ b/src/cmd/compile/internal/types2/importer_test.go
@@ -0,0 +1,36 @@
+// UNREVIEWED
+// 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.
+
+// This file implements the (temporary) plumbing to get importing to work.
+
+package types2_test
+
+import (
+ gcimporter "cmd/compile/internal/importer"
+ "cmd/compile/internal/types2"
+ "io"
+)
+
+func defaultImporter() types2.Importer {
+ return &gcimports{
+ packages: make(map[string]*types2.Package),
+ }
+}
+
+type gcimports struct {
+ packages map[string]*types2.Package
+ lookup func(path string) (io.ReadCloser, error)
+}
+
+func (m *gcimports) Import(path string) (*types2.Package, error) {
+ return m.ImportFrom(path, "" /* no vendoring */, 0)
+}
+
+func (m *gcimports) ImportFrom(path, srcDir string, mode types2.ImportMode) (*types2.Package, error) {
+ if mode != 0 {
+ panic("mode must be 0")
+ }
+ return gcimporter.Import(m.packages, path, srcDir, m.lookup)
+}
diff --git a/src/cmd/compile/internal/types2/infer.go b/src/cmd/compile/internal/types2/infer.go
new file mode 100644
index 0000000000..b52a834e5a
--- /dev/null
+++ b/src/cmd/compile/internal/types2/infer.go
@@ -0,0 +1,359 @@
+// UNREVIEWED
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements type parameter inference given
+// a list of concrete arguments and a parameter list.
+
+package types2
+
+import "strings"
+
+// infer returns the list of actual type arguments for the given list of type parameters tparams
+// by inferring them from the actual arguments args for the parameters params. If type inference
+// is impossible because unification fails, an error is reported and the resulting types list is
+// nil, and index is 0. Otherwise, types is the list of inferred type arguments, and index is
+// the index of the first type argument in that list that couldn't be inferred (and thus is nil).
+// If all type arguments where inferred successfully, index is < 0.
+func (check *Checker) infer(tparams []*TypeName, params *Tuple, args []*operand) (types []Type, index int) {
+ assert(params.Len() == len(args))
+
+ u := newUnifier(check, false)
+ u.x.init(tparams)
+
+ errorf := func(kind string, tpar, targ Type, arg *operand) {
+ // provide a better error message if we can
+ targs, failed := u.x.types()
+ if failed == 0 {
+ // The first type parameter couldn't be inferred.
+ // If none of them could be inferred, don't try
+ // to provide the inferred type in the error msg.
+ allFailed := true
+ for _, targ := range targs {
+ if targ != nil {
+ allFailed = false
+ break
+ }
+ }
+ if allFailed {
+ check.errorf(arg, "%s %s of %s does not match %s (cannot infer %s)", kind, targ, arg.expr, tpar, typeNamesString(tparams))
+ return
+ }
+ }
+ smap := makeSubstMap(tparams, targs)
+ inferred := check.subst(arg.Pos(), tpar, smap)
+ if inferred != tpar {
+ check.errorf(arg, "%s %s of %s does not match inferred type %s for %s", kind, targ, arg.expr, inferred, tpar)
+ } else {
+ check.errorf(arg, "%s %s of %s does not match %s", kind, targ, arg.expr, tpar)
+ }
+ }
+
+ // Terminology: generic parameter = function parameter with a type-parameterized type
+
+ // 1st pass: Unify parameter and argument types for generic parameters with typed arguments
+ // and collect the indices of generic parameters with untyped arguments.
+ var indices []int
+ for i, arg := range args {
+ par := params.At(i)
+ // If we permit bidirectional unification, this conditional code needs to be
+ // executed even if par.typ is not parameterized since the argument may be a
+ // generic function (for which we want to infer // its type arguments).
+ if isParameterized(tparams, par.typ) {
+ if arg.mode == invalid {
+ // An error was reported earlier. Ignore this targ
+ // and continue, we may still be able to infer all
+ // targs resulting in fewer follon-on errors.
+ continue
+ }
+ if targ := arg.typ; isTyped(targ) {
+ // If we permit bidirectional unification, and targ is
+ // a generic function, we need to initialize u.y with
+ // the respectice type parameters of targ.
+ if !u.unify(par.typ, targ) {
+ errorf("type", par.typ, targ, arg)
+ return nil, 0
+ }
+ } else {
+ indices = append(indices, i)
+ }
+ }
+ }
+
+ // Some generic parameters with untyped arguments may have been given a type
+ // indirectly through another generic parameter with a typed argument; we can
+ // ignore those now. (This only means that we know the types for those generic
+ // parameters; it doesn't mean untyped arguments can be passed safely. We still
+ // need to verify that assignment of those arguments is valid when we check
+ // function parameter passing external to infer.)
+ j := 0
+ for _, i := range indices {
+ par := params.At(i)
+ // Since untyped types are all basic (i.e., non-composite) types, an
+ // untyped argument will never match a composite parameter type; the
+ // only parameter type it can possibly match against is a *TypeParam.
+ // Thus, only keep the indices of generic parameters that are not of
+ // composite types and which don't have a type inferred yet.
+ if tpar, _ := par.typ.(*TypeParam); tpar != nil && u.x.at(tpar.index) == nil {
+ indices[j] = i
+ j++
+ }
+ }
+ indices = indices[:j]
+
+ // 2nd pass: Unify parameter and default argument types for remaining generic parameters.
+ for _, i := range indices {
+ par := params.At(i)
+ arg := args[i]
+ targ := Default(arg.typ)
+ // The default type for an untyped nil is untyped nil. We must not
+ // infer an untyped nil type as type parameter type. Ignore untyped
+ // nil by making sure all default argument types are typed.
+ if isTyped(targ) && !u.unify(par.typ, targ) {
+ errorf("default type", par.typ, targ, arg)
+ return nil, 0
+ }
+ }
+
+ return u.x.types()
+}
+
+// typeNamesString produces a string containing all the
+// type names in list suitable for human consumption.
+func typeNamesString(list []*TypeName) string {
+ // common cases
+ n := len(list)
+ switch n {
+ case 0:
+ return ""
+ case 1:
+ return list[0].name
+ case 2:
+ return list[0].name + " and " + list[1].name
+ }
+
+ // general case (n > 2)
+ var b strings.Builder
+ for i, tname := range list[:n-1] {
+ if i > 0 {
+ b.WriteString(", ")
+ }
+ b.WriteString(tname.name)
+ }
+ b.WriteString(", and ")
+ b.WriteString(list[n-1].name)
+ return b.String()
+}
+
+// IsParameterized reports whether typ contains any of the type parameters of tparams.
+func isParameterized(tparams []*TypeName, typ Type) bool {
+ w := tpWalker{
+ seen: make(map[Type]bool),
+ tparams: tparams,
+ }
+ return w.isParameterized(typ)
+}
+
+type tpWalker struct {
+ seen map[Type]bool
+ tparams []*TypeName
+}
+
+func (w *tpWalker) isParameterized(typ Type) (res bool) {
+ // detect cycles
+ if x, ok := w.seen[typ]; ok {
+ return x
+ }
+ w.seen[typ] = false
+ defer func() {
+ w.seen[typ] = res
+ }()
+
+ switch t := typ.(type) {
+ case nil, *Basic: // TODO(gri) should nil be handled here?
+ break
+
+ case *Array:
+ return w.isParameterized(t.elem)
+
+ case *Slice:
+ return w.isParameterized(t.elem)
+
+ case *Struct:
+ for _, fld := range t.fields {
+ if w.isParameterized(fld.typ) {
+ return true
+ }
+ }
+
+ case *Pointer:
+ return w.isParameterized(t.base)
+
+ case *Tuple:
+ n := t.Len()
+ for i := 0; i < n; i++ {
+ if w.isParameterized(t.At(i).typ) {
+ return true
+ }
+ }
+
+ case *Sum:
+ return w.isParameterizedList(t.types)
+
+ case *Signature:
+ // t.tparams may not be nil if we are looking at a signature
+ // of a generic function type (or an interface method) that is
+ // part of the type we're testing. We don't care about these type
+ // parameters.
+ // Similarly, the receiver of a method may declare (rather then
+ // use) type parameters, we don't care about those either.
+ // Thus, we only need to look at the input and result parameters.
+ return w.isParameterized(t.params) || w.isParameterized(t.results)
+
+ case *Interface:
+ if t.allMethods != nil {
+ // interface is complete - quick test
+ for _, m := range t.allMethods {
+ if w.isParameterized(m.typ) {
+ return true
+ }
+ }
+ return w.isParameterizedList(unpack(t.allTypes))
+ }
+
+ return t.iterate(func(t *Interface) bool {
+ for _, m := range t.methods {
+ if w.isParameterized(m.typ) {
+ return true
+ }
+ }
+ return w.isParameterizedList(unpack(t.types))
+ }, nil)
+
+ case *Map:
+ return w.isParameterized(t.key) || w.isParameterized(t.elem)
+
+ case *Chan:
+ return w.isParameterized(t.elem)
+
+ case *Named:
+ return w.isParameterizedList(t.targs)
+
+ case *TypeParam:
+ // t must be one of w.tparams
+ return t.index < len(w.tparams) && w.tparams[t.index].typ == t
+
+ case *instance:
+ return w.isParameterizedList(t.targs)
+
+ default:
+ unreachable()
+ }
+
+ return false
+}
+
+func (w *tpWalker) isParameterizedList(list []Type) bool {
+ for _, t := range list {
+ if w.isParameterized(t) {
+ return true
+ }
+ }
+ return false
+}
+
+// inferB returns the list of actual type arguments inferred from the type parameters'
+// bounds and an initial set of type arguments. If type inference is impossible because
+// unification fails, an error is reported, the resulting types list is nil, and index is 0.
+// Otherwise, types is the list of inferred type arguments, and index is the index of the
+// first type argument in that list that couldn't be inferred (and thus is nil). If all
+// type arguments where inferred successfully, index is < 0. The number of type arguments
+// provided may be less than the number of type parameters, but there must be at least one.
+func (check *Checker) inferB(tparams []*TypeName, targs []Type) (types []Type, index int) {
+ assert(len(tparams) >= len(targs) && len(targs) > 0)
+
+ // Setup bidirectional unification between those structural bounds
+ // and the corresponding type arguments (which may be nil!).
+ u := newUnifier(check, false)
+ u.x.init(tparams)
+ u.y = u.x // type parameters between LHS and RHS of unification are identical
+
+ // Set the type arguments which we know already.
+ for i, targ := range targs {
+ if targ != nil {
+ u.x.set(i, targ)
+ }
+ }
+
+ // Unify type parameters with their structural constraints, if any.
+ for _, tpar := range tparams {
+ typ := tpar.typ.(*TypeParam)
+ sbound := check.structuralType(typ.bound.Under())
+ if sbound != nil {
+ //check.dump(">>> unify(%s, %s)", tpar, sbound)
+ if !u.unify(typ, sbound) {
+ check.errorf(tpar.pos, "%s does not match %s", tpar, sbound)
+ return nil, 0
+ }
+ //check.dump(">>> => indices = %v, types = %s", u.x.indices, u.types)
+ }
+ }
+
+ // u.x.types() now contains the incoming type arguments plus any additional type
+ // arguments for which there were structural constraints. The newly inferred non-
+ // nil entries may still contain references to other type parameters. For instance,
+ // for [type A interface{}, B interface{type []C}, C interface{type *A}], if A == int
+ // was given, unification produced the type list [int, []C, *A]. We eliminate the
+ // remaining type parameters by substituting the type parameters in this type list
+ // until nothing changes anymore.
+ types, index = u.x.types()
+ if debug {
+ for i, targ := range targs {
+ assert(targ == nil || types[i] == targ)
+ }
+ }
+
+ // dirty tracks the indices of all types that may still contain type parameters.
+ // We know that nil types entries and entries corresponding to provided (non-nil)
+ // type arguments are clean, so exclude them from the start.
+ var dirty []int
+ for i, typ := range types {
+ if typ != nil && (i >= len(targs) || targs[i] == nil) {
+ dirty = append(dirty, i)
+ }
+ }
+
+ for len(dirty) > 0 {
+ // TODO(gri) Instead of creating a new smap for each iteration,
+ // provide an update operation for smaps and only change when
+ // needed. Optimization.
+ smap := makeSubstMap(tparams, types)
+ n := 0
+ for _, index := range dirty {
+ t0 := types[index]
+ if t1 := check.subst(nopos, t0, smap); t1 != t0 {
+ types[index] = t1
+ dirty[n] = index
+ n++
+ }
+ }
+ dirty = dirty[:n]
+ }
+ //check.dump(">>> inferred types = %s", types)
+
+ return
+}
+
+// structuralType returns the structural type of a constraint, if any.
+func (check *Checker) structuralType(constraint Type) Type {
+ if iface, _ := constraint.(*Interface); iface != nil {
+ check.completeInterface(nopos, iface)
+ types := unpack(iface.allTypes)
+ if len(types) == 1 {
+ return types[0]
+ }
+ return nil
+ }
+ return constraint
+}
diff --git a/src/cmd/compile/internal/types2/initorder.go b/src/cmd/compile/internal/types2/initorder.go
new file mode 100644
index 0000000000..3bb92d9622
--- /dev/null
+++ b/src/cmd/compile/internal/types2/initorder.go
@@ -0,0 +1,298 @@
+// UNREVIEWED
+// Copyright 2014 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 types2
+
+import (
+ "container/heap"
+ "fmt"
+)
+
+// initOrder computes the Info.InitOrder for package variables.
+func (check *Checker) initOrder() {
+ // An InitOrder may already have been computed if a package is
+ // built from several calls to (*Checker).Files. Clear it.
+ check.Info.InitOrder = check.Info.InitOrder[:0]
+
+ // Compute the object dependency graph and initialize
+ // a priority queue with the list of graph nodes.
+ pq := nodeQueue(dependencyGraph(check.objMap))
+ heap.Init(&pq)
+
+ const debug = false
+ if debug {
+ fmt.Printf("Computing initialization order for %s\n\n", check.pkg)
+ fmt.Println("Object dependency graph:")
+ for obj, d := range check.objMap {
+ // only print objects that may appear in the dependency graph
+ if obj, _ := obj.(dependency); obj != nil {
+ if len(d.deps) > 0 {
+ fmt.Printf("\t%s depends on\n", obj.Name())
+ for dep := range d.deps {
+ fmt.Printf("\t\t%s\n", dep.Name())
+ }
+ } else {
+ fmt.Printf("\t%s has no dependencies\n", obj.Name())
+ }
+ }
+ }
+ fmt.Println()
+
+ fmt.Println("Transposed object dependency graph (functions eliminated):")
+ for _, n := range pq {
+ fmt.Printf("\t%s depends on %d nodes\n", n.obj.Name(), n.ndeps)
+ for p := range n.pred {
+ fmt.Printf("\t\t%s is dependent\n", p.obj.Name())
+ }
+ }
+ fmt.Println()
+
+ fmt.Println("Processing nodes:")
+ }
+
+ // Determine initialization order by removing the highest priority node
+ // (the one with the fewest dependencies) and its edges from the graph,
+ // repeatedly, until there are no nodes left.
+ // In a valid Go program, those nodes always have zero dependencies (after
+ // removing all incoming dependencies), otherwise there are initialization
+ // cycles.
+ emitted := make(map[*declInfo]bool)
+ for len(pq) > 0 {
+ // get the next node
+ n := heap.Pop(&pq).(*graphNode)
+
+ if debug {
+ fmt.Printf("\t%s (src pos %d) depends on %d nodes now\n",
+ n.obj.Name(), n.obj.order(), n.ndeps)
+ }
+
+ // if n still depends on other nodes, we have a cycle
+ if n.ndeps > 0 {
+ cycle := findPath(check.objMap, n.obj, n.obj, make(map[Object]bool))
+ // If n.obj is not part of the cycle (e.g., n.obj->b->c->d->c),
+ // cycle will be nil. Don't report anything in that case since
+ // the cycle is reported when the algorithm gets to an object
+ // in the cycle.
+ // Furthermore, once an object in the cycle is encountered,
+ // the cycle will be broken (dependency count will be reduced
+ // below), and so the remaining nodes in the cycle don't trigger
+ // another error (unless they are part of multiple cycles).
+ if cycle != nil {
+ check.reportCycle(cycle)
+ }
+ // Ok to continue, but the variable initialization order
+ // will be incorrect at this point since it assumes no
+ // cycle errors.
+ }
+
+ // reduce dependency count of all dependent nodes
+ // and update priority queue
+ for p := range n.pred {
+ p.ndeps--
+ heap.Fix(&pq, p.index)
+ }
+
+ // record the init order for variables with initializers only
+ v, _ := n.obj.(*Var)
+ info := check.objMap[v]
+ if v == nil || !info.hasInitializer() {
+ continue
+ }
+
+ // n:1 variable declarations such as: a, b = f()
+ // introduce a node for each lhs variable (here: a, b);
+ // but they all have the same initializer - emit only
+ // one, for the first variable seen
+ if emitted[info] {
+ continue // initializer already emitted, if any
+ }
+ emitted[info] = true
+
+ infoLhs := info.lhs // possibly nil (see declInfo.lhs field comment)
+ if infoLhs == nil {
+ infoLhs = []*Var{v}
+ }
+ init := &Initializer{infoLhs, info.init}
+ check.Info.InitOrder = append(check.Info.InitOrder, init)
+ }
+
+ if debug {
+ fmt.Println()
+ fmt.Println("Initialization order:")
+ for _, init := range check.Info.InitOrder {
+ fmt.Printf("\t%s\n", init)
+ }
+ fmt.Println()
+ }
+}
+
+// findPath returns the (reversed) list of objects []Object{to, ... from}
+// such that there is a path of object dependencies from 'from' to 'to'.
+// If there is no such path, the result is nil.
+func findPath(objMap map[Object]*declInfo, from, to Object, seen map[Object]bool) []Object {
+ if seen[from] {
+ return nil
+ }
+ seen[from] = true
+
+ for d := range objMap[from].deps {
+ if d == to {
+ return []Object{d}
+ }
+ if P := findPath(objMap, d, to, seen); P != nil {
+ return append(P, d)
+ }
+ }
+
+ return nil
+}
+
+// reportCycle reports an error for the given cycle.
+func (check *Checker) reportCycle(cycle []Object) {
+ obj := cycle[0]
+ check.errorf(obj, "initialization cycle for %s", obj.Name())
+ // subtle loop: print cycle[i] for i = 0, n-1, n-2, ... 1 for len(cycle) = n
+ for i := len(cycle) - 1; i >= 0; i-- {
+ check.errorf(obj, "\t%s refers to", obj.Name()) // secondary error, \t indented
+ obj = cycle[i]
+ }
+ // print cycle[0] again to close the cycle
+ check.errorf(obj, "\t%s", obj.Name())
+}
+
+// ----------------------------------------------------------------------------
+// Object dependency graph
+
+// A dependency is an object that may be a dependency in an initialization
+// expression. Only constants, variables, and functions can be dependencies.
+// Constants are here because constant expression cycles are reported during
+// initialization order computation.
+type dependency interface {
+ Object
+ isDependency()
+}
+
+// A graphNode represents a node in the object dependency graph.
+// Each node p in n.pred represents an edge p->n, and each node
+// s in n.succ represents an edge n->s; with a->b indicating that
+// a depends on b.
+type graphNode struct {
+ obj dependency // object represented by this node
+ pred, succ nodeSet // consumers and dependencies of this node (lazily initialized)
+ index int // node index in graph slice/priority queue
+ ndeps int // number of outstanding dependencies before this object can be initialized
+}
+
+type nodeSet map[*graphNode]bool
+
+func (s *nodeSet) add(p *graphNode) {
+ if *s == nil {
+ *s = make(nodeSet)
+ }
+ (*s)[p] = true
+}
+
+// dependencyGraph computes the object dependency graph from the given objMap,
+// with any function nodes removed. The resulting graph contains only constants
+// and variables.
+func dependencyGraph(objMap map[Object]*declInfo) []*graphNode {
+ // M is the dependency (Object) -> graphNode mapping
+ M := make(map[dependency]*graphNode)
+ for obj := range objMap {
+ // only consider nodes that may be an initialization dependency
+ if obj, _ := obj.(dependency); obj != nil {
+ M[obj] = &graphNode{obj: obj}
+ }
+ }
+
+ // compute edges for graph M
+ // (We need to include all nodes, even isolated ones, because they still need
+ // to be scheduled for initialization in correct order relative to other nodes.)
+ for obj, n := range M {
+ // for each dependency obj -> d (= deps[i]), create graph edges n->s and s->n
+ for d := range objMap[obj].deps {
+ // only consider nodes that may be an initialization dependency
+ if d, _ := d.(dependency); d != nil {
+ d := M[d]
+ n.succ.add(d)
+ d.pred.add(n)
+ }
+ }
+ }
+
+ // remove function nodes and collect remaining graph nodes in G
+ // (Mutually recursive functions may introduce cycles among themselves
+ // which are permitted. Yet such cycles may incorrectly inflate the dependency
+ // count for variables which in turn may not get scheduled for initialization
+ // in correct order.)
+ var G []*graphNode
+ for obj, n := range M {
+ if _, ok := obj.(*Func); ok {
+ // connect each predecessor p of n with each successor s
+ // and drop the function node (don't collect it in G)
+ for p := range n.pred {
+ // ignore self-cycles
+ if p != n {
+ // Each successor s of n becomes a successor of p, and
+ // each predecessor p of n becomes a predecessor of s.
+ for s := range n.succ {
+ // ignore self-cycles
+ if s != n {
+ p.succ.add(s)
+ s.pred.add(p)
+ delete(s.pred, n) // remove edge to n
+ }
+ }
+ delete(p.succ, n) // remove edge to n
+ }
+ }
+ } else {
+ // collect non-function nodes
+ G = append(G, n)
+ }
+ }
+
+ // fill in index and ndeps fields
+ for i, n := range G {
+ n.index = i
+ n.ndeps = len(n.succ)
+ }
+
+ return G
+}
+
+// ----------------------------------------------------------------------------
+// Priority queue
+
+// nodeQueue implements the container/heap interface;
+// a nodeQueue may be used as a priority queue.
+type nodeQueue []*graphNode
+
+func (a nodeQueue) Len() int { return len(a) }
+
+func (a nodeQueue) Swap(i, j int) {
+ x, y := a[i], a[j]
+ a[i], a[j] = y, x
+ x.index, y.index = j, i
+}
+
+func (a nodeQueue) Less(i, j int) bool {
+ x, y := a[i], a[j]
+ // nodes are prioritized by number of incoming dependencies (1st key)
+ // and source order (2nd key)
+ return x.ndeps < y.ndeps || x.ndeps == y.ndeps && x.obj.order() < y.obj.order()
+}
+
+func (a *nodeQueue) Push(x interface{}) {
+ panic("unreachable")
+}
+
+func (a *nodeQueue) Pop() interface{} {
+ n := len(*a)
+ x := (*a)[n-1]
+ x.index = -1 // for safety
+ *a = (*a)[:n-1]
+ return x
+}
diff --git a/src/cmd/compile/internal/types2/issues_test.go b/src/cmd/compile/internal/types2/issues_test.go
new file mode 100644
index 0000000000..6d39f99424
--- /dev/null
+++ b/src/cmd/compile/internal/types2/issues_test.go
@@ -0,0 +1,533 @@
+// UNREVIEWED
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements tests for various issues.
+
+package types2_test
+
+import (
+ "bytes"
+ "cmd/compile/internal/syntax"
+ "fmt"
+ "internal/testenv"
+ "sort"
+ "strings"
+ "testing"
+
+ . "cmd/compile/internal/types2"
+)
+
+func mustParse(t *testing.T, src string) *syntax.File {
+ f, err := parseSrc("", src)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return f
+}
+func TestIssue5770(t *testing.T) {
+ f := mustParse(t, `package p; type S struct{T}`)
+ var conf Config
+ // conf := Config{Importer: importer.Default()}
+ _, err := conf.Check(f.PkgName.Value, []*syntax.File{f}, nil) // do not crash
+ want := "undeclared name: T"
+ if err == nil || !strings.Contains(err.Error(), want) {
+ t.Errorf("got: %v; want: %s", err, want)
+ }
+}
+
+func TestIssue5849(t *testing.T) {
+ src := `
+package p
+var (
+ s uint
+ _ = uint8(8)
+ _ = uint16(16) << s
+ _ = uint32(32 << s)
+ _ = uint64(64 << s + s)
+ _ = (interface{})("foo")
+ _ = (interface{})(nil)
+)`
+ f := mustParse(t, src)
+
+ var conf Config
+ types := make(map[syntax.Expr]TypeAndValue)
+ _, err := conf.Check(f.PkgName.Value, []*syntax.File{f}, &Info{Types: types})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for x, tv := range types {
+ var want Type
+ switch x := x.(type) {
+ case *syntax.BasicLit:
+ switch x.Value {
+ case `8`:
+ want = Typ[Uint8]
+ case `16`:
+ want = Typ[Uint16]
+ case `32`:
+ want = Typ[Uint32]
+ case `64`:
+ want = Typ[Uint] // because of "+ s", s is of type uint
+ case `"foo"`:
+ want = Typ[String]
+ }
+ case *syntax.Name:
+ if x.Value == "nil" {
+ want = Typ[UntypedNil]
+ }
+ }
+ if want != nil && !Identical(tv.Type, want) {
+ t.Errorf("got %s; want %s", tv.Type, want)
+ }
+ }
+}
+
+func TestIssue6413(t *testing.T) {
+ src := `
+package p
+func f() int {
+ defer f()
+ go f()
+ return 0
+}
+`
+ f := mustParse(t, src)
+
+ var conf Config
+ types := make(map[syntax.Expr]TypeAndValue)
+ _, err := conf.Check(f.PkgName.Value, []*syntax.File{f}, &Info{Types: types})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ want := Typ[Int]
+ n := 0
+ for x, tv := range types {
+ if _, ok := x.(*syntax.CallExpr); ok {
+ if tv.Type != want {
+ t.Errorf("%s: got %s; want %s", x.Pos(), tv.Type, want)
+ }
+ n++
+ }
+ }
+
+ if n != 2 {
+ t.Errorf("got %d CallExprs; want 2", n)
+ }
+}
+
+func TestIssue7245(t *testing.T) {
+ src := `
+package p
+func (T) m() (res bool) { return }
+type T struct{} // receiver type after method declaration
+`
+ f := mustParse(t, src)
+
+ var conf Config
+ defs := make(map[*syntax.Name]Object)
+ _, err := conf.Check(f.PkgName.Value, []*syntax.File{f}, &Info{Defs: defs})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ m := f.DeclList[0].(*syntax.FuncDecl)
+ res1 := defs[m.Name].(*Func).Type().(*Signature).Results().At(0)
+ res2 := defs[m.Type.ResultList[0].Name].(*Var)
+
+ if res1 != res2 {
+ t.Errorf("got %s (%p) != %s (%p)", res1, res2, res1, res2)
+ }
+}
+
+// This tests that uses of existing vars on the LHS of an assignment
+// are Uses, not Defs; and also that the (illegal) use of a non-var on
+// the LHS of an assignment is a Use nonetheless.
+func TestIssue7827(t *testing.T) {
+ const src = `
+package p
+func _() {
+ const w = 1 // defs w
+ x, y := 2, 3 // defs x, y
+ w, x, z := 4, 5, 6 // uses w, x, defs z; error: cannot assign to w
+ _, _, _ = x, y, z // uses x, y, z
+}
+`
+ f := mustParse(t, src)
+
+ const want = `L3 defs func p._()
+L4 defs const w untyped int
+L5 defs var x int
+L5 defs var y int
+L6 defs var z int
+L6 uses const w untyped int
+L6 uses var x int
+L7 uses var x int
+L7 uses var y int
+L7 uses var z int`
+
+ // don't abort at the first error
+ conf := Config{Error: func(err error) { t.Log(err) }}
+ defs := make(map[*syntax.Name]Object)
+ uses := make(map[*syntax.Name]Object)
+ _, err := conf.Check(f.PkgName.Value, []*syntax.File{f}, &Info{Defs: defs, Uses: uses})
+ if s := fmt.Sprint(err); !strings.HasSuffix(s, "cannot assign to w") {
+ t.Errorf("Check: unexpected error: %s", s)
+ }
+
+ var facts []string
+ for id, obj := range defs {
+ if obj != nil {
+ fact := fmt.Sprintf("L%d defs %s", id.Pos().Line(), obj)
+ facts = append(facts, fact)
+ }
+ }
+ for id, obj := range uses {
+ fact := fmt.Sprintf("L%d uses %s", id.Pos().Line(), obj)
+ facts = append(facts, fact)
+ }
+ sort.Strings(facts)
+
+ got := strings.Join(facts, "\n")
+ if got != want {
+ t.Errorf("Unexpected defs/uses\ngot:\n%s\nwant:\n%s", got, want)
+ }
+}
+
+// This tests that the package associated with the types.Object.Pkg method
+// is the type's package independent of the order in which the imports are
+// listed in the sources src1, src2 below.
+// The actual issue is in go/internal/gcimporter which has a corresponding
+// test; we leave this test here to verify correct behavior at the go/types
+// level.
+func TestIssue13898(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ const src0 = `
+package main
+
+import "go/types"
+
+func main() {
+ var info types.Info
+ for _, obj := range info.Uses {
+ _ = obj.Pkg()
+ }
+}
+`
+ // like src0, but also imports go/importer
+ const src1 = `
+package main
+
+import (
+ "go/types"
+ _ "go/importer"
+)
+
+func main() {
+ var info types.Info
+ for _, obj := range info.Uses {
+ _ = obj.Pkg()
+ }
+}
+`
+ // like src1 but with different import order
+ // (used to fail with this issue)
+ const src2 = `
+package main
+
+import (
+ _ "go/importer"
+ "go/types"
+)
+
+func main() {
+ var info types.Info
+ for _, obj := range info.Uses {
+ _ = obj.Pkg()
+ }
+}
+`
+ f := func(test, src string) {
+ f := mustParse(t, src)
+ conf := Config{Importer: defaultImporter()}
+ info := Info{Uses: make(map[*syntax.Name]Object)}
+ _, err := conf.Check("main", []*syntax.File{f}, &info)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var pkg *Package
+ count := 0
+ for id, obj := range info.Uses {
+ if id.Value == "Pkg" {
+ pkg = obj.Pkg()
+ count++
+ }
+ }
+ if count != 1 {
+ t.Fatalf("%s: got %d entries named Pkg; want 1", test, count)
+ }
+ if pkg.Name() != "types" {
+ t.Fatalf("%s: got %v; want package types2", test, pkg)
+ }
+ }
+
+ f("src0", src0)
+ f("src1", src1)
+ f("src2", src2)
+}
+
+func TestIssue22525(t *testing.T) {
+ f := mustParse(t, `package p; func f() { var a, b, c, d, e int }`)
+
+ got := "\n"
+ conf := Config{Error: func(err error) { got += err.Error() + "\n" }}
+ conf.Check(f.PkgName.Value, []*syntax.File{f}, nil) // do not crash
+ want := `
+:1:27: a declared but not used
+:1:30: b declared but not used
+:1:33: c declared but not used
+:1:36: d declared but not used
+:1:39: e declared but not used
+`
+ if got != want {
+ t.Errorf("got: %swant: %s", got, want)
+ }
+}
+
+func TestIssue25627(t *testing.T) {
+ t.Skip("requires syntax tree inspection")
+
+ const prefix = `package p; import "unsafe"; type P *struct{}; type I interface{}; type T `
+ // The src strings (without prefix) are constructed such that the number of semicolons
+ // plus one corresponds to the number of fields expected in the respective struct.
+ for _, src := range []string{
+ `struct { x Missing }`,
+ `struct { Missing }`,
+ `struct { *Missing }`,
+ `struct { unsafe.Pointer }`,
+ `struct { P }`,
+ `struct { *I }`,
+ `struct { a int; b Missing; *Missing }`,
+ } {
+ f := mustParse(t, prefix+src)
+
+ conf := Config{Importer: defaultImporter(), Error: func(err error) {}}
+ info := &Info{Types: make(map[syntax.Expr]TypeAndValue)}
+ _, err := conf.Check(f.PkgName.Value, []*syntax.File{f}, info)
+ if err != nil {
+ if _, ok := err.(Error); !ok {
+ t.Fatal(err)
+ }
+ }
+
+ unimplemented()
+ /*
+ ast.Inspect(f, func(n syntax.Node) bool {
+ if spec, _ := n.(*syntax.TypeDecl); spec != nil {
+ if tv, ok := info.Types[spec.Type]; ok && spec.Name.Value == "T" {
+ want := strings.Count(src, ";") + 1
+ if got := tv.Type.(*Struct).NumFields(); got != want {
+ t.Errorf("%s: got %d fields; want %d", src, got, want)
+ }
+ }
+ }
+ return true
+ })
+ */
+ }
+}
+
+func TestIssue28005(t *testing.T) {
+ // method names must match defining interface name for this test
+ // (see last comment in this function)
+ sources := [...]string{
+ "package p; type A interface{ A() }",
+ "package p; type B interface{ B() }",
+ "package p; type X interface{ A; B }",
+ }
+
+ // compute original file ASTs
+ var orig [len(sources)]*syntax.File
+ for i, src := range sources {
+ orig[i] = mustParse(t, src)
+ }
+
+ // run the test for all order permutations of the incoming files
+ for _, perm := range [][len(sources)]int{
+ {0, 1, 2},
+ {0, 2, 1},
+ {1, 0, 2},
+ {1, 2, 0},
+ {2, 0, 1},
+ {2, 1, 0},
+ } {
+ // create file order permutation
+ files := make([]*syntax.File, len(sources))
+ for i := range perm {
+ files[i] = orig[perm[i]]
+ }
+
+ // type-check package with given file order permutation
+ var conf Config
+ info := &Info{Defs: make(map[*syntax.Name]Object)}
+ _, err := conf.Check("", files, info)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // look for interface object X
+ var obj Object
+ for name, def := range info.Defs {
+ if name.Value == "X" {
+ obj = def
+ break
+ }
+ }
+ if obj == nil {
+ t.Fatal("object X not found")
+ }
+ iface := obj.Type().Interface() // object X must be an interface
+ if iface == nil {
+ t.Fatalf("%s is not an interface", obj)
+ }
+
+ // Each iface method m is embedded; and m's receiver base type name
+ // must match the method's name per the choice in the source file.
+ for i := 0; i < iface.NumMethods(); i++ {
+ m := iface.Method(i)
+ recvName := m.Type().(*Signature).Recv().Type().(*Named).Obj().Name()
+ if recvName != m.Name() {
+ t.Errorf("perm %v: got recv %s; want %s", perm, recvName, m.Name())
+ }
+ }
+ }
+}
+
+func TestIssue28282(t *testing.T) {
+ // create type interface { error }
+ et := Universe.Lookup("error").Type()
+ it := NewInterfaceType(nil, []Type{et})
+ it.Complete()
+ // verify that after completing the interface, the embedded method remains unchanged
+ want := et.Interface().Method(0)
+ got := it.Method(0)
+ if got != want {
+ t.Fatalf("%s.Method(0): got %q (%p); want %q (%p)", it, got, got, want, want)
+ }
+ // verify that lookup finds the same method in both interfaces (redundant check)
+ obj, _, _ := LookupFieldOrMethod(et, false, nil, "Error")
+ if obj != want {
+ t.Fatalf("%s.Lookup: got %q (%p); want %q (%p)", et, obj, obj, want, want)
+ }
+ obj, _, _ = LookupFieldOrMethod(it, false, nil, "Error")
+ if obj != want {
+ t.Fatalf("%s.Lookup: got %q (%p); want %q (%p)", it, obj, obj, want, want)
+ }
+}
+
+func TestIssue29029(t *testing.T) {
+ f1 := mustParse(t, `package p; type A interface { M() }`)
+ f2 := mustParse(t, `package p; var B interface { A }`)
+
+ // printInfo prints the *Func definitions recorded in info, one *Func per line.
+ printInfo := func(info *Info) string {
+ var buf bytes.Buffer
+ for _, obj := range info.Defs {
+ if fn, ok := obj.(*Func); ok {
+ fmt.Fprintln(&buf, fn)
+ }
+ }
+ return buf.String()
+ }
+
+ // The *Func (method) definitions for package p must be the same
+ // independent on whether f1 and f2 are type-checked together, or
+ // incrementally.
+
+ // type-check together
+ var conf Config
+ info := &Info{Defs: make(map[*syntax.Name]Object)}
+ check := NewChecker(&conf, NewPackage("", "p"), info)
+ if err := check.Files([]*syntax.File{f1, f2}); err != nil {
+ t.Fatal(err)
+ }
+ want := printInfo(info)
+
+ // type-check incrementally
+ info = &Info{Defs: make(map[*syntax.Name]Object)}
+ check = NewChecker(&conf, NewPackage("", "p"), info)
+ if err := check.Files([]*syntax.File{f1}); err != nil {
+ t.Fatal(err)
+ }
+ if err := check.Files([]*syntax.File{f2}); err != nil {
+ t.Fatal(err)
+ }
+ got := printInfo(info)
+
+ if got != want {
+ t.Errorf("\ngot : %swant: %s", got, want)
+ }
+}
+
+func TestIssue34151(t *testing.T) {
+ const asrc = `package a; type I interface{ M() }; type T struct { F interface { I } }`
+ const bsrc = `package b; import "a"; type T struct { F interface { a.I } }; var _ = a.T(T{})`
+
+ a, err := pkgFor("a", asrc, nil)
+ if err != nil {
+ t.Fatalf("package %s failed to typecheck: %v", a.Name(), err)
+ }
+
+ bast := mustParse(t, bsrc)
+ conf := Config{Importer: importHelper{a}}
+ b, err := conf.Check(bast.PkgName.Value, []*syntax.File{bast}, nil)
+ if err != nil {
+ t.Errorf("package %s failed to typecheck: %v", b.Name(), err)
+ }
+}
+
+type importHelper struct {
+ pkg *Package
+}
+
+func (h importHelper) Import(path string) (*Package, error) {
+ if path != h.pkg.Path() {
+ return nil, fmt.Errorf("got package path %q; want %q", path, h.pkg.Path())
+ }
+ return h.pkg, nil
+}
+
+// TestIssue34921 verifies that we don't update an imported type's underlying
+// type when resolving an underlying type. Specifically, when determining the
+// underlying type of b.T (which is the underlying type of a.T, which is int)
+// we must not set the underlying type of a.T again since that would lead to
+// a race condition if package b is imported elsewhere, in a package that is
+// concurrently type-checked.
+func TestIssue34921(t *testing.T) {
+ defer func() {
+ if r := recover(); r != nil {
+ t.Error(r)
+ }
+ }()
+
+ var sources = []string{
+ `package a; type T int`,
+ `package b; import "a"; type T a.T`,
+ }
+
+ var pkg *Package
+ for _, src := range sources {
+ f := mustParse(t, src)
+ conf := Config{Importer: importHelper{pkg}}
+ res, err := conf.Check(f.PkgName.Value, []*syntax.File{f}, nil)
+ if err != nil {
+ t.Errorf("%q failed to typecheck: %v", src, err)
+ }
+ pkg = res // res is imported by the next package in this test
+ }
+}
diff --git a/src/cmd/compile/internal/types2/labels.go b/src/cmd/compile/internal/types2/labels.go
new file mode 100644
index 0000000000..ca5fe6b389
--- /dev/null
+++ b/src/cmd/compile/internal/types2/labels.go
@@ -0,0 +1,260 @@
+// UNREVIEWED
+// Copyright 2013 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 types2
+
+import (
+ "cmd/compile/internal/syntax"
+)
+
+// labels checks correct label use in body.
+func (check *Checker) labels(body *syntax.BlockStmt) {
+ // set of all labels in this body
+ all := NewScope(nil, body.Pos(), endPos(body), "label")
+
+ fwdJumps := check.blockBranches(all, nil, nil, body.List)
+
+ // If there are any forward jumps left, no label was found for
+ // the corresponding goto statements. Either those labels were
+ // never defined, or they are inside blocks and not reachable
+ // for the respective gotos.
+ for _, jmp := range fwdJumps {
+ var msg string
+ name := jmp.Label.Value
+ if alt := all.Lookup(name); alt != nil {
+ msg = "goto %s jumps into block"
+ alt.(*Label).used = true // avoid another error
+ } else {
+ msg = "label %s not declared"
+ }
+ check.errorf(jmp.Label, msg, name)
+ }
+
+ // spec: "It is illegal to define a label that is never used."
+ for _, obj := range all.elems {
+ if lbl := obj.(*Label); !lbl.used {
+ check.softErrorf(lbl.pos, "label %s declared but not used", lbl.name)
+ }
+ }
+}
+
+// A block tracks label declarations in a block and its enclosing blocks.
+type block struct {
+ parent *block // enclosing block
+ lstmt *syntax.LabeledStmt // labeled statement to which this block belongs, or nil
+ labels map[string]*syntax.LabeledStmt // allocated lazily
+}
+
+// insert records a new label declaration for the current block.
+// The label must not have been declared before in any block.
+func (b *block) insert(s *syntax.LabeledStmt) {
+ name := s.Label.Value
+ if debug {
+ assert(b.gotoTarget(name) == nil)
+ }
+ labels := b.labels
+ if labels == nil {
+ labels = make(map[string]*syntax.LabeledStmt)
+ b.labels = labels
+ }
+ labels[name] = s
+}
+
+// gotoTarget returns the labeled statement in the current
+// or an enclosing block with the given label name, or nil.
+func (b *block) gotoTarget(name string) *syntax.LabeledStmt {
+ for s := b; s != nil; s = s.parent {
+ if t := s.labels[name]; t != nil {
+ return t
+ }
+ }
+ return nil
+}
+
+// enclosingTarget returns the innermost enclosing labeled
+// statement with the given label name, or nil.
+func (b *block) enclosingTarget(name string) *syntax.LabeledStmt {
+ for s := b; s != nil; s = s.parent {
+ if t := s.lstmt; t != nil && t.Label.Value == name {
+ return t
+ }
+ }
+ return nil
+}
+
+// blockBranches processes a block's statement list and returns the set of outgoing forward jumps.
+// all is the scope of all declared labels, parent the set of labels declared in the immediately
+// enclosing block, and lstmt is the labeled statement this block is associated with (or nil).
+func (check *Checker) blockBranches(all *Scope, parent *block, lstmt *syntax.LabeledStmt, list []syntax.Stmt) []*syntax.BranchStmt {
+ b := &block{parent, lstmt, nil}
+
+ var (
+ varDeclPos syntax.Pos
+ fwdJumps, badJumps []*syntax.BranchStmt
+ )
+
+ // All forward jumps jumping over a variable declaration are possibly
+ // invalid (they may still jump out of the block and be ok).
+ // recordVarDecl records them for the given position.
+ recordVarDecl := func(pos syntax.Pos) {
+ varDeclPos = pos
+ badJumps = append(badJumps[:0], fwdJumps...) // copy fwdJumps to badJumps
+ }
+
+ jumpsOverVarDecl := func(jmp *syntax.BranchStmt) bool {
+ if varDeclPos.IsKnown() {
+ for _, bad := range badJumps {
+ if jmp == bad {
+ return true
+ }
+ }
+ }
+ return false
+ }
+
+ var stmtBranches func(syntax.Stmt)
+ stmtBranches = func(s syntax.Stmt) {
+ switch s := s.(type) {
+ case *syntax.DeclStmt:
+ for _, d := range s.DeclList {
+ if d, _ := d.(*syntax.VarDecl); d != nil {
+ recordVarDecl(d.Pos())
+ }
+ }
+
+ case *syntax.LabeledStmt:
+ // declare non-blank label
+ if name := s.Label.Value; name != "_" {
+ lbl := NewLabel(s.Label.Pos(), check.pkg, name)
+ if alt := all.Insert(lbl); alt != nil {
+ check.softErrorf(lbl.pos, "label %s already declared", name)
+ check.reportAltDecl(alt)
+ // ok to continue
+ } else {
+ b.insert(s)
+ check.recordDef(s.Label, lbl)
+ }
+ // resolve matching forward jumps and remove them from fwdJumps
+ i := 0
+ for _, jmp := range fwdJumps {
+ if jmp.Label.Value == name {
+ // match
+ lbl.used = true
+ check.recordUse(jmp.Label, lbl)
+ if jumpsOverVarDecl(jmp) {
+ check.softErrorf(
+ jmp.Label,
+ "goto %s jumps over variable declaration at line %d",
+ name,
+ varDeclPos.Line(),
+ )
+ // ok to continue
+ }
+ } else {
+ // no match - record new forward jump
+ fwdJumps[i] = jmp
+ i++
+ }
+ }
+ fwdJumps = fwdJumps[:i]
+ lstmt = s
+ }
+ stmtBranches(s.Stmt)
+
+ case *syntax.BranchStmt:
+ if s.Label == nil {
+ return // checked in 1st pass (check.stmt)
+ }
+
+ // determine and validate target
+ name := s.Label.Value
+ switch s.Tok {
+ case syntax.Break:
+ // spec: "If there is a label, it must be that of an enclosing
+ // "for", "switch", or "select" statement, and that is the one
+ // whose execution terminates."
+ valid := false
+ if t := b.enclosingTarget(name); t != nil {
+ switch t.Stmt.(type) {
+ case *syntax.SwitchStmt, *syntax.SelectStmt, *syntax.ForStmt:
+ valid = true
+ }
+ }
+ if !valid {
+ check.errorf(s.Label, "invalid break label %s", name)
+ return
+ }
+
+ case syntax.Continue:
+ // spec: "If there is a label, it must be that of an enclosing
+ // "for" statement, and that is the one whose execution advances."
+ valid := false
+ if t := b.enclosingTarget(name); t != nil {
+ switch t.Stmt.(type) {
+ case *syntax.ForStmt:
+ valid = true
+ }
+ }
+ if !valid {
+ check.errorf(s.Label, "invalid continue label %s", name)
+ return
+ }
+
+ case syntax.Goto:
+ if b.gotoTarget(name) == nil {
+ // label may be declared later - add branch to forward jumps
+ fwdJumps = append(fwdJumps, s)
+ return
+ }
+
+ default:
+ check.invalidASTf(s, "branch statement: %s %s", s.Tok, name)
+ return
+ }
+
+ // record label use
+ obj := all.Lookup(name)
+ obj.(*Label).used = true
+ check.recordUse(s.Label, obj)
+
+ case *syntax.AssignStmt:
+ if s.Op == syntax.Def {
+ recordVarDecl(s.Pos())
+ }
+
+ case *syntax.BlockStmt:
+ // Unresolved forward jumps inside the nested block
+ // become forward jumps in the current block.
+ fwdJumps = append(fwdJumps, check.blockBranches(all, b, lstmt, s.List)...)
+
+ case *syntax.IfStmt:
+ stmtBranches(s.Then)
+ if s.Else != nil {
+ stmtBranches(s.Else)
+ }
+
+ case *syntax.SwitchStmt:
+ b := &block{b, lstmt, nil}
+ for _, s := range s.Body {
+ fwdJumps = append(fwdJumps, check.blockBranches(all, b, nil, s.Body)...)
+ }
+
+ case *syntax.SelectStmt:
+ b := &block{b, lstmt, nil}
+ for _, s := range s.Body {
+ fwdJumps = append(fwdJumps, check.blockBranches(all, b, nil, s.Body)...)
+ }
+
+ case *syntax.ForStmt:
+ stmtBranches(s.Body)
+ }
+ }
+
+ for _, s := range list {
+ stmtBranches(s)
+ }
+
+ return fwdJumps
+}
diff --git a/src/cmd/compile/internal/types2/lookup.go b/src/cmd/compile/internal/types2/lookup.go
new file mode 100644
index 0000000000..277212c568
--- /dev/null
+++ b/src/cmd/compile/internal/types2/lookup.go
@@ -0,0 +1,493 @@
+// UNREVIEWED
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements various field and method lookup functions.
+
+package types2
+
+// LookupFieldOrMethod looks up a field or method with given package and name
+// in T and returns the corresponding *Var or *Func, an index sequence, and a
+// bool indicating if there were any pointer indirections on the path to the
+// field or method. If addressable is set, T is the type of an addressable
+// variable (only matters for method lookups).
+//
+// The last index entry is the field or method index in the (possibly embedded)
+// type where the entry was found, either:
+//
+// 1) the list of declared methods of a named type; or
+// 2) the list of all methods (method set) of an interface type; or
+// 3) the list of fields of a struct type.
+//
+// The earlier index entries are the indices of the embedded struct fields
+// traversed to get to the found entry, starting at depth 0.
+//
+// If no entry is found, a nil object is returned. In this case, the returned
+// index and indirect values have the following meaning:
+//
+// - If index != nil, the index sequence points to an ambiguous entry
+// (the same name appeared more than once at the same embedding level).
+//
+// - If indirect is set, a method with a pointer receiver type was found
+// but there was no pointer on the path from the actual receiver type to
+// the method's formal receiver base type, nor was the receiver addressable.
+//
+func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
+ return (*Checker)(nil).lookupFieldOrMethod(T, addressable, pkg, name)
+}
+
+// Internal use of Checker.lookupFieldOrMethod: If the obj result is a method
+// associated with a concrete (non-interface) type, the method's signature
+// may not be fully set up. Call Checker.objDecl(obj, nil) before accessing
+// the method's type.
+// TODO(gri) Now that we provide the *Checker, we can probably remove this
+// caveat by calling Checker.objDecl from lookupFieldOrMethod. Investigate.
+
+// lookupFieldOrMethod is like the external version but completes interfaces
+// as necessary.
+func (check *Checker) lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
+ // Methods cannot be associated to a named pointer type
+ // (spec: "The type denoted by T is called the receiver base type;
+ // it must not be a pointer or interface type and it must be declared
+ // in the same package as the method.").
+ // Thus, if we have a named pointer type, proceed with the underlying
+ // pointer type but discard the result if it is a method since we would
+ // not have found it for T (see also issue 8590).
+ if t := T.Named(); t != nil {
+ if p, _ := t.underlying.(*Pointer); p != nil {
+ obj, index, indirect = check.rawLookupFieldOrMethod(p, false, pkg, name)
+ if _, ok := obj.(*Func); ok {
+ return nil, nil, false
+ }
+ return
+ }
+ }
+
+ return check.rawLookupFieldOrMethod(T, addressable, pkg, name)
+}
+
+// TODO(gri) The named type consolidation and seen maps below must be
+// indexed by unique keys for a given type. Verify that named
+// types always have only one representation (even when imported
+// indirectly via different packages.)
+
+// rawLookupFieldOrMethod should only be called by lookupFieldOrMethod and missingMethod.
+func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
+ // WARNING: The code in this function is extremely subtle - do not modify casually!
+ // This function and NewMethodSet should be kept in sync.
+
+ if name == "_" {
+ return // blank fields/methods are never found
+ }
+
+ typ, isPtr := deref(T)
+
+ // *typ where typ is an interface has no methods.
+ // Be cautious: typ may be nil (issue 39634, crash #3).
+ if typ == nil || isPtr && IsInterface(typ) {
+ return
+ }
+
+ // Start with typ as single entry at shallowest depth.
+ current := []embeddedType{{typ, nil, isPtr, false}}
+
+ // Named types that we have seen already, allocated lazily.
+ // Used to avoid endless searches in case of recursive types.
+ // Since only Named types can be used for recursive types, we
+ // only need to track those.
+ // (If we ever allow type aliases to construct recursive types,
+ // we must use type identity rather than pointer equality for
+ // the map key comparison, as we do in consolidateMultiples.)
+ var seen map[*Named]bool
+
+ // search current depth
+ for len(current) > 0 {
+ var next []embeddedType // embedded types found at current depth
+
+ // look for (pkg, name) in all types at current depth
+ var tpar *TypeParam // set if obj receiver is a type parameter
+ for _, e := range current {
+ typ := e.typ
+
+ // If we have a named type, we may have associated methods.
+ // Look for those first.
+ if named := typ.Named(); named != nil {
+ if seen[named] {
+ // We have seen this type before, at a more shallow depth
+ // (note that multiples of this type at the current depth
+ // were consolidated before). The type at that depth shadows
+ // this same type at the current depth, so we can ignore
+ // this one.
+ continue
+ }
+ if seen == nil {
+ seen = make(map[*Named]bool)
+ }
+ seen[named] = true
+
+ // look for a matching attached method
+ if i, m := lookupMethod(named.methods, pkg, name); m != nil {
+ // potential match
+ // caution: method may not have a proper signature yet
+ index = concat(e.index, i)
+ if obj != nil || e.multiples {
+ return nil, index, false // collision
+ }
+ obj = m
+ indirect = e.indirect
+ continue // we can't have a matching field or interface method
+ }
+
+ // continue with underlying type, but only if it's not a type parameter
+ // TODO(gri) is this what we want to do for type parameters? (spec question)
+ typ = named.Under()
+ if typ.TypeParam() != nil {
+ continue
+ }
+ }
+
+ tpar = nil
+ switch t := typ.(type) {
+ case *Struct:
+ // look for a matching field and collect embedded types
+ for i, f := range t.fields {
+ if f.sameId(pkg, name) {
+ assert(f.typ != nil)
+ index = concat(e.index, i)
+ if obj != nil || e.multiples {
+ return nil, index, false // collision
+ }
+ obj = f
+ indirect = e.indirect
+ continue // we can't have a matching interface method
+ }
+ // Collect embedded struct fields for searching the next
+ // lower depth, but only if we have not seen a match yet
+ // (if we have a match it is either the desired field or
+ // we have a name collision on the same depth; in either
+ // case we don't need to look further).
+ // Embedded fields are always of the form T or *T where
+ // T is a type name. If e.typ appeared multiple times at
+ // this depth, f.typ appears multiple times at the next
+ // depth.
+ if obj == nil && f.embedded {
+ typ, isPtr := deref(f.typ)
+ // TODO(gri) optimization: ignore types that can't
+ // have fields or methods (only Named, Struct, and
+ // Interface types need to be considered).
+ next = append(next, embeddedType{typ, concat(e.index, i), e.indirect || isPtr, e.multiples})
+ }
+ }
+
+ case *Interface:
+ // look for a matching method
+ // TODO(gri) t.allMethods is sorted - use binary search
+ check.completeInterface(nopos, t)
+ if i, m := lookupMethod(t.allMethods, pkg, name); m != nil {
+ assert(m.typ != nil)
+ index = concat(e.index, i)
+ if obj != nil || e.multiples {
+ return nil, index, false // collision
+ }
+ obj = m
+ indirect = e.indirect
+ }
+
+ case *TypeParam:
+ if i, m := lookupMethod(t.Bound().allMethods, pkg, name); m != nil {
+ assert(m.typ != nil)
+ index = concat(e.index, i)
+ if obj != nil || e.multiples {
+ return nil, index, false // collision
+ }
+ tpar = t
+ obj = m
+ indirect = e.indirect
+ }
+ if obj == nil {
+ // At this point we're not (yet) looking into methods
+ // that any underlyng type of the types in the type list
+ // migth have.
+ // TODO(gri) Do we want to specify the language that way?
+ }
+ }
+ }
+
+ if obj != nil {
+ // found a potential match
+ // spec: "A method call x.m() is valid if the method set of (the type of) x
+ // contains m and the argument list can be assigned to the parameter
+ // list of m. If x is addressable and &x's method set contains m, x.m()
+ // is shorthand for (&x).m()".
+ if f, _ := obj.(*Func); f != nil {
+ // determine if method has a pointer receiver
+ hasPtrRecv := tpar == nil && ptrRecv(f) || tpar != nil && tpar.ptr
+ if hasPtrRecv && !indirect && !addressable {
+ return nil, nil, true // pointer/addressable receiver required
+ }
+ }
+ return
+ }
+
+ current = check.consolidateMultiples(next)
+ }
+
+ return nil, nil, false // not found
+}
+
+// embeddedType represents an embedded type
+type embeddedType struct {
+ typ Type
+ index []int // embedded field indices, starting with index at depth 0
+ indirect bool // if set, there was a pointer indirection on the path to this field
+ multiples bool // if set, typ appears multiple times at this depth
+}
+
+// consolidateMultiples collects multiple list entries with the same type
+// into a single entry marked as containing multiples. The result is the
+// consolidated list.
+func (check *Checker) consolidateMultiples(list []embeddedType) []embeddedType {
+ if len(list) <= 1 {
+ return list // at most one entry - nothing to do
+ }
+
+ n := 0 // number of entries w/ unique type
+ prev := make(map[Type]int) // index at which type was previously seen
+ for _, e := range list {
+ if i, found := check.lookupType(prev, e.typ); found {
+ list[i].multiples = true
+ // ignore this entry
+ } else {
+ prev[e.typ] = n
+ list[n] = e
+ n++
+ }
+ }
+ return list[:n]
+}
+
+func (check *Checker) lookupType(m map[Type]int, typ Type) (int, bool) {
+ // fast path: maybe the types are equal
+ if i, found := m[typ]; found {
+ return i, true
+ }
+
+ for t, i := range m {
+ if check.identical(t, typ) {
+ return i, true
+ }
+ }
+
+ return 0, false
+}
+
+// MissingMethod returns (nil, false) if V implements T, otherwise it
+// returns a missing method required by T and whether it is missing or
+// just has the wrong type.
+//
+// For non-interface types V, or if static is set, V implements T if all
+// methods of T are present in V. Otherwise (V is an interface and static
+// is not set), MissingMethod only checks that methods of T which are also
+// present in V have matching types (e.g., for a type assertion x.(T) where
+// x is of interface type V).
+//
+func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool) {
+ m, typ := (*Checker)(nil).missingMethod(V, T, static)
+ return m, typ != nil
+}
+
+// missingMethod is like MissingMethod but accepts a *Checker as
+// receiver and an addressable flag.
+// The receiver may be nil if missingMethod is invoked through
+// an exported API call (such as MissingMethod), i.e., when all
+// methods have been type-checked.
+// If the type has the correctly named method, but with the wrong
+// signature, the existing method is returned as well.
+// To improve error messages, also report the wrong signature
+// when the method exists on *V instead of V.
+func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, wrongType *Func) {
+ check.completeInterface(nopos, T)
+
+ // fast path for common case
+ if T.Empty() {
+ return
+ }
+
+ if ityp := V.Interface(); ityp != nil {
+ check.completeInterface(nopos, ityp)
+ // TODO(gri) allMethods is sorted - can do this more efficiently
+ for _, m := range T.allMethods {
+ _, f := lookupMethod(ityp.allMethods, m.pkg, m.name)
+
+ if f == nil {
+ // if m is the magic method == we're ok (interfaces are comparable)
+ if m.name == "==" || !static {
+ continue
+ }
+ return m, f
+ }
+
+ // both methods must have the same number of type parameters
+ ftyp := f.typ.(*Signature)
+ mtyp := m.typ.(*Signature)
+ if len(ftyp.tparams) != len(mtyp.tparams) {
+ return m, f
+ }
+
+ // If the methods have type parameters we don't care whether they
+ // are the same or not, as long as they match up. Use unification
+ // to see if they can be made to match.
+ // TODO(gri) is this always correct? what about type bounds?
+ // (Alternative is to rename/subst type parameters and compare.)
+ u := newUnifier(check, true)
+ u.x.init(ftyp.tparams)
+ if !u.unify(ftyp, mtyp) {
+ return m, f
+ }
+ }
+
+ return
+ }
+
+ // A concrete type implements T if it implements all methods of T.
+ Vd, _ := deref(V)
+ Vn := Vd.Named()
+ for _, m := range T.allMethods {
+ // TODO(gri) should this be calling lookupFieldOrMethod instead (and why not)?
+ obj, _, _ := check.rawLookupFieldOrMethod(V, false, m.pkg, m.name)
+
+ // Check if *V implements this method of T.
+ if obj == nil {
+ ptr := NewPointer(V)
+ obj, _, _ = check.rawLookupFieldOrMethod(ptr, false, m.pkg, m.name)
+ if obj != nil {
+ return m, obj.(*Func)
+ }
+ }
+
+ // we must have a method (not a field of matching function type)
+ f, _ := obj.(*Func)
+ if f == nil {
+ // if m is the magic method == and V is comparable, we're ok
+ if m.name == "==" && Comparable(V) {
+ continue
+ }
+ return m, nil
+ }
+
+ // methods may not have a fully set up signature yet
+ if check != nil {
+ check.objDecl(f, nil)
+ }
+
+ // both methods must have the same number of type parameters
+ ftyp := f.typ.(*Signature)
+ mtyp := m.typ.(*Signature)
+ if len(ftyp.tparams) != len(mtyp.tparams) {
+ return m, f
+ }
+
+ // If V is a (instantiated) generic type, its methods are still
+ // parameterized using the original (declaration) receiver type
+ // parameters (subst simply copies the existing method list, it
+ // does not instantiate the methods).
+ // In order to compare the signatures, substitute the receiver
+ // type parameters of ftyp with V's instantiation type arguments.
+ // This lazily instantiates the signature of method f.
+ if Vn != nil && len(Vn.tparams) > 0 {
+ // Be careful: The number of type arguments may not match
+ // the number of receiver parameters. If so, an error was
+ // reported earlier but the length discrepancy is still
+ // here. Exit early in this case to prevent an assertion
+ // failure in makeSubstMap.
+ // TODO(gri) Can we avoid this check by fixing the lengths?
+ if len(ftyp.rparams) != len(Vn.targs) {
+ return
+ }
+ ftyp = check.subst(nopos, ftyp, makeSubstMap(ftyp.rparams, Vn.targs)).(*Signature)
+ }
+
+ // If the methods have type parameters we don't care whether they
+ // are the same or not, as long as they match up. Use unification
+ // to see if they can be made to match.
+ // TODO(gri) is this always correct? what about type bounds?
+ // (Alternative is to rename/subst type parameters and compare.)
+ u := newUnifier(check, true)
+ u.x.init(ftyp.tparams)
+ if !u.unify(ftyp, mtyp) {
+ return m, f
+ }
+ }
+
+ return
+}
+
+// assertableTo reports whether a value of type V can be asserted to have type T.
+// It returns (nil, false) as affirmative answer. Otherwise it returns a missing
+// method required by V and whether it is missing or just has the wrong type.
+// The receiver may be nil if assertableTo is invoked through an exported API call
+// (such as AssertableTo), i.e., when all methods have been type-checked.
+// If strict (or the global constant forceStrict) is set, assertions that
+// are known to fail are not permitted.
+func (check *Checker) assertableTo(V *Interface, T Type, strict bool) (method, wrongType *Func) {
+ // no static check is required if T is an interface
+ // spec: "If T is an interface type, x.(T) asserts that the
+ // dynamic type of x implements the interface T."
+ if T.Interface() != nil && !(strict || forceStrict) {
+ return
+ }
+ return check.missingMethod(T, V, false)
+}
+
+// deref dereferences typ if it is a *Pointer and returns its base and true.
+// Otherwise it returns (typ, false).
+func deref(typ Type) (Type, bool) {
+ if p, _ := typ.(*Pointer); p != nil {
+ return p.base, true
+ }
+ return typ, false
+}
+
+// derefStructPtr dereferences typ if it is a (named or unnamed) pointer to a
+// (named or unnamed) struct and returns its base. Otherwise it returns typ.
+func derefStructPtr(typ Type) Type {
+ if p := typ.Pointer(); p != nil {
+ if p.base.Struct() != nil {
+ return p.base
+ }
+ }
+ return typ
+}
+
+// concat returns the result of concatenating list and i.
+// The result does not share its underlying array with list.
+func concat(list []int, i int) []int {
+ var t []int
+ t = append(t, list...)
+ return append(t, i)
+}
+
+// fieldIndex returns the index for the field with matching package and name, or a value < 0.
+func fieldIndex(fields []*Var, pkg *Package, name string) int {
+ if name != "_" {
+ for i, f := range fields {
+ if f.sameId(pkg, name) {
+ return i
+ }
+ }
+ }
+ return -1
+}
+
+// lookupMethod returns the index of and method with matching package and name, or (-1, nil).
+func lookupMethod(methods []*Func, pkg *Package, name string) (int, *Func) {
+ if name != "_" {
+ for i, m := range methods {
+ if m.sameId(pkg, name) {
+ return i, m
+ }
+ }
+ }
+ return -1, nil
+}
diff --git a/src/cmd/compile/internal/types2/methodset.go b/src/cmd/compile/internal/types2/methodset.go
new file mode 100644
index 0000000000..9f7315a0fa
--- /dev/null
+++ b/src/cmd/compile/internal/types2/methodset.go
@@ -0,0 +1,261 @@
+// UNREVIEWED
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements method sets.
+
+package types2
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+)
+
+// A MethodSet is an ordered set of concrete or abstract (interface) methods;
+// a method is a MethodVal selection, and they are ordered by ascending m.Obj().Id().
+// The zero value for a MethodSet is a ready-to-use empty method set.
+type MethodSet struct {
+ list []*Selection
+}
+
+func (s *MethodSet) String() string {
+ if s.Len() == 0 {
+ return "MethodSet {}"
+ }
+
+ var buf strings.Builder
+ fmt.Fprintln(&buf, "MethodSet {")
+ for _, f := range s.list {
+ fmt.Fprintf(&buf, "\t%s\n", f)
+ }
+ fmt.Fprintln(&buf, "}")
+ return buf.String()
+}
+
+// Len returns the number of methods in s.
+func (s *MethodSet) Len() int { return len(s.list) }
+
+// At returns the i'th method in s for 0 <= i < s.Len().
+func (s *MethodSet) At(i int) *Selection { return s.list[i] }
+
+// Lookup returns the method with matching package and name, or nil if not found.
+func (s *MethodSet) Lookup(pkg *Package, name string) *Selection {
+ if s.Len() == 0 {
+ return nil
+ }
+
+ key := Id(pkg, name)
+ i := sort.Search(len(s.list), func(i int) bool {
+ m := s.list[i]
+ return m.obj.Id() >= key
+ })
+ if i < len(s.list) {
+ m := s.list[i]
+ if m.obj.Id() == key {
+ return m
+ }
+ }
+ return nil
+}
+
+// Shared empty method set.
+var emptyMethodSet MethodSet
+
+// Note: NewMethodSet is intended for external use only as it
+// requires interfaces to be complete. If may be used
+// internally if LookupFieldOrMethod completed the same
+// interfaces beforehand.
+
+// NewMethodSet returns the method set for the given type T.
+// It always returns a non-nil method set, even if it is empty.
+func NewMethodSet(T Type) *MethodSet {
+ // WARNING: The code in this function is extremely subtle - do not modify casually!
+ // This function and lookupFieldOrMethod should be kept in sync.
+
+ // method set up to the current depth, allocated lazily
+ var base methodSet
+
+ typ, isPtr := deref(T)
+
+ // *typ where typ is an interface has no methods.
+ if isPtr && IsInterface(typ) {
+ return &emptyMethodSet
+ }
+
+ // Start with typ as single entry at shallowest depth.
+ current := []embeddedType{{typ, nil, isPtr, false}}
+
+ // Named types that we have seen already, allocated lazily.
+ // Used to avoid endless searches in case of recursive types.
+ // Since only Named types can be used for recursive types, we
+ // only need to track those.
+ // (If we ever allow type aliases to construct recursive types,
+ // we must use type identity rather than pointer equality for
+ // the map key comparison, as we do in consolidateMultiples.)
+ var seen map[*Named]bool
+
+ // collect methods at current depth
+ for len(current) > 0 {
+ var next []embeddedType // embedded types found at current depth
+
+ // field and method sets at current depth, indexed by names (Id's), and allocated lazily
+ var fset map[string]bool // we only care about the field names
+ var mset methodSet
+
+ for _, e := range current {
+ typ := e.typ
+
+ // If we have a named type, we may have associated methods.
+ // Look for those first.
+ if named := typ.Named(); named != nil {
+ if seen[named] {
+ // We have seen this type before, at a more shallow depth
+ // (note that multiples of this type at the current depth
+ // were consolidated before). The type at that depth shadows
+ // this same type at the current depth, so we can ignore
+ // this one.
+ continue
+ }
+ if seen == nil {
+ seen = make(map[*Named]bool)
+ }
+ seen[named] = true
+
+ mset = mset.add(named.methods, e.index, e.indirect, e.multiples)
+
+ // continue with underlying type
+ typ = named.underlying
+ }
+
+ switch t := typ.(type) {
+ case *Struct:
+ for i, f := range t.fields {
+ if fset == nil {
+ fset = make(map[string]bool)
+ }
+ fset[f.Id()] = true
+
+ // Embedded fields are always of the form T or *T where
+ // T is a type name. If typ appeared multiple times at
+ // this depth, f.Type appears multiple times at the next
+ // depth.
+ if f.embedded {
+ typ, isPtr := deref(f.typ)
+ // TODO(gri) optimization: ignore types that can't
+ // have fields or methods (only Named, Struct, and
+ // Interface types need to be considered).
+ next = append(next, embeddedType{typ, concat(e.index, i), e.indirect || isPtr, e.multiples})
+ }
+ }
+
+ case *Interface:
+ mset = mset.add(t.allMethods, e.index, true, e.multiples)
+ }
+ }
+
+ // Add methods and collisions at this depth to base if no entries with matching
+ // names exist already.
+ for k, m := range mset {
+ if _, found := base[k]; !found {
+ // Fields collide with methods of the same name at this depth.
+ if fset[k] {
+ m = nil // collision
+ }
+ if base == nil {
+ base = make(methodSet)
+ }
+ base[k] = m
+ }
+ }
+
+ // Add all (remaining) fields at this depth as collisions (since they will
+ // hide any method further down) if no entries with matching names exist already.
+ for k := range fset {
+ if _, found := base[k]; !found {
+ if base == nil {
+ base = make(methodSet)
+ }
+ base[k] = nil // collision
+ }
+ }
+
+ // It's ok to call consolidateMultiples with a nil *Checker because
+ // MethodSets are not used internally (outside debug mode). When used
+ // externally, interfaces are expected to be completed and then we do
+ // not need a *Checker to complete them when (indirectly) calling
+ // Checker.identical via consolidateMultiples.
+ current = (*Checker)(nil).consolidateMultiples(next)
+ }
+
+ if len(base) == 0 {
+ return &emptyMethodSet
+ }
+
+ // collect methods
+ var list []*Selection
+ for _, m := range base {
+ if m != nil {
+ m.recv = T
+ list = append(list, m)
+ }
+ }
+ // sort by unique name
+ sort.Slice(list, func(i, j int) bool {
+ return list[i].obj.Id() < list[j].obj.Id()
+ })
+ return &MethodSet{list}
+}
+
+// A methodSet is a set of methods and name collisions.
+// A collision indicates that multiple methods with the
+// same unique id, or a field with that id appeared.
+type methodSet map[string]*Selection // a nil entry indicates a name collision
+
+// Add adds all functions in list to the method set s.
+// If multiples is set, every function in list appears multiple times
+// and is treated as a collision.
+func (s methodSet) add(list []*Func, index []int, indirect bool, multiples bool) methodSet {
+ if len(list) == 0 {
+ return s
+ }
+ if s == nil {
+ s = make(methodSet)
+ }
+ for i, f := range list {
+ key := f.Id()
+ // if f is not in the set, add it
+ if !multiples {
+ // TODO(gri) A found method may not be added because it's not in the method set
+ // (!indirect && ptrRecv(f)). A 2nd method on the same level may be in the method
+ // set and may not collide with the first one, thus leading to a false positive.
+ // Is that possible? Investigate.
+ if _, found := s[key]; !found && (indirect || !ptrRecv(f)) {
+ s[key] = &Selection{MethodVal, nil, f, concat(index, i), indirect}
+ continue
+ }
+ }
+ s[key] = nil // collision
+ }
+ return s
+}
+
+// ptrRecv reports whether the receiver is of the form *T.
+func ptrRecv(f *Func) bool {
+ // If a method's receiver type is set, use that as the source of truth for the receiver.
+ // Caution: Checker.funcDecl (decl.go) marks a function by setting its type to an empty
+ // signature. We may reach here before the signature is fully set up: we must explicitly
+ // check if the receiver is set (we cannot just look for non-nil f.typ).
+ if sig, _ := f.typ.(*Signature); sig != nil && sig.recv != nil {
+ _, isPtr := deref(sig.recv.typ)
+ return isPtr
+ }
+
+ // If a method's type is not set it may be a method/function that is:
+ // 1) client-supplied (via NewFunc with no signature), or
+ // 2) internally created but not yet type-checked.
+ // For case 1) we can't do anything; the client must know what they are doing.
+ // For case 2) we can use the information gathered by the resolver.
+ return f.hasPtrRecv
+}
diff --git a/src/cmd/compile/internal/types2/object.go b/src/cmd/compile/internal/types2/object.go
new file mode 100644
index 0000000000..6e6f48c036
--- /dev/null
+++ b/src/cmd/compile/internal/types2/object.go
@@ -0,0 +1,492 @@
+// UNREVIEWED
+// Copyright 2013 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 types2
+
+import (
+ "bytes"
+ "cmd/compile/internal/syntax"
+ "fmt"
+ "go/constant"
+ "go/token"
+)
+
+// An Object describes a named language entity such as a package,
+// constant, type, variable, function (incl. methods), or label.
+// All objects implement the Object interface.
+//
+type Object interface {
+ Parent() *Scope // scope in which this object is declared; nil for methods and struct fields
+ Pos() syntax.Pos // position of object identifier in declaration
+ Pkg() *Package // package to which this object belongs; nil for labels and objects in the Universe scope
+ Name() string // package local object name
+ Type() Type // object type
+ Exported() bool // reports whether the name starts with a capital letter
+ Id() string // object name if exported, qualified name if not exported (see func Id)
+
+ // String returns a human-readable string of the object.
+ String() string
+
+ // order reflects a package-level object's source order: if object
+ // a is before object b in the source, then a.order() < b.order().
+ // order returns a value > 0 for package-level objects; it returns
+ // 0 for all other objects (including objects in file scopes).
+ order() uint32
+
+ // color returns the object's color.
+ color() color
+
+ // setType sets the type of the object.
+ setType(Type)
+
+ // setOrder sets the order number of the object. It must be > 0.
+ setOrder(uint32)
+
+ // setColor sets the object's color. It must not be white.
+ setColor(color color)
+
+ // setParent sets the parent scope of the object.
+ setParent(*Scope)
+
+ // sameId reports whether obj.Id() and Id(pkg, name) are the same.
+ sameId(pkg *Package, name string) bool
+
+ // scopePos returns the start position of the scope of this Object
+ scopePos() syntax.Pos
+
+ // setScopePos sets the start position of the scope for this Object.
+ setScopePos(pos syntax.Pos)
+}
+
+// Id returns name if it is exported, otherwise it
+// returns the name qualified with the package path.
+func Id(pkg *Package, name string) string {
+ if token.IsExported(name) {
+ return name
+ }
+ // unexported names need the package path for differentiation
+ // (if there's no package, make sure we don't start with '.'
+ // as that may change the order of methods between a setup
+ // inside a package and outside a package - which breaks some
+ // tests)
+ path := "_"
+ // pkg is nil for objects in Universe scope and possibly types
+ // introduced via Eval (see also comment in object.sameId)
+ if pkg != nil && pkg.path != "" {
+ path = pkg.path
+ }
+ return path + "." + name
+}
+
+// An object implements the common parts of an Object.
+type object struct {
+ parent *Scope
+ pos syntax.Pos
+ pkg *Package
+ name string
+ typ Type
+ order_ uint32
+ color_ color
+ scopePos_ syntax.Pos
+}
+
+// color encodes the color of an object (see Checker.objDecl for details).
+type color uint32
+
+// An object may be painted in one of three colors.
+// Color values other than white or black are considered grey.
+const (
+ white color = iota
+ black
+ grey // must be > white and black
+)
+
+func (c color) String() string {
+ switch c {
+ case white:
+ return "white"
+ case black:
+ return "black"
+ default:
+ return "grey"
+ }
+}
+
+// colorFor returns the (initial) color for an object depending on
+// whether its type t is known or not.
+func colorFor(t Type) color {
+ if t != nil {
+ return black
+ }
+ return white
+}
+
+// Parent returns the scope in which the object is declared.
+// The result is nil for methods and struct fields.
+func (obj *object) Parent() *Scope { return obj.parent }
+
+// Pos returns the declaration position of the object's identifier.
+func (obj *object) Pos() syntax.Pos { return obj.pos }
+
+// Pkg returns the package to which the object belongs.
+// The result is nil for labels and objects in the Universe scope.
+func (obj *object) Pkg() *Package { return obj.pkg }
+
+// Name returns the object's (package-local, unqualified) name.
+func (obj *object) Name() string { return obj.name }
+
+// Type returns the object's type.
+func (obj *object) Type() Type { return obj.typ }
+
+// Exported reports whether the object is exported (starts with a capital letter).
+// It doesn't take into account whether the object is in a local (function) scope
+// or not.
+func (obj *object) Exported() bool { return token.IsExported(obj.name) }
+
+// Id is a wrapper for Id(obj.Pkg(), obj.Name()).
+func (obj *object) Id() string { return Id(obj.pkg, obj.name) }
+
+func (obj *object) String() string { panic("abstract") }
+func (obj *object) order() uint32 { return obj.order_ }
+func (obj *object) color() color { return obj.color_ }
+func (obj *object) scopePos() syntax.Pos { return obj.scopePos_ }
+
+func (obj *object) setParent(parent *Scope) { obj.parent = parent }
+func (obj *object) setType(typ Type) { obj.typ = typ }
+func (obj *object) setOrder(order uint32) { assert(order > 0); obj.order_ = order }
+func (obj *object) setColor(color color) { assert(color != white); obj.color_ = color }
+func (obj *object) setScopePos(pos syntax.Pos) { obj.scopePos_ = pos }
+
+func (obj *object) sameId(pkg *Package, name string) bool {
+ // spec:
+ // "Two identifiers are different if they are spelled differently,
+ // or if they appear in different packages and are not exported.
+ // Otherwise, they are the same."
+ if name != obj.name {
+ return false
+ }
+ // obj.Name == name
+ if obj.Exported() {
+ return true
+ }
+ // not exported, so packages must be the same (pkg == nil for
+ // fields in Universe scope; this can only happen for types
+ // introduced via Eval)
+ if pkg == nil || obj.pkg == nil {
+ return pkg == obj.pkg
+ }
+ // pkg != nil && obj.pkg != nil
+ return pkg.path == obj.pkg.path
+}
+
+// A PkgName represents an imported Go package.
+// PkgNames don't have a type.
+type PkgName struct {
+ object
+ imported *Package
+ used bool // set if the package was used
+}
+
+// NewPkgName returns a new PkgName object representing an imported package.
+// The remaining arguments set the attributes found with all Objects.
+func NewPkgName(pos syntax.Pos, pkg *Package, name string, imported *Package) *PkgName {
+ return &PkgName{object{nil, pos, pkg, name, Typ[Invalid], 0, black, nopos}, imported, false}
+}
+
+// Imported returns the package that was imported.
+// It is distinct from Pkg(), which is the package containing the import statement.
+func (obj *PkgName) Imported() *Package { return obj.imported }
+
+// A Const represents a declared constant.
+type Const struct {
+ object
+ val constant.Value
+}
+
+// NewConst returns a new constant with value val.
+// The remaining arguments set the attributes found with all Objects.
+func NewConst(pos syntax.Pos, pkg *Package, name string, typ Type, val constant.Value) *Const {
+ return &Const{object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, val}
+}
+
+// Val returns the constant's value.
+func (obj *Const) Val() constant.Value { return obj.val }
+
+func (*Const) isDependency() {} // a constant may be a dependency of an initialization expression
+
+// A TypeName represents a name for a (defined or alias) type.
+type TypeName struct {
+ object
+}
+
+// NewTypeName returns a new type name denoting the given typ.
+// The remaining arguments set the attributes found with all Objects.
+//
+// The typ argument may be a defined (Named) type or an alias type.
+// It may also be nil such that the returned TypeName can be used as
+// argument for NewNamed, which will set the TypeName's type as a side-
+// effect.
+func NewTypeName(pos syntax.Pos, pkg *Package, name string, typ Type) *TypeName {
+ return &TypeName{object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}}
+}
+
+// IsAlias reports whether obj is an alias name for a type.
+func (obj *TypeName) IsAlias() bool {
+ switch t := obj.typ.(type) {
+ case nil:
+ return false
+ case *Basic:
+ // unsafe.Pointer is not an alias.
+ if obj.pkg == Unsafe {
+ return false
+ }
+ // Any user-defined type name for a basic type is an alias for a
+ // basic type (because basic types are pre-declared in the Universe
+ // scope, outside any package scope), and so is any type name with
+ // a different name than the name of the basic type it refers to.
+ // Additionally, we need to look for "byte" and "rune" because they
+ // are aliases but have the same names (for better error messages).
+ return obj.pkg != nil || t.name != obj.name || t == universeByte || t == universeRune
+ case *Named:
+ return obj != t.obj
+ default:
+ return true
+ }
+}
+
+// A Variable represents a declared variable (including function parameters and results, and struct fields).
+type Var struct {
+ object
+ embedded bool // if set, the variable is an embedded struct field, and name is the type name
+ isField bool // var is struct field
+ used bool // set if the variable was used
+}
+
+// NewVar returns a new variable.
+// The arguments set the attributes found with all Objects.
+func NewVar(pos syntax.Pos, pkg *Package, name string, typ Type) *Var {
+ return &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}}
+}
+
+// NewParam returns a new variable representing a function parameter.
+func NewParam(pos syntax.Pos, pkg *Package, name string, typ Type) *Var {
+ return &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, used: true} // parameters are always 'used'
+}
+
+// NewField returns a new variable representing a struct field.
+// For embedded fields, the name is the unqualified type name
+/// under which the field is accessible.
+func NewField(pos syntax.Pos, pkg *Package, name string, typ Type, embedded bool) *Var {
+ return &Var{object: object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, embedded: embedded, isField: true}
+}
+
+// Anonymous reports whether the variable is an embedded field.
+// Same as Embedded; only present for backward-compatibility.
+func (obj *Var) Anonymous() bool { return obj.embedded }
+
+// Embedded reports whether the variable is an embedded field.
+func (obj *Var) Embedded() bool { return obj.embedded }
+
+// IsField reports whether the variable is a struct field.
+func (obj *Var) IsField() bool { return obj.isField }
+
+func (*Var) isDependency() {} // a variable may be a dependency of an initialization expression
+
+// A Func represents a declared function, concrete method, or abstract
+// (interface) method. Its Type() is always a *Signature.
+// An abstract method may belong to many interfaces due to embedding.
+type Func struct {
+ object
+ hasPtrRecv bool // only valid for methods that don't have a type yet
+}
+
+// NewFunc returns a new function with the given signature, representing
+// the function's type.
+func NewFunc(pos syntax.Pos, pkg *Package, name string, sig *Signature) *Func {
+ // don't store a (typed) nil signature
+ var typ Type
+ if sig != nil {
+ typ = sig
+ }
+ return &Func{object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, false}
+}
+
+// FullName returns the package- or receiver-type-qualified name of
+// function or method obj.
+func (obj *Func) FullName() string {
+ var buf bytes.Buffer
+ writeFuncName(&buf, obj, nil)
+ return buf.String()
+}
+
+// Scope returns the scope of the function's body block.
+func (obj *Func) Scope() *Scope { return obj.typ.(*Signature).scope }
+
+func (*Func) isDependency() {} // a function may be a dependency of an initialization expression
+
+// A Label represents a declared label.
+// Labels don't have a type.
+type Label struct {
+ object
+ used bool // set if the label was used
+}
+
+// NewLabel returns a new label.
+func NewLabel(pos syntax.Pos, pkg *Package, name string) *Label {
+ return &Label{object{pos: pos, pkg: pkg, name: name, typ: Typ[Invalid], color_: black}, false}
+}
+
+// A Builtin represents a built-in function.
+// Builtins don't have a valid type.
+type Builtin struct {
+ object
+ id builtinId
+}
+
+func newBuiltin(id builtinId) *Builtin {
+ return &Builtin{object{name: predeclaredFuncs[id].name, typ: Typ[Invalid], color_: black}, id}
+}
+
+// Nil represents the predeclared value nil.
+type Nil struct {
+ object
+}
+
+func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
+ var tname *TypeName
+ typ := obj.Type()
+
+ switch obj := obj.(type) {
+ case *PkgName:
+ fmt.Fprintf(buf, "package %s", obj.Name())
+ if path := obj.imported.path; path != "" && path != obj.name {
+ fmt.Fprintf(buf, " (%q)", path)
+ }
+ return
+
+ case *Const:
+ buf.WriteString("const")
+
+ case *TypeName:
+ tname = obj
+ buf.WriteString("type")
+
+ case *Var:
+ if obj.isField {
+ buf.WriteString("field")
+ } else {
+ buf.WriteString("var")
+ }
+
+ case *Func:
+ buf.WriteString("func ")
+ writeFuncName(buf, obj, qf)
+ if typ != nil {
+ WriteSignature(buf, typ.(*Signature), qf)
+ }
+ return
+
+ case *Label:
+ buf.WriteString("label")
+ typ = nil
+
+ case *Builtin:
+ buf.WriteString("builtin")
+ typ = nil
+
+ case *Nil:
+ buf.WriteString("nil")
+ return
+
+ default:
+ panic(fmt.Sprintf("writeObject(%T)", obj))
+ }
+
+ buf.WriteByte(' ')
+
+ // For package-level objects, qualify the name.
+ if obj.Pkg() != nil && obj.Pkg().scope.Lookup(obj.Name()) == obj {
+ writePackage(buf, obj.Pkg(), qf)
+ }
+ buf.WriteString(obj.Name())
+
+ if typ == nil {
+ return
+ }
+
+ if tname != nil {
+ // We have a type object: Don't print anything more for
+ // basic types since there's no more information (names
+ // are the same; see also comment in TypeName.IsAlias).
+ if _, ok := typ.(*Basic); ok {
+ return
+ }
+ if tname.IsAlias() {
+ buf.WriteString(" =")
+ } else {
+ typ = typ.Under()
+ }
+ }
+
+ buf.WriteByte(' ')
+ WriteType(buf, typ, qf)
+}
+
+func writePackage(buf *bytes.Buffer, pkg *Package, qf Qualifier) {
+ if pkg == nil {
+ return
+ }
+ var s string
+ if qf != nil {
+ s = qf(pkg)
+ } else {
+ s = pkg.Path()
+ }
+ if s != "" {
+ buf.WriteString(s)
+ buf.WriteByte('.')
+ }
+}
+
+// ObjectString returns the string form of obj.
+// The Qualifier controls the printing of
+// package-level objects, and may be nil.
+func ObjectString(obj Object, qf Qualifier) string {
+ var buf bytes.Buffer
+ writeObject(&buf, obj, qf)
+ return buf.String()
+}
+
+func (obj *PkgName) String() string { return ObjectString(obj, nil) }
+func (obj *Const) String() string { return ObjectString(obj, nil) }
+func (obj *TypeName) String() string { return ObjectString(obj, nil) }
+func (obj *Var) String() string { return ObjectString(obj, nil) }
+func (obj *Func) String() string { return ObjectString(obj, nil) }
+func (obj *Label) String() string { return ObjectString(obj, nil) }
+func (obj *Builtin) String() string { return ObjectString(obj, nil) }
+func (obj *Nil) String() string { return ObjectString(obj, nil) }
+
+func writeFuncName(buf *bytes.Buffer, f *Func, qf Qualifier) {
+ if f.typ != nil {
+ sig := f.typ.(*Signature)
+ if recv := sig.Recv(); recv != nil {
+ buf.WriteByte('(')
+ if _, ok := recv.Type().(*Interface); ok {
+ // gcimporter creates abstract methods of
+ // named interfaces using the interface type
+ // (not the named type) as the receiver.
+ // Don't print it in full.
+ buf.WriteString("interface")
+ } else {
+ WriteType(buf, recv.Type(), qf)
+ }
+ buf.WriteByte(')')
+ buf.WriteByte('.')
+ } else if f.pkg != nil {
+ writePackage(buf, f.pkg, qf)
+ }
+ }
+ buf.WriteString(f.name)
+}
diff --git a/src/cmd/compile/internal/types2/object_test.go b/src/cmd/compile/internal/types2/object_test.go
new file mode 100644
index 0000000000..8f11c87451
--- /dev/null
+++ b/src/cmd/compile/internal/types2/object_test.go
@@ -0,0 +1,89 @@
+// UNREVIEWED
+// Copyright 2016 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 types2
+
+import (
+ "cmd/compile/internal/syntax"
+ "strings"
+ "testing"
+)
+
+func parseSrc(path, src string) (*syntax.File, error) {
+ return syntax.Parse(syntax.NewFileBase(path), strings.NewReader(src), nil, nil, 0)
+}
+
+func TestIsAlias(t *testing.T) {
+ check := func(obj *TypeName, want bool) {
+ if got := obj.IsAlias(); got != want {
+ t.Errorf("%v: got IsAlias = %v; want %v", obj, got, want)
+ }
+ }
+
+ // predeclared types
+ check(Unsafe.Scope().Lookup("Pointer").(*TypeName), false)
+ for _, name := range Universe.Names() {
+ if obj, _ := Universe.Lookup(name).(*TypeName); obj != nil {
+ check(obj, name == "byte" || name == "rune")
+ }
+ }
+
+ // various other types
+ pkg := NewPackage("p", "p")
+ t1 := NewTypeName(nopos, pkg, "t1", nil)
+ n1 := NewNamed(t1, new(Struct), nil)
+ for _, test := range []struct {
+ name *TypeName
+ alias bool
+ }{
+ {NewTypeName(nopos, nil, "t0", nil), false}, // no type yet
+ {NewTypeName(nopos, pkg, "t0", nil), false}, // no type yet
+ {t1, false}, // type name refers to named type and vice versa
+ {NewTypeName(nopos, nil, "t2", &emptyInterface), true}, // type name refers to unnamed type
+ {NewTypeName(nopos, pkg, "t3", n1), true}, // type name refers to named type with different type name
+ {NewTypeName(nopos, nil, "t4", Typ[Int32]), true}, // type name refers to basic type with different name
+ {NewTypeName(nopos, nil, "int32", Typ[Int32]), false}, // type name refers to basic type with same name
+ {NewTypeName(nopos, pkg, "int32", Typ[Int32]), true}, // type name is declared in user-defined package (outside Universe)
+ {NewTypeName(nopos, nil, "rune", Typ[Rune]), true}, // type name refers to basic type rune which is an alias already
+ } {
+ check(test.name, test.alias)
+ }
+}
+
+// TestEmbeddedMethod checks that an embedded method is represented by
+// the same Func Object as the original method. See also issue #34421.
+func TestEmbeddedMethod(t *testing.T) {
+ const src = `package p; type I interface { error }`
+
+ // type-check src
+ f, err := parseSrc("", src)
+ if err != nil {
+ t.Fatalf("parse failed: %s", err)
+ }
+ var conf Config
+ pkg, err := conf.Check(f.PkgName.Value, []*syntax.File{f}, nil)
+ if err != nil {
+ t.Fatalf("typecheck failed: %s", err)
+ }
+
+ // get original error.Error method
+ eface := Universe.Lookup("error")
+ orig, _, _ := LookupFieldOrMethod(eface.Type(), false, nil, "Error")
+ if orig == nil {
+ t.Fatalf("original error.Error not found")
+ }
+
+ // get embedded error.Error method
+ iface := pkg.Scope().Lookup("I")
+ embed, _, _ := LookupFieldOrMethod(iface.Type(), false, nil, "Error")
+ if embed == nil {
+ t.Fatalf("embedded error.Error not found")
+ }
+
+ // original and embedded Error object should be identical
+ if orig != embed {
+ t.Fatalf("%s (%p) != %s (%p)", orig, orig, embed, embed)
+ }
+}
diff --git a/src/cmd/compile/internal/types2/objset.go b/src/cmd/compile/internal/types2/objset.go
new file mode 100644
index 0000000000..ef06315705
--- /dev/null
+++ b/src/cmd/compile/internal/types2/objset.go
@@ -0,0 +1,32 @@
+// UNREVIEWED
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements objsets.
+//
+// An objset is similar to a Scope but objset elements
+// are identified by their unique id, instead of their
+// object name.
+
+package types2
+
+// An objset is a set of objects identified by their unique id.
+// The zero value for objset is a ready-to-use empty objset.
+type objset map[string]Object // initialized lazily
+
+// insert attempts to insert an object obj into objset s.
+// If s already contains an alternative object alt with
+// the same name, insert leaves s unchanged and returns alt.
+// Otherwise it inserts obj and returns nil.
+func (s *objset) insert(obj Object) Object {
+ id := obj.Id()
+ if alt := (*s)[id]; alt != nil {
+ return alt
+ }
+ if *s == nil {
+ *s = make(map[string]Object)
+ }
+ (*s)[id] = obj
+ return nil
+}
diff --git a/src/cmd/compile/internal/types2/operand.go b/src/cmd/compile/internal/types2/operand.go
new file mode 100644
index 0000000000..fe88921893
--- /dev/null
+++ b/src/cmd/compile/internal/types2/operand.go
@@ -0,0 +1,315 @@
+// UNREVIEWED
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file defines operands and associated operations.
+
+package types2
+
+import (
+ "bytes"
+ "cmd/compile/internal/syntax"
+ "fmt"
+ "go/constant"
+ "go/token"
+)
+
+// An operandMode specifies the (addressing) mode of an operand.
+type operandMode byte
+
+const (
+ invalid operandMode = iota // operand is invalid
+ novalue // operand represents no value (result of a function call w/o result)
+ builtin // operand is a built-in function
+ typexpr // operand is a type
+ constant_ // operand is a constant; the operand's typ is a Basic type
+ variable // operand is an addressable variable
+ mapindex // operand is a map index expression (acts like a variable on lhs, commaok on rhs of an assignment)
+ value // operand is a computed value
+ commaok // like value, but operand may be used in a comma,ok expression
+ commaerr // like commaok, but second value is error, not boolean
+ cgofunc // operand is a cgo function
+)
+
+var operandModeString = [...]string{
+ invalid: "invalid operand",
+ novalue: "no value",
+ builtin: "built-in",
+ typexpr: "type",
+ constant_: "constant",
+ variable: "variable",
+ mapindex: "map index expression",
+ value: "value",
+ commaok: "comma, ok expression",
+ commaerr: "comma, error expression",
+ cgofunc: "cgo function",
+}
+
+// An operand represents an intermediate value during type checking.
+// Operands have an (addressing) mode, the expression evaluating to
+// the operand, the operand's type, a value for constants, and an id
+// for built-in functions.
+// The zero value of operand is a ready to use invalid operand.
+//
+type operand struct {
+ mode operandMode
+ expr syntax.Expr
+ typ Type
+ val constant.Value
+ id builtinId
+}
+
+// Pos returns the position of the expression corresponding to x.
+// If x is invalid the position is nopos.
+//
+func (x *operand) Pos() syntax.Pos {
+ // x.expr may not be set if x is invalid
+ if x.expr == nil {
+ return nopos
+ }
+ return x.expr.Pos()
+}
+
+// Operand string formats
+// (not all "untyped" cases can appear due to the type system,
+// but they fall out naturally here)
+//
+// mode format
+//
+// invalid <expr> ( <mode> )
+// novalue <expr> ( <mode> )
+// builtin <expr> ( <mode> )
+// typexpr <expr> ( <mode> )
+//
+// constant <expr> (<untyped kind> <mode> )
+// constant <expr> ( <mode> of type <typ>)
+// constant <expr> (<untyped kind> <mode> <val> )
+// constant <expr> ( <mode> <val> of type <typ>)
+//
+// variable <expr> (<untyped kind> <mode> )
+// variable <expr> ( <mode> of type <typ>)
+//
+// mapindex <expr> (<untyped kind> <mode> )
+// mapindex <expr> ( <mode> of type <typ>)
+//
+// value <expr> (<untyped kind> <mode> )
+// value <expr> ( <mode> of type <typ>)
+//
+// commaok <expr> (<untyped kind> <mode> )
+// commaok <expr> ( <mode> of type <typ>)
+//
+// commaerr <expr> (<untyped kind> <mode> )
+// commaerr <expr> ( <mode> of type <typ>)
+//
+// cgofunc <expr> (<untyped kind> <mode> )
+// cgofunc <expr> ( <mode> of type <typ>)
+//
+func operandString(x *operand, qf Qualifier) string {
+ var buf bytes.Buffer
+
+ var expr string
+ if x.expr != nil {
+ expr = ExprString(x.expr)
+ } else {
+ switch x.mode {
+ case builtin:
+ expr = predeclaredFuncs[x.id].name
+ case typexpr:
+ expr = TypeString(x.typ, qf)
+ case constant_:
+ expr = x.val.String()
+ }
+ }
+
+ // <expr> (
+ if expr != "" {
+ buf.WriteString(expr)
+ buf.WriteString(" (")
+ }
+
+ // <untyped kind>
+ hasType := false
+ switch x.mode {
+ case invalid, novalue, builtin, typexpr:
+ // no type
+ default:
+ // should have a type, but be cautious (don't crash during printing)
+ if x.typ != nil {
+ if isUntyped(x.typ) {
+ buf.WriteString(x.typ.(*Basic).name)
+ buf.WriteByte(' ')
+ break
+ }
+ hasType = true
+ }
+ }
+
+ // <mode>
+ buf.WriteString(operandModeString[x.mode])
+
+ // <val>
+ if x.mode == constant_ {
+ if s := x.val.String(); s != expr {
+ buf.WriteByte(' ')
+ buf.WriteString(s)
+ }
+ }
+
+ // <typ>
+ if hasType {
+ if x.typ != Typ[Invalid] {
+ var intro string
+ switch {
+ case isGeneric(x.typ):
+ intro = " of generic type "
+ case x.typ.TypeParam() != nil:
+ intro = " of type parameter type "
+ default:
+ intro = " of type "
+ }
+ buf.WriteString(intro)
+ WriteType(&buf, x.typ, qf)
+ } else {
+ buf.WriteString(" with invalid type")
+ }
+ }
+
+ // )
+ if expr != "" {
+ buf.WriteByte(')')
+ }
+
+ return buf.String()
+}
+
+func (x *operand) String() string {
+ return operandString(x, nil)
+}
+
+// setConst sets x to the untyped constant for literal lit.
+func (x *operand) setConst(k syntax.LitKind, lit string) {
+ var tok token.Token
+ var kind BasicKind
+ switch k {
+ case syntax.IntLit:
+ tok = token.INT
+ kind = UntypedInt
+ case syntax.FloatLit:
+ tok = token.FLOAT
+ kind = UntypedFloat
+ case syntax.ImagLit:
+ tok = token.IMAG
+ kind = UntypedComplex
+ case syntax.RuneLit:
+ tok = token.CHAR
+ kind = UntypedRune
+ case syntax.StringLit:
+ tok = token.STRING
+ kind = UntypedString
+ default:
+ unreachable()
+ }
+
+ x.mode = constant_
+ x.typ = Typ[kind]
+ x.val = constant.MakeFromLiteral(lit, tok, 0)
+}
+
+// isNil reports whether x is the nil value.
+func (x *operand) isNil() bool {
+ return x.mode == value && x.typ == Typ[UntypedNil]
+}
+
+// TODO(gri) The functions operand.assignableTo, checker.convertUntyped,
+// checker.representable, and checker.assignment are
+// overlapping in functionality. Need to simplify and clean up.
+
+// assignableTo reports whether x is assignable to a variable of type T.
+// If the result is false and a non-nil reason is provided, it may be set
+// to a more detailed explanation of the failure (result != "").
+// The check parameter may be nil if assignableTo is invoked through
+// an exported API call, i.e., when all methods have been type-checked.
+func (x *operand) assignableTo(check *Checker, T Type, reason *string) bool {
+ if x.mode == invalid || T == Typ[Invalid] {
+ return true // avoid spurious errors
+ }
+
+ V := x.typ
+
+ // x's type is identical to T
+ if check.identical(V, T) {
+ return true
+ }
+
+ Vu := optype(V.Under())
+ Tu := optype(T.Under())
+
+ // x is an untyped value representable by a value of type T
+ // TODO(gri) This is borrowing from checker.convertUntyped and
+ // checker.representable. Need to clean up.
+ if isUntyped(Vu) {
+ switch t := Tu.(type) {
+ case *Basic:
+ if x.isNil() && t.kind == UnsafePointer {
+ return true
+ }
+ if x.mode == constant_ {
+ return representableConst(x.val, check, t, nil)
+ }
+ // The result of a comparison is an untyped boolean,
+ // but may not be a constant.
+ if Vb, _ := Vu.(*Basic); Vb != nil {
+ return Vb.kind == UntypedBool && isBoolean(Tu)
+ }
+ case *Sum:
+ return t.is(func(t Type) bool {
+ // TODO(gri) this could probably be more efficient
+ return x.assignableTo(check, t, reason)
+ })
+ case *Interface:
+ check.completeInterface(nopos, t)
+ return x.isNil() || t.Empty()
+ case *Pointer, *Signature, *Slice, *Map, *Chan:
+ return x.isNil()
+ }
+ }
+ // Vu is typed
+
+ // x's type V and T have identical underlying types
+ // and at least one of V or T is not a named type
+ if check.identical(Vu, Tu) && (!isNamed(V) || !isNamed(T)) {
+ return true
+ }
+
+ // T is an interface type and x implements T
+ if Ti, ok := Tu.(*Interface); ok {
+ if m, wrongType := check.missingMethod(V, Ti, true); m != nil /* Implements(V, Ti) */ {
+ if reason != nil {
+ if wrongType != nil {
+ if check.identical(m.typ, wrongType.typ) {
+ *reason = fmt.Sprintf("missing method %s (%s has pointer receiver)", m.name, m.name)
+ } else {
+ *reason = fmt.Sprintf("wrong type for method %s (have %s, want %s)", m.Name(), wrongType.typ, m.typ)
+ }
+
+ } else {
+ *reason = "missing method " + m.Name()
+ }
+ }
+ return false
+ }
+ return true
+ }
+
+ // x is a bidirectional channel value, T is a channel
+ // type, x's type V and T have identical element types,
+ // and at least one of V or T is not a named type
+ if Vc, ok := Vu.(*Chan); ok && Vc.dir == SendRecv {
+ if Tc, ok := Tu.(*Chan); ok && check.identical(Vc.elem, Tc.elem) {
+ return !isNamed(V) || !isNamed(T)
+ }
+ }
+
+ return false
+}
diff --git a/src/cmd/compile/internal/types2/package.go b/src/cmd/compile/internal/types2/package.go
new file mode 100644
index 0000000000..03ae6ff5b7
--- /dev/null
+++ b/src/cmd/compile/internal/types2/package.go
@@ -0,0 +1,65 @@
+// UNREVIEWED
+// Copyright 2013 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 types2
+
+import (
+ "fmt"
+)
+
+// A Package describes a Go package.
+type Package struct {
+ path string
+ name string
+ scope *Scope
+ complete bool
+ imports []*Package
+ fake bool // scope lookup errors are silently dropped if package is fake (internal use only)
+ cgo bool // uses of this package will be rewritten into uses of declarations from _cgo_gotypes.go
+}
+
+// NewPackage returns a new Package for the given package path and name.
+// The package is not complete and contains no explicit imports.
+func NewPackage(path, name string) *Package {
+ scope := NewScope(Universe, nopos, nopos, fmt.Sprintf("package %q", path))
+ return &Package{path: path, name: name, scope: scope}
+}
+
+// Path returns the package path.
+func (pkg *Package) Path() string { return pkg.path }
+
+// Name returns the package name.
+func (pkg *Package) Name() string { return pkg.name }
+
+// SetName sets the package name.
+func (pkg *Package) SetName(name string) { pkg.name = name }
+
+// Scope returns the (complete or incomplete) package scope
+// holding the objects declared at package level (TypeNames,
+// Consts, Vars, and Funcs).
+func (pkg *Package) Scope() *Scope { return pkg.scope }
+
+// A package is complete if its scope contains (at least) all
+// exported objects; otherwise it is incomplete.
+func (pkg *Package) Complete() bool { return pkg.complete }
+
+// MarkComplete marks a package as complete.
+func (pkg *Package) MarkComplete() { pkg.complete = true }
+
+// Imports returns the list of packages directly imported by
+// pkg; the list is in source order.
+//
+// If pkg was loaded from export data, Imports includes packages that
+// provide package-level objects referenced by pkg. This may be more or
+// less than the set of packages directly imported by pkg's source code.
+func (pkg *Package) Imports() []*Package { return pkg.imports }
+
+// SetImports sets the list of explicitly imported packages to list.
+// It is the caller's responsibility to make sure list elements are unique.
+func (pkg *Package) SetImports(list []*Package) { pkg.imports = list }
+
+func (pkg *Package) String() string {
+ return fmt.Sprintf("package %s (%q)", pkg.name, pkg.path)
+}
diff --git a/src/cmd/compile/internal/types2/pos.go b/src/cmd/compile/internal/types2/pos.go
new file mode 100644
index 0000000000..4dd839b7dc
--- /dev/null
+++ b/src/cmd/compile/internal/types2/pos.go
@@ -0,0 +1,364 @@
+// UNREVIEWED
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements helper functions for scope position computations.
+
+package types2
+
+import "cmd/compile/internal/syntax"
+
+// startPos returns the start position of n.
+func startPos(n syntax.Node) syntax.Pos {
+ // Cases for nodes which don't need a correction are commented out.
+ for m := n; ; {
+ switch n := m.(type) {
+ case nil:
+ panic("internal error: nil")
+
+ // packages
+ case *syntax.File:
+ // file block starts at the beginning of the file
+ return syntax.MakePos(n.Pos().Base(), 1, 1)
+
+ // declarations
+ // case *syntax.ImportDecl:
+ // case *syntax.ConstDecl:
+ // case *syntax.TypeDecl:
+ // case *syntax.VarDecl:
+ // case *syntax.FuncDecl:
+
+ // expressions
+ // case *syntax.BadExpr:
+ // case *syntax.Name:
+ // case *syntax.BasicLit:
+ case *syntax.CompositeLit:
+ if n.Type != nil {
+ m = n.Type
+ continue
+ }
+ return n.Pos()
+ // case *syntax.KeyValueExpr:
+ // case *syntax.FuncLit:
+ // case *syntax.ParenExpr:
+ case *syntax.SelectorExpr:
+ m = n.X
+ case *syntax.IndexExpr:
+ m = n.X
+ // case *syntax.SliceExpr:
+ case *syntax.AssertExpr:
+ m = n.X
+ case *syntax.TypeSwitchGuard:
+ if n.Lhs != nil {
+ m = n.Lhs
+ continue
+ }
+ m = n.X
+ case *syntax.Operation:
+ if n.Y != nil {
+ m = n.X
+ continue
+ }
+ return n.Pos()
+ case *syntax.CallExpr:
+ m = n.Fun
+ case *syntax.ListExpr:
+ if len(n.ElemList) > 0 {
+ m = n.ElemList[0]
+ continue
+ }
+ return n.Pos()
+ // types
+ // case *syntax.ArrayType:
+ // case *syntax.SliceType:
+ // case *syntax.DotsType:
+ // case *syntax.StructType:
+ // case *syntax.Field:
+ // case *syntax.InterfaceType:
+ // case *syntax.FuncType:
+ // case *syntax.MapType:
+ // case *syntax.ChanType:
+
+ // statements
+ // case *syntax.EmptyStmt:
+ // case *syntax.LabeledStmt:
+ // case *syntax.BlockStmt:
+ // case *syntax.ExprStmt:
+ case *syntax.SendStmt:
+ m = n.Chan
+ // case *syntax.DeclStmt:
+ case *syntax.AssignStmt:
+ m = n.Lhs
+ // case *syntax.BranchStmt:
+ // case *syntax.CallStmt:
+ // case *syntax.ReturnStmt:
+ // case *syntax.IfStmt:
+ // case *syntax.ForStmt:
+ // case *syntax.SwitchStmt:
+ // case *syntax.SelectStmt:
+
+ // helper nodes
+ case *syntax.RangeClause:
+ if n.Lhs != nil {
+ m = n.Lhs
+ continue
+ }
+ m = n.X
+ // case *syntax.CaseClause:
+ // case *syntax.CommClause:
+
+ default:
+ return n.Pos()
+ }
+ }
+}
+
+// endPos returns the approximate end position of n in the source.
+// For some nodes (*syntax.Name, *syntax.BasicLit) it returns
+// the position immediately following the node; for others
+// (*syntax.BlockStmt, *syntax.SwitchStmt, etc.) it returns
+// the position of the closing '}'; and for some (*syntax.ParenExpr)
+// the returned position is the end position of the last enclosed
+// expression.
+// Thus, endPos should not be used for exact demarcation of the
+// end of a node in the source; it is mostly useful to determine
+// scope ranges where there is some leeway.
+func endPos(n syntax.Node) syntax.Pos {
+ for m := n; ; {
+ switch n := m.(type) {
+ case nil:
+ panic("internal error: nil")
+
+ // packages
+ case *syntax.File:
+ return n.EOF
+
+ // declarations
+ case *syntax.ImportDecl:
+ m = n.Path
+ case *syntax.ConstDecl:
+ if n.Values != nil {
+ m = n.Values
+ continue
+ }
+ if n.Type != nil {
+ m = n.Type
+ continue
+ }
+ if l := len(n.NameList); l > 0 {
+ m = n.NameList[l-1]
+ continue
+ }
+ return n.Pos()
+ case *syntax.TypeDecl:
+ m = n.Type
+ case *syntax.VarDecl:
+ if n.Values != nil {
+ m = n.Values
+ continue
+ }
+ if n.Type != nil {
+ m = n.Type
+ continue
+ }
+ if l := len(n.NameList); l > 0 {
+ m = n.NameList[l-1]
+ continue
+ }
+ return n.Pos()
+ case *syntax.FuncDecl:
+ if n.Body != nil {
+ m = n.Body
+ continue
+ }
+ m = n.Type
+
+ // expressions
+ case *syntax.BadExpr:
+ return n.Pos()
+ case *syntax.Name:
+ p := n.Pos()
+ return syntax.MakePos(p.Base(), p.Line(), p.Col()+uint(len(n.Value)))
+ case *syntax.BasicLit:
+ p := n.Pos()
+ return syntax.MakePos(p.Base(), p.Line(), p.Col()+uint(len(n.Value)))
+ case *syntax.CompositeLit:
+ return n.Rbrace
+ case *syntax.KeyValueExpr:
+ m = n.Value
+ case *syntax.FuncLit:
+ m = n.Body
+ case *syntax.ParenExpr:
+ m = n.X
+ case *syntax.SelectorExpr:
+ m = n.Sel
+ case *syntax.IndexExpr:
+ m = n.Index
+ case *syntax.SliceExpr:
+ for i := len(n.Index) - 1; i >= 0; i-- {
+ if x := n.Index[i]; x != nil {
+ m = x
+ continue
+ }
+ }
+ m = n.X
+ case *syntax.AssertExpr:
+ m = n.Type
+ case *syntax.TypeSwitchGuard:
+ m = n.X
+ case *syntax.Operation:
+ if n.Y != nil {
+ m = n.Y
+ continue
+ }
+ m = n.X
+ case *syntax.CallExpr:
+ if l := lastExpr(n.ArgList); l != nil {
+ m = l
+ continue
+ }
+ m = n.Fun
+ case *syntax.ListExpr:
+ if l := lastExpr(n.ElemList); l != nil {
+ m = l
+ continue
+ }
+ return n.Pos()
+
+ // types
+ case *syntax.ArrayType:
+ m = n.Elem
+ case *syntax.SliceType:
+ m = n.Elem
+ case *syntax.DotsType:
+ m = n.Elem
+ case *syntax.StructType:
+ if l := lastField(n.FieldList); l != nil {
+ m = l
+ continue
+ }
+ return n.Pos()
+ // TODO(gri) need to take TagList into account
+ case *syntax.Field:
+ if n.Type != nil {
+ m = n.Type
+ continue
+ }
+ m = n.Name
+ case *syntax.InterfaceType:
+ if l := lastField(n.MethodList); l != nil {
+ m = l
+ continue
+ }
+ return n.Pos()
+ case *syntax.FuncType:
+ if l := lastField(n.ResultList); l != nil {
+ m = l
+ continue
+ }
+ if l := lastField(n.ParamList); l != nil {
+ m = l
+ continue
+ }
+ return n.Pos()
+ case *syntax.MapType:
+ m = n.Value
+ case *syntax.ChanType:
+ m = n.Elem
+
+ // statements
+ case *syntax.EmptyStmt:
+ return n.Pos()
+ case *syntax.LabeledStmt:
+ m = n.Stmt
+ case *syntax.BlockStmt:
+ return n.Rbrace
+ case *syntax.ExprStmt:
+ m = n.X
+ case *syntax.SendStmt:
+ m = n.Value
+ case *syntax.DeclStmt:
+ if l := lastDecl(n.DeclList); l != nil {
+ m = l
+ continue
+ }
+ return n.Pos()
+ case *syntax.AssignStmt:
+ m = n.Rhs
+ case *syntax.BranchStmt:
+ if n.Label != nil {
+ m = n.Label
+ continue
+ }
+ return n.Pos()
+ case *syntax.CallStmt:
+ m = n.Call
+ case *syntax.ReturnStmt:
+ if n.Results != nil {
+ m = n.Results
+ continue
+ }
+ return n.Pos()
+ case *syntax.IfStmt:
+ if n.Else != nil {
+ m = n.Else
+ continue
+ }
+ m = n.Then
+ case *syntax.ForStmt:
+ m = n.Body
+ case *syntax.SwitchStmt:
+ return n.Rbrace
+ case *syntax.SelectStmt:
+ return n.Rbrace
+
+ // helper nodes
+ case *syntax.RangeClause:
+ m = n.X
+ case *syntax.CaseClause:
+ if l := lastStmt(n.Body); l != nil {
+ m = l
+ continue
+ }
+ return n.Colon
+ case *syntax.CommClause:
+ if l := lastStmt(n.Body); l != nil {
+ m = l
+ continue
+ }
+ return n.Colon
+
+ default:
+ return n.Pos()
+ }
+ }
+}
+
+func lastDecl(list []syntax.Decl) syntax.Decl {
+ if l := len(list); l > 0 {
+ return list[l-1]
+ }
+ return nil
+}
+
+func lastExpr(list []syntax.Expr) syntax.Expr {
+ if l := len(list); l > 0 {
+ return list[l-1]
+ }
+ return nil
+}
+
+func lastStmt(list []syntax.Stmt) syntax.Stmt {
+ if l := len(list); l > 0 {
+ return list[l-1]
+ }
+ return nil
+}
+
+func lastField(list []*syntax.Field) *syntax.Field {
+ if l := len(list); l > 0 {
+ return list[l-1]
+ }
+ return nil
+}
diff --git a/src/cmd/compile/internal/types2/predicates.go b/src/cmd/compile/internal/types2/predicates.go
new file mode 100644
index 0000000000..f3a5818b3f
--- /dev/null
+++ b/src/cmd/compile/internal/types2/predicates.go
@@ -0,0 +1,413 @@
+// UNREVIEWED
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements commonly used type predicates.
+
+package types2
+
+import (
+ "sort"
+)
+
+// isNamed reports whether typ has a name.
+// isNamed may be called with types that are not fully set up.
+func isNamed(typ Type) bool {
+ switch typ.(type) {
+ case *Basic, *Named, *TypeParam, *instance:
+ return true
+ }
+ return false
+}
+
+// isGeneric reports whether a type is a generic, uninstantiated type (generic signatures are not included).
+func isGeneric(typ Type) bool {
+ // A parameterized type is only instantiated if it doesn't have an instantiation already.
+ named, _ := typ.(*Named)
+ return named != nil && named.obj != nil && named.tparams != nil && named.targs == nil
+}
+
+func is(typ Type, what BasicInfo) bool {
+ switch t := optype(typ.Under()).(type) {
+ case *Basic:
+ return t.info&what != 0
+ case *Sum:
+ return t.is(func(typ Type) bool { return is(typ, what) })
+ }
+ return false
+}
+
+func isBoolean(typ Type) bool { return is(typ, IsBoolean) }
+func isInteger(typ Type) bool { return is(typ, IsInteger) }
+func isUnsigned(typ Type) bool { return is(typ, IsUnsigned) }
+func isFloat(typ Type) bool { return is(typ, IsFloat) }
+func isComplex(typ Type) bool { return is(typ, IsComplex) }
+func isNumeric(typ Type) bool { return is(typ, IsNumeric) }
+func isString(typ Type) bool { return is(typ, IsString) }
+
+// Note that if typ is a type parameter, isInteger(typ) || isFloat(typ) does not
+// produce the expected result because a type list that contains both an integer
+// and a floating-point type is neither (all) integers, nor (all) floats.
+// Use isIntegerOrFloat instead.
+func isIntegerOrFloat(typ Type) bool { return is(typ, IsInteger|IsFloat) }
+
+// isNumericOrString is the equivalent of isIntegerOrFloat for isNumeric(typ) || isString(typ).
+func isNumericOrString(typ Type) bool { return is(typ, IsNumeric|IsString) }
+
+// isTyped reports whether typ is typed; i.e., not an untyped
+// constant or boolean. isTyped may be called with types that
+// are not fully set up.
+func isTyped(typ Type) bool {
+ // isTyped is called with types that are not fully
+ // set up. Must not call Basic()!
+ // A *Named or *instance type is always typed, so
+ // we only need to check if we have a true *Basic
+ // type.
+ t, _ := typ.(*Basic)
+ return t == nil || t.info&IsUntyped == 0
+}
+
+// isUntyped(typ) is the same as !isTyped(typ).
+func isUntyped(typ Type) bool {
+ return !isTyped(typ)
+}
+
+func isOrdered(typ Type) bool { return is(typ, IsOrdered) }
+
+func isConstType(typ Type) bool {
+ t := typ.Basic()
+ return t != nil && t.info&IsConstType != 0
+}
+
+// IsInterface reports whether typ is an interface type.
+func IsInterface(typ Type) bool {
+ return typ.Interface() != nil
+}
+
+// Comparable reports whether values of type T are comparable.
+func Comparable(T Type) bool {
+ // If T is a type parameter not constraint by any type
+ // list (i.e., it's underlying type is the top type),
+ // T is comparable if it has the == method. Otherwise,
+ // the underlying type "wins". For instance
+ //
+ // interface{ comparable; type []byte }
+ //
+ // is not comparable because []byte is not comparable.
+ if t := T.TypeParam(); t != nil && optype(t) == theTop {
+ return t.Bound().IsComparable()
+ }
+
+ switch t := optype(T.Under()).(type) {
+ case *Basic:
+ // assume invalid types to be comparable
+ // to avoid follow-up errors
+ return t.kind != UntypedNil
+ case *Pointer, *Interface, *Chan:
+ return true
+ case *Struct:
+ for _, f := range t.fields {
+ if !Comparable(f.typ) {
+ return false
+ }
+ }
+ return true
+ case *Array:
+ return Comparable(t.elem)
+ case *Sum:
+ return t.is(Comparable)
+ case *TypeParam:
+ return t.Bound().IsComparable()
+ }
+ return false
+}
+
+// hasNil reports whether a type includes the nil value.
+func hasNil(typ Type) bool {
+ switch t := optype(typ.Under()).(type) {
+ case *Basic:
+ return t.kind == UnsafePointer
+ case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan:
+ return true
+ case *Sum:
+ return t.is(hasNil)
+ }
+ return false
+}
+
+// identical reports whether x and y are identical types.
+// Receivers of Signature types are ignored.
+func (check *Checker) identical(x, y Type) bool {
+ return check.identical0(x, y, true, nil)
+}
+
+// identicalIgnoreTags reports whether x and y are identical types if tags are ignored.
+// Receivers of Signature types are ignored.
+func (check *Checker) identicalIgnoreTags(x, y Type) bool {
+ return check.identical0(x, y, false, nil)
+}
+
+// An ifacePair is a node in a stack of interface type pairs compared for identity.
+type ifacePair struct {
+ x, y *Interface
+ prev *ifacePair
+}
+
+func (p *ifacePair) identical(q *ifacePair) bool {
+ return p.x == q.x && p.y == q.y || p.x == q.y && p.y == q.x
+}
+
+// For changes to this code the corresponding changes should be made to unifier.nify.
+func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
+ // types must be expanded for comparison
+ x = expandf(x)
+ y = expandf(y)
+
+ if x == y {
+ return true
+ }
+
+ switch x := x.(type) {
+ case *Basic:
+ // Basic types are singletons except for the rune and byte
+ // aliases, thus we cannot solely rely on the x == y check
+ // above. See also comment in TypeName.IsAlias.
+ if y, ok := y.(*Basic); ok {
+ return x.kind == y.kind
+ }
+
+ case *Array:
+ // Two array types are identical if they have identical element types
+ // and the same array length.
+ if y, ok := y.(*Array); ok {
+ // If one or both array lengths are unknown (< 0) due to some error,
+ // assume they are the same to avoid spurious follow-on errors.
+ return (x.len < 0 || y.len < 0 || x.len == y.len) && check.identical0(x.elem, y.elem, cmpTags, p)
+ }
+
+ case *Slice:
+ // Two slice types are identical if they have identical element types.
+ if y, ok := y.(*Slice); ok {
+ return check.identical0(x.elem, y.elem, cmpTags, p)
+ }
+
+ case *Struct:
+ // Two struct types are identical if they have the same sequence of fields,
+ // and if corresponding fields have the same names, and identical types,
+ // and identical tags. Two embedded fields are considered to have the same
+ // name. Lower-case field names from different packages are always different.
+ if y, ok := y.(*Struct); ok {
+ if x.NumFields() == y.NumFields() {
+ for i, f := range x.fields {
+ g := y.fields[i]
+ if f.embedded != g.embedded ||
+ cmpTags && x.Tag(i) != y.Tag(i) ||
+ !f.sameId(g.pkg, g.name) ||
+ !check.identical0(f.typ, g.typ, cmpTags, p) {
+ return false
+ }
+ }
+ return true
+ }
+ }
+
+ case *Pointer:
+ // Two pointer types are identical if they have identical base types.
+ if y, ok := y.(*Pointer); ok {
+ return check.identical0(x.base, y.base, cmpTags, p)
+ }
+
+ case *Tuple:
+ // Two tuples types are identical if they have the same number of elements
+ // and corresponding elements have identical types.
+ if y, ok := y.(*Tuple); ok {
+ if x.Len() == y.Len() {
+ if x != nil {
+ for i, v := range x.vars {
+ w := y.vars[i]
+ if !check.identical0(v.typ, w.typ, cmpTags, p) {
+ return false
+ }
+ }
+ }
+ return true
+ }
+ }
+
+ case *Signature:
+ // Two function types are identical if they have the same number of parameters
+ // and result values, corresponding parameter and result types are identical,
+ // and either both functions are variadic or neither is. Parameter and result
+ // names are not required to match.
+ // Generic functions must also have matching type parameter lists, but for the
+ // parameter names.
+ if y, ok := y.(*Signature); ok {
+ return x.variadic == y.variadic &&
+ check.identicalTParams(x.tparams, y.tparams, cmpTags, p) &&
+ check.identical0(x.params, y.params, cmpTags, p) &&
+ check.identical0(x.results, y.results, cmpTags, p)
+ }
+
+ case *Sum:
+ // Two sum types are identical if they contain the same types.
+ // (Sum types always consist of at least two types. Also, the
+ // the set (list) of types in a sum type consists of unique
+ // types - each type appears exactly once. Thus, two sum types
+ // must contain the same number of types to have chance of
+ // being equal.
+ if y, ok := y.(*Sum); ok && len(x.types) == len(y.types) {
+ // Every type in x.types must be in y.types.
+ // Quadratic algorithm, but probably good enough for now.
+ // TODO(gri) we need a fast quick type ID/hash for all types.
+ L:
+ for _, x := range x.types {
+ for _, y := range y.types {
+ if Identical(x, y) {
+ continue L // x is in y.types
+ }
+ }
+ return false // x is not in y.types
+ }
+ return true
+ }
+
+ case *Interface:
+ // Two interface types are identical if they have the same set of methods with
+ // the same names and identical function types. Lower-case method names from
+ // different packages are always different. The order of the methods is irrelevant.
+ if y, ok := y.(*Interface); ok {
+ // If identical0 is called (indirectly) via an external API entry point
+ // (such as Identical, IdenticalIgnoreTags, etc.), check is nil. But in
+ // that case, interfaces are expected to be complete and lazy completion
+ // here is not needed.
+ if check != nil {
+ check.completeInterface(nopos, x)
+ check.completeInterface(nopos, y)
+ }
+ a := x.allMethods
+ b := y.allMethods
+ if len(a) == len(b) {
+ // Interface types are the only types where cycles can occur
+ // that are not "terminated" via named types; and such cycles
+ // can only be created via method parameter types that are
+ // anonymous interfaces (directly or indirectly) embedding
+ // the current interface. Example:
+ //
+ // type T interface {
+ // m() interface{T}
+ // }
+ //
+ // If two such (differently named) interfaces are compared,
+ // endless recursion occurs if the cycle is not detected.
+ //
+ // If x and y were compared before, they must be equal
+ // (if they were not, the recursion would have stopped);
+ // search the ifacePair stack for the same pair.
+ //
+ // This is a quadratic algorithm, but in practice these stacks
+ // are extremely short (bounded by the nesting depth of interface
+ // type declarations that recur via parameter types, an extremely
+ // rare occurrence). An alternative implementation might use a
+ // "visited" map, but that is probably less efficient overall.
+ q := &ifacePair{x, y, p}
+ for p != nil {
+ if p.identical(q) {
+ return true // same pair was compared before
+ }
+ p = p.prev
+ }
+ if debug {
+ assert(sort.IsSorted(byUniqueMethodName(a)))
+ assert(sort.IsSorted(byUniqueMethodName(b)))
+ }
+ for i, f := range a {
+ g := b[i]
+ if f.Id() != g.Id() || !check.identical0(f.typ, g.typ, cmpTags, q) {
+ return false
+ }
+ }
+ return true
+ }
+ }
+
+ case *Map:
+ // Two map types are identical if they have identical key and value types.
+ if y, ok := y.(*Map); ok {
+ return check.identical0(x.key, y.key, cmpTags, p) && check.identical0(x.elem, y.elem, cmpTags, p)
+ }
+
+ case *Chan:
+ // Two channel types are identical if they have identical value types
+ // and the same direction.
+ if y, ok := y.(*Chan); ok {
+ return x.dir == y.dir && check.identical0(x.elem, y.elem, cmpTags, p)
+ }
+
+ case *Named:
+ // Two named types are identical if their type names originate
+ // in the same type declaration.
+ if y, ok := y.(*Named); ok {
+ // TODO(gri) Why is x == y not sufficient? And if it is,
+ // we can just return false here because x == y
+ // is caught in the very beginning of this function.
+ return x.obj == y.obj
+ }
+
+ case *TypeParam:
+ // nothing to do (x and y being equal is caught in the very beginning of this function)
+
+ // case *instance:
+ // unreachable since types are expanded
+
+ case *bottom, *top:
+ // Either both types are theBottom, or both are theTop in which
+ // case the initial x == y check will have caught them. Otherwise
+ // they are not identical.
+
+ case nil:
+ // avoid a crash in case of nil type
+
+ default:
+ unreachable()
+ }
+
+ return false
+}
+
+func (check *Checker) identicalTParams(x, y []*TypeName, cmpTags bool, p *ifacePair) bool {
+ if len(x) != len(y) {
+ return false
+ }
+ for i, x := range x {
+ y := y[i]
+ if !check.identical0(x.typ.(*TypeParam).bound, y.typ.(*TypeParam).bound, cmpTags, p) {
+ return false
+ }
+ }
+ return true
+}
+
+// Default returns the default "typed" type for an "untyped" type;
+// it returns the incoming type for all other types. The default type
+// for untyped nil is untyped nil.
+//
+func Default(typ Type) Type {
+ if t, ok := typ.(*Basic); ok {
+ switch t.kind {
+ case UntypedBool:
+ return Typ[Bool]
+ case UntypedInt:
+ return Typ[Int]
+ case UntypedRune:
+ return universeRune // use 'rune' name
+ case UntypedFloat:
+ return Typ[Float64]
+ case UntypedComplex:
+ return Typ[Complex128]
+ case UntypedString:
+ return Typ[String]
+ }
+ }
+ return typ
+}
diff --git a/src/cmd/compile/internal/types2/resolver.go b/src/cmd/compile/internal/types2/resolver.go
new file mode 100644
index 0000000000..b116888bf2
--- /dev/null
+++ b/src/cmd/compile/internal/types2/resolver.go
@@ -0,0 +1,714 @@
+// UNREVIEWED
+// Copyright 2013 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 types2
+
+import (
+ "cmd/compile/internal/syntax"
+ "fmt"
+ "go/constant"
+ "sort"
+ "strconv"
+ "strings"
+ "unicode"
+)
+
+// A declInfo describes a package-level const, type, var, or func declaration.
+type declInfo struct {
+ file *Scope // scope of file containing this declaration
+ lhs []*Var // lhs of n:1 variable declarations, or nil
+ vtyp syntax.Expr // type, or nil (for const and var declarations only)
+ init syntax.Expr // init/orig expression, or nil (for const and var declarations only)
+ tdecl *syntax.TypeDecl // type declaration, or nil
+ fdecl *syntax.FuncDecl // func declaration, or nil
+
+ // The deps field tracks initialization expression dependencies.
+ deps map[Object]bool // lazily initialized
+}
+
+// hasInitializer reports whether the declared object has an initialization
+// expression or function body.
+func (d *declInfo) hasInitializer() bool {
+ return d.init != nil || d.fdecl != nil && d.fdecl.Body != nil
+}
+
+// addDep adds obj to the set of objects d's init expression depends on.
+func (d *declInfo) addDep(obj Object) {
+ m := d.deps
+ if m == nil {
+ m = make(map[Object]bool)
+ d.deps = m
+ }
+ m[obj] = true
+}
+
+// arity checks that the lhs and rhs of a const or var decl
+// have a matching number of names and initialization values.
+// If inherited is set, the initialization values are from
+// another (constant) declaration.
+func (check *Checker) arity(pos syntax.Pos, names []*syntax.Name, inits []syntax.Expr, constDecl, inherited bool) {
+ l := len(names)
+ r := len(inits)
+
+ switch {
+ case l < r:
+ n := inits[l]
+ if inherited {
+ check.errorf(pos, "extra init expr at %s", n.Pos())
+ } else {
+ check.errorf(n, "extra init expr %s", n)
+ }
+ case l > r && (constDecl || r != 1): // if r == 1 it may be a multi-valued function and we can't say anything yet
+ n := names[r]
+ check.errorf(n, "missing init expr for %s", n.Value)
+ }
+}
+
+func validatedImportPath(path string) (string, error) {
+ s, err := strconv.Unquote(path)
+ if err != nil {
+ return "", err
+ }
+ if s == "" {
+ return "", fmt.Errorf("empty string")
+ }
+ const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
+ for _, r := range s {
+ if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
+ return s, fmt.Errorf("invalid character %#U", r)
+ }
+ }
+ return s, nil
+}
+
+// declarePkgObj declares obj in the package scope, records its ident -> obj mapping,
+// and updates check.objMap. The object must not be a function or method.
+func (check *Checker) declarePkgObj(ident *syntax.Name, obj Object, d *declInfo) {
+ assert(ident.Value == obj.Name())
+
+ // spec: "A package-scope or file-scope identifier with name init
+ // may only be declared to be a function with this (func()) signature."
+ if ident.Value == "init" {
+ check.errorf(ident, "cannot declare init - must be func")
+ return
+ }
+
+ // spec: "The main package must have package name main and declare
+ // a function main that takes no arguments and returns no value."
+ if ident.Value == "main" && check.pkg.name == "main" {
+ check.errorf(ident, "cannot declare main - must be func")
+ return
+ }
+
+ check.declare(check.pkg.scope, ident, obj, nopos)
+ check.objMap[obj] = d
+ obj.setOrder(uint32(len(check.objMap)))
+}
+
+// filename returns a filename suitable for debugging output.
+func (check *Checker) filename(fileNo int) string {
+ file := check.files[fileNo]
+ if pos := file.Pos(); pos.IsKnown() {
+ // return check.fset.File(pos).Name()
+ // TODO(gri) do we need the actual file name here?
+ return pos.RelFilename()
+ }
+ return fmt.Sprintf("file[%d]", fileNo)
+}
+
+func (check *Checker) importPackage(pos syntax.Pos, path, dir string) *Package {
+ // If we already have a package for the given (path, dir)
+ // pair, use it instead of doing a full import.
+ // Checker.impMap only caches packages that are marked Complete
+ // or fake (dummy packages for failed imports). Incomplete but
+ // non-fake packages do require an import to complete them.
+ key := importKey{path, dir}
+ imp := check.impMap[key]
+ if imp != nil {
+ return imp
+ }
+
+ // no package yet => import it
+ if path == "C" && (check.conf.FakeImportC || check.conf.go115UsesCgo) {
+ imp = NewPackage("C", "C")
+ imp.fake = true // package scope is not populated
+ imp.cgo = check.conf.go115UsesCgo
+ } else {
+ // ordinary import
+ var err error
+ if importer := check.conf.Importer; importer == nil {
+ err = fmt.Errorf("Config.Importer not installed")
+ } else if importerFrom, ok := importer.(ImporterFrom); ok {
+ imp, err = importerFrom.ImportFrom(path, dir, 0)
+ if imp == nil && err == nil {
+ err = fmt.Errorf("Config.Importer.ImportFrom(%s, %s, 0) returned nil but no error", path, dir)
+ }
+ } else {
+ imp, err = importer.Import(path)
+ if imp == nil && err == nil {
+ err = fmt.Errorf("Config.Importer.Import(%s) returned nil but no error", path)
+ }
+ }
+ // make sure we have a valid package name
+ // (errors here can only happen through manipulation of packages after creation)
+ if err == nil && imp != nil && (imp.name == "_" || imp.name == "") {
+ err = fmt.Errorf("invalid package name: %q", imp.name)
+ imp = nil // create fake package below
+ }
+ if err != nil {
+ check.errorf(pos, "could not import %s (%s)", path, err)
+ if imp == nil {
+ // create a new fake package
+ // come up with a sensible package name (heuristic)
+ name := path
+ if i := len(name); i > 0 && name[i-1] == '/' {
+ name = name[:i-1]
+ }
+ if i := strings.LastIndex(name, "/"); i >= 0 {
+ name = name[i+1:]
+ }
+ imp = NewPackage(path, name)
+ }
+ // continue to use the package as best as we can
+ imp.fake = true // avoid follow-up lookup failures
+ }
+ }
+
+ // package should be complete or marked fake, but be cautious
+ if imp.complete || imp.fake {
+ check.impMap[key] = imp
+ check.pkgCnt[imp.name]++
+ return imp
+ }
+
+ // something went wrong (importer may have returned incomplete package without error)
+ return nil
+}
+
+// collectObjects collects all file and package objects and inserts them
+// into their respective scopes. It also performs imports and associates
+// methods with receiver base type names.
+func (check *Checker) collectObjects() {
+ pkg := check.pkg
+
+ // pkgImports is the set of packages already imported by any package file seen
+ // so far. Used to avoid duplicate entries in pkg.imports. Allocate and populate
+ // it (pkg.imports may not be empty if we are checking test files incrementally).
+ // Note that pkgImports is keyed by package (and thus package path), not by an
+ // importKey value. Two different importKey values may map to the same package
+ // which is why we cannot use the check.impMap here.
+ var pkgImports = make(map[*Package]bool)
+ for _, imp := range pkg.imports {
+ pkgImports[imp] = true
+ }
+
+ type methodInfo struct {
+ obj *Func // method
+ ptr bool // true if pointer receiver
+ recv *syntax.Name // receiver type name
+ }
+ var methods []methodInfo // collected methods with valid receivers and non-blank _ names
+ var fileScopes []*Scope
+ for fileNo, file := range check.files {
+ // The package identifier denotes the current package,
+ // but there is no corresponding package object.
+ check.recordDef(file.PkgName, nil)
+
+ fileScope := NewScope(check.pkg.scope, startPos(file), endPos(file), check.filename(fileNo))
+ fileScopes = append(fileScopes, fileScope)
+ check.recordScope(file, fileScope)
+
+ // determine file directory, necessary to resolve imports
+ // FileName may be "" (typically for tests) in which case
+ // we get "." as the directory which is what we would want.
+ fileDir := dir(file.PkgName.Pos().RelFilename()) // TODO(gri) should this be filename?
+
+ first := -1 // index of first ConstDecl in the current group, or -1
+ var last *syntax.ConstDecl // last ConstDecl with init expressions, or nil
+ for index, decl := range file.DeclList {
+ if _, ok := decl.(*syntax.ConstDecl); !ok {
+ first = -1 // we're not in a constant declaration
+ }
+
+ switch s := decl.(type) {
+ case *syntax.ImportDecl:
+ // import package
+ path, err := validatedImportPath(s.Path.Value)
+ if err != nil {
+ check.errorf(s.Path, "invalid import path (%s)", err)
+ continue
+ }
+
+ imp := check.importPackage(s.Path.Pos(), path, fileDir)
+ if imp == nil {
+ continue
+ }
+
+ // add package to list of explicit imports
+ // (this functionality is provided as a convenience
+ // for clients; it is not needed for type-checking)
+ if !pkgImports[imp] {
+ pkgImports[imp] = true
+ pkg.imports = append(pkg.imports, imp)
+ }
+
+ // local name overrides imported package name
+ name := imp.name
+ if s.LocalPkgName != nil {
+ name = s.LocalPkgName.Value
+ if path == "C" {
+ // match cmd/compile (not prescribed by spec)
+ check.errorf(s.LocalPkgName, `cannot rename import "C"`)
+ continue
+ }
+ if name == "init" {
+ check.errorf(s.LocalPkgName, "cannot declare init - must be func")
+ continue
+ }
+ }
+
+ obj := NewPkgName(s.Pos(), pkg, name, imp)
+ if s.LocalPkgName != nil {
+ // in a dot-import, the dot represents the package
+ check.recordDef(s.LocalPkgName, obj)
+ } else {
+ check.recordImplicit(s, obj)
+ }
+
+ if path == "C" {
+ // match cmd/compile (not prescribed by spec)
+ obj.used = true
+ }
+
+ // add import to file scope
+ if name == "." {
+ // merge imported scope with file scope
+ for _, obj := range imp.scope.elems {
+ // A package scope may contain non-exported objects,
+ // do not import them!
+ if obj.Exported() {
+ // declare dot-imported object
+ // (Do not use check.declare because it modifies the object
+ // via Object.setScopePos, which leads to a race condition;
+ // the object may be imported into more than one file scope
+ // concurrently. See issue #32154.)
+ if alt := fileScope.Insert(obj); alt != nil {
+ check.errorf(s.LocalPkgName, "%s redeclared in this block", obj.Name())
+ check.reportAltDecl(alt)
+ }
+ }
+ }
+ // add position to set of dot-import positions for this file
+ // (this is only needed for "imported but not used" errors)
+ check.addUnusedDotImport(fileScope, imp, s.Pos())
+ } else {
+ // declare imported package object in file scope
+ // (no need to provide s.LocalPkgName since we called check.recordDef earlier)
+ check.declare(fileScope, nil, obj, nopos)
+ }
+
+ case *syntax.ConstDecl:
+ // iota is the index of the current constDecl within the group
+ if first < 0 || file.DeclList[index-1].(*syntax.ConstDecl).Group != s.Group {
+ first = index
+ last = nil
+ }
+ iota := constant.MakeInt64(int64(index - first))
+
+ // determine which initialization expressions to use
+ inherited := true
+ switch {
+ case s.Type != nil || s.Values != nil:
+ last = s
+ inherited = false
+ case last == nil:
+ last = new(syntax.ConstDecl) // make sure last exists
+ inherited = false
+ }
+
+ // declare all constants
+ values := unpackExpr(last.Values)
+ for i, name := range s.NameList {
+ obj := NewConst(name.Pos(), pkg, name.Value, nil, iota)
+
+ var init syntax.Expr
+ if i < len(values) {
+ init = values[i]
+ }
+
+ d := &declInfo{file: fileScope, vtyp: last.Type, init: init}
+ check.declarePkgObj(name, obj, d)
+ }
+
+ // Constants must always have init values.
+ check.arity(s.Pos(), s.NameList, values, true, inherited)
+
+ case *syntax.VarDecl:
+ lhs := make([]*Var, len(s.NameList))
+ // If there's exactly one rhs initializer, use
+ // the same declInfo d1 for all lhs variables
+ // so that each lhs variable depends on the same
+ // rhs initializer (n:1 var declaration).
+ var d1 *declInfo
+ if _, ok := s.Values.(*syntax.ListExpr); !ok {
+ // The lhs elements are only set up after the for loop below,
+ // but that's ok because declarePkgObj only collects the declInfo
+ // for a later phase.
+ d1 = &declInfo{file: fileScope, lhs: lhs, vtyp: s.Type, init: s.Values}
+ }
+
+ // declare all variables
+ values := unpackExpr(s.Values)
+ for i, name := range s.NameList {
+ obj := NewVar(name.Pos(), pkg, name.Value, nil)
+ lhs[i] = obj
+
+ d := d1
+ if d == nil {
+ // individual assignments
+ var init syntax.Expr
+ if i < len(values) {
+ init = values[i]
+ }
+ d = &declInfo{file: fileScope, vtyp: s.Type, init: init}
+ }
+
+ check.declarePkgObj(name, obj, d)
+ }
+
+ // If we have no type, we must have values.
+ if s.Type == nil || values != nil {
+ check.arity(s.Pos(), s.NameList, values, false, false)
+ }
+
+ case *syntax.TypeDecl:
+ obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Value, nil)
+ check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, tdecl: s})
+
+ case *syntax.FuncDecl:
+ d := s // TODO(gri) get rid of this
+ name := d.Name.Value
+ obj := NewFunc(d.Name.Pos(), pkg, name, nil)
+ if d.Recv == nil {
+ // regular function
+ if name == "init" {
+ if d.TParamList != nil {
+ //check.softErrorf(d.TParamList.Pos(), "func init must have no type parameters")
+ check.softErrorf(d.Name, "func init must have no type parameters")
+ }
+ if t := d.Type; len(t.ParamList) != 0 || len(t.ResultList) != 0 {
+ check.softErrorf(d, "func init must have no arguments and no return values")
+ }
+ // don't declare init functions in the package scope - they are invisible
+ obj.parent = pkg.scope
+ check.recordDef(d.Name, obj)
+ // init functions must have a body
+ if d.Body == nil {
+ // TODO(gri) make this error message consistent with the others above
+ check.softErrorf(obj.pos, "missing function body")
+ }
+ } else {
+ check.declare(pkg.scope, d.Name, obj, nopos)
+ }
+ } else {
+ // method
+ // d.Recv != nil
+ if !methodTypeParamsOk && len(d.TParamList) != 0 {
+ //check.invalidASTf(d.TParamList.Pos(), "method must have no type parameters")
+ check.invalidASTf(d, "method must have no type parameters")
+ }
+ ptr, recv, _ := check.unpackRecv(d.Recv.Type, false)
+ // (Methods with invalid receiver cannot be associated to a type, and
+ // methods with blank _ names are never found; no need to collect any
+ // of them. They will still be type-checked with all the other functions.)
+ if recv != nil && name != "_" {
+ methods = append(methods, methodInfo{obj, ptr, recv})
+ }
+ check.recordDef(d.Name, obj)
+ }
+ info := &declInfo{file: fileScope, fdecl: d}
+ // Methods are not package-level objects but we still track them in the
+ // object map so that we can handle them like regular functions (if the
+ // receiver is invalid); also we need their fdecl info when associating
+ // them with their receiver base type, below.
+ check.objMap[obj] = info
+ obj.setOrder(uint32(len(check.objMap)))
+
+ default:
+ check.invalidASTf(s, "unknown syntax.Decl node %T", s)
+ }
+ }
+ }
+
+ // verify that objects in package and file scopes have different names
+ for _, scope := range fileScopes {
+ for _, obj := range scope.elems {
+ if alt := pkg.scope.Lookup(obj.Name()); alt != nil {
+ if pkg, ok := obj.(*PkgName); ok {
+ check.errorf(alt, "%s already declared through import of %s", alt.Name(), pkg.Imported())
+ check.reportAltDecl(pkg)
+ } else {
+ check.errorf(alt, "%s already declared through dot-import of %s", alt.Name(), obj.Pkg())
+ // TODO(gri) dot-imported objects don't have a position; reportAltDecl won't print anything
+ check.reportAltDecl(obj)
+ }
+ }
+ }
+ }
+
+ // Now that we have all package scope objects and all methods,
+ // associate methods with receiver base type name where possible.
+ // Ignore methods that have an invalid receiver. They will be
+ // type-checked later, with regular functions.
+ if methods != nil {
+ check.methods = make(map[*TypeName][]*Func)
+ for i := range methods {
+ m := &methods[i]
+ // Determine the receiver base type and associate m with it.
+ ptr, base := check.resolveBaseTypeName(m.ptr, m.recv)
+ if base != nil {
+ m.obj.hasPtrRecv = ptr
+ check.methods[base] = append(check.methods[base], m.obj)
+ }
+ }
+ }
+}
+
+// unpackRecv unpacks a receiver type and returns its components: ptr indicates whether
+// rtyp is a pointer receiver, rname is the receiver type name, and tparams are its
+// type parameters, if any. The type parameters are only unpacked if unpackParams is
+// set. If rname is nil, the receiver is unusable (i.e., the source has a bug which we
+// cannot easily work around).
+func (check *Checker) unpackRecv(rtyp syntax.Expr, unpackParams bool) (ptr bool, rname *syntax.Name, tparams []*syntax.Name) {
+L: // unpack receiver type
+ // This accepts invalid receivers such as ***T and does not
+ // work for other invalid receivers, but we don't care. The
+ // validity of receiver expressions is checked elsewhere.
+ for {
+ switch t := rtyp.(type) {
+ case *syntax.ParenExpr:
+ rtyp = t.X
+ // case *ast.StarExpr:
+ // rtyp = t.X
+ case *syntax.Operation:
+ if t.Op != syntax.Mul || t.Y != nil {
+ break
+ }
+ rtyp = t.X
+ default:
+ break L
+ }
+ }
+
+ // unpack type parameters, if any
+ if ptyp, _ := rtyp.(*syntax.IndexExpr); ptyp != nil {
+ rtyp = ptyp.X
+ if unpackParams {
+ for _, arg := range unpackExpr(ptyp.Index) {
+ var par *syntax.Name
+ switch arg := arg.(type) {
+ case *syntax.Name:
+ par = arg
+ case *syntax.BadExpr:
+ // ignore - error already reported by parser
+ case nil:
+ check.invalidASTf(ptyp, "parameterized receiver contains nil parameters")
+ default:
+ check.errorf(arg, "receiver type parameter %s must be an identifier", arg)
+ }
+ if par == nil {
+ par = newName(arg.Pos(), "_")
+ }
+ tparams = append(tparams, par)
+ }
+
+ }
+ }
+
+ // unpack receiver name
+ if name, _ := rtyp.(*syntax.Name); name != nil {
+ rname = name
+ }
+
+ return
+}
+
+// resolveBaseTypeName returns the non-alias base type name for typ, and whether
+// there was a pointer indirection to get to it. The base type name must be declared
+// in package scope, and there can be at most one pointer indirection. If no such type
+// name exists, the returned base is nil.
+func (check *Checker) resolveBaseTypeName(seenPtr bool, typ syntax.Expr) (ptr bool, base *TypeName) {
+ // Algorithm: Starting from a type expression, which may be a name,
+ // we follow that type through alias declarations until we reach a
+ // non-alias type name. If we encounter anything but pointer types or
+ // parentheses we're done. If we encounter more than one pointer type
+ // we're done.
+ ptr = seenPtr
+ var seen map[*TypeName]bool
+ for {
+ typ = unparen(typ)
+
+ // check if we have a pointer type
+ // if pexpr, _ := typ.(*ast.StarExpr); pexpr != nil {
+ if pexpr, _ := typ.(*syntax.Operation); pexpr != nil && pexpr.Op == syntax.Mul && pexpr.Y == nil {
+ // if we've already seen a pointer, we're done
+ if ptr {
+ return false, nil
+ }
+ ptr = true
+ typ = unparen(pexpr.X) // continue with pointer base type
+ }
+
+ // typ must be a name
+ name, _ := typ.(*syntax.Name)
+ if name == nil {
+ return false, nil
+ }
+
+ // name must denote an object found in the current package scope
+ // (note that dot-imported objects are not in the package scope!)
+ obj := check.pkg.scope.Lookup(name.Value)
+ if obj == nil {
+ return false, nil
+ }
+
+ // the object must be a type name...
+ tname, _ := obj.(*TypeName)
+ if tname == nil {
+ return false, nil
+ }
+
+ // ... which we have not seen before
+ if seen[tname] {
+ return false, nil
+ }
+
+ // we're done if tdecl defined tname as a new type
+ // (rather than an alias)
+ tdecl := check.objMap[tname].tdecl // must exist for objects in package scope
+ if !tdecl.Alias {
+ return ptr, tname
+ }
+
+ // otherwise, continue resolving
+ typ = tdecl.Type
+ if seen == nil {
+ seen = make(map[*TypeName]bool)
+ }
+ seen[tname] = true
+ }
+}
+
+// packageObjects typechecks all package objects, but not function bodies.
+func (check *Checker) packageObjects() {
+ // process package objects in source order for reproducible results
+ objList := make([]Object, len(check.objMap))
+ i := 0
+ for obj := range check.objMap {
+ objList[i] = obj
+ i++
+ }
+ sort.Sort(inSourceOrder(objList))
+
+ // add new methods to already type-checked types (from a prior Checker.Files call)
+ for _, obj := range objList {
+ if obj, _ := obj.(*TypeName); obj != nil && obj.typ != nil {
+ check.collectMethods(obj)
+ }
+ }
+
+ // We process non-alias declarations first, in order to avoid situations where
+ // the type of an alias declaration is needed before it is available. In general
+ // this is still not enough, as it is possible to create sufficiently convoluted
+ // recursive type definitions that will cause a type alias to be needed before it
+ // is available (see issue #25838 for examples).
+ // As an aside, the cmd/compiler suffers from the same problem (#25838).
+ var aliasList []*TypeName
+ // phase 1
+ for _, obj := range objList {
+ // If we have a type alias, collect it for the 2nd phase.
+ if tname, _ := obj.(*TypeName); tname != nil && check.objMap[tname].tdecl.Alias {
+ aliasList = append(aliasList, tname)
+ continue
+ }
+
+ check.objDecl(obj, nil)
+ }
+ // phase 2
+ for _, obj := range aliasList {
+ check.objDecl(obj, nil)
+ }
+
+ // At this point we may have a non-empty check.methods map; this means that not all
+ // entries were deleted at the end of typeDecl because the respective receiver base
+ // types were not found. In that case, an error was reported when declaring those
+ // methods. We can now safely discard this map.
+ check.methods = nil
+}
+
+// inSourceOrder implements the sort.Sort interface.
+type inSourceOrder []Object
+
+func (a inSourceOrder) Len() int { return len(a) }
+func (a inSourceOrder) Less(i, j int) bool { return a[i].order() < a[j].order() }
+func (a inSourceOrder) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+// unusedImports checks for unused imports.
+func (check *Checker) unusedImports() {
+ // if function bodies are not checked, packages' uses are likely missing - don't check
+ if check.conf.IgnoreFuncBodies {
+ return
+ }
+
+ // spec: "It is illegal (...) to directly import a package without referring to
+ // any of its exported identifiers. To import a package solely for its side-effects
+ // (initialization), use the blank identifier as explicit package name."
+
+ // check use of regular imported packages
+ for _, scope := range check.pkg.scope.children /* file scopes */ {
+ for _, obj := range scope.elems {
+ if obj, ok := obj.(*PkgName); ok {
+ // Unused "blank imports" are automatically ignored
+ // since _ identifiers are not entered into scopes.
+ if !obj.used {
+ path := obj.imported.path
+ base := pkgName(path)
+ if obj.name == base {
+ check.softErrorf(obj.pos, "%q imported but not used", path)
+ } else {
+ check.softErrorf(obj.pos, "%q imported but not used as %s", path, obj.name)
+ }
+ }
+ }
+ }
+ }
+
+ // check use of dot-imported packages
+ for _, unusedDotImports := range check.unusedDotImports {
+ for pkg, pos := range unusedDotImports {
+ check.softErrorf(pos, "%q imported but not used", pkg.path)
+ }
+ }
+}
+
+// pkgName returns the package name (last element) of an import path.
+func pkgName(path string) string {
+ if i := strings.LastIndex(path, "/"); i >= 0 {
+ path = path[i+1:]
+ }
+ return path
+}
+
+// dir makes a good-faith attempt to return the directory
+// portion of path. If path is empty, the result is ".".
+// (Per the go/build package dependency tests, we cannot import
+// path/filepath and simply use filepath.Dir.)
+func dir(path string) string {
+ if i := strings.LastIndexAny(path, `/\`); i > 0 {
+ return path[:i]
+ }
+ // i <= 0
+ return "."
+}
diff --git a/src/cmd/compile/internal/types2/resolver_test.go b/src/cmd/compile/internal/types2/resolver_test.go
new file mode 100644
index 0000000000..cdfdba6b43
--- /dev/null
+++ b/src/cmd/compile/internal/types2/resolver_test.go
@@ -0,0 +1,223 @@
+// UNREVIEWED
+// Copyright 2011 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 types2_test
+
+import (
+ "cmd/compile/internal/syntax"
+ "fmt"
+ "internal/testenv"
+ "sort"
+ "testing"
+
+ . "cmd/compile/internal/types2"
+)
+
+type resolveTestImporter struct {
+ importer ImporterFrom
+ imported map[string]bool
+}
+
+func (imp *resolveTestImporter) Import(string) (*Package, error) {
+ panic("should not be called")
+}
+
+func (imp *resolveTestImporter) ImportFrom(path, srcDir string, mode ImportMode) (*Package, error) {
+ if mode != 0 {
+ panic("mode must be 0")
+ }
+ if imp.importer == nil {
+ imp.importer = defaultImporter().(ImporterFrom)
+ imp.imported = make(map[string]bool)
+ }
+ pkg, err := imp.importer.ImportFrom(path, srcDir, mode)
+ if err != nil {
+ return nil, err
+ }
+ imp.imported[path] = true
+ return pkg, nil
+}
+
+func TestResolveIdents(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ sources := []string{
+ `
+ package p
+ import "fmt"
+ import "math"
+ const pi = math.Pi
+ func sin(x float64) float64 {
+ return math.Sin(x)
+ }
+ var Println = fmt.Println
+ `,
+ `
+ package p
+ import "fmt"
+ type errorStringer struct { fmt.Stringer; error }
+ func f() string {
+ _ = "foo"
+ return fmt.Sprintf("%d", g())
+ }
+ func g() (x int) { return }
+ `,
+ `
+ package p
+ import . "go/parser"
+ import "sync"
+ func h() Mode { return ImportsOnly }
+ var _, x int = 1, 2
+ func init() {}
+ type T struct{ *sync.Mutex; a, b, c int}
+ type I interface{ m() }
+ var _ = T{a: 1, b: 2, c: 3}
+ func (_ T) m() {}
+ func (T) _() {}
+ var i I
+ var _ = i.m
+ func _(s []int) { for i, x := range s { _, _ = i, x } }
+ func _(x interface{}) {
+ switch x := x.(type) {
+ case int:
+ _ = x
+ }
+ switch {} // implicit 'true' tag
+ }
+ `,
+ `
+ package p
+ type S struct{}
+ func (T) _() {}
+ func (T) _() {}
+ `,
+ `
+ package p
+ func _() {
+ L0:
+ L1:
+ goto L0
+ for {
+ goto L1
+ }
+ if true {
+ goto L2
+ }
+ L2:
+ }
+ `,
+ }
+
+ pkgnames := []string{
+ "fmt",
+ "math",
+ }
+
+ // parse package files
+ var files []*syntax.File
+ for i, src := range sources {
+ f, err := parseSrc(fmt.Sprintf("sources[%d]", i), src)
+ if err != nil {
+ t.Fatal(err)
+ }
+ files = append(files, f)
+ }
+
+ // resolve and type-check package AST
+ importer := new(resolveTestImporter)
+ conf := Config{Importer: importer}
+ uses := make(map[*syntax.Name]Object)
+ defs := make(map[*syntax.Name]Object)
+ _, err := conf.Check("testResolveIdents", files, &Info{Defs: defs, Uses: uses})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // check that all packages were imported
+ for _, name := range pkgnames {
+ if !importer.imported[name] {
+ t.Errorf("package %s not imported", name)
+ }
+ }
+
+ // check that qualified identifiers are resolved
+ for _, f := range files {
+ Walk(f, func(n syntax.Node) bool {
+ if s, ok := n.(*syntax.SelectorExpr); ok {
+ if x, ok := s.X.(*syntax.Name); ok {
+ obj := uses[x]
+ if obj == nil {
+ t.Errorf("%s: unresolved qualified identifier %s", x.Pos(), x.Value)
+ return true
+ }
+ if _, ok := obj.(*PkgName); ok && uses[s.Sel] == nil {
+ t.Errorf("%s: unresolved selector %s", s.Sel.Pos(), s.Sel.Value)
+ return true
+ }
+ return true
+ }
+ return true
+ }
+ return false
+ })
+ }
+
+ for id, obj := range uses {
+ if obj == nil {
+ t.Errorf("%s: Uses[%s] == nil", id.Pos(), id.Value)
+ }
+ }
+
+ // Check that each identifier in the source is found in uses or defs or both.
+ // We need the foundUses/Defs maps (rather then just deleting the found objects
+ // from the uses and defs maps) because Walk traverses shared nodes multiple
+ // times (e.g. types in field lists such as "a, b, c int").
+ foundUses := make(map[*syntax.Name]bool)
+ foundDefs := make(map[*syntax.Name]bool)
+ var both []string
+ for _, f := range files {
+ Walk(f, func(n syntax.Node) bool {
+ if x, ok := n.(*syntax.Name); ok {
+ var objects int
+ if _, found := uses[x]; found {
+ objects |= 1
+ foundUses[x] = true
+ }
+ if _, found := defs[x]; found {
+ objects |= 2
+ foundDefs[x] = true
+ }
+ switch objects {
+ case 0:
+ t.Errorf("%s: unresolved identifier %s", x.Pos(), x.Value)
+ case 3:
+ both = append(both, x.Value)
+ }
+ return true
+ }
+ return false
+ })
+ }
+
+ // check the expected set of idents that are simultaneously uses and defs
+ sort.Strings(both)
+ if got, want := fmt.Sprint(both), "[Mutex Stringer error]"; got != want {
+ t.Errorf("simultaneous uses/defs = %s, want %s", got, want)
+ }
+
+ // any left-over identifiers didn't exist in the source
+ for x := range uses {
+ if !foundUses[x] {
+ t.Errorf("%s: identifier %s not present in source", x.Pos(), x.Value)
+ }
+ }
+ for x := range defs {
+ if !foundDefs[x] {
+ t.Errorf("%s: identifier %s not present in source", x.Pos(), x.Value)
+ }
+ }
+
+ // TODO(gri) add tests to check ImplicitObj callbacks
+}
diff --git a/src/cmd/compile/internal/types2/return.go b/src/cmd/compile/internal/types2/return.go
new file mode 100644
index 0000000000..88234b1723
--- /dev/null
+++ b/src/cmd/compile/internal/types2/return.go
@@ -0,0 +1,180 @@
+// UNREVIEWED
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements isTerminating.
+
+package types2
+
+import (
+ "cmd/compile/internal/syntax"
+)
+
+// isTerminating reports if s is a terminating statement.
+// If s is labeled, label is the label name; otherwise s
+// is "".
+func (check *Checker) isTerminating(s syntax.Stmt, label string) bool {
+ switch s := s.(type) {
+ default:
+ unreachable()
+
+ case *syntax.DeclStmt, *syntax.EmptyStmt, *syntax.SendStmt,
+ *syntax.AssignStmt, *syntax.CallStmt:
+ // no chance
+
+ case *syntax.LabeledStmt:
+ return check.isTerminating(s.Stmt, s.Label.Value)
+
+ case *syntax.ExprStmt:
+ // calling the predeclared (possibly parenthesized) panic() function is terminating
+ if call, ok := unparen(s.X).(*syntax.CallExpr); ok && check.isPanic[call] {
+ return true
+ }
+
+ case *syntax.ReturnStmt:
+ return true
+
+ case *syntax.BranchStmt:
+ if s.Tok == syntax.Goto || s.Tok == syntax.Fallthrough {
+ return true
+ }
+
+ case *syntax.BlockStmt:
+ return check.isTerminatingList(s.List, "")
+
+ case *syntax.IfStmt:
+ if s.Else != nil &&
+ check.isTerminating(s.Then, "") &&
+ check.isTerminating(s.Else, "") {
+ return true
+ }
+
+ case *syntax.SwitchStmt:
+ return check.isTerminatingSwitch(s.Body, label)
+
+ case *syntax.SelectStmt:
+ for _, cc := range s.Body {
+ if !check.isTerminatingList(cc.Body, "") || hasBreakList(cc.Body, label, true) {
+ return false
+ }
+
+ }
+ return true
+
+ case *syntax.ForStmt:
+ if s.Cond == nil && !hasBreak(s.Body, label, true) {
+ return true
+ }
+ }
+
+ return false
+}
+
+func (check *Checker) isTerminatingList(list []syntax.Stmt, label string) bool {
+ // trailing empty statements are permitted - skip them
+ for i := len(list) - 1; i >= 0; i-- {
+ if _, ok := list[i].(*syntax.EmptyStmt); !ok {
+ return check.isTerminating(list[i], label)
+ }
+ }
+ return false // all statements are empty
+}
+
+func (check *Checker) isTerminatingSwitch(body []*syntax.CaseClause, label string) bool {
+ hasDefault := false
+ for _, cc := range body {
+ if cc.Cases == nil {
+ hasDefault = true
+ }
+ if !check.isTerminatingList(cc.Body, "") || hasBreakList(cc.Body, label, true) {
+ return false
+ }
+ }
+ return hasDefault
+}
+
+// TODO(gri) For nested breakable statements, the current implementation of hasBreak
+// will traverse the same subtree repeatedly, once for each label. Replace
+// with a single-pass label/break matching phase.
+
+// hasBreak reports if s is or contains a break statement
+// referring to the label-ed statement or implicit-ly the
+// closest outer breakable statement.
+func hasBreak(s syntax.Stmt, label string, implicit bool) bool {
+ switch s := s.(type) {
+ default:
+ unreachable()
+
+ case *syntax.DeclStmt, *syntax.EmptyStmt, *syntax.ExprStmt,
+ *syntax.SendStmt, *syntax.AssignStmt, *syntax.CallStmt,
+ *syntax.ReturnStmt:
+ // no chance
+
+ case *syntax.LabeledStmt:
+ return hasBreak(s.Stmt, label, implicit)
+
+ case *syntax.BranchStmt:
+ if s.Tok == syntax.Break {
+ if s.Label == nil {
+ return implicit
+ }
+ if s.Label.Value == label {
+ return true
+ }
+ }
+
+ case *syntax.BlockStmt:
+ return hasBreakList(s.List, label, implicit)
+
+ case *syntax.IfStmt:
+ if hasBreak(s.Then, label, implicit) ||
+ s.Else != nil && hasBreak(s.Else, label, implicit) {
+ return true
+ }
+
+ case *syntax.SwitchStmt:
+ if label != "" && hasBreakCaseList(s.Body, label, false) {
+ return true
+ }
+
+ case *syntax.SelectStmt:
+ if label != "" && hasBreakCommList(s.Body, label, false) {
+ return true
+ }
+
+ case *syntax.ForStmt:
+ if label != "" && hasBreak(s.Body, label, false) {
+ return true
+ }
+ }
+
+ return false
+}
+
+func hasBreakList(list []syntax.Stmt, label string, implicit bool) bool {
+ for _, s := range list {
+ if hasBreak(s, label, implicit) {
+ return true
+ }
+ }
+ return false
+}
+
+func hasBreakCaseList(list []*syntax.CaseClause, label string, implicit bool) bool {
+ for _, s := range list {
+ if hasBreakList(s.Body, label, implicit) {
+ return true
+ }
+ }
+ return false
+}
+
+func hasBreakCommList(list []*syntax.CommClause, label string, implicit bool) bool {
+ for _, s := range list {
+ if hasBreakList(s.Body, label, implicit) {
+ return true
+ }
+ }
+ return false
+}
diff --git a/src/cmd/compile/internal/types2/sanitize.go b/src/cmd/compile/internal/types2/sanitize.go
new file mode 100644
index 0000000000..bac569416b
--- /dev/null
+++ b/src/cmd/compile/internal/types2/sanitize.go
@@ -0,0 +1,149 @@
+// UNREVIEWED
+// 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 types2
+
+func sanitizeInfo(info *Info) {
+ var s sanitizer = make(map[Type]Type)
+
+ // Note: Some map entries are not references.
+ // If modified, they must be assigned back.
+
+ for e, tv := range info.Types {
+ tv.Type = s.typ(tv.Type)
+ info.Types[e] = tv
+ }
+
+ for e, inf := range info.Inferred {
+ for i, targ := range inf.Targs {
+ inf.Targs[i] = s.typ(targ)
+ }
+ inf.Sig = s.typ(inf.Sig).(*Signature)
+ info.Inferred[e] = inf
+ }
+
+ for _, obj := range info.Defs {
+ if obj != nil {
+ obj.setType(s.typ(obj.Type()))
+ }
+ }
+
+ for _, obj := range info.Uses {
+ if obj != nil {
+ obj.setType(s.typ(obj.Type()))
+ }
+ }
+
+ // TODO(gri) sanitize as needed
+ // - info.Implicits
+ // - info.Selections
+ // - info.Scopes
+ // - info.InitOrder
+}
+
+type sanitizer map[Type]Type
+
+func (s sanitizer) typ(typ Type) Type {
+ if t, found := s[typ]; found {
+ return t
+ }
+ s[typ] = typ
+
+ switch t := typ.(type) {
+ case nil, *Basic, *bottom, *top:
+ // nothing to do
+
+ case *Array:
+ t.elem = s.typ(t.elem)
+
+ case *Slice:
+ t.elem = s.typ(t.elem)
+
+ case *Struct:
+ s.varList(t.fields)
+
+ case *Pointer:
+ t.base = s.typ(t.base)
+
+ case *Tuple:
+ s.tuple(t)
+
+ case *Signature:
+ s.var_(t.recv)
+ s.tuple(t.params)
+ s.tuple(t.results)
+
+ case *Sum:
+ s.typeList(t.types)
+
+ case *Interface:
+ s.funcList(t.methods)
+ s.typ(t.types)
+ s.typeList(t.embeddeds)
+ s.funcList(t.allMethods)
+ s.typ(t.allTypes)
+
+ case *Map:
+ t.key = s.typ(t.key)
+ t.elem = s.typ(t.elem)
+
+ case *Chan:
+ t.elem = s.typ(t.elem)
+
+ case *Named:
+ t.orig = s.typ(t.orig)
+ t.underlying = s.typ(t.underlying)
+ s.typeList(t.targs)
+ s.funcList(t.methods)
+
+ case *TypeParam:
+ t.bound = s.typ(t.bound)
+
+ case *instance:
+ typ = t.expand()
+ s[t] = typ
+
+ default:
+ unimplemented()
+ }
+
+ return typ
+}
+
+func (s sanitizer) var_(v *Var) {
+ if v != nil {
+ v.typ = s.typ(v.typ)
+ }
+}
+
+func (s sanitizer) varList(list []*Var) {
+ for _, v := range list {
+ s.var_(v)
+ }
+}
+
+func (s sanitizer) tuple(t *Tuple) {
+ if t != nil {
+ s.varList(t.vars)
+ }
+}
+
+func (s sanitizer) func_(f *Func) {
+ if f != nil {
+ f.typ = s.typ(f.typ)
+ }
+}
+
+func (s sanitizer) funcList(list []*Func) {
+ for _, f := range list {
+ s.func_(f)
+ }
+}
+
+func (s sanitizer) typeList(list []Type) {
+ for i, t := range list {
+ list[i] = s.typ(t)
+ }
+}
diff --git a/src/cmd/compile/internal/types2/scope.go b/src/cmd/compile/internal/types2/scope.go
new file mode 100644
index 0000000000..c8243ac36c
--- /dev/null
+++ b/src/cmd/compile/internal/types2/scope.go
@@ -0,0 +1,217 @@
+// UNREVIEWED
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements Scopes.
+
+package types2
+
+import (
+ "bytes"
+ "cmd/compile/internal/syntax"
+ "fmt"
+ "io"
+ "sort"
+ "strings"
+)
+
+// A Scope maintains a set of objects and links to its containing
+// (parent) and contained (children) scopes. Objects may be inserted
+// and looked up by name. The zero value for Scope is a ready-to-use
+// empty scope.
+type Scope struct {
+ parent *Scope
+ children []*Scope
+ elems map[string]Object // lazily allocated
+ pos, end syntax.Pos // scope extent; may be invalid
+ comment string // for debugging only
+ isFunc bool // set if this is a function scope (internal use only)
+}
+
+// NewScope returns a new, empty scope contained in the given parent
+// scope, if any. The comment is for debugging only.
+func NewScope(parent *Scope, pos, end syntax.Pos, comment string) *Scope {
+ s := &Scope{parent, nil, nil, pos, end, comment, false}
+ // don't add children to Universe scope!
+ if parent != nil && parent != Universe {
+ parent.children = append(parent.children, s)
+ }
+ return s
+}
+
+// Parent returns the scope's containing (parent) scope.
+func (s *Scope) Parent() *Scope { return s.parent }
+
+// Len returns the number of scope elements.
+func (s *Scope) Len() int { return len(s.elems) }
+
+// Names returns the scope's element names in sorted order.
+func (s *Scope) Names() []string {
+ names := make([]string, len(s.elems))
+ i := 0
+ for name := range s.elems {
+ names[i] = name
+ i++
+ }
+ sort.Strings(names)
+ return names
+}
+
+// NumChildren returns the number of scopes nested in s.
+func (s *Scope) NumChildren() int { return len(s.children) }
+
+// Child returns the i'th child scope for 0 <= i < NumChildren().
+func (s *Scope) Child(i int) *Scope { return s.children[i] }
+
+// Lookup returns the object in scope s with the given name if such an
+// object exists; otherwise the result is nil.
+func (s *Scope) Lookup(name string) Object {
+ return s.elems[name]
+}
+
+// LookupParent follows the parent chain of scopes starting with s until
+// it finds a scope where Lookup(name) returns a non-nil object, and then
+// returns that scope and object. If a valid position pos is provided,
+// only objects that were declared at or before pos are considered.
+// If no such scope and object exists, the result is (nil, nil).
+//
+// Note that obj.Parent() may be different from the returned scope if the
+// object was inserted into the scope and already had a parent at that
+// time (see Insert). This can only happen for dot-imported objects
+// whose scope is the scope of the package that exported them.
+func (s *Scope) LookupParent(name string, pos syntax.Pos) (*Scope, Object) {
+ for ; s != nil; s = s.parent {
+ if obj := s.elems[name]; obj != nil && (!pos.IsKnown() || cmpPos(obj.scopePos(), pos) <= 0) {
+ return s, obj
+ }
+ }
+ return nil, nil
+}
+
+// Insert attempts to insert an object obj into scope s.
+// If s already contains an alternative object alt with
+// the same name, Insert leaves s unchanged and returns alt.
+// Otherwise it inserts obj, sets the object's parent scope
+// if not already set, and returns nil.
+func (s *Scope) Insert(obj Object) Object {
+ name := obj.Name()
+ if alt := s.elems[name]; alt != nil {
+ return alt
+ }
+ if s.elems == nil {
+ s.elems = make(map[string]Object)
+ }
+ s.elems[name] = obj
+ if obj.Parent() == nil {
+ obj.setParent(s)
+ }
+ return nil
+}
+
+// Squash merges s with its parent scope p by adding all
+// objects of s to p, adding all children of s to the
+// children of p, and removing s from p's children.
+// The function f is called for each object obj in s which
+// has an object alt in p. s should be discarded after
+// having been squashed.
+func (s *Scope) Squash(err func(obj, alt Object)) {
+ p := s.parent
+ assert(p != nil)
+ for _, obj := range s.elems {
+ obj.setParent(nil)
+ if alt := p.Insert(obj); alt != nil {
+ err(obj, alt)
+ }
+ }
+
+ j := -1 // index of s in p.children
+ for i, ch := range p.children {
+ if ch == s {
+ j = i
+ break
+ }
+ }
+ assert(j >= 0)
+ k := len(p.children) - 1
+ p.children[j] = p.children[k]
+ p.children = p.children[:k]
+
+ p.children = append(p.children, s.children...)
+
+ s.children = nil
+ s.elems = nil
+}
+
+// Pos and End describe the scope's source code extent [pos, end).
+// The results are guaranteed to be valid only if the type-checked
+// AST has complete position information. The extent is undefined
+// for Universe and package scopes.
+func (s *Scope) Pos() syntax.Pos { return s.pos }
+func (s *Scope) End() syntax.Pos { return s.end }
+
+// Contains reports whether pos is within the scope's extent.
+// The result is guaranteed to be valid only if the type-checked
+// AST has complete position information.
+func (s *Scope) Contains(pos syntax.Pos) bool {
+ return cmpPos(s.pos, pos) <= 0 && cmpPos(pos, s.end) < 0
+}
+
+// Innermost returns the innermost (child) scope containing
+// pos. If pos is not within any scope, the result is nil.
+// The result is also nil for the Universe scope.
+// The result is guaranteed to be valid only if the type-checked
+// AST has complete position information.
+func (s *Scope) Innermost(pos syntax.Pos) *Scope {
+ // Package scopes do not have extents since they may be
+ // discontiguous, so iterate over the package's files.
+ if s.parent == Universe {
+ for _, s := range s.children {
+ if inner := s.Innermost(pos); inner != nil {
+ return inner
+ }
+ }
+ }
+
+ if s.Contains(pos) {
+ for _, s := range s.children {
+ if s.Contains(pos) {
+ return s.Innermost(pos)
+ }
+ }
+ return s
+ }
+ return nil
+}
+
+// WriteTo writes a string representation of the scope to w,
+// with the scope elements sorted by name.
+// The level of indentation is controlled by n >= 0, with
+// n == 0 for no indentation.
+// If recurse is set, it also writes nested (children) scopes.
+func (s *Scope) WriteTo(w io.Writer, n int, recurse bool) {
+ const ind = ". "
+ indn := strings.Repeat(ind, n)
+
+ fmt.Fprintf(w, "%s%s scope %p {\n", indn, s.comment, s)
+
+ indn1 := indn + ind
+ for _, name := range s.Names() {
+ fmt.Fprintf(w, "%s%s\n", indn1, s.elems[name])
+ }
+
+ if recurse {
+ for _, s := range s.children {
+ s.WriteTo(w, n+1, recurse)
+ }
+ }
+
+ fmt.Fprintf(w, "%s}\n", indn)
+}
+
+// String returns a string representation of the scope, for debugging.
+func (s *Scope) String() string {
+ var buf bytes.Buffer
+ s.WriteTo(&buf, 0, false)
+ return buf.String()
+}
diff --git a/src/cmd/compile/internal/types2/selection.go b/src/cmd/compile/internal/types2/selection.go
new file mode 100644
index 0000000000..da0e9ab526
--- /dev/null
+++ b/src/cmd/compile/internal/types2/selection.go
@@ -0,0 +1,144 @@
+// UNREVIEWED
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements Selections.
+
+package types2
+
+import (
+ "bytes"
+ "fmt"
+)
+
+// SelectionKind describes the kind of a selector expression x.f
+// (excluding qualified identifiers).
+type SelectionKind int
+
+const (
+ FieldVal SelectionKind = iota // x.f is a struct field selector
+ MethodVal // x.f is a method selector
+ MethodExpr // x.f is a method expression
+)
+
+// A Selection describes a selector expression x.f.
+// For the declarations:
+//
+// type T struct{ x int; E }
+// type E struct{}
+// func (e E) m() {}
+// var p *T
+//
+// the following relations exist:
+//
+// Selector Kind Recv Obj Type Index Indirect
+//
+// p.x FieldVal T x int {0} true
+// p.m MethodVal *T m func() {1, 0} true
+// T.m MethodExpr T m func(T) {1, 0} false
+//
+type Selection struct {
+ kind SelectionKind
+ recv Type // type of x
+ obj Object // object denoted by x.f
+ index []int // path from x to x.f
+ indirect bool // set if there was any pointer indirection on the path
+}
+
+// Kind returns the selection kind.
+func (s *Selection) Kind() SelectionKind { return s.kind }
+
+// Recv returns the type of x in x.f.
+func (s *Selection) Recv() Type { return s.recv }
+
+// Obj returns the object denoted by x.f; a *Var for
+// a field selection, and a *Func in all other cases.
+func (s *Selection) Obj() Object { return s.obj }
+
+// Type returns the type of x.f, which may be different from the type of f.
+// See Selection for more information.
+func (s *Selection) Type() Type {
+ switch s.kind {
+ case MethodVal:
+ // The type of x.f is a method with its receiver type set
+ // to the type of x.
+ sig := *s.obj.(*Func).typ.(*Signature)
+ recv := *sig.recv
+ recv.typ = s.recv
+ sig.recv = &recv
+ return &sig
+
+ case MethodExpr:
+ // The type of x.f is a function (without receiver)
+ // and an additional first argument with the same type as x.
+ // TODO(gri) Similar code is already in call.go - factor!
+ // TODO(gri) Compute this eagerly to avoid allocations.
+ sig := *s.obj.(*Func).typ.(*Signature)
+ arg0 := *sig.recv
+ sig.recv = nil
+ arg0.typ = s.recv
+ var params []*Var
+ if sig.params != nil {
+ params = sig.params.vars
+ }
+ sig.params = NewTuple(append([]*Var{&arg0}, params...)...)
+ return &sig
+ }
+
+ // In all other cases, the type of x.f is the type of x.
+ return s.obj.Type()
+}
+
+// Index describes the path from x to f in x.f.
+// The last index entry is the field or method index of the type declaring f;
+// either:
+//
+// 1) the list of declared methods of a named type; or
+// 2) the list of methods of an interface type; or
+// 3) the list of fields of a struct type.
+//
+// The earlier index entries are the indices of the embedded fields implicitly
+// traversed to get from (the type of) x to f, starting at embedding depth 0.
+func (s *Selection) Index() []int { return s.index }
+
+// Indirect reports whether any pointer indirection was required to get from
+// x to f in x.f.
+func (s *Selection) Indirect() bool { return s.indirect }
+
+func (s *Selection) String() string { return SelectionString(s, nil) }
+
+// SelectionString returns the string form of s.
+// The Qualifier controls the printing of
+// package-level objects, and may be nil.
+//
+// Examples:
+// "field (T) f int"
+// "method (T) f(X) Y"
+// "method expr (T) f(X) Y"
+//
+func SelectionString(s *Selection, qf Qualifier) string {
+ var k string
+ switch s.kind {
+ case FieldVal:
+ k = "field "
+ case MethodVal:
+ k = "method "
+ case MethodExpr:
+ k = "method expr "
+ default:
+ unreachable()
+ }
+ var buf bytes.Buffer
+ buf.WriteString(k)
+ buf.WriteByte('(')
+ WriteType(&buf, s.Recv(), qf)
+ fmt.Fprintf(&buf, ") %s", s.obj.Name())
+ if T := s.Type(); s.kind == FieldVal {
+ buf.WriteByte(' ')
+ WriteType(&buf, T, qf)
+ } else {
+ WriteSignature(&buf, T.(*Signature), qf)
+ }
+ return buf.String()
+}
diff --git a/src/cmd/compile/internal/types2/self_test.go b/src/cmd/compile/internal/types2/self_test.go
new file mode 100644
index 0000000000..6d7971e50f
--- /dev/null
+++ b/src/cmd/compile/internal/types2/self_test.go
@@ -0,0 +1,97 @@
+// UNREVIEWED
+// Copyright 2013 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 types2_test
+
+import (
+ "cmd/compile/internal/syntax"
+ "flag"
+ "fmt"
+ "path/filepath"
+ "testing"
+ "time"
+
+ . "cmd/compile/internal/types2"
+)
+
+var benchmark = flag.Bool("b", false, "run benchmarks")
+
+func TestSelf(t *testing.T) {
+ files, err := pkgFiles(".")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ conf := Config{Importer: defaultImporter()}
+ _, err = conf.Check("go/types", files, nil)
+ if err != nil {
+ // Importing go/constant doesn't work in the
+ // build dashboard environment. Don't report an error
+ // for now so that the build remains green.
+ // TODO(gri) fix this
+ t.Log(err) // replace w/ t.Fatal eventually
+ return
+ }
+}
+
+func TestBenchmark(t *testing.T) {
+ if !*benchmark {
+ return
+ }
+
+ // We're not using testing's benchmarking mechanism directly
+ // because we want custom output.
+
+ for _, p := range []string{"types", "constant", filepath.Join("internal", "gcimporter")} {
+ path := filepath.Join("..", p)
+ runbench(t, path, false)
+ runbench(t, path, true)
+ fmt.Println()
+ }
+}
+
+func runbench(t *testing.T, path string, ignoreFuncBodies bool) {
+ files, err := pkgFiles(path)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ b := testing.Benchmark(func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ conf := Config{IgnoreFuncBodies: ignoreFuncBodies}
+ conf.Check(path, files, nil)
+ }
+ })
+
+ // determine line count
+ var lines uint
+ for _, f := range files {
+ lines += f.EOF.Line()
+ }
+
+ d := time.Duration(b.NsPerOp())
+ fmt.Printf(
+ "%s: %s for %d lines (%d lines/s), ignoreFuncBodies = %v\n",
+ filepath.Base(path), d, lines, int64(float64(lines)/d.Seconds()), ignoreFuncBodies,
+ )
+}
+
+func pkgFiles(path string) ([]*syntax.File, error) {
+ filenames, err := pkgFilenames(path) // from stdlib_test.go
+ if err != nil {
+ return nil, err
+ }
+
+ var files []*syntax.File
+ for _, filename := range filenames {
+ file, err := syntax.ParseFile(filename, nil, nil, 0)
+ if err != nil {
+ return nil, err
+ }
+ files = append(files, file)
+ }
+
+ return files, nil
+}
diff --git a/src/cmd/compile/internal/types2/sizes.go b/src/cmd/compile/internal/types2/sizes.go
new file mode 100644
index 0000000000..cae71c139c
--- /dev/null
+++ b/src/cmd/compile/internal/types2/sizes.go
@@ -0,0 +1,266 @@
+// UNREVIEWED
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements Sizes.
+
+package types2
+
+// Sizes defines the sizing functions for package unsafe.
+type Sizes interface {
+ // Alignof returns the alignment of a variable of type T.
+ // Alignof must implement the alignment guarantees required by the spec.
+ Alignof(T Type) int64
+
+ // Offsetsof returns the offsets of the given struct fields, in bytes.
+ // Offsetsof must implement the offset guarantees required by the spec.
+ Offsetsof(fields []*Var) []int64
+
+ // Sizeof returns the size of a variable of type T.
+ // Sizeof must implement the size guarantees required by the spec.
+ Sizeof(T Type) int64
+}
+
+// StdSizes is a convenience type for creating commonly used Sizes.
+// It makes the following simplifying assumptions:
+//
+// - The size of explicitly sized basic types (int16, etc.) is the
+// specified size.
+// - The size of strings and interfaces is 2*WordSize.
+// - The size of slices is 3*WordSize.
+// - The size of an array of n elements corresponds to the size of
+// a struct of n consecutive fields of the array's element type.
+// - The size of a struct is the offset of the last field plus that
+// field's size. As with all element types, if the struct is used
+// in an array its size must first be aligned to a multiple of the
+// struct's alignment.
+// - All other types have size WordSize.
+// - Arrays and structs are aligned per spec definition; all other
+// types are naturally aligned with a maximum alignment MaxAlign.
+//
+// *StdSizes implements Sizes.
+//
+type StdSizes struct {
+ WordSize int64 // word size in bytes - must be >= 4 (32bits)
+ MaxAlign int64 // maximum alignment in bytes - must be >= 1
+}
+
+func (s *StdSizes) Alignof(T Type) int64 {
+ // For arrays and structs, alignment is defined in terms
+ // of alignment of the elements and fields, respectively.
+ switch t := optype(T.Under()).(type) {
+ case *Array:
+ // spec: "For a variable x of array type: unsafe.Alignof(x)
+ // is the same as unsafe.Alignof(x[0]), but at least 1."
+ return s.Alignof(t.elem)
+ case *Struct:
+ // spec: "For a variable x of struct type: unsafe.Alignof(x)
+ // is the largest of the values unsafe.Alignof(x.f) for each
+ // field f of x, but at least 1."
+ max := int64(1)
+ for _, f := range t.fields {
+ if a := s.Alignof(f.typ); a > max {
+ max = a
+ }
+ }
+ return max
+ case *Slice, *Interface:
+ // Multiword data structures are effectively structs
+ // in which each element has size WordSize.
+ return s.WordSize
+ case *Basic:
+ // Strings are like slices and interfaces.
+ if t.Info()&IsString != 0 {
+ return s.WordSize
+ }
+ }
+ a := s.Sizeof(T) // may be 0
+ // spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
+ if a < 1 {
+ return 1
+ }
+ // complex{64,128} are aligned like [2]float{32,64}.
+ if isComplex(T) {
+ a /= 2
+ }
+ if a > s.MaxAlign {
+ return s.MaxAlign
+ }
+ return a
+}
+
+func (s *StdSizes) Offsetsof(fields []*Var) []int64 {
+ offsets := make([]int64, len(fields))
+ var o int64
+ for i, f := range fields {
+ a := s.Alignof(f.typ)
+ o = align(o, a)
+ offsets[i] = o
+ o += s.Sizeof(f.typ)
+ }
+ return offsets
+}
+
+var basicSizes = [...]byte{
+ Bool: 1,
+ Int8: 1,
+ Int16: 2,
+ Int32: 4,
+ Int64: 8,
+ Uint8: 1,
+ Uint16: 2,
+ Uint32: 4,
+ Uint64: 8,
+ Float32: 4,
+ Float64: 8,
+ Complex64: 8,
+ Complex128: 16,
+}
+
+func (s *StdSizes) Sizeof(T Type) int64 {
+ switch t := optype(T.Under()).(type) {
+ case *Basic:
+ assert(isTyped(T))
+ k := t.kind
+ if int(k) < len(basicSizes) {
+ if s := basicSizes[k]; s > 0 {
+ return int64(s)
+ }
+ }
+ if k == String {
+ return s.WordSize * 2
+ }
+ case *Array:
+ n := t.len
+ if n <= 0 {
+ return 0
+ }
+ // n > 0
+ a := s.Alignof(t.elem)
+ z := s.Sizeof(t.elem)
+ return align(z, a)*(n-1) + z
+ case *Slice:
+ return s.WordSize * 3
+ case *Struct:
+ n := t.NumFields()
+ if n == 0 {
+ return 0
+ }
+ offsets := s.Offsetsof(t.fields)
+ return offsets[n-1] + s.Sizeof(t.fields[n-1].typ)
+ case *Sum:
+ panic("Sizeof unimplemented for type sum")
+ case *Interface:
+ return s.WordSize * 2
+ }
+ return s.WordSize // catch-all
+}
+
+// common architecture word sizes and alignments
+var gcArchSizes = map[string]*StdSizes{
+ "386": {4, 4},
+ "arm": {4, 4},
+ "arm64": {8, 8},
+ "amd64": {8, 8},
+ "amd64p32": {4, 8},
+ "mips": {4, 4},
+ "mipsle": {4, 4},
+ "mips64": {8, 8},
+ "mips64le": {8, 8},
+ "ppc64": {8, 8},
+ "ppc64le": {8, 8},
+ "riscv64": {8, 8},
+ "s390x": {8, 8},
+ "sparc64": {8, 8},
+ "wasm": {8, 8},
+ // When adding more architectures here,
+ // update the doc string of SizesFor below.
+}
+
+// SizesFor returns the Sizes used by a compiler for an architecture.
+// The result is nil if a compiler/architecture pair is not known.
+//
+// Supported architectures for compiler "gc":
+// "386", "arm", "arm64", "amd64", "amd64p32", "mips", "mipsle",
+// "mips64", "mips64le", "ppc64", "ppc64le", "riscv64", "s390x", "sparc64", "wasm".
+func SizesFor(compiler, arch string) Sizes {
+ var m map[string]*StdSizes
+ switch compiler {
+ case "gc":
+ m = gcArchSizes
+ case "gccgo":
+ m = gccgoArchSizes
+ default:
+ return nil
+ }
+ s, ok := m[arch]
+ if !ok {
+ return nil
+ }
+ return s
+}
+
+// stdSizes is used if Config.Sizes == nil.
+var stdSizes = SizesFor("gc", "amd64")
+
+func (conf *Config) alignof(T Type) int64 {
+ if s := conf.Sizes; s != nil {
+ if a := s.Alignof(T); a >= 1 {
+ return a
+ }
+ panic("Config.Sizes.Alignof returned an alignment < 1")
+ }
+ return stdSizes.Alignof(T)
+}
+
+func (conf *Config) offsetsof(T *Struct) []int64 {
+ var offsets []int64
+ if T.NumFields() > 0 {
+ // compute offsets on demand
+ if s := conf.Sizes; s != nil {
+ offsets = s.Offsetsof(T.fields)
+ // sanity checks
+ if len(offsets) != T.NumFields() {
+ panic("Config.Sizes.Offsetsof returned the wrong number of offsets")
+ }
+ for _, o := range offsets {
+ if o < 0 {
+ panic("Config.Sizes.Offsetsof returned an offset < 0")
+ }
+ }
+ } else {
+ offsets = stdSizes.Offsetsof(T.fields)
+ }
+ }
+ return offsets
+}
+
+// offsetof returns the offset of the field specified via
+// the index sequence relative to typ. All embedded fields
+// must be structs (rather than pointer to structs).
+func (conf *Config) offsetof(typ Type, index []int) int64 {
+ var o int64
+ for _, i := range index {
+ s := typ.Struct()
+ o += conf.offsetsof(s)[i]
+ typ = s.fields[i].typ
+ }
+ return o
+}
+
+func (conf *Config) sizeof(T Type) int64 {
+ if s := conf.Sizes; s != nil {
+ if z := s.Sizeof(T); z >= 0 {
+ return z
+ }
+ panic("Config.Sizes.Sizeof returned a size < 0")
+ }
+ return stdSizes.Sizeof(T)
+}
+
+// align returns the smallest y >= x such that y % a == 0.
+func align(x, a int64) int64 {
+ y := x + a - 1
+ return y - y%a
+}
diff --git a/src/cmd/compile/internal/types2/sizes_test.go b/src/cmd/compile/internal/types2/sizes_test.go
new file mode 100644
index 0000000000..b246909d2a
--- /dev/null
+++ b/src/cmd/compile/internal/types2/sizes_test.go
@@ -0,0 +1,108 @@
+// UNREVIEWED
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains tests for sizes.
+
+package types2_test
+
+import (
+ "cmd/compile/internal/syntax"
+ "cmd/compile/internal/types2"
+ "testing"
+)
+
+// findStructType typechecks src and returns the first struct type encountered.
+func findStructType(t *testing.T, src string) *types2.Struct {
+ f, err := parseSrc("x.go", src)
+ if err != nil {
+ t.Fatal(err)
+ }
+ info := types2.Info{Types: make(map[syntax.Expr]types2.TypeAndValue)}
+ var conf types2.Config
+ _, err = conf.Check("x", []*syntax.File{f}, &info)
+ if err != nil {
+ t.Fatal(err)
+ }
+ for _, tv := range info.Types {
+ if ts, ok := tv.Type.(*types2.Struct); ok {
+ return ts
+ }
+ }
+ t.Fatalf("failed to find a struct type in src:\n%s\n", src)
+ return nil
+}
+
+// Issue 16316
+func TestMultipleSizeUse(t *testing.T) {
+ const src = `
+package main
+
+type S struct {
+ i int
+ b bool
+ s string
+ n int
+}
+`
+ ts := findStructType(t, src)
+ sizes := types2.StdSizes{WordSize: 4, MaxAlign: 4}
+ if got := sizes.Sizeof(ts); got != 20 {
+ t.Errorf("Sizeof(%v) with WordSize 4 = %d want 20", ts, got)
+ }
+ sizes = types2.StdSizes{WordSize: 8, MaxAlign: 8}
+ if got := sizes.Sizeof(ts); got != 40 {
+ t.Errorf("Sizeof(%v) with WordSize 8 = %d want 40", ts, got)
+ }
+}
+
+// Issue 16464
+func TestAlignofNaclSlice(t *testing.T) {
+ const src = `
+package main
+
+var s struct {
+ x *int
+ y []byte
+}
+`
+ ts := findStructType(t, src)
+ sizes := &types2.StdSizes{WordSize: 4, MaxAlign: 8}
+ var fields []*types2.Var
+ // Make a copy manually :(
+ for i := 0; i < ts.NumFields(); i++ {
+ fields = append(fields, ts.Field(i))
+ }
+ offsets := sizes.Offsetsof(fields)
+ if offsets[0] != 0 || offsets[1] != 4 {
+ t.Errorf("OffsetsOf(%v) = %v want %v", ts, offsets, []int{0, 4})
+ }
+}
+
+func TestIssue16902(t *testing.T) {
+ const src = `
+package a
+
+import "unsafe"
+
+const _ = unsafe.Offsetof(struct{ x int64 }{}.x)
+`
+ f, err := parseSrc("x.go", src)
+ if err != nil {
+ t.Fatal(err)
+ }
+ info := types2.Info{Types: make(map[syntax.Expr]types2.TypeAndValue)}
+ conf := types2.Config{
+ Importer: defaultImporter(),
+ Sizes: &types2.StdSizes{WordSize: 8, MaxAlign: 8},
+ }
+ _, err = conf.Check("x", []*syntax.File{f}, &info)
+ if err != nil {
+ t.Fatal(err)
+ }
+ for _, tv := range info.Types {
+ _ = conf.Sizes.Sizeof(tv.Type)
+ _ = conf.Sizes.Alignof(tv.Type)
+ }
+}
diff --git a/src/cmd/compile/internal/types2/stdlib_test.go b/src/cmd/compile/internal/types2/stdlib_test.go
new file mode 100644
index 0000000000..bc8b81998a
--- /dev/null
+++ b/src/cmd/compile/internal/types2/stdlib_test.go
@@ -0,0 +1,320 @@
+// UNREVIEWED
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file tests types.Check by using it to
+// typecheck the standard library and tests.
+
+package types2_test
+
+import (
+ "bytes"
+ "cmd/compile/internal/syntax"
+ "fmt"
+ "go/build"
+ "internal/testenv"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+ "time"
+
+ . "cmd/compile/internal/types2"
+)
+
+var stdLibImporter = defaultImporter()
+
+func TestStdlib(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ pkgCount := 0
+ duration := walkPkgDirs(filepath.Join(runtime.GOROOT(), "src"), func(dir string, filenames []string) {
+ typecheck(t, dir, filenames)
+ pkgCount++
+ }, t.Error)
+
+ if testing.Verbose() {
+ fmt.Println(pkgCount, "packages typechecked in", duration)
+ }
+}
+
+// firstComment returns the contents of the first non-empty comment in
+// the given file, "skip", or the empty string. No matter the present
+// comments, if any of them contains a build tag, the result is always
+// "skip". Only comments within the first 4K of the file are considered.
+// TODO(gri) should only read until we see "package" token.
+func firstComment(filename string) (first string) {
+ f, err := os.Open(filename)
+ if err != nil {
+ return ""
+ }
+ defer f.Close()
+
+ // read at most 4KB
+ var buf [4 << 10]byte
+ n, _ := f.Read(buf[:])
+ src := bytes.NewBuffer(buf[:n])
+
+ // TODO(gri) we need a better way to terminate CommentsDo
+ defer func() {
+ if p := recover(); p != nil {
+ if s, ok := p.(string); ok {
+ first = s
+ }
+ }
+ }()
+
+ syntax.CommentsDo(src, func(_, _ uint, text string) {
+ if text[0] != '/' {
+ return // not a comment
+ }
+
+ // extract comment text
+ if text[1] == '*' {
+ text = text[:len(text)-2]
+ }
+ text = strings.TrimSpace(text[2:])
+
+ if strings.HasPrefix(text, "+build ") {
+ panic("skip")
+ }
+ if first == "" {
+ first = text // text may be "" but that's ok
+ }
+ // continue as we may still see build tags
+ })
+
+ return
+}
+
+func testTestDir(t *testing.T, path string, ignore ...string) {
+ files, err := ioutil.ReadDir(path)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ excluded := make(map[string]bool)
+ for _, filename := range ignore {
+ excluded[filename] = true
+ }
+
+ for _, f := range files {
+ // filter directory contents
+ if f.IsDir() || !strings.HasSuffix(f.Name(), ".go") || excluded[f.Name()] {
+ continue
+ }
+
+ // get per-file instructions
+ expectErrors := false
+ filename := filepath.Join(path, f.Name())
+ if comment := firstComment(filename); comment != "" {
+ fields := strings.Fields(comment)
+ switch fields[0] {
+ case "skip", "compiledir":
+ continue // ignore this file
+ case "errorcheck":
+ expectErrors = true
+ for _, arg := range fields[1:] {
+ if arg == "-0" || arg == "-+" || arg == "-std" {
+ // Marked explicitly as not expected errors (-0),
+ // or marked as compiling runtime/stdlib, which is only done
+ // to trigger runtime/stdlib-only error output.
+ // In both cases, the code should typecheck.
+ expectErrors = false
+ break
+ }
+ }
+ }
+ }
+
+ // parse and type-check file
+ if testing.Verbose() {
+ fmt.Println("\t", filename)
+ }
+ file, err := syntax.ParseFile(filename, nil, nil, 0)
+ if err == nil {
+ conf := Config{Importer: stdLibImporter}
+ _, err = conf.Check(filename, []*syntax.File{file}, nil)
+ }
+
+ if expectErrors {
+ if err == nil {
+ t.Errorf("expected errors but found none in %s", filename)
+ }
+ } else {
+ if err != nil {
+ t.Error(err)
+ }
+ }
+ }
+}
+
+func TestStdTest(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if testing.Short() && testenv.Builder() == "" {
+ t.Skip("skipping in short mode")
+ }
+
+ testTestDir(t, filepath.Join(runtime.GOROOT(), "test"),
+ "cmplxdivide.go", // also needs file cmplxdivide1.go - ignore
+ "directive.go", // tests compiler rejection of bad directive placement - ignore
+ )
+}
+
+func TestStdFixed(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ if testing.Short() && testenv.Builder() == "" {
+ t.Skip("skipping in short mode")
+ }
+
+ testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "fixedbugs"),
+ "bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore
+ "issue6889.go", // gc-specific test
+ "issue7746.go", // large constants - consumes too much memory
+ "issue11362.go", // canonical import path check
+ "issue16369.go", // go/types handles this correctly - not an issue
+ "issue18459.go", // go/types doesn't check validity of //go:xxx directives
+ "issue18882.go", // go/types doesn't check validity of //go:xxx directives
+ "issue20232.go", // go/types handles larger constants than gc
+ "issue20529.go", // go/types does not have constraints on stack size
+ "issue22200.go", // go/types does not have constraints on stack size
+ "issue22200b.go", // go/types does not have constraints on stack size
+ "issue25507.go", // go/types does not have constraints on stack size
+ "issue20780.go", // go/types does not have constraints on stack size
+ "issue31747.go", // go/types does not have constraints on language level (-lang=go1.12) (see #31793)
+ "issue34329.go", // go/types does not have constraints on language level (-lang=go1.13) (see #31793)
+ "bug251.go", // issue #34333 which was exposed with fix for #34151
+ )
+}
+
+func TestStdKen(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "ken"))
+}
+
+// Package paths of excluded packages.
+var excluded = map[string]bool{
+ "builtin": true,
+}
+
+// typecheck typechecks the given package files.
+func typecheck(t *testing.T, path string, filenames []string) {
+ // parse package files
+ var files []*syntax.File
+ for _, filename := range filenames {
+ errh := func(err error) { t.Error(err) }
+ file, err := syntax.ParseFile(filename, errh, nil, 0)
+ if err != nil {
+ return
+ }
+
+ if testing.Verbose() {
+ if len(files) == 0 {
+ fmt.Println("package", file.PkgName.Value)
+ }
+ fmt.Println("\t", filename)
+ }
+
+ files = append(files, file)
+ }
+
+ // typecheck package files
+ conf := Config{
+ Error: func(err error) { t.Error(err) },
+ Importer: stdLibImporter,
+ }
+ info := Info{Uses: make(map[*syntax.Name]Object)}
+ conf.Check(path, files, &info)
+
+ // Perform checks of API invariants.
+
+ // All Objects have a package, except predeclared ones.
+ errorError := Universe.Lookup("error").Type().Interface().ExplicitMethod(0) // (error).Error
+ for id, obj := range info.Uses {
+ predeclared := obj == Universe.Lookup(obj.Name()) || obj == errorError
+ if predeclared == (obj.Pkg() != nil) {
+ posn := id.Pos()
+ if predeclared {
+ t.Errorf("%s: predeclared object with package: %s", posn, obj)
+ } else {
+ t.Errorf("%s: user-defined object without package: %s", posn, obj)
+ }
+ }
+ }
+}
+
+// pkgFilenames returns the list of package filenames for the given directory.
+func pkgFilenames(dir string) ([]string, error) {
+ ctxt := build.Default
+ ctxt.CgoEnabled = false
+ pkg, err := ctxt.ImportDir(dir, 0)
+ if err != nil {
+ if _, nogo := err.(*build.NoGoError); nogo {
+ return nil, nil // no *.go files, not an error
+ }
+ return nil, err
+ }
+ if excluded[pkg.ImportPath] {
+ return nil, nil
+ }
+ var filenames []string
+ for _, name := range pkg.GoFiles {
+ filenames = append(filenames, filepath.Join(pkg.Dir, name))
+ }
+ for _, name := range pkg.TestGoFiles {
+ filenames = append(filenames, filepath.Join(pkg.Dir, name))
+ }
+ return filenames, nil
+}
+
+func walkPkgDirs(dir string, pkgh func(dir string, filenames []string), errh func(args ...interface{})) time.Duration {
+ w := walker{time.Now(), 10 * time.Millisecond, pkgh, errh}
+ w.walk(dir)
+ return time.Since(w.start)
+}
+
+type walker struct {
+ start time.Time
+ dmax time.Duration
+ pkgh func(dir string, filenames []string)
+ errh func(args ...interface{})
+}
+
+func (w *walker) walk(dir string) {
+ // limit run time for short tests
+ if testing.Short() && time.Since(w.start) >= w.dmax {
+ return
+ }
+
+ fis, err := ioutil.ReadDir(dir)
+ if err != nil {
+ w.errh(err)
+ return
+ }
+
+ // apply pkgh to the files in directory dir
+ // but ignore files directly under $GOROOT/src (might be temporary test files).
+ if dir != filepath.Join(runtime.GOROOT(), "src") {
+ files, err := pkgFilenames(dir)
+ if err != nil {
+ w.errh(err)
+ return
+ }
+ if files != nil {
+ w.pkgh(dir, files)
+ }
+ }
+
+ // traverse subdirectories, but don't walk into testdata
+ for _, fi := range fis {
+ if fi.IsDir() && fi.Name() != "testdata" {
+ w.walk(filepath.Join(dir, fi.Name()))
+ }
+ }
+}
diff --git a/src/cmd/compile/internal/types2/stmt.go b/src/cmd/compile/internal/types2/stmt.go
new file mode 100644
index 0000000000..2f1347faf4
--- /dev/null
+++ b/src/cmd/compile/internal/types2/stmt.go
@@ -0,0 +1,919 @@
+// UNREVIEWED
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements typechecking of statements.
+
+package types2
+
+import (
+ "cmd/compile/internal/syntax"
+ "go/constant"
+ "sort"
+)
+
+func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body *syntax.BlockStmt, iota constant.Value) {
+ if check.conf.Trace {
+ check.trace(body.Pos(), "--- %s: %s", name, sig)
+ defer func() {
+ check.trace(endPos(body), "--- <end>")
+ }()
+ }
+
+ // set function scope extent
+ sig.scope.pos = body.Pos()
+ sig.scope.end = endPos(body)
+
+ // save/restore current context and setup function context
+ // (and use 0 indentation at function start)
+ defer func(ctxt context, indent int) {
+ check.context = ctxt
+ check.indent = indent
+ }(check.context, check.indent)
+ check.context = context{
+ decl: decl,
+ scope: sig.scope,
+ iota: iota,
+ sig: sig,
+ }
+ check.indent = 0
+
+ check.stmtList(0, body.List)
+
+ if check.hasLabel {
+ check.labels(body)
+ }
+
+ if sig.results.Len() > 0 && !check.isTerminating(body, "") {
+ check.error(body.Rbrace, "missing return")
+ }
+
+ // TODO(gri) Should we make it an error to declare generic functions
+ // where the type parameters are not used?
+ // 12/19/2018: Probably not - it can make sense to have an API with
+ // all functions uniformly sharing the same type parameters.
+
+ // spec: "Implementation restriction: A compiler may make it illegal to
+ // declare a variable inside a function body if the variable is never used."
+ check.usage(sig.scope)
+}
+
+func (check *Checker) usage(scope *Scope) {
+ var unused []*Var
+ for _, elem := range scope.elems {
+ if v, _ := elem.(*Var); v != nil && !v.used {
+ unused = append(unused, v)
+ }
+ }
+ sort.Slice(unused, func(i, j int) bool {
+ return cmpPos(unused[i].pos, unused[j].pos) < 0
+ })
+ for _, v := range unused {
+ check.softErrorf(v.pos, "%s declared but not used", v.name)
+ }
+
+ for _, scope := range scope.children {
+ // Don't go inside function literal scopes a second time;
+ // they are handled explicitly by funcBody.
+ if !scope.isFunc {
+ check.usage(scope)
+ }
+ }
+}
+
+// stmtContext is a bitset describing which
+// control-flow statements are permissible,
+// and provides additional context information
+// for better error messages.
+type stmtContext uint
+
+const (
+ // permissible control-flow statements
+ breakOk stmtContext = 1 << iota
+ continueOk
+ fallthroughOk
+
+ // additional context information
+ finalSwitchCase
+)
+
+func (check *Checker) simpleStmt(s syntax.Stmt) {
+ if s != nil {
+ check.stmt(0, s)
+ }
+}
+
+func trimTrailingEmptyStmts(list []syntax.Stmt) []syntax.Stmt {
+ for i := len(list); i > 0; i-- {
+ if _, ok := list[i-1].(*syntax.EmptyStmt); !ok {
+ return list[:i]
+ }
+ }
+ return nil
+}
+
+func (check *Checker) stmtList(ctxt stmtContext, list []syntax.Stmt) {
+ ok := ctxt&fallthroughOk != 0
+ inner := ctxt &^ fallthroughOk
+ list = trimTrailingEmptyStmts(list) // trailing empty statements are "invisible" to fallthrough analysis
+ for i, s := range list {
+ inner := inner
+ if ok && i+1 == len(list) {
+ inner |= fallthroughOk
+ }
+ check.stmt(inner, s)
+ }
+}
+
+func (check *Checker) multipleSwitchDefaults(list []*syntax.CaseClause) {
+ var first *syntax.CaseClause
+ for _, c := range list {
+ if c.Cases == nil {
+ if first != nil {
+ check.errorf(c, "multiple defaults (first at %s)", first.Pos())
+ // TODO(gri) probably ok to bail out after first error (and simplify this code)
+ } else {
+ first = c
+ }
+ }
+ }
+}
+
+func (check *Checker) multipleSelectDefaults(list []*syntax.CommClause) {
+ var first *syntax.CommClause
+ for _, c := range list {
+ if c.Comm == nil {
+ if first != nil {
+ check.errorf(c, "multiple defaults (first at %s)", first.Pos())
+ // TODO(gri) probably ok to bail out after first error (and simplify this code)
+ } else {
+ first = c
+ }
+ }
+ }
+}
+
+func (check *Checker) openScope(node syntax.Node, comment string) {
+ scope := NewScope(check.scope, node.Pos(), endPos(node), comment)
+ check.recordScope(node, scope)
+ check.scope = scope
+}
+
+func (check *Checker) closeScope() {
+ check.scope = check.scope.Parent()
+}
+
+func (check *Checker) suspendedCall(keyword string, call *syntax.CallExpr) {
+ var x operand
+ var msg string
+ switch check.rawExpr(&x, call, nil) {
+ case conversion:
+ msg = "requires function call, not conversion"
+ case expression:
+ msg = "discards result of"
+ case statement:
+ return
+ default:
+ unreachable()
+ }
+ check.errorf(&x, "%s %s %s", keyword, msg, &x)
+}
+
+// goVal returns the Go value for val, or nil.
+func goVal(val constant.Value) interface{} {
+ // val should exist, but be conservative and check
+ if val == nil {
+ return nil
+ }
+ // Match implementation restriction of other compilers.
+ // gc only checks duplicates for integer, floating-point
+ // and string values, so only create Go values for these
+ // types.
+ switch val.Kind() {
+ case constant.Int:
+ if x, ok := constant.Int64Val(val); ok {
+ return x
+ }
+ if x, ok := constant.Uint64Val(val); ok {
+ return x
+ }
+ case constant.Float:
+ if x, ok := constant.Float64Val(val); ok {
+ return x
+ }
+ case constant.String:
+ return constant.StringVal(val)
+ }
+ return nil
+}
+
+// A valueMap maps a case value (of a basic Go type) to a list of positions
+// where the same case value appeared, together with the corresponding case
+// types.
+// Since two case values may have the same "underlying" value but different
+// types we need to also check the value's types (e.g., byte(1) vs myByte(1))
+// when the switch expression is of interface type.
+type (
+ valueMap map[interface{}][]valueType // underlying Go value -> valueType
+ valueType struct {
+ pos syntax.Pos
+ typ Type
+ }
+)
+
+func (check *Checker) caseValues(x *operand, values []syntax.Expr, seen valueMap) {
+L:
+ for _, e := range values {
+ var v operand
+ check.expr(&v, e)
+ if x.mode == invalid || v.mode == invalid {
+ continue L
+ }
+ check.convertUntyped(&v, x.typ)
+ if v.mode == invalid {
+ continue L
+ }
+ // Order matters: By comparing v against x, error positions are at the case values.
+ res := v // keep original v unchanged
+ check.comparison(&res, x, syntax.Eql)
+ if res.mode == invalid {
+ continue L
+ }
+ if v.mode != constant_ {
+ continue L // we're done
+ }
+ // look for duplicate values
+ if val := goVal(v.val); val != nil {
+ // look for duplicate types for a given value
+ // (quadratic algorithm, but these lists tend to be very short)
+ for _, vt := range seen[val] {
+ if check.identical(v.typ, vt.typ) {
+ check.errorf(&v, "duplicate case %s in expression switch", &v)
+ check.error(vt.pos, "\tprevious case") // secondary error, \t indented
+ continue L
+ }
+ }
+ seen[val] = append(seen[val], valueType{v.Pos(), v.typ})
+ }
+ }
+}
+
+func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []syntax.Expr, seen map[Type]syntax.Pos, strict bool) (T Type) {
+L:
+ for _, e := range types {
+ T = check.typOrNil(e)
+ if T == Typ[Invalid] {
+ continue L
+ }
+ if T != nil {
+ check.ordinaryType(e.Pos(), T)
+ }
+ // look for duplicate types
+ // (quadratic algorithm, but type switches tend to be reasonably small)
+ for t, pos := range seen {
+ if T == nil && t == nil || T != nil && t != nil && check.identical(T, t) {
+ // talk about "case" rather than "type" because of nil case
+ Ts := "nil"
+ if T != nil {
+ Ts = T.String()
+ }
+ check.errorf(e, "duplicate case %s in type switch", Ts)
+ check.error(pos, "\tprevious case") // secondary error, \t indented
+ continue L
+ }
+ }
+ seen[T] = e.Pos()
+ if T != nil {
+ check.typeAssertion(e.Pos(), x, xtyp, T, strict)
+ }
+ }
+ return
+}
+
+// stmt typechecks statement s.
+func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) {
+ // statements must end with the same top scope as they started with
+ if debug {
+ defer func(scope *Scope) {
+ // don't check if code is panicking
+ if p := recover(); p != nil {
+ panic(p)
+ }
+ assert(scope == check.scope)
+ }(check.scope)
+ }
+
+ // process collected function literals before scope changes
+ defer check.processDelayed(len(check.delayed))
+
+ inner := ctxt &^ (fallthroughOk | finalSwitchCase)
+ switch s := s.(type) {
+ case *syntax.EmptyStmt:
+ // ignore
+
+ case *syntax.DeclStmt:
+ check.declStmt(s.DeclList)
+
+ case *syntax.LabeledStmt:
+ check.hasLabel = true
+ check.stmt(ctxt, s.Stmt)
+
+ case *syntax.ExprStmt:
+ // spec: "With the exception of specific built-in functions,
+ // function and method calls and receive operations can appear
+ // in statement context. Such statements may be parenthesized."
+ var x operand
+ kind := check.rawExpr(&x, s.X, nil)
+ var msg string
+ switch x.mode {
+ default:
+ if kind == statement {
+ return
+ }
+ msg = "is not used"
+ case builtin:
+ msg = "must be called"
+ case typexpr:
+ msg = "is not an expression"
+ }
+ check.errorf(&x, "%s %s", &x, msg)
+
+ case *syntax.SendStmt:
+ var ch, x operand
+ check.expr(&ch, s.Chan)
+ check.expr(&x, s.Value)
+ if ch.mode == invalid || x.mode == invalid {
+ return
+ }
+
+ tch := ch.typ.Chan()
+ if tch == nil {
+ check.invalidOpf(s, "cannot send to non-chan type %s", ch.typ)
+ return
+ }
+
+ if tch.dir == RecvOnly {
+ check.invalidOpf(s, "cannot send to receive-only type %s", tch)
+ return
+ }
+
+ check.assignment(&x, tch.elem, "send")
+
+ case *syntax.AssignStmt:
+ lhs := unpackExpr(s.Lhs)
+ rhs := unpackExpr(s.Rhs)
+ if s.Op == 0 || s.Op == syntax.Def {
+ // regular assignment or short variable declaration
+ if len(lhs) == 0 {
+ check.invalidASTf(s, "missing lhs in assignment")
+ return
+ }
+ if s.Op == syntax.Def {
+ check.shortVarDecl(s.Pos(), lhs, rhs)
+ } else {
+ // regular assignment
+ check.assignVars(lhs, rhs)
+ }
+ } else {
+ // assignment operations
+ if len(lhs) != 1 || len(rhs) != 1 {
+ check.errorf(s, "assignment operation %s requires single-valued expressions", s.Op)
+ return
+ }
+
+ // provide better error messages for x++ and x--
+ if rhs[0] == syntax.ImplicitOne {
+ var x operand
+ check.expr(&x, lhs[0])
+ if x.mode == invalid {
+ return
+ }
+ if !isNumeric(x.typ) {
+ check.invalidOpf(lhs[0], "%s%s%s (non-numeric type %s)", lhs[0], s.Op, s.Op, x.typ)
+ return
+ }
+ }
+
+ var x operand
+ check.binary(&x, nil, lhs[0], rhs[0], s.Op)
+ if x.mode == invalid {
+ return
+ }
+ check.assignVar(lhs[0], &x)
+ }
+
+ // case *syntax.GoStmt:
+ // check.suspendedCall("go", s.Call)
+
+ // case *syntax.DeferStmt:
+ // check.suspendedCall("defer", s.Call)
+ case *syntax.CallStmt:
+ // TODO(gri) get rid of this conversion to string
+ kind := "go"
+ if s.Tok == syntax.Defer {
+ kind = "defer"
+ }
+ check.suspendedCall(kind, s.Call)
+
+ case *syntax.ReturnStmt:
+ res := check.sig.results
+ results := unpackExpr(s.Results)
+ if res.Len() > 0 {
+ // function returns results
+ // (if one, say the first, result parameter is named, all of them are named)
+ if len(results) == 0 && res.vars[0].name != "" {
+ // spec: "Implementation restriction: A compiler may disallow an empty expression
+ // list in a "return" statement if a different entity (constant, type, or variable)
+ // with the same name as a result parameter is in scope at the place of the return."
+ for _, obj := range res.vars {
+ if alt := check.lookup(obj.name); alt != nil && alt != obj {
+ check.errorf(s, "result parameter %s not in scope at return", obj.name)
+ check.errorf(alt, "\tinner declaration of %s", obj)
+ // ok to continue
+ }
+ }
+ } else {
+ // return has results or result parameters are unnamed
+ check.initVars(res.vars, results, s.Pos())
+ }
+ } else if len(results) > 0 {
+ check.error(results[0], "no result values expected")
+ check.use(results...)
+ }
+
+ case *syntax.BranchStmt:
+ if s.Label != nil {
+ check.hasLabel = true
+ return // checked in 2nd pass (check.labels)
+ }
+ switch s.Tok {
+ case syntax.Break:
+ if ctxt&breakOk == 0 {
+ check.error(s, "break not in for, switch, or select statement")
+ }
+ case syntax.Continue:
+ if ctxt&continueOk == 0 {
+ check.error(s, "continue not in for statement")
+ }
+ case syntax.Fallthrough:
+ if ctxt&fallthroughOk == 0 {
+ msg := "fallthrough statement out of place"
+ if ctxt&finalSwitchCase != 0 {
+ msg = "cannot fallthrough final case in switch"
+ }
+ check.error(s, msg)
+ }
+ default:
+ check.invalidASTf(s, "branch statement: %s", s.Tok)
+ }
+
+ case *syntax.BlockStmt:
+ check.openScope(s, "block")
+ defer check.closeScope()
+
+ check.stmtList(inner, s.List)
+
+ case *syntax.IfStmt:
+ check.openScope(s, "if")
+ defer check.closeScope()
+
+ check.simpleStmt(s.Init)
+ var x operand
+ check.expr(&x, s.Cond)
+ if x.mode != invalid && !isBoolean(x.typ) {
+ check.error(s.Cond, "non-boolean condition in if statement")
+ }
+ check.stmt(inner, s.Then)
+ // The parser produces a correct AST but if it was modified
+ // elsewhere the else branch may be invalid. Check again.
+ switch s.Else.(type) {
+ case nil:
+ // valid or error already reported
+ case *syntax.IfStmt, *syntax.BlockStmt:
+ check.stmt(inner, s.Else)
+ default:
+ check.error(s.Else, "invalid else branch in if statement")
+ }
+
+ case *syntax.SwitchStmt:
+ inner |= breakOk
+ check.openScope(s, "switch")
+ defer check.closeScope()
+
+ check.simpleStmt(s.Init)
+
+ if g, _ := s.Tag.(*syntax.TypeSwitchGuard); g != nil {
+ check.typeSwitchStmt(inner, s, g)
+ } else {
+ check.switchStmt(inner, s)
+ }
+
+ case *syntax.SelectStmt:
+ inner |= breakOk
+
+ check.multipleSelectDefaults(s.Body)
+
+ for _, clause := range s.Body {
+ if clause == nil {
+ continue // error reported before
+ }
+
+ // clause.Comm must be a SendStmt, RecvStmt, or default case
+ valid := false
+ var rhs syntax.Expr // rhs of RecvStmt, or nil
+ switch s := clause.Comm.(type) {
+ case nil, *syntax.SendStmt:
+ valid = true
+ case *syntax.AssignStmt:
+ if _, ok := s.Rhs.(*syntax.ListExpr); !ok {
+ rhs = s.Rhs
+ }
+ case *syntax.ExprStmt:
+ rhs = s.X
+ }
+
+ // if present, rhs must be a receive operation
+ if rhs != nil {
+ if x, _ := unparen(rhs).(*syntax.Operation); x != nil && x.Y == nil && x.Op == syntax.Recv {
+ valid = true
+ }
+ }
+
+ if !valid {
+ check.error(clause.Comm, "select case must be send or receive (possibly with assignment)")
+ continue
+ }
+
+ check.openScope(s, "case")
+ if clause.Comm != nil {
+ check.stmt(inner, clause.Comm)
+ }
+ check.stmtList(inner, clause.Body)
+ check.closeScope()
+ }
+
+ case *syntax.ForStmt:
+ inner |= breakOk | continueOk
+ check.openScope(s, "for")
+ defer check.closeScope()
+
+ if rclause, _ := s.Init.(*syntax.RangeClause); rclause != nil {
+ check.rangeStmt(inner, s, rclause)
+ break
+ }
+
+ check.simpleStmt(s.Init)
+ if s.Cond != nil {
+ var x operand
+ check.expr(&x, s.Cond)
+ if x.mode != invalid && !isBoolean(x.typ) {
+ check.error(s.Cond, "non-boolean condition in for statement")
+ }
+ }
+ check.simpleStmt(s.Post)
+ // spec: "The init statement may be a short variable
+ // declaration, but the post statement must not."
+ if s, _ := s.Post.(*syntax.AssignStmt); s != nil && s.Op == syntax.Def {
+ // The parser already reported an error.
+ // Don't call useLHS here because we want to use the lhs in
+ // this erroneous statement so that we don't get errors about
+ // these lhs variables being declared but not used.
+ check.use(s.Lhs) // avoid follow-up errors
+ }
+ check.stmt(inner, s.Body)
+
+ default:
+ check.error(s, "invalid statement")
+ }
+}
+
+func newName(pos syntax.Pos, value string) *syntax.Name {
+ n := new(syntax.Name)
+ // TODO(gri) why does this not work?
+ //n.pos = pos
+ n.Value = value
+ return n
+}
+
+func (check *Checker) switchStmt(inner stmtContext, s *syntax.SwitchStmt) {
+ // init statement already handled
+
+ var x operand
+ if s.Tag != nil {
+ check.expr(&x, s.Tag)
+ // By checking assignment of x to an invisible temporary
+ // (as a compiler would), we get all the relevant checks.
+ check.assignment(&x, nil, "switch expression")
+ } else {
+ // spec: "A missing switch expression is
+ // equivalent to the boolean value true."
+ x.mode = constant_
+ x.typ = Typ[Bool]
+ x.val = constant.MakeBool(true)
+ // TODO(gri) should have a better position here
+ pos := s.Rbrace
+ if len(s.Body) > 0 {
+ pos = s.Body[0].Pos()
+ }
+ x.expr = newName(pos, "true")
+ }
+
+ check.multipleSwitchDefaults(s.Body)
+
+ seen := make(valueMap) // map of seen case values to positions and types
+ for i, clause := range s.Body {
+ if clause == nil {
+ check.invalidASTf(clause, "incorrect expression switch case")
+ continue
+ }
+ check.caseValues(&x, unpackExpr(clause.Cases), seen)
+ check.openScope(clause, "case")
+ inner := inner
+ if i+1 < len(s.Body) {
+ inner |= fallthroughOk
+ } else {
+ inner |= finalSwitchCase
+ }
+ check.stmtList(inner, clause.Body)
+ check.closeScope()
+ }
+}
+
+func (check *Checker) typeSwitchStmt(inner stmtContext, s *syntax.SwitchStmt, guard *syntax.TypeSwitchGuard) {
+ // init statement already handled
+
+ // A type switch guard must be of the form:
+ //
+ // TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" .
+ // \__lhs__/ \___rhs___/
+
+ // check lhs, if any
+ lhs := guard.Lhs
+ if lhs != nil {
+ if lhs.Value == "_" {
+ // _ := x.(type) is an invalid short variable declaration
+ check.softErrorf(lhs, "no new variable on left side of :=")
+ lhs = nil // avoid declared but not used error below
+ } else {
+ check.recordDef(lhs, nil) // lhs variable is implicitly declared in each cause clause
+ }
+ }
+
+ // check rhs
+ var x operand
+ check.expr(&x, guard.X)
+ if x.mode == invalid {
+ return
+ }
+ var xtyp *Interface
+ var strict bool
+ switch t := x.typ.Under().(type) {
+ case *Interface:
+ xtyp = t
+ // Disabled for now. See comment in the implementation of type assertions (expr.go).
+ // case *TypeParam:
+ // xtyp = t.Bound()
+ // strict = true
+ default:
+ check.errorf(&x, "%s is not an interface or generic type", &x)
+ return
+ }
+
+ check.multipleSwitchDefaults(s.Body)
+
+ var lhsVars []*Var // list of implicitly declared lhs variables
+ seen := make(map[Type]syntax.Pos) // map of seen types to positions
+ for _, clause := range s.Body {
+ if clause == nil {
+ check.invalidASTf(s, "incorrect type switch case")
+ continue
+ }
+ // Check each type in this type switch case.
+ cases := unpackExpr(clause.Cases)
+ T := check.caseTypes(&x, xtyp, cases, seen, strict)
+ check.openScope(clause, "case")
+ // If lhs exists, declare a corresponding variable in the case-local scope.
+ if lhs != nil {
+ // spec: "The TypeSwitchGuard may include a short variable declaration.
+ // When that form is used, the variable is declared at the beginning of
+ // the implicit block in each clause. In clauses with a case listing
+ // exactly one type, the variable has that type; otherwise, the variable
+ // has the type of the expression in the TypeSwitchGuard."
+ if len(cases) != 1 || T == nil {
+ T = x.typ
+ }
+ obj := NewVar(lhs.Pos(), check.pkg, lhs.Value, T)
+ scopePos := clause.Pos() // for default clause (len(List) == 0)
+ if n := len(cases); n > 0 {
+ scopePos = endPos(cases[n-1])
+ }
+ check.declare(check.scope, nil, obj, scopePos)
+ check.recordImplicit(clause, obj)
+ // For the "declared but not used" error, all lhs variables act as
+ // one; i.e., if any one of them is 'used', all of them are 'used'.
+ // Collect them for later analysis.
+ lhsVars = append(lhsVars, obj)
+ }
+ check.stmtList(inner, clause.Body)
+ check.closeScope()
+ }
+
+ // If lhs exists, we must have at least one lhs variable that was used.
+ if lhs != nil {
+ var used bool
+ for _, v := range lhsVars {
+ if v.used {
+ used = true
+ }
+ v.used = true // avoid usage error when checking entire function
+ }
+ if !used {
+ check.softErrorf(lhs, "%s declared but not used", lhs.Value)
+ }
+ }
+}
+
+func (check *Checker) rangeStmt(inner stmtContext, s *syntax.ForStmt, rclause *syntax.RangeClause) {
+ // scope already opened
+
+ // check expression to iterate over
+ var x operand
+ check.expr(&x, rclause.X)
+
+ // determine lhs, if any
+ sKey := rclause.Lhs // possibly nil
+ var sValue syntax.Expr
+ if p, _ := sKey.(*syntax.ListExpr); p != nil {
+ if len(p.ElemList) != 2 {
+ check.invalidASTf(s, "invalid lhs in range clause")
+ return
+ }
+ sKey = p.ElemList[0]
+ sValue = p.ElemList[1]
+ }
+
+ // determine key/value types
+ var key, val Type
+ if x.mode != invalid {
+ typ := optype(x.typ.Under())
+ if _, ok := typ.(*Chan); ok && sValue != nil {
+ // TODO(gri) this also needs to happen for channels in generic variables
+ check.softErrorf(sValue, "range over %s permits only one iteration variable", &x)
+ // ok to continue
+ }
+ var msg string
+ key, val, msg = rangeKeyVal(typ, isVarName(sKey), isVarName(sValue))
+ if key == nil || msg != "" {
+ if msg != "" {
+ msg = ": " + msg
+ }
+ check.softErrorf(&x, "cannot range over %s%s", &x, msg)
+ // ok to continue
+ }
+ }
+
+ // check assignment to/declaration of iteration variables
+ // (irregular assignment, cannot easily map to existing assignment checks)
+
+ // lhs expressions and initialization value (rhs) types
+ lhs := [2]syntax.Expr{sKey, sValue}
+ rhs := [2]Type{key, val} // key, val may be nil
+
+ if rclause.Def {
+ // short variable declaration; variable scope starts after the range clause
+ // (the for loop opens a new scope, so variables on the lhs never redeclare
+ // previously declared variables)
+ var vars []*Var
+ for i, lhs := range lhs {
+ if lhs == nil {
+ continue
+ }
+
+ // determine lhs variable
+ var obj *Var
+ if ident, _ := lhs.(*syntax.Name); ident != nil {
+ // declare new variable
+ name := ident.Value
+ obj = NewVar(ident.Pos(), check.pkg, name, nil)
+ check.recordDef(ident, obj)
+ // _ variables don't count as new variables
+ if name != "_" {
+ vars = append(vars, obj)
+ }
+ } else {
+ check.errorf(lhs, "cannot declare %s", lhs)
+ obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable
+ }
+
+ // initialize lhs variable
+ if typ := rhs[i]; typ != nil {
+ x.mode = value
+ x.expr = lhs // we don't have a better rhs expression to use here
+ x.typ = typ
+ check.initVar(obj, &x, "range clause")
+ } else {
+ obj.typ = Typ[Invalid]
+ obj.used = true // don't complain about unused variable
+ }
+ }
+
+ // declare variables
+ if len(vars) > 0 {
+ scopePos := endPos(rclause.X) // TODO(gri) should this just be s.Body.Pos (spec clarification)?
+ for _, obj := range vars {
+ // spec: "The scope of a constant or variable identifier declared inside
+ // a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl
+ // for short variable declarations) and ends at the end of the innermost
+ // containing block."
+ check.declare(check.scope, nil /* recordDef already called */, obj, scopePos)
+ }
+ } else {
+ check.error(s, "no new variables on left side of :=")
+ }
+ } else {
+ // ordinary assignment
+ for i, lhs := range lhs {
+ if lhs == nil {
+ continue
+ }
+ if typ := rhs[i]; typ != nil {
+ x.mode = value
+ x.expr = lhs // we don't have a better rhs expression to use here
+ x.typ = typ
+ check.assignVar(lhs, &x)
+ }
+ }
+ }
+
+ check.stmt(inner, s.Body)
+}
+
+// isVarName reports whether x is a non-nil, non-blank (_) expression.
+func isVarName(x syntax.Expr) bool {
+ if x == nil {
+ return false
+ }
+ ident, _ := unparen(x).(*syntax.Name)
+ return ident == nil || ident.Value != "_"
+}
+
+// rangeKeyVal returns the key and value type produced by a range clause
+// over an expression of type typ, and possibly an error message. If the
+// range clause is not permitted the returned key is nil or msg is not
+// empty (in that case we still may have a non-nil key type which can be
+// used to reduce the chance for follow-on errors).
+// The wantKey, wantVal, and hasVal flags indicate which of the iteration
+// variables are used or present; this matters if we range over a generic
+// type where not all keys or values are of the same type.
+func rangeKeyVal(typ Type, wantKey, wantVal bool) (Type, Type, string) {
+ switch typ := typ.(type) {
+ case *Basic:
+ if isString(typ) {
+ return Typ[Int], universeRune, "" // use 'rune' name
+ }
+ case *Array:
+ return Typ[Int], typ.elem, ""
+ case *Slice:
+ return Typ[Int], typ.elem, ""
+ case *Pointer:
+ if typ := typ.base.Array(); typ != nil {
+ return Typ[Int], typ.elem, ""
+ }
+ case *Map:
+ return typ.key, typ.elem, ""
+ case *Chan:
+ var msg string
+ if typ.dir == SendOnly {
+ msg = "send-only channel"
+ }
+ return typ.elem, Typ[Invalid], msg
+ case *Sum:
+ first := true
+ var key, val Type
+ var msg string
+ typ.is(func(t Type) bool {
+ k, v, m := rangeKeyVal(t.Under(), wantKey, wantVal)
+ if k == nil || m != "" {
+ key, val, msg = k, v, m
+ return false
+ }
+ if first {
+ key, val, msg = k, v, m
+ first = false
+ return true
+ }
+ if wantKey && !Identical(key, k) {
+ key, val, msg = nil, nil, "all possible values must have the same key type"
+ return false
+ }
+ if wantVal && !Identical(val, v) {
+ key, val, msg = nil, nil, "all possible values must have the same element type"
+ return false
+ }
+ return true
+ })
+ return key, val, msg
+ }
+ return nil, nil, ""
+}
diff --git a/src/cmd/compile/internal/types2/subst.go b/src/cmd/compile/internal/types2/subst.go
new file mode 100644
index 0000000000..27c907de10
--- /dev/null
+++ b/src/cmd/compile/internal/types2/subst.go
@@ -0,0 +1,554 @@
+// UNREVIEWED
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements instantiation of generic types
+// through substitution of type parameters by actual
+// types.
+
+package types2
+
+import (
+ "bytes"
+ "cmd/compile/internal/syntax"
+ "fmt"
+)
+
+type substMap struct {
+ // The targs field is currently needed for *Named type substitution.
+ // TODO(gri) rewrite that code, get rid of this field, and make this
+ // struct just the map (proj)
+ targs []Type
+ proj map[*TypeParam]Type
+}
+
+// makeSubstMap creates a new substitution map mapping tpars[i] to targs[i].
+// If targs[i] is nil, tpars[i] is not substituted.
+func makeSubstMap(tpars []*TypeName, targs []Type) *substMap {
+ assert(len(tpars) == len(targs))
+ proj := make(map[*TypeParam]Type, len(tpars))
+ for i, tpar := range tpars {
+ // We must expand type arguments otherwise *Instance
+ // types end up as components in composite types.
+ // TODO(gri) explain why this causes problems, if it does
+ targ := expand(targs[i]) // possibly nil
+ targs[i] = targ
+ proj[tpar.typ.(*TypeParam)] = targ
+ }
+ return &substMap{targs, proj}
+}
+
+func (m *substMap) String() string {
+ return fmt.Sprintf("%s", m.proj)
+}
+
+func (m *substMap) empty() bool {
+ return len(m.proj) == 0
+}
+
+func (m *substMap) lookup(tpar *TypeParam) Type {
+ if t := m.proj[tpar]; t != nil {
+ return t
+ }
+ return tpar
+}
+
+func (check *Checker) instantiate(pos syntax.Pos, typ Type, targs []Type, poslist []syntax.Pos) (res Type) {
+ if check.conf.Trace {
+ check.trace(pos, "-- instantiating %s with %s", typ, typeListString(targs))
+ check.indent++
+ defer func() {
+ check.indent--
+ var under Type
+ if res != nil {
+ // Calling Under() here may lead to endless instantiations.
+ // Test case: type T[P any] T[P]
+ // TODO(gri) investigate if that's a bug or to be expected.
+ under = res.Underlying()
+ }
+ check.trace(pos, "=> %s (under = %s)", res, under)
+ }()
+ }
+
+ assert(poslist == nil || len(poslist) <= len(targs))
+
+ // TODO(gri) What is better here: work with TypeParams, or work with TypeNames?
+ var tparams []*TypeName
+ switch t := typ.(type) {
+ case *Named:
+ tparams = t.tparams
+ case *Signature:
+ tparams = t.tparams
+ defer func() {
+ // If we had an unexpected failure somewhere don't
+ // panic below when asserting res.(*Signature).
+ if res == nil {
+ return
+ }
+ // If the signature doesn't use its type parameters, subst
+ // will not make a copy. In that case, make a copy now (so
+ // we can set tparams to nil w/o causing side-effects).
+ if t == res {
+ copy := *t
+ res = &copy
+ }
+ // After instantiating a generic signature, it is not generic
+ // anymore; we need to set tparams to nil.
+ res.(*Signature).tparams = nil
+ }()
+
+ default:
+ check.dump("%v: cannot instantiate %v", pos, typ)
+ unreachable() // only defined types and (defined) functions can be generic
+
+ }
+
+ // the number of supplied types must match the number of type parameters
+ if len(targs) != len(tparams) {
+ // TODO(gri) provide better error message
+ check.errorf(pos, "got %d arguments but %d type parameters", len(targs), len(tparams))
+ return Typ[Invalid]
+ }
+
+ if len(tparams) == 0 {
+ return typ // nothing to do (minor optimization)
+ }
+
+ smap := makeSubstMap(tparams, targs)
+
+ // check bounds
+ for i, tname := range tparams {
+ tpar := tname.typ.(*TypeParam)
+ iface := tpar.Bound()
+ if iface.Empty() {
+ continue // no type bound
+ }
+
+ targ := targs[i]
+
+ // best position for error reporting
+ pos := pos
+ if i < len(poslist) {
+ pos = poslist[i]
+ }
+
+ // The type parameter bound is parameterized with the same type parameters
+ // as the instantiated type; before we can use it for bounds checking we
+ // need to instantiate it with the type arguments with which we instantiate
+ // the parameterized type.
+ iface = check.subst(pos, iface, smap).(*Interface)
+
+ // targ must implement iface (methods)
+ // - check only if we have methods
+ check.completeInterface(nopos, iface)
+ if len(iface.allMethods) > 0 {
+ // If the type argument is a type parameter itself, its pointer designation
+ // must match the pointer designation of the callee's type parameter.
+ // If the type argument is a pointer to a type parameter, the type argument's
+ // method set is empty.
+ // TODO(gri) is this what we want? (spec question)
+ if tparg := targ.TypeParam(); tparg != nil {
+ if tparg.ptr != tpar.ptr {
+ check.errorf(pos, "pointer designation mismatch")
+ break
+ }
+ } else if base, isPtr := deref(targ); isPtr && base.TypeParam() != nil {
+ check.errorf(pos, "%s has no methods", targ)
+ break
+ }
+ // If a type parameter is marked as a pointer type, the type bound applies
+ // to a pointer of the type argument.
+ actual := targ
+ if tpar.ptr {
+ actual = NewPointer(targ)
+ }
+ if m, wrong := check.missingMethod(actual, iface, true); m != nil {
+ // TODO(gri) needs to print updated name to avoid major confusion in error message!
+ // (print warning for now)
+ // check.softErrorf(pos, "%s does not satisfy %s (warning: name not updated) = %s (missing method %s)", targ, tpar.bound, iface, m)
+ if m.name == "==" {
+ // We don't want to report "missing method ==".
+ check.softErrorf(pos, "%s does not satisfy comparable", targ)
+ } else if wrong != nil {
+ // TODO(gri) This can still report uninstantiated types which makes the error message
+ // more difficult to read then necessary.
+ check.softErrorf(pos,
+ "%s does not satisfy %s: wrong method signature\n\tgot %s\n\twant %s",
+ targ, tpar.bound, wrong, m,
+ )
+ } else {
+ check.softErrorf(pos, "%s does not satisfy %s (missing method %s)", targ, tpar.bound, m.name)
+ }
+ break
+ }
+ }
+
+ // targ's underlying type must also be one of the interface types listed, if any
+ if iface.allTypes == nil {
+ continue // nothing to do
+ }
+ // iface.allTypes != nil
+
+ // If targ is itself a type parameter, each of its possible types, but at least one, must be in the
+ // list of iface types (i.e., the targ type list must be a non-empty subset of the iface types).
+ if targ := targ.TypeParam(); targ != nil {
+ targBound := targ.Bound()
+ if targBound.allTypes == nil {
+ check.softErrorf(pos, "%s does not satisfy %s (%s has no type constraints)", targ, tpar.bound, targ)
+ break
+ }
+ for _, t := range unpack(targBound.allTypes) {
+ if !iface.isSatisfiedBy(t.Under()) {
+ // TODO(gri) match this error message with the one below (or vice versa)
+ check.softErrorf(pos, "%s does not satisfy %s (%s type constraint %s not found in %s)", targ, tpar.bound, targ, t, iface.allTypes)
+ break
+ }
+ }
+ break
+ }
+
+ // Otherwise, targ's type or underlying type must also be one of the interface types listed, if any.
+ if !iface.isSatisfiedBy(targ) {
+ check.softErrorf(pos, "%s does not satisfy %s (%s not found in %s)", targ, tpar.bound, targ.Under(), iface.allTypes)
+ break
+ }
+ }
+
+ return check.subst(pos, typ, smap)
+}
+
+// subst returns the type typ with its type parameters tpars replaced by
+// the corresponding type arguments targs, recursively.
+// subst is functional in the sense that it doesn't modify the incoming
+// type. If a substitution took place, the result type is different from
+// from the incoming type.
+func (check *Checker) subst(pos syntax.Pos, typ Type, smap *substMap) Type {
+ if smap.empty() {
+ return typ
+ }
+
+ // common cases
+ switch t := typ.(type) {
+ case *Basic:
+ return typ // nothing to do
+ case *TypeParam:
+ return smap.lookup(t)
+ }
+
+ // general case
+ subst := subster{check, pos, make(map[Type]Type), smap}
+ return subst.typ(typ)
+}
+
+type subster struct {
+ check *Checker
+ pos syntax.Pos
+ cache map[Type]Type
+ smap *substMap
+}
+
+func (subst *subster) typ(typ Type) Type {
+ switch t := typ.(type) {
+ case nil:
+ // Call typOrNil if it's possible that typ is nil.
+ panic("nil typ")
+
+ case *Basic, *bottom, *top:
+ // nothing to do
+
+ case *Array:
+ elem := subst.typOrNil(t.elem)
+ if elem != t.elem {
+ return &Array{len: t.len, elem: elem}
+ }
+
+ case *Slice:
+ elem := subst.typOrNil(t.elem)
+ if elem != t.elem {
+ return &Slice{elem: elem}
+ }
+
+ case *Struct:
+ if fields, copied := subst.varList(t.fields); copied {
+ return &Struct{fields: fields, tags: t.tags}
+ }
+
+ case *Pointer:
+ base := subst.typ(t.base)
+ if base != t.base {
+ return &Pointer{base: base}
+ }
+
+ case *Tuple:
+ return subst.tuple(t)
+
+ case *Signature:
+ // TODO(gri) rethink the recv situation with respect to methods on parameterized types
+ // recv := subst.var_(t.recv) // TODO(gri) this causes a stack overflow - explain
+ recv := t.recv
+ params := subst.tuple(t.params)
+ results := subst.tuple(t.results)
+ if recv != t.recv || params != t.params || results != t.results {
+ return &Signature{
+ rparams: t.rparams,
+ tparams: t.tparams,
+ scope: t.scope,
+ recv: recv,
+ params: params,
+ results: results,
+ variadic: t.variadic,
+ }
+ }
+
+ case *Sum:
+ types, copied := subst.typeList(t.types)
+ if copied {
+ // Don't do it manually, with a Sum literal: the new
+ // types list may not be unique and NewSum may remove
+ // duplicates.
+ return NewSum(types)
+ }
+
+ case *Interface:
+ methods, mcopied := subst.funcList(t.methods)
+ types := t.types
+ if t.types != nil {
+ types = subst.typ(t.types)
+ }
+ embeddeds, ecopied := subst.typeList(t.embeddeds)
+ if mcopied || types != t.types || ecopied {
+ iface := &Interface{methods: methods, types: types, embeddeds: embeddeds}
+ subst.check.posMap[iface] = subst.check.posMap[t] // satisfy completeInterface requirement
+ subst.check.completeInterface(nopos, iface)
+ return iface
+ }
+
+ case *Map:
+ key := subst.typ(t.key)
+ elem := subst.typ(t.elem)
+ if key != t.key || elem != t.elem {
+ return &Map{key: key, elem: elem}
+ }
+
+ case *Chan:
+ elem := subst.typ(t.elem)
+ if elem != t.elem {
+ return &Chan{dir: t.dir, elem: elem}
+ }
+
+ case *Named:
+ subst.check.indent++
+ defer func() {
+ subst.check.indent--
+ }()
+ dump := func(format string, args ...interface{}) {
+ if subst.check.conf.Trace {
+ subst.check.trace(subst.pos, format, args...)
+ }
+ }
+
+ if t.tparams == nil {
+ dump(">>> %s is not parameterized", t)
+ return t // type is not parameterized
+ }
+
+ var new_targs []Type
+
+ if len(t.targs) > 0 {
+
+ // already instantiated
+ dump(">>> %s already instantiated", t)
+ assert(len(t.targs) == len(t.tparams))
+ // For each (existing) type argument targ, determine if it needs
+ // to be substituted; i.e., if it is or contains a type parameter
+ // that has a type argument for it.
+ for i, targ := range t.targs {
+ dump(">>> %d targ = %s", i, targ)
+ new_targ := subst.typ(targ)
+ if new_targ != targ {
+ dump(">>> substituted %d targ %s => %s", i, targ, new_targ)
+ if new_targs == nil {
+ new_targs = make([]Type, len(t.tparams))
+ copy(new_targs, t.targs)
+ }
+ new_targs[i] = new_targ
+ }
+ }
+
+ if new_targs == nil {
+ dump(">>> nothing to substitute in %s", t)
+ return t // nothing to substitute
+ }
+
+ } else {
+
+ // not yet instantiated
+ dump(">>> first instantiation of %s", t)
+ new_targs = subst.smap.targs
+
+ }
+
+ // before creating a new named type, check if we have this one already
+ h := instantiatedHash(t, new_targs)
+ dump(">>> new type hash: %s", h)
+ if named, found := subst.check.typMap[h]; found {
+ dump(">>> found %s", named)
+ subst.cache[t] = named
+ return named
+ }
+
+ // create a new named type and populate caches to avoid endless recursion
+ tname := NewTypeName(subst.pos, t.obj.pkg, t.obj.name, nil)
+ named := subst.check.NewNamed(tname, t.underlying, t.methods) // method signatures are updated lazily
+ named.tparams = t.tparams // new type is still parameterized
+ named.targs = new_targs
+ subst.check.typMap[h] = named
+ subst.cache[t] = named
+
+ // do the substitution
+ dump(">>> subst %s with %s (new: %s)", t.underlying, subst.smap, new_targs)
+ named.underlying = subst.typOrNil(t.underlying)
+ named.orig = named.underlying // for cycle detection (Checker.validType)
+
+ return named
+
+ case *TypeParam:
+ return subst.smap.lookup(t)
+
+ case *instance:
+ // TODO(gri) can we avoid the expansion here and just substitute the type parameters?
+ return subst.typ(t.expand())
+
+ default:
+ unimplemented()
+ }
+
+ return typ
+}
+
+// TODO(gri) Eventually, this should be more sophisticated.
+// It won't work correctly for locally declared types.
+func instantiatedHash(typ *Named, targs []Type) string {
+ var buf bytes.Buffer
+ writeTypeName(&buf, typ.obj, nil)
+ buf.WriteByte('(')
+ writeTypeList(&buf, targs, nil, nil)
+ buf.WriteByte(')')
+
+ // With respect to the represented type, whether a
+ // type is fully expanded or stored as instance
+ // does not matter - they are the same types.
+ // Remove the instanceMarkers printed for instances.
+ res := buf.Bytes()
+ i := 0
+ for _, b := range res {
+ if b != instanceMarker {
+ res[i] = b
+ i++
+ }
+ }
+
+ return string(res[:i])
+}
+
+func typeListString(list []Type) string {
+ var buf bytes.Buffer
+ writeTypeList(&buf, list, nil, nil)
+ return buf.String()
+}
+
+// typOrNil is like typ but if the argument is nil it is replaced with Typ[Invalid].
+// A nil type may appear in pathological cases such as type T[P any] []func(_ T([]_))
+// where an array/slice element is accessed before it is set up.
+func (subst *subster) typOrNil(typ Type) Type {
+ if typ == nil {
+ return Typ[Invalid]
+ }
+ return subst.typ(typ)
+}
+
+func (subst *subster) var_(v *Var) *Var {
+ if v != nil {
+ if typ := subst.typ(v.typ); typ != v.typ {
+ copy := *v
+ copy.typ = typ
+ return &copy
+ }
+ }
+ return v
+}
+
+func (subst *subster) tuple(t *Tuple) *Tuple {
+ if t != nil {
+ if vars, copied := subst.varList(t.vars); copied {
+ return &Tuple{vars: vars}
+ }
+ }
+ return t
+}
+
+func (subst *subster) varList(in []*Var) (out []*Var, copied bool) {
+ out = in
+ for i, v := range in {
+ if w := subst.var_(v); w != v {
+ if !copied {
+ // first variable that got substituted => allocate new out slice
+ // and copy all variables
+ new := make([]*Var, len(in))
+ copy(new, out)
+ out = new
+ copied = true
+ }
+ out[i] = w
+ }
+ }
+ return
+}
+
+func (subst *subster) func_(f *Func) *Func {
+ if f != nil {
+ if typ := subst.typ(f.typ); typ != f.typ {
+ copy := *f
+ copy.typ = typ
+ return &copy
+ }
+ }
+ return f
+}
+
+func (subst *subster) funcList(in []*Func) (out []*Func, copied bool) {
+ out = in
+ for i, f := range in {
+ if g := subst.func_(f); g != f {
+ if !copied {
+ // first function that got substituted => allocate new out slice
+ // and copy all functions
+ new := make([]*Func, len(in))
+ copy(new, out)
+ out = new
+ copied = true
+ }
+ out[i] = g
+ }
+ }
+ return
+}
+
+func (subst *subster) typeList(in []Type) (out []Type, copied bool) {
+ out = in
+ for i, t := range in {
+ if u := subst.typ(t); u != t {
+ if !copied {
+ // first function that got substituted => allocate new out slice
+ // and copy all functions
+ new := make([]Type, len(in))
+ copy(new, out)
+ out = new
+ copied = true
+ }
+ out[i] = u
+ }
+ }
+ return
+}
diff --git a/src/cmd/compile/internal/types2/type.go b/src/cmd/compile/internal/types2/type.go
new file mode 100644
index 0000000000..c26d243f3c
--- /dev/null
+++ b/src/cmd/compile/internal/types2/type.go
@@ -0,0 +1,1061 @@
+// UNREVIEWED
+// Copyright 2011 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 types2
+
+import (
+ "cmd/compile/internal/syntax"
+ "fmt"
+ "sort"
+)
+
+// A Type represents a type of Go.
+// All types implement the Type interface.
+type Type interface {
+ // Underlying returns the underlying type of a type
+ // w/o following forwarding chains. Only used by
+ // client packages (here for backward-compatibility).
+ Underlying() Type
+
+ // Under returns the true expanded underlying type.
+ // If it doesn't exist, the result is Typ[Invalid].
+ // Under must only be called when a type is known
+ // to be fully set up.
+ Under() Type
+
+ // String returns a string representation of a type.
+ String() string
+
+ // Converters
+ // A converter must only be called when a type is
+ // known to be fully set up. A converter returns
+ // a type's operational type (see comment for optype)
+ // or nil if the type is receiver is not of the
+ // respective type.
+ Basic() *Basic
+ Array() *Array
+ Slice() *Slice
+ Struct() *Struct
+ Pointer() *Pointer
+ Tuple() *Tuple
+ Signature() *Signature
+ Sum() *Sum
+ Interface() *Interface
+ Map() *Map
+ Chan() *Chan
+
+ // If the receiver for Named and TypeParam is of
+ // the respective type (possibly after unpacking
+ // an instance type), these methods return that
+ // type. Otherwise the result is nil.
+ Named() *Named
+ TypeParam() *TypeParam
+}
+
+// aType implements default type behavior
+type aType struct{}
+
+// These methods must be implemented by each type.
+func (aType) Underlying() Type { panic("unreachable") }
+func (aType) Under() Type { panic("unreachable") }
+func (aType) String() string { panic("unreachable") }
+
+// Each type is implementing its version of these methods
+// (Basic must implement Basic, etc.), the other methods
+// are inherited.
+func (aType) Basic() *Basic { return nil }
+func (aType) Array() *Array { return nil }
+func (aType) Slice() *Slice { return nil }
+func (aType) Struct() *Struct { return nil }
+func (aType) Pointer() *Pointer { return nil }
+func (aType) Tuple() *Tuple { return nil }
+func (aType) Signature() *Signature { return nil }
+func (aType) Sum() *Sum { return nil }
+func (aType) Interface() *Interface { return nil }
+func (aType) Map() *Map { return nil }
+func (aType) Chan() *Chan { return nil }
+
+func (aType) Named() *Named { return nil }
+func (aType) TypeParam() *TypeParam { return nil }
+
+// BasicKind describes the kind of basic type.
+type BasicKind int
+
+const (
+ Invalid BasicKind = iota // type is invalid
+
+ // predeclared types
+ Bool
+ Int
+ Int8
+ Int16
+ Int32
+ Int64
+ Uint
+ Uint8
+ Uint16
+ Uint32
+ Uint64
+ Uintptr
+ Float32
+ Float64
+ Complex64
+ Complex128
+ String
+ UnsafePointer
+
+ // types for untyped values
+ UntypedBool
+ UntypedInt
+ UntypedRune
+ UntypedFloat
+ UntypedComplex
+ UntypedString
+ UntypedNil
+
+ // aliases
+ Byte = Uint8
+ Rune = Int32
+)
+
+// BasicInfo is a set of flags describing properties of a basic type.
+type BasicInfo int
+
+// Properties of basic types.
+const (
+ IsBoolean BasicInfo = 1 << iota
+ IsInteger
+ IsUnsigned
+ IsFloat
+ IsComplex
+ IsString
+ IsUntyped
+
+ IsOrdered = IsInteger | IsFloat | IsString
+ IsNumeric = IsInteger | IsFloat | IsComplex
+ IsConstType = IsBoolean | IsNumeric | IsString
+)
+
+// A Basic represents a basic type.
+type Basic struct {
+ kind BasicKind
+ info BasicInfo
+ name string
+ aType
+}
+
+// Kind returns the kind of basic type b.
+func (b *Basic) Kind() BasicKind { return b.kind }
+
+// Info returns information about properties of basic type b.
+func (b *Basic) Info() BasicInfo { return b.info }
+
+// Name returns the name of basic type b.
+func (b *Basic) Name() string { return b.name }
+
+// An Array represents an array type.
+type Array struct {
+ len int64
+ elem Type
+ aType
+}
+
+// NewArray returns a new array type for the given element type and length.
+// A negative length indicates an unknown length.
+func NewArray(elem Type, len int64) *Array { return &Array{len: len, elem: elem} }
+
+// Len returns the length of array a.
+// A negative result indicates an unknown length.
+func (a *Array) Len() int64 { return a.len }
+
+// Elem returns element type of array a.
+func (a *Array) Elem() Type { return a.elem }
+
+// A Slice represents a slice type.
+type Slice struct {
+ elem Type
+ aType
+}
+
+// NewSlice returns a new slice type for the given element type.
+func NewSlice(elem Type) *Slice { return &Slice{elem: elem} }
+
+// Elem returns the element type of slice s.
+func (s *Slice) Elem() Type { return s.elem }
+
+// A Struct represents a struct type.
+type Struct struct {
+ fields []*Var
+ tags []string // field tags; nil if there are no tags
+ aType
+}
+
+// NewStruct returns a new struct with the given fields and corresponding field tags.
+// If a field with index i has a tag, tags[i] must be that tag, but len(tags) may be
+// only as long as required to hold the tag with the largest index i. Consequently,
+// if no field has a tag, tags may be nil.
+func NewStruct(fields []*Var, tags []string) *Struct {
+ var fset objset
+ for _, f := range fields {
+ if f.name != "_" && fset.insert(f) != nil {
+ panic("multiple fields with the same name")
+ }
+ }
+ if len(tags) > len(fields) {
+ panic("more tags than fields")
+ }
+ return &Struct{fields: fields, tags: tags}
+}
+
+// NumFields returns the number of fields in the struct (including blank and embedded fields).
+func (s *Struct) NumFields() int { return len(s.fields) }
+
+// Field returns the i'th field for 0 <= i < NumFields().
+func (s *Struct) Field(i int) *Var { return s.fields[i] }
+
+// Tag returns the i'th field tag for 0 <= i < NumFields().
+func (s *Struct) Tag(i int) string {
+ if i < len(s.tags) {
+ return s.tags[i]
+ }
+ return ""
+}
+
+// A Pointer represents a pointer type.
+type Pointer struct {
+ base Type // element type
+ aType
+}
+
+// NewPointer returns a new pointer type for the given element (base) type.
+func NewPointer(elem Type) *Pointer { return &Pointer{base: elem} }
+
+// Elem returns the element type for the given pointer p.
+func (p *Pointer) Elem() Type { return p.base }
+
+// A Tuple represents an ordered list of variables; a nil *Tuple is a valid (empty) tuple.
+// Tuples are used as components of signatures and to represent the type of multiple
+// assignments; they are not first class types of Go.
+type Tuple struct {
+ vars []*Var
+ aType
+}
+
+// NewTuple returns a new tuple for the given variables.
+func NewTuple(x ...*Var) *Tuple {
+ if len(x) > 0 {
+ return &Tuple{vars: x}
+ }
+ return nil
+}
+
+// We cannot rely on the embedded X() *X methods because (*Tuple)(nil)
+// is a valid *Tuple value but (*Tuple)(nil).X() would panic without
+// these implementations. At the moment we only need X = Basic, Named,
+// but add all because missing one leads to very confusing bugs.
+// TODO(gri) Don't represent empty tuples with a (*Tuple)(nil) pointer;
+// it's too subtle and causes problems.
+func (*Tuple) Basic() *Basic { return nil }
+func (*Tuple) Array() *Array { return nil }
+func (*Tuple) Slice() *Slice { return nil }
+func (*Tuple) Struct() *Struct { return nil }
+func (*Tuple) Pointer() *Pointer { return nil }
+
+// func (*Tuple) Tuple() *Tuple // implemented below
+func (*Tuple) Signature() *Signature { return nil }
+func (*Tuple) Sum() *Sum { return nil }
+func (*Tuple) Interface() *Interface { return nil }
+func (*Tuple) Map() *Map { return nil }
+func (*Tuple) Chan() *Chan { return nil }
+
+func (*Tuple) Named() *Named { return nil }
+func (*Tuple) TypeParam() *TypeParam { return nil }
+
+// Len returns the number variables of tuple t.
+func (t *Tuple) Len() int {
+ if t != nil {
+ return len(t.vars)
+ }
+ return 0
+}
+
+// At returns the i'th variable of tuple t.
+func (t *Tuple) At(i int) *Var { return t.vars[i] }
+
+// A Signature represents a (non-builtin) function or method type.
+// The receiver is ignored when comparing signatures for identity.
+type Signature struct {
+ // We need to keep the scope in Signature (rather than passing it around
+ // and store it in the Func Object) because when type-checking a function
+ // literal we call the general type checker which returns a general Type.
+ // We then unpack the *Signature and use the scope for the literal body.
+ rparams []*TypeName // reveiver type parameters from left to right; or nil
+ tparams []*TypeName // type parameters from left to right; or nil
+ scope *Scope // function scope, present for package-local signatures
+ recv *Var // nil if not a method
+ params *Tuple // (incoming) parameters from left to right; or nil
+ results *Tuple // (outgoing) results from left to right; or nil
+ variadic bool // true if the last parameter's type is of the form ...T (or string, for append built-in only)
+ aType
+}
+
+// NewSignature returns a new function type for the given receiver, parameters,
+// and results, either of which may be nil. If variadic is set, the function
+// is variadic, it must have at least one parameter, and the last parameter
+// must be of unnamed slice type.
+func NewSignature(recv *Var, params, results *Tuple, variadic bool) *Signature {
+ if variadic {
+ n := params.Len()
+ if n == 0 {
+ panic("types.NewSignature: variadic function must have at least one parameter")
+ }
+ if _, ok := params.At(n - 1).typ.(*Slice); !ok {
+ panic("types.NewSignature: variadic parameter must be of unnamed slice type")
+ }
+ }
+ return &Signature{recv: recv, params: params, results: results, variadic: variadic}
+}
+
+// Recv returns the receiver of signature s (if a method), or nil if a
+// function. It is ignored when comparing signatures for identity.
+//
+// For an abstract method, Recv returns the enclosing interface either
+// as a *Named or an *Interface. Due to embedding, an interface may
+// contain methods whose receiver type is a different interface.
+func (s *Signature) Recv() *Var { return s.recv }
+
+// TParams returns the type parameters of signature s, or nil.
+func (s *Signature) TParams() []*TypeName { return s.tparams }
+
+// SetTParams sets the type parameters of signature s.
+func (s *Signature) SetTParams(tparams []*TypeName) { s.tparams = tparams }
+
+// Params returns the parameters of signature s, or nil.
+func (s *Signature) Params() *Tuple { return s.params }
+
+// Results returns the results of signature s, or nil.
+func (s *Signature) Results() *Tuple { return s.results }
+
+// Variadic reports whether the signature s is variadic.
+func (s *Signature) Variadic() bool { return s.variadic }
+
+// A Sum represents a set of possible types.
+// Sums are currently used to represent type lists of interfaces
+// and thus the underlying types of type parameters; they are not
+// first class types of Go.
+type Sum struct {
+ types []Type // types are unique
+ aType
+}
+
+// NewSum returns a new Sum type consisting of the provided
+// types if there are more than one. If there is exactly one
+// type, it returns that type. If the list of types is empty
+// the result is nil.
+func NewSum(types []Type) Type {
+ if len(types) == 0 {
+ return nil
+ }
+
+ // What should happen if types contains a sum type?
+ // Do we flatten the types list? For now we check
+ // and panic. This should not be possible for the
+ // current use case of type lists.
+ // TODO(gri) Come up with the rules for sum types.
+ for _, t := range types {
+ if _, ok := t.(*Sum); ok {
+ panic("sum type contains sum type - unimplemented")
+ }
+ }
+
+ if len(types) == 1 {
+ return types[0]
+ }
+ return &Sum{types: types}
+}
+
+// is reports whether all types in t satisfy pred.
+func (s *Sum) is(pred func(Type) bool) bool {
+ if s == nil {
+ return false
+ }
+ for _, t := range s.types {
+ if !pred(t) {
+ return false
+ }
+ }
+ return true
+}
+
+// An Interface represents an interface type.
+type Interface struct {
+ methods []*Func // ordered list of explicitly declared methods
+ types Type // (possibly a Sum) type declared with a type list (TODO(gri) need better field name)
+ embeddeds []Type // ordered list of explicitly embedded types
+
+ allMethods []*Func // ordered list of methods declared with or embedded in this interface (TODO(gri): replace with mset)
+ allTypes Type // intersection of all embedded and locally declared types (TODO(gri) need better field name)
+
+ obj Object // type declaration defining this interface; or nil (for better error messages)
+
+ aType
+}
+
+// unpack unpacks a type into a list of types.
+// TODO(gri) Try to eliminate the need for this function.
+func unpack(typ Type) []Type {
+ if typ == nil {
+ return nil
+ }
+ if sum := typ.Sum(); sum != nil {
+ return sum.types
+ }
+ return []Type{typ}
+}
+
+// is reports whether interface t represents types that all satisfy pred.
+func (t *Interface) is(pred func(Type) bool) bool {
+ if t.allTypes == nil {
+ return false // we must have at least one type! (was bug)
+ }
+ for _, t := range unpack(t.allTypes) {
+ if !pred(t) {
+ return false
+ }
+ }
+ return true
+}
+
+// emptyInterface represents the empty (completed) interface
+var emptyInterface = Interface{allMethods: markComplete}
+
+// markComplete is used to mark an empty interface as completely
+// set up by setting the allMethods field to a non-nil empty slice.
+var markComplete = make([]*Func, 0)
+
+// NewInterface returns a new (incomplete) interface for the given methods and embedded types.
+// Each embedded type must have an underlying type of interface type.
+// NewInterface takes ownership of the provided methods and may modify their types by setting
+// missing receivers. To compute the method set of the interface, Complete must be called.
+//
+// Deprecated: Use NewInterfaceType instead which allows any (even non-defined) interface types
+// to be embedded. This is necessary for interfaces that embed alias type names referring to
+// non-defined (literal) interface types.
+func NewInterface(methods []*Func, embeddeds []*Named) *Interface {
+ tnames := make([]Type, len(embeddeds))
+ for i, t := range embeddeds {
+ tnames[i] = t
+ }
+ return NewInterfaceType(methods, tnames)
+}
+
+// NewInterfaceType returns a new (incomplete) interface for the given methods and embedded types.
+// Each embedded type must have an underlying type of interface type (this property is not
+// verified for defined types, which may be in the process of being set up and which don't
+// have a valid underlying type yet).
+// NewInterfaceType takes ownership of the provided methods and may modify their types by setting
+// missing receivers. To compute the method set of the interface, Complete must be called.
+func NewInterfaceType(methods []*Func, embeddeds []Type) *Interface {
+ if len(methods) == 0 && len(embeddeds) == 0 {
+ return &emptyInterface
+ }
+
+ // set method receivers if necessary
+ typ := new(Interface)
+ for _, m := range methods {
+ if sig := m.typ.(*Signature); sig.recv == nil {
+ sig.recv = NewVar(m.pos, m.pkg, "", typ)
+ }
+ }
+
+ // All embedded types should be interfaces; however, defined types
+ // may not yet be fully resolved. Only verify that non-defined types
+ // are interfaces. This matches the behavior of the code before the
+ // fix for #25301 (issue #25596).
+ for _, t := range embeddeds {
+ if _, ok := t.(*Named); !ok && !IsInterface(t) {
+ panic("embedded type is not an interface")
+ }
+ }
+
+ // sort for API stability
+ sort.Sort(byUniqueMethodName(methods))
+ sort.Stable(byUniqueTypeName(embeddeds))
+
+ typ.methods = methods
+ typ.embeddeds = embeddeds
+ return typ
+}
+
+// NumExplicitMethods returns the number of explicitly declared methods of interface t.
+func (t *Interface) NumExplicitMethods() int { return len(t.methods) }
+
+// ExplicitMethod returns the i'th explicitly declared method of interface t for 0 <= i < t.NumExplicitMethods().
+// The methods are ordered by their unique Id.
+func (t *Interface) ExplicitMethod(i int) *Func { return t.methods[i] }
+
+// NumEmbeddeds returns the number of embedded types in interface t.
+func (t *Interface) NumEmbeddeds() int { return len(t.embeddeds) }
+
+// Embedded returns the i'th embedded defined (*Named) type of interface t for 0 <= i < t.NumEmbeddeds().
+// The result is nil if the i'th embedded type is not a defined type.
+//
+// Deprecated: Use EmbeddedType which is not restricted to defined (*Named) types.
+func (t *Interface) Embedded(i int) *Named { tname, _ := t.embeddeds[i].(*Named); return tname }
+
+// EmbeddedType returns the i'th embedded type of interface t for 0 <= i < t.NumEmbeddeds().
+func (t *Interface) EmbeddedType(i int) Type { return t.embeddeds[i] }
+
+// NumMethods returns the total number of methods of interface t.
+// The interface must have been completed.
+func (t *Interface) NumMethods() int { t.assertCompleteness(); return len(t.allMethods) }
+
+func (t *Interface) assertCompleteness() {
+ if t.allMethods == nil {
+ panic("interface is incomplete")
+ }
+}
+
+// Method returns the i'th method of interface t for 0 <= i < t.NumMethods().
+// The methods are ordered by their unique Id.
+// The interface must have been completed.
+func (t *Interface) Method(i int) *Func { t.assertCompleteness(); return t.allMethods[i] }
+
+// Empty reports whether t is the empty interface.
+func (t *Interface) Empty() bool {
+ if t.allMethods != nil {
+ // interface is complete - quick test
+ // A non-nil allTypes may still be empty and represents the bottom type.
+ return len(t.allMethods) == 0 && t.allTypes == nil
+ }
+ return !t.iterate(func(t *Interface) bool {
+ if len(t.methods) > 0 || t.types != nil {
+ return true
+ }
+ return false
+ }, nil)
+}
+
+// HasTypeList reports whether interface t has a type list, possibly from an embedded type.
+func (t *Interface) HasTypeList() bool {
+ if t.allMethods != nil {
+ // interface is complete - quick test
+ return t.allTypes != nil
+ }
+
+ return t.iterate(func(t *Interface) bool {
+ if t.types != nil {
+ return true
+ }
+ return false
+ }, nil)
+}
+
+// IsComparable reports whether interface t is or embeds the predeclared interface "comparable".
+func (t *Interface) IsComparable() bool {
+ if t.allMethods != nil {
+ // interface is complete - quick test
+ _, m := lookupMethod(t.allMethods, nil, "==")
+ return m != nil
+ }
+
+ return t.iterate(func(t *Interface) bool {
+ _, m := lookupMethod(t.methods, nil, "==")
+ return m != nil
+ }, nil)
+}
+
+// IsConstraint reports t.HasTypeList() || t.IsComparable().
+func (t *Interface) IsConstraint() bool {
+ if t.allMethods != nil {
+ // interface is complete - quick test
+ if t.allTypes != nil {
+ return true
+ }
+ _, m := lookupMethod(t.allMethods, nil, "==")
+ return m != nil
+ }
+
+ return t.iterate(func(t *Interface) bool {
+ if t.types != nil {
+ return true
+ }
+ _, m := lookupMethod(t.methods, nil, "==")
+ return m != nil
+ }, nil)
+}
+
+// iterate calls f with t and then with any embedded interface of t, recursively, until f returns true.
+// iterate reports whether any call to f returned true.
+func (t *Interface) iterate(f func(*Interface) bool, seen map[*Interface]bool) bool {
+ if f(t) {
+ return true
+ }
+ for _, e := range t.embeddeds {
+ // e should be an interface but be careful (it may be invalid)
+ if e := e.Interface(); e != nil {
+ // Cyclic interfaces such as "type E interface { E }" are not permitted
+ // but they are still constructed and we need to detect such cycles.
+ if seen[e] {
+ continue
+ }
+ if seen == nil {
+ seen = make(map[*Interface]bool)
+ }
+ seen[e] = true
+ if e.iterate(f, seen) {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+// isSatisfiedBy reports whether interface t's type list is satisfied by the type typ.
+// If the the type list is empty (absent), typ trivially satisfies the interface.
+// TODO(gri) This is not a great name. Eventually, we should have a more comprehensive
+// "implements" predicate.
+func (t *Interface) isSatisfiedBy(typ Type) bool {
+ t.Complete()
+ if t.allTypes == nil {
+ return true
+ }
+ types := unpack(t.allTypes)
+ return includes(types, typ) || includes(types, typ.Under())
+}
+
+// Complete computes the interface's method set. It must be called by users of
+// NewInterfaceType and NewInterface after the interface's embedded types are
+// fully defined and before using the interface type in any way other than to
+// form other types. The interface must not contain duplicate methods or a
+// panic occurs. Complete returns the receiver.
+func (t *Interface) Complete() *Interface {
+ // TODO(gri) consolidate this method with Checker.completeInterface
+ if t.allMethods != nil {
+ return t
+ }
+
+ t.allMethods = markComplete // avoid infinite recursion
+
+ var todo []*Func
+ var methods []*Func
+ var seen objset
+ addMethod := func(m *Func, explicit bool) {
+ switch other := seen.insert(m); {
+ case other == nil:
+ methods = append(methods, m)
+ case explicit:
+ panic("duplicate method " + m.name)
+ default:
+ // check method signatures after all locally embedded interfaces are computed
+ todo = append(todo, m, other.(*Func))
+ }
+ }
+
+ for _, m := range t.methods {
+ addMethod(m, true)
+ }
+
+ allTypes := t.types
+
+ for _, typ := range t.embeddeds {
+ utyp := typ.Under()
+ etyp := utyp.Interface()
+ if etyp == nil {
+ if utyp != Typ[Invalid] {
+ panic(fmt.Sprintf("%s is not an interface", typ))
+ }
+ continue
+ }
+ etyp.Complete()
+ for _, m := range etyp.allMethods {
+ addMethod(m, false)
+ }
+ allTypes = intersect(allTypes, etyp.allTypes)
+ }
+
+ for i := 0; i < len(todo); i += 2 {
+ m := todo[i]
+ other := todo[i+1]
+ if !Identical(m.typ, other.typ) {
+ panic("duplicate method " + m.name)
+ }
+ }
+
+ if methods != nil {
+ sort.Sort(byUniqueMethodName(methods))
+ t.allMethods = methods
+ }
+ t.allTypes = allTypes
+
+ return t
+}
+
+// A Map represents a map type.
+type Map struct {
+ key, elem Type
+ aType
+}
+
+// NewMap returns a new map for the given key and element types.
+func NewMap(key, elem Type) *Map {
+ return &Map{key: key, elem: elem}
+}
+
+// Key returns the key type of map m.
+func (m *Map) Key() Type { return m.key }
+
+// Elem returns the element type of map m.
+func (m *Map) Elem() Type { return m.elem }
+
+// A Chan represents a channel type.
+type Chan struct {
+ dir ChanDir
+ elem Type
+ aType
+}
+
+// A ChanDir value indicates a channel direction.
+type ChanDir int
+
+// The direction of a channel is indicated by one of these constants.
+const (
+ SendRecv ChanDir = iota
+ SendOnly
+ RecvOnly
+)
+
+// NewChan returns a new channel type for the given direction and element type.
+func NewChan(dir ChanDir, elem Type) *Chan {
+ return &Chan{dir: dir, elem: elem}
+}
+
+// Dir returns the direction of channel c.
+func (c *Chan) Dir() ChanDir { return c.dir }
+
+// Elem returns the element type of channel c.
+func (c *Chan) Elem() Type { return c.elem }
+
+// A Named represents a named (defined) type.
+type Named struct {
+ check *Checker // for Named.Under implementation
+ info typeInfo // for cycle detection
+ obj *TypeName // corresponding declared object
+ orig Type // type (on RHS of declaration) this *Named type is derived of (for cycle reporting)
+ underlying Type // possibly a *Named during setup; never a *Named once set up completely
+ tparams []*TypeName // type parameters, or nil
+ targs []Type // type arguments (after instantiation), or nil
+ methods []*Func // methods declared for this type (not the method set of this type); signatures are type-checked lazily
+ aType
+}
+
+// NewNamed returns a new named type for the given type name, underlying type, and associated methods.
+// If the given type name obj doesn't have a type yet, its type is set to the returned named type.
+// The underlying type must not be a *Named.
+func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
+ if _, ok := underlying.(*Named); ok {
+ panic("types.NewNamed: underlying type must not be *Named")
+ }
+ typ := &Named{obj: obj, orig: underlying, underlying: underlying, methods: methods}
+ if obj.typ == nil {
+ obj.typ = typ
+ }
+ return typ
+}
+
+func (check *Checker) NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
+ typ := &Named{check: check, obj: obj, orig: underlying, underlying: underlying, methods: methods}
+ if obj.typ == nil {
+ obj.typ = typ
+ }
+ return typ
+}
+
+// Obj returns the type name for the named type t.
+func (t *Named) Obj() *TypeName { return t.obj }
+
+// Converter methods
+func (t *Named) Basic() *Basic { return t.Under().Basic() }
+func (t *Named) Array() *Array { return t.Under().Array() }
+func (t *Named) Slice() *Slice { return t.Under().Slice() }
+func (t *Named) Struct() *Struct { return t.Under().Struct() }
+func (t *Named) Pointer() *Pointer { return t.Under().Pointer() }
+func (t *Named) Tuple() *Tuple { return t.Under().Tuple() }
+func (t *Named) Signature() *Signature { return t.Under().Signature() }
+func (t *Named) Interface() *Interface { return t.Under().Interface() }
+func (t *Named) Map() *Map { return t.Under().Map() }
+func (t *Named) Chan() *Chan { return t.Under().Chan() }
+
+// func (t *Named) Named() *Named // declared below
+func (t *Named) TypeParam() *TypeParam { return t.Under().TypeParam() }
+
+// TODO(gri) Come up with a better representation and API to distinguish
+// between parameterized instantiated and non-instantiated types.
+
+// TParams returns the type parameters of the named type t, or nil.
+// The result is non-nil for an (originally) parameterized type even if it is instantiated.
+func (t *Named) TParams() []*TypeName { return t.tparams }
+
+// TArgs returns the type arguments after instantiation of the named type t, or nil if not instantiated.
+func (t *Named) TArgs() []Type { return t.targs }
+
+// SetTArgs sets the type arguments of Named.
+func (t *Named) SetTArgs(args []Type) { t.targs = args }
+
+// NumMethods returns the number of explicit methods whose receiver is named type t.
+func (t *Named) NumMethods() int { return len(t.methods) }
+
+// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
+func (t *Named) Method(i int) *Func { return t.methods[i] }
+
+// SetUnderlying sets the underlying type and marks t as complete.
+func (t *Named) SetUnderlying(underlying Type) {
+ if underlying == nil {
+ panic("types.Named.SetUnderlying: underlying type must not be nil")
+ }
+ if _, ok := underlying.(*Named); ok {
+ panic("types.Named.SetUnderlying: underlying type must not be *Named")
+ }
+ t.underlying = underlying
+}
+
+// AddMethod adds method m unless it is already in the method list.
+func (t *Named) AddMethod(m *Func) {
+ if i, _ := lookupMethod(t.methods, m.pkg, m.name); i < 0 {
+ t.methods = append(t.methods, m)
+ }
+}
+
+// A TypeParam represents a type parameter type.
+type TypeParam struct {
+ check *Checker // for lazy type bound completion
+ id uint64 // unique id
+ ptr bool // pointer designation
+ obj *TypeName // corresponding type name
+ index int // parameter index
+ bound Type // *Named or *Interface; underlying type is always *Interface
+ aType
+}
+
+// NewTypeParam returns a new TypeParam.
+func (check *Checker) NewTypeParam(ptr bool, obj *TypeName, index int, bound Type) *TypeParam {
+ assert(bound != nil)
+ typ := &TypeParam{check: check, id: check.nextId, ptr: ptr, obj: obj, index: index, bound: bound}
+ check.nextId++
+ if obj.typ == nil {
+ obj.typ = typ
+ }
+ return typ
+}
+
+func (t *TypeParam) Bound() *Interface {
+ iface := t.bound.Interface()
+ // use the type bound position if we have one
+ pos := nopos
+ if n, _ := t.bound.(*Named); n != nil {
+ pos = n.obj.pos
+ }
+ t.check.completeInterface(pos, iface)
+ return iface
+}
+
+// optype returns a type's operational type. Except for
+// type parameters, the operational type is the same
+// as the underlying type (as returned by Under). For
+// Type parameters, the operational type is determined
+// by the corresponding type bound's type list. The
+// result may be the bottom or top type, but it is never
+// the incoming type parameter.
+func optype(typ Type) Type {
+ if t := typ.TypeParam(); t != nil {
+ // If the optype is typ, return the top type as we have
+ // no information. It also prevents infinite recursion
+ // via the TypeParam converter methods. This can happen
+ // for a type parameter list of the form:
+ // (type T interface { type T }).
+ // See also issue #39680.
+ if u := t.Bound().allTypes; u != nil && u != typ {
+ // u != typ and u is a type parameter => u.Under() != typ, so this is ok
+ return u.Under()
+ }
+ return theTop
+ }
+ return typ
+}
+
+// Converter methods
+func (t *TypeParam) Basic() *Basic { return optype(t).Basic() }
+func (t *TypeParam) Array() *Array { return optype(t).Array() }
+func (t *TypeParam) Slice() *Slice { return optype(t).Slice() }
+func (t *TypeParam) Struct() *Struct { return optype(t).Struct() }
+func (t *TypeParam) Pointer() *Pointer { return optype(t).Pointer() }
+func (t *TypeParam) Tuple() *Tuple { return optype(t).Tuple() }
+func (t *TypeParam) Signature() *Signature { return optype(t).Signature() }
+func (t *TypeParam) Sum() *Sum { return optype(t).Sum() }
+func (t *TypeParam) Interface() *Interface { return optype(t).Interface() }
+func (t *TypeParam) Map() *Map { return optype(t).Map() }
+func (t *TypeParam) Chan() *Chan { return optype(t).Chan() }
+
+// func (t *TypeParam) Named() *Named // Named does not unpack type parameters
+// func (t *TypeParam) TypeParam() *TypeParam // declared below
+
+// An instance represents an instantiated generic type syntactically
+// (without expanding the instantiation). Type instances appear only
+// during type-checking and are replaced by their fully instantiated
+// (expanded) types before the end of type-checking.
+type instance struct {
+ check *Checker // for lazy instantiation
+ pos syntax.Pos // position of type instantiation; for error reporting only
+ base *Named // parameterized type to be instantiated
+ targs []Type // type arguments
+ poslist []syntax.Pos // position of each targ; for error reporting only
+ value Type // base(targs...) after instantiation or Typ[Invalid]; nil if not yet set
+ aType
+}
+
+// Converter methods
+func (t *instance) Basic() *Basic { return t.Under().Basic() }
+func (t *instance) Array() *Array { return t.Under().Array() }
+func (t *instance) Slice() *Slice { return t.Under().Slice() }
+func (t *instance) Struct() *Struct { return t.Under().Struct() }
+func (t *instance) Pointer() *Pointer { return t.Under().Pointer() }
+func (t *instance) Tuple() *Tuple { return t.Under().Tuple() }
+func (t *instance) Signature() *Signature { return t.Under().Signature() }
+func (t *instance) Sum() *Sum { return t.Under().Sum() }
+func (t *instance) Interface() *Interface { return t.Under().Interface() }
+func (t *instance) Map() *Map { return t.Under().Map() }
+func (t *instance) Chan() *Chan { return t.Under().Chan() }
+
+func (t *instance) Named() *Named { return t.expand().Named() }
+func (t *instance) TypeParam() *TypeParam { return t.expand().TypeParam() }
+
+// expand returns the instantiated (= expanded) type of t.
+// The result is either an instantiated *Named type, or
+// Typ[Invalid] if there was an error.
+func (t *instance) expand() Type {
+ v := t.value
+ if v == nil {
+ v = t.check.instantiate(t.pos, t.base, t.targs, t.poslist)
+ if v == nil {
+ v = Typ[Invalid]
+ }
+ t.value = v
+ }
+ // After instantiation we must have an invalid or a *Named type.
+ if debug && v != Typ[Invalid] {
+ _ = v.(*Named)
+ }
+ return v
+}
+
+// expand expands a type instance into its instantiated
+// type and leaves all other types alone. expand does
+// not recurse.
+func expand(typ Type) Type {
+ if t, _ := typ.(*instance); t != nil {
+ return t.expand()
+ }
+ return typ
+}
+
+// expandf is set to expand.
+// Call expandf when calling expand causes compile-time cycle error.
+var expandf func(Type) Type
+
+func init() { expandf = expand }
+
+// bottom represents the bottom of the type lattice.
+// It is the underlying type of a type parameter that
+// cannot be satisfied by any type, usually because
+// the intersection of type constraints left nothing).
+type bottom struct {
+ aType
+}
+
+// theBottom is the singleton bottom type.
+var theBottom = &bottom{}
+
+// top represents the top of the type lattice.
+// It is the underlying type of a type parameter that
+// can be satisfied by any type (ignoring methods),
+// usually because the type constraint has no type
+// list.
+type top struct {
+ aType
+}
+
+// theTop is the singleton top type.
+var theTop = &top{}
+
+// Type-specific implementations of type converters.
+func (t *Basic) Basic() *Basic { return t }
+func (t *Array) Array() *Array { return t }
+func (t *Slice) Slice() *Slice { return t }
+func (t *Struct) Struct() *Struct { return t }
+func (t *Pointer) Pointer() *Pointer { return t }
+func (t *Tuple) Tuple() *Tuple { return t }
+func (t *Signature) Signature() *Signature { return t }
+func (t *Sum) Sum() *Sum { return t }
+func (t *Interface) Interface() *Interface { return t }
+func (t *Map) Map() *Map { return t }
+func (t *Chan) Chan() *Chan { return t }
+
+func (t *Named) Named() *Named { return t }
+func (t *TypeParam) TypeParam() *TypeParam { return t }
+
+// Type-specific implementations of Underlying.
+func (t *Basic) Underlying() Type { return t }
+func (t *Array) Underlying() Type { return t }
+func (t *Slice) Underlying() Type { return t }
+func (t *Struct) Underlying() Type { return t }
+func (t *Pointer) Underlying() Type { return t }
+func (t *Tuple) Underlying() Type { return t }
+func (t *Signature) Underlying() Type { return t }
+func (t *Sum) Underlying() Type { return t }
+func (t *Interface) Underlying() Type { return t }
+func (t *Map) Underlying() Type { return t }
+func (t *Chan) Underlying() Type { return t }
+func (t *Named) Underlying() Type { return t.underlying }
+func (t *TypeParam) Underlying() Type { return t }
+func (t *instance) Underlying() Type { return t }
+func (t *bottom) Underlying() Type { return t }
+func (t *top) Underlying() Type { return t }
+
+// Type-specific implementations of Under.
+func (t *Basic) Under() Type { return t }
+func (t *Array) Under() Type { return t }
+func (t *Slice) Under() Type { return t }
+func (t *Struct) Under() Type { return t }
+func (t *Pointer) Under() Type { return t }
+func (t *Tuple) Under() Type { return t }
+func (t *Signature) Under() Type { return t }
+func (t *Sum) Under() Type { return t } // TODO(gri) is this correct?
+func (t *Interface) Under() Type { return t }
+func (t *Map) Under() Type { return t }
+func (t *Chan) Under() Type { return t }
+
+// see decl.go for implementation of Named.Under
+func (t *TypeParam) Under() Type { return t }
+func (t *instance) Under() Type { return t.expand().Under() }
+func (t *bottom) Under() Type { return t }
+func (t *top) Under() Type { return t }
+
+// Type-specific implementations of String.
+func (t *Basic) String() string { return TypeString(t, nil) }
+func (t *Array) String() string { return TypeString(t, nil) }
+func (t *Slice) String() string { return TypeString(t, nil) }
+func (t *Struct) String() string { return TypeString(t, nil) }
+func (t *Pointer) String() string { return TypeString(t, nil) }
+func (t *Tuple) String() string { return TypeString(t, nil) }
+func (t *Signature) String() string { return TypeString(t, nil) }
+func (t *Sum) String() string { return TypeString(t, nil) }
+func (t *Interface) String() string { return TypeString(t, nil) }
+func (t *Map) String() string { return TypeString(t, nil) }
+func (t *Chan) String() string { return TypeString(t, nil) }
+func (t *Named) String() string { return TypeString(t, nil) }
+func (t *TypeParam) String() string { return TypeString(t, nil) }
+func (t *instance) String() string { return TypeString(t, nil) }
+func (t *bottom) String() string { return TypeString(t, nil) }
+func (t *top) String() string { return TypeString(t, nil) }
diff --git a/src/cmd/compile/internal/types2/typestring.go b/src/cmd/compile/internal/types2/typestring.go
new file mode 100644
index 0000000000..98021797a1
--- /dev/null
+++ b/src/cmd/compile/internal/types2/typestring.go
@@ -0,0 +1,480 @@
+// UNREVIEWED
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements printing of types.
+
+package types2
+
+import (
+ "bytes"
+ "fmt"
+ "unicode/utf8"
+)
+
+// A Qualifier controls how named package-level objects are printed in
+// calls to TypeString, ObjectString, and SelectionString.
+//
+// These three formatting routines call the Qualifier for each
+// package-level object O, and if the Qualifier returns a non-empty
+// string p, the object is printed in the form p.O.
+// If it returns an empty string, only the object name O is printed.
+//
+// Using a nil Qualifier is equivalent to using (*Package).Path: the
+// object is qualified by the import path, e.g., "encoding/json.Marshal".
+//
+type Qualifier func(*Package) string
+
+// RelativeTo returns a Qualifier that fully qualifies members of
+// all packages other than pkg.
+func RelativeTo(pkg *Package) Qualifier {
+ if pkg == nil {
+ return nil
+ }
+ return func(other *Package) string {
+ if pkg == other {
+ return "" // same package; unqualified
+ }
+ return other.Path()
+ }
+}
+
+// If gcCompatibilityMode is set, printing of types is modified
+// to match the representation of some types in the gc compiler:
+//
+// - byte and rune lose their alias name and simply stand for
+// uint8 and int32 respectively
+// - embedded interfaces get flattened (the embedding info is lost,
+// and certain recursive interface types cannot be printed anymore)
+//
+// This makes it easier to compare packages computed with the type-
+// checker vs packages imported from gc export data.
+//
+// Caution: This flag affects all uses of WriteType, globally.
+// It is only provided for testing in conjunction with
+// gc-generated data.
+//
+// This flag is exported in the x/tools/go/types package. We don't
+// need it at the moment in the std repo and so we don't export it
+// anymore. We should eventually try to remove it altogether.
+// TODO(gri) remove this
+var gcCompatibilityMode bool
+
+// TypeString returns the string representation of typ.
+// The Qualifier controls the printing of
+// package-level objects, and may be nil.
+func TypeString(typ Type, qf Qualifier) string {
+ var buf bytes.Buffer
+ WriteType(&buf, typ, qf)
+ return buf.String()
+}
+
+// WriteType writes the string representation of typ to buf.
+// The Qualifier controls the printing of
+// package-level objects, and may be nil.
+func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) {
+ writeType(buf, typ, qf, make([]Type, 0, 8))
+}
+
+// instanceMarker is the prefix for an instantiated type
+// in "non-evaluated" instance form.
+const instanceMarker = '#'
+
+func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
+ // Theoretically, this is a quadratic lookup algorithm, but in
+ // practice deeply nested composite types with unnamed component
+ // types are uncommon. This code is likely more efficient than
+ // using a map.
+ for _, t := range visited {
+ if t == typ {
+ fmt.Fprintf(buf, "○%T", goTypeName(typ)) // cycle to typ
+ return
+ }
+ }
+ visited = append(visited, typ)
+
+ switch t := typ.(type) {
+ case nil:
+ buf.WriteString("<nil>")
+
+ case *Basic:
+ if t.kind == UnsafePointer {
+ buf.WriteString("unsafe.")
+ }
+ if gcCompatibilityMode {
+ // forget the alias names
+ switch t.kind {
+ case Byte:
+ t = Typ[Uint8]
+ case Rune:
+ t = Typ[Int32]
+ }
+ }
+ buf.WriteString(t.name)
+
+ case *Array:
+ fmt.Fprintf(buf, "[%d]", t.len)
+ writeType(buf, t.elem, qf, visited)
+
+ case *Slice:
+ buf.WriteString("[]")
+ writeType(buf, t.elem, qf, visited)
+
+ case *Struct:
+ buf.WriteString("struct{")
+ for i, f := range t.fields {
+ if i > 0 {
+ buf.WriteString("; ")
+ }
+ buf.WriteString(f.name)
+ if f.embedded {
+ // emphasize that the embedded field's name
+ // doesn't match the field's type name
+ if f.name != embeddedFieldName(f.typ) {
+ buf.WriteString(" /* = ")
+ writeType(buf, f.typ, qf, visited)
+ buf.WriteString(" */")
+ }
+ } else {
+ buf.WriteByte(' ')
+ writeType(buf, f.typ, qf, visited)
+ }
+ if tag := t.Tag(i); tag != "" {
+ fmt.Fprintf(buf, " %q", tag)
+ }
+ }
+ buf.WriteByte('}')
+
+ case *Pointer:
+ buf.WriteByte('*')
+ writeType(buf, t.base, qf, visited)
+
+ case *Tuple:
+ writeTuple(buf, t, false, qf, visited)
+
+ case *Signature:
+ buf.WriteString("func")
+ writeSignature(buf, t, qf, visited)
+
+ case *Sum:
+ for i, t := range t.types {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ writeType(buf, t, qf, visited)
+ }
+
+ case *Interface:
+ // We write the source-level methods and embedded types rather
+ // than the actual method set since resolved method signatures
+ // may have non-printable cycles if parameters have embedded
+ // interface types that (directly or indirectly) embed the
+ // current interface. For instance, consider the result type
+ // of m:
+ //
+ // type T interface{
+ // m() interface{ T }
+ // }
+ //
+ buf.WriteString("interface{")
+ empty := true
+ if gcCompatibilityMode {
+ // print flattened interface
+ // (useful to compare against gc-generated interfaces)
+ for i, m := range t.allMethods {
+ if i > 0 {
+ buf.WriteString("; ")
+ }
+ buf.WriteString(m.name)
+ writeSignature(buf, m.typ.(*Signature), qf, visited)
+ empty = false
+ }
+ if !empty && t.allTypes != nil {
+ buf.WriteString("; ")
+ }
+ if t.allTypes != nil {
+ buf.WriteString("type ")
+ writeType(buf, t.allTypes, qf, visited)
+ }
+ } else {
+ // print explicit interface methods and embedded types
+ for i, m := range t.methods {
+ if i > 0 {
+ buf.WriteString("; ")
+ }
+ buf.WriteString(m.name)
+ writeSignature(buf, m.typ.(*Signature), qf, visited)
+ empty = false
+ }
+ if !empty && t.types != nil {
+ buf.WriteString("; ")
+ }
+ if t.types != nil {
+ buf.WriteString("type ")
+ writeType(buf, t.types, qf, visited)
+ empty = false
+ }
+ if !empty && len(t.embeddeds) > 0 {
+ buf.WriteString("; ")
+ }
+ for i, typ := range t.embeddeds {
+ if i > 0 {
+ buf.WriteString("; ")
+ }
+ writeType(buf, typ, qf, visited)
+ empty = false
+ }
+ }
+ if t.allMethods == nil || len(t.methods) > len(t.allMethods) {
+ if !empty {
+ buf.WriteByte(' ')
+ }
+ buf.WriteString("/* incomplete */")
+ }
+ buf.WriteByte('}')
+
+ case *Map:
+ buf.WriteString("map[")
+ writeType(buf, t.key, qf, visited)
+ buf.WriteByte(']')
+ writeType(buf, t.elem, qf, visited)
+
+ case *Chan:
+ var s string
+ var parens bool
+ switch t.dir {
+ case SendRecv:
+ s = "chan "
+ // chan (<-chan T) requires parentheses
+ if c, _ := t.elem.(*Chan); c != nil && c.dir == RecvOnly {
+ parens = true
+ }
+ case SendOnly:
+ s = "chan<- "
+ case RecvOnly:
+ s = "<-chan "
+ default:
+ panic("unreachable")
+ }
+ buf.WriteString(s)
+ if parens {
+ buf.WriteByte('(')
+ }
+ writeType(buf, t.elem, qf, visited)
+ if parens {
+ buf.WriteByte(')')
+ }
+
+ case *Named:
+ writeTypeName(buf, t.obj, qf)
+ if t.targs != nil {
+ // instantiated type
+ buf.WriteByte('[')
+ writeTypeList(buf, t.targs, qf, visited)
+ buf.WriteByte(']')
+ } else if t.tparams != nil {
+ // parameterized type
+ writeTParamList(buf, t.tparams, qf, visited)
+ }
+
+ case *TypeParam:
+ s := "?"
+ if t.obj != nil {
+ s = t.obj.name
+ }
+ buf.WriteString(s + subscript(t.id))
+
+ case *instance:
+ buf.WriteByte(instanceMarker) // indicate "non-evaluated" syntactic instance
+ writeTypeName(buf, t.base.obj, qf)
+ buf.WriteByte('[')
+ writeTypeList(buf, t.targs, qf, visited)
+ buf.WriteByte(']')
+
+ case *bottom:
+ buf.WriteString("⊥")
+
+ case *top:
+ buf.WriteString("⊤")
+
+ default:
+ // For externally defined implementations of Type.
+ buf.WriteString(t.String())
+ }
+}
+
+func writeTypeList(buf *bytes.Buffer, list []Type, qf Qualifier, visited []Type) {
+ for i, typ := range list {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ writeType(buf, typ, qf, visited)
+ }
+}
+
+func writeTParamList(buf *bytes.Buffer, list []*TypeName, qf Qualifier, visited []Type) {
+ // bound returns the type bound for tname. The result is never nil.
+ bound := func(tname *TypeName) Type {
+ // be careful to avoid crashes in case of inconsistencies
+ if t, _ := tname.typ.(*TypeParam); t != nil && t.bound != nil {
+ return t.bound
+ }
+ return &emptyInterface
+ }
+
+ // If a single type bound is not the empty interface, we have to write them all.
+ var writeBounds bool
+ for _, p := range list {
+ // bound(p) should be an interface but be careful (it may be invalid)
+ b := bound(p).Interface()
+ if b != nil && !b.Empty() {
+ writeBounds = true
+ break
+ }
+ }
+ writeBounds = true // always write the bounds for new type parameter list syntax
+
+ buf.WriteString("[")
+ var prev Type
+ for i, p := range list {
+ b := bound(p)
+ if i > 0 {
+ if writeBounds && b != prev {
+ // type bound changed - write previous one before advancing
+ buf.WriteByte(' ')
+ writeType(buf, prev, qf, visited)
+ }
+ buf.WriteString(", ")
+ }
+ prev = b
+
+ if t, _ := p.typ.(*TypeParam); t != nil {
+ if t.ptr {
+ buf.WriteByte('*')
+ }
+ writeType(buf, t, qf, visited)
+ } else {
+ buf.WriteString(p.name)
+ }
+ }
+ if writeBounds && prev != nil {
+ buf.WriteByte(' ')
+ writeType(buf, prev, qf, visited)
+ }
+ buf.WriteByte(']')
+}
+
+func writeTypeName(buf *bytes.Buffer, obj *TypeName, qf Qualifier) {
+ s := "<Named w/o object>"
+ if obj != nil {
+ if obj.pkg != nil {
+ writePackage(buf, obj.pkg, qf)
+ }
+ // TODO(gri): function-local named types should be displayed
+ // differently from named types at package level to avoid
+ // ambiguity.
+ s = obj.name
+ }
+ buf.WriteString(s)
+}
+
+func writeTuple(buf *bytes.Buffer, tup *Tuple, variadic bool, qf Qualifier, visited []Type) {
+ buf.WriteByte('(')
+ if tup != nil {
+ for i, v := range tup.vars {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ if v.name != "" {
+ buf.WriteString(v.name)
+ buf.WriteByte(' ')
+ }
+ typ := v.typ
+ if variadic && i == len(tup.vars)-1 {
+ if s, ok := typ.(*Slice); ok {
+ buf.WriteString("...")
+ typ = s.elem
+ } else {
+ // special case:
+ // append(s, "foo"...) leads to signature func([]byte, string...)
+ if t := typ.Basic(); t == nil || t.kind != String {
+ panic("internal error: string type expected")
+ }
+ writeType(buf, typ, qf, visited)
+ buf.WriteString("...")
+ continue
+ }
+ }
+ writeType(buf, typ, qf, visited)
+ }
+ }
+ buf.WriteByte(')')
+}
+
+// WriteSignature writes the representation of the signature sig to buf,
+// without a leading "func" keyword.
+// The Qualifier controls the printing of
+// package-level objects, and may be nil.
+func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) {
+ writeSignature(buf, sig, qf, make([]Type, 0, 8))
+}
+
+func writeSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier, visited []Type) {
+ if sig.tparams != nil {
+ writeTParamList(buf, sig.tparams, qf, visited)
+ }
+
+ writeTuple(buf, sig.params, sig.variadic, qf, visited)
+
+ n := sig.results.Len()
+ if n == 0 {
+ // no result
+ return
+ }
+
+ buf.WriteByte(' ')
+ if n == 1 && sig.results.vars[0].name == "" {
+ // single unnamed result
+ writeType(buf, sig.results.vars[0].typ, qf, visited)
+ return
+ }
+
+ // multiple or named result(s)
+ writeTuple(buf, sig.results, false, qf, visited)
+}
+
+// embeddedFieldName returns an embedded field's name given its type.
+// The result is "" if the type doesn't have an embedded field name.
+func embeddedFieldName(typ Type) string {
+ switch t := typ.(type) {
+ case *Basic:
+ return t.name
+ case *Named:
+ return t.obj.name
+ case *Pointer:
+ // *T is ok, but **T is not
+ if _, ok := t.base.(*Pointer); !ok {
+ return embeddedFieldName(t.base)
+ }
+ case *instance:
+ return t.base.obj.name
+ }
+ return "" // not a (pointer to) a defined type
+}
+
+// subscript returns the decimal (utf8) representation of x using subscript digits.
+func subscript(x uint64) string {
+ const w = len("₀") // all digits 0...9 have the same utf8 width
+ var buf [32 * w]byte
+ i := len(buf)
+ for {
+ i -= w
+ utf8.EncodeRune(buf[i:], '₀'+rune(x%10)) // '₀' == U+2080
+ x /= 10
+ if x == 0 {
+ break
+ }
+ }
+ return string(buf[i:])
+}
diff --git a/src/cmd/compile/internal/types2/typestring_test.go b/src/cmd/compile/internal/types2/typestring_test.go
new file mode 100644
index 0000000000..f1f7e34bf8
--- /dev/null
+++ b/src/cmd/compile/internal/types2/typestring_test.go
@@ -0,0 +1,221 @@
+// UNREVIEWED
+// Copyright 2012 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 types2_test
+
+import (
+ "internal/testenv"
+ "testing"
+
+ "cmd/compile/internal/syntax"
+ . "cmd/compile/internal/types2"
+)
+
+const filename = "<src>"
+
+func makePkg(src string) (*Package, error) {
+ file, err := parseSrc(filename, src)
+ if err != nil {
+ return nil, err
+ }
+ // use the package name as package path
+ conf := Config{Importer: defaultImporter()}
+ return conf.Check(file.PkgName.Value, []*syntax.File{file}, nil)
+}
+
+type testEntry struct {
+ src, str string
+}
+
+// dup returns a testEntry where both src and str are the same.
+func dup(s string) testEntry {
+ return testEntry{s, s}
+}
+
+// types that don't depend on any other type declarations
+var independentTestTypes = []testEntry{
+ // basic types
+ dup("int"),
+ dup("float32"),
+ dup("string"),
+
+ // arrays
+ dup("[10]int"),
+
+ // slices
+ dup("[]int"),
+ dup("[][]int"),
+
+ // structs
+ dup("struct{}"),
+ dup("struct{x int}"),
+ {`struct {
+ x, y int
+ z float32 "foo"
+ }`, `struct{x int; y int; z float32 "foo"}`},
+ {`struct {
+ string
+ elems []complex128
+ }`, `struct{string; elems []complex128}`},
+
+ // pointers
+ dup("*int"),
+ dup("***struct{}"),
+ dup("*struct{a int; b float32}"),
+
+ // functions
+ dup("func()"),
+ dup("func(x int)"),
+ {"func(x, y int)", "func(x int, y int)"},
+ {"func(x, y int, z string)", "func(x int, y int, z string)"},
+ dup("func(int)"),
+ {"func(int, string, byte)", "func(int, string, byte)"},
+
+ dup("func() int"),
+ {"func() (string)", "func() string"},
+ dup("func() (u int)"),
+ {"func() (u, v int, w string)", "func() (u int, v int, w string)"},
+
+ dup("func(int) string"),
+ dup("func(x int) string"),
+ dup("func(x int) (u string)"),
+ {"func(x, y int) (u string)", "func(x int, y int) (u string)"},
+
+ dup("func(...int) string"),
+ dup("func(x ...int) string"),
+ dup("func(x ...int) (u string)"),
+ {"func(x int, y ...int) (u string)", "func(x int, y ...int) (u string)"},
+
+ // interfaces
+ dup("interface{}"),
+ dup("interface{m()}"),
+ dup(`interface{String() string; m(int) float32}`),
+ dup(`interface{type int, float32, complex128}`),
+
+ // maps
+ dup("map[string]int"),
+ {"map[struct{x, y int}][]byte", "map[struct{x int; y int}][]byte"},
+
+ // channels
+ dup("chan<- chan int"),
+ dup("chan<- <-chan int"),
+ dup("<-chan <-chan int"),
+ dup("chan (<-chan int)"),
+ dup("chan<- func()"),
+ dup("<-chan []func() int"),
+}
+
+// types that depend on other type declarations (src in TestTypes)
+var dependentTestTypes = []testEntry{
+ // interfaces
+ dup(`interface{io.Reader; io.Writer}`),
+ dup(`interface{m() int; io.Writer}`),
+ {`interface{m() interface{T}}`, `interface{m() interface{p.T}}`},
+}
+
+func TestTypeString(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ var tests []testEntry
+ tests = append(tests, independentTestTypes...)
+ tests = append(tests, dependentTestTypes...)
+
+ for _, test := range tests {
+ src := `package p; import "io"; type _ io.Writer; type T ` + test.src
+ pkg, err := makePkg(src)
+ if err != nil {
+ t.Errorf("%s: %s", src, err)
+ continue
+ }
+ typ := pkg.Scope().Lookup("T").Type().Underlying()
+ if got := typ.String(); got != test.str {
+ t.Errorf("%s: got %s, want %s", test.src, got, test.str)
+ }
+ }
+}
+
+var nopos syntax.Pos
+
+func TestIncompleteInterfaces(t *testing.T) {
+ sig := NewSignature(nil, nil, nil, false)
+ m := NewFunc(nopos, nil, "m", sig)
+ for _, test := range []struct {
+ typ *Interface
+ want string
+ }{
+ {new(Interface), "interface{/* incomplete */}"},
+ {new(Interface).Complete(), "interface{}"},
+
+ {NewInterface(nil, nil), "interface{}"},
+ {NewInterface(nil, nil).Complete(), "interface{}"},
+ {NewInterface([]*Func{}, nil), "interface{}"},
+ {NewInterface([]*Func{}, nil).Complete(), "interface{}"},
+ {NewInterface(nil, []*Named{}), "interface{}"},
+ {NewInterface(nil, []*Named{}).Complete(), "interface{}"},
+ {NewInterface([]*Func{m}, nil), "interface{m() /* incomplete */}"},
+ {NewInterface([]*Func{m}, nil).Complete(), "interface{m()}"},
+ {NewInterface(nil, []*Named{newDefined(new(Interface).Complete())}), "interface{T /* incomplete */}"},
+ {NewInterface(nil, []*Named{newDefined(new(Interface).Complete())}).Complete(), "interface{T}"},
+ {NewInterface(nil, []*Named{newDefined(NewInterface([]*Func{m}, nil))}), "interface{T /* incomplete */}"},
+ {NewInterface(nil, []*Named{newDefined(NewInterface([]*Func{m}, nil).Complete())}), "interface{T /* incomplete */}"},
+ {NewInterface(nil, []*Named{newDefined(NewInterface([]*Func{m}, nil).Complete())}).Complete(), "interface{T}"},
+
+ {NewInterfaceType(nil, nil), "interface{}"},
+ {NewInterfaceType(nil, nil).Complete(), "interface{}"},
+ {NewInterfaceType([]*Func{}, nil), "interface{}"},
+ {NewInterfaceType([]*Func{}, nil).Complete(), "interface{}"},
+ {NewInterfaceType(nil, []Type{}), "interface{}"},
+ {NewInterfaceType(nil, []Type{}).Complete(), "interface{}"},
+ {NewInterfaceType([]*Func{m}, nil), "interface{m() /* incomplete */}"},
+ {NewInterfaceType([]*Func{m}, nil).Complete(), "interface{m()}"},
+ {NewInterfaceType(nil, []Type{new(Interface).Complete()}), "interface{interface{} /* incomplete */}"},
+ {NewInterfaceType(nil, []Type{new(Interface).Complete()}).Complete(), "interface{interface{}}"},
+ {NewInterfaceType(nil, []Type{NewInterfaceType([]*Func{m}, nil)}), "interface{interface{m() /* incomplete */} /* incomplete */}"},
+ {NewInterfaceType(nil, []Type{NewInterfaceType([]*Func{m}, nil).Complete()}), "interface{interface{m()} /* incomplete */}"},
+ {NewInterfaceType(nil, []Type{NewInterfaceType([]*Func{m}, nil).Complete()}).Complete(), "interface{interface{m()}}"},
+ } {
+ got := test.typ.String()
+ if got != test.want {
+ t.Errorf("got: %s, want: %s", got, test.want)
+ }
+ }
+}
+
+// newDefined creates a new defined type named T with the given underlying type.
+// Helper function for use with TestIncompleteInterfaces only.
+func newDefined(underlying Type) *Named {
+ tname := NewTypeName(nopos, nil, "T", nil)
+ return NewNamed(tname, underlying, nil)
+}
+
+func TestQualifiedTypeString(t *testing.T) {
+ p, _ := pkgFor("p.go", "package p; type T int", nil)
+ q, _ := pkgFor("q.go", "package q", nil)
+
+ pT := p.Scope().Lookup("T").Type()
+ for _, test := range []struct {
+ typ Type
+ this *Package
+ want string
+ }{
+ {nil, nil, "<nil>"},
+ {pT, nil, "p.T"},
+ {pT, p, "T"},
+ {pT, q, "p.T"},
+ {NewPointer(pT), p, "*T"},
+ {NewPointer(pT), q, "*p.T"},
+ } {
+ qualifier := func(pkg *Package) string {
+ if pkg != test.this {
+ return pkg.Name()
+ }
+ return ""
+ }
+ if got := TypeString(test.typ, qualifier); got != test.want {
+ t.Errorf("TypeString(%s, %s) = %s, want %s",
+ test.this, test.typ, got, test.want)
+ }
+ }
+}
diff --git a/src/cmd/compile/internal/types2/typexpr.go b/src/cmd/compile/internal/types2/typexpr.go
new file mode 100644
index 0000000000..ae5ea669f5
--- /dev/null
+++ b/src/cmd/compile/internal/types2/typexpr.go
@@ -0,0 +1,1280 @@
+// UNREVIEWED
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements type-checking of identifiers and type expressions.
+
+package types2
+
+import (
+ "cmd/compile/internal/syntax"
+ "fmt"
+ "go/constant"
+ "sort"
+ "strconv"
+ "strings"
+)
+
+// ident type-checks identifier e and initializes x with the value or type of e.
+// If an error occurred, x.mode is set to invalid.
+// For the meaning of def, see Checker.definedType, below.
+// If wantType is set, the identifier e is expected to denote a type.
+//
+func (check *Checker) ident(x *operand, e *syntax.Name, def *Named, wantType bool) {
+ x.mode = invalid
+ x.expr = e
+
+ // Note that we cannot use check.lookup here because the returned scope
+ // may be different from obj.Parent(). See also Scope.LookupParent doc.
+ scope, obj := check.scope.LookupParent(e.Value, check.pos)
+ if obj == nil {
+ if e.Value == "_" {
+ check.errorf(e, "cannot use _ as value or type")
+ } else {
+ check.errorf(e, "undeclared name: %s", e.Value)
+ }
+ return
+ }
+ check.recordUse(e, obj)
+
+ // Type-check the object.
+ // Only call Checker.objDecl if the object doesn't have a type yet
+ // (in which case we must actually determine it) or the object is a
+ // TypeName and we also want a type (in which case we might detect
+ // a cycle which needs to be reported). Otherwise we can skip the
+ // call and avoid a possible cycle error in favor of the more
+ // informative "not a type/value" error that this function's caller
+ // will issue (see issue #25790).
+ typ := obj.Type()
+ if _, gotType := obj.(*TypeName); typ == nil || gotType && wantType {
+ check.objDecl(obj, def)
+ typ = obj.Type() // type must have been assigned by Checker.objDecl
+ }
+ assert(typ != nil)
+
+ // The object may be dot-imported: If so, remove its package from
+ // the map of unused dot imports for the respective file scope.
+ // (This code is only needed for dot-imports. Without them,
+ // we only have to mark variables, see *Var case below).
+ if pkg := obj.Pkg(); pkg != check.pkg && pkg != nil {
+ delete(check.unusedDotImports[scope], pkg)
+ }
+
+ switch obj := obj.(type) {
+ case *PkgName:
+ check.errorf(e, "use of package %s not in selector", obj.name)
+ return
+
+ case *Const:
+ check.addDeclDep(obj)
+ if typ == Typ[Invalid] {
+ return
+ }
+ if obj == universeIota {
+ if check.iota == nil {
+ check.errorf(e, "cannot use iota outside constant declaration")
+ return
+ }
+ x.val = check.iota
+ } else {
+ x.val = obj.val
+ }
+ assert(x.val != nil)
+ x.mode = constant_
+
+ case *TypeName:
+ x.mode = typexpr
+
+ case *Var:
+ // It's ok to mark non-local variables, but ignore variables
+ // from other packages to avoid potential race conditions with
+ // dot-imported variables.
+ if obj.pkg == check.pkg {
+ obj.used = true
+ }
+ check.addDeclDep(obj)
+ if typ == Typ[Invalid] {
+ return
+ }
+ x.mode = variable
+
+ case *Func:
+ check.addDeclDep(obj)
+ x.mode = value
+
+ case *Builtin:
+ x.id = obj.id
+ x.mode = builtin
+
+ case *Nil:
+ x.mode = value
+
+ default:
+ unreachable()
+ }
+
+ x.typ = typ
+}
+
+// typ type-checks the type expression e and returns its type, or Typ[Invalid].
+// The type must not be an (uninstantiated) generic type.
+func (check *Checker) typ(e syntax.Expr) Type {
+ return check.definedType(e, nil)
+}
+
+// varType type-checks the type expression e and returns its type, or Typ[Invalid].
+// The type must not be an (uninstantiated) generic type and it must be ordinary
+// (see ordinaryType).
+func (check *Checker) varType(e syntax.Expr) Type {
+ typ := check.definedType(e, nil)
+ check.ordinaryType(startPos(e), typ)
+ return typ
+}
+
+// ordinaryType reports an error if typ is an interface type containing
+// type lists or is (or embeds) the predeclared type comparable.
+func (check *Checker) ordinaryType(pos syntax.Pos, typ Type) {
+ // We don't want to call Under() (via Interface) or complete interfaces while we
+ // are in the middle of type-checking parameter declarations that might belong to
+ // interface methods. Delay this check to the end of type-checking.
+ check.atEnd(func() {
+ if t := typ.Interface(); t != nil {
+ check.completeInterface(pos, t) // TODO(gri) is this the correct position?
+ if t.allTypes != nil {
+ check.softErrorf(pos, "interface contains type constraints (%s)", t.allTypes)
+ return
+ }
+ if t.IsComparable() {
+ check.softErrorf(pos, "interface is (or embeds) comparable")
+ }
+ }
+ })
+}
+
+// anyType type-checks the type expression e and returns its type, or Typ[Invalid].
+// The type may be generic or instantiated.
+func (check *Checker) anyType(e syntax.Expr) Type {
+ typ := check.typInternal(e, nil)
+ assert(isTyped(typ))
+ check.recordTypeAndValue(e, typexpr, typ, nil)
+ return typ
+}
+
+// definedType is like typ but also accepts a type name def.
+// If def != nil, e is the type specification for the defined type def, declared
+// in a type declaration, and def.underlying will be set to the type of e before
+// any components of e are type-checked.
+//
+func (check *Checker) definedType(e syntax.Expr, def *Named) Type {
+ typ := check.typInternal(e, def)
+ assert(isTyped(typ))
+ if isGeneric(typ) {
+ check.errorf(e, "cannot use generic type %s without instantiation", typ)
+ typ = Typ[Invalid]
+ }
+ check.recordTypeAndValue(e, typexpr, typ, nil)
+ return typ
+}
+
+// genericType is like typ but the type must be an (uninstantiated) generic type.
+func (check *Checker) genericType(e syntax.Expr, reportErr bool) Type {
+ typ := check.typInternal(e, nil)
+ assert(isTyped(typ))
+ if typ != Typ[Invalid] && !isGeneric(typ) {
+ if reportErr {
+ check.errorf(e, "%s is not a generic type", typ)
+ }
+ typ = Typ[Invalid]
+ }
+ // TODO(gri) what is the correct call below?
+ check.recordTypeAndValue(e, typexpr, typ, nil)
+ return typ
+}
+
+// isubst returns an x with identifiers substituted per the substitution map smap.
+// isubst only handles the case of (valid) method receiver type expressions correctly.
+func isubst(x syntax.Expr, smap map[*syntax.Name]*syntax.Name) syntax.Expr {
+ switch n := x.(type) {
+ case *syntax.Name:
+ if alt := smap[n]; alt != nil {
+ return alt
+ }
+ // case *syntax.StarExpr:
+ // X := isubst(n.X, smap)
+ // if X != n.X {
+ // new := *n
+ // new.X = X
+ // return &new
+ // }
+ case *syntax.Operation:
+ if n.Op == syntax.Mul && n.Y == nil {
+ X := isubst(n.X, smap)
+ if X != n.X {
+ new := *n
+ new.X = X
+ return &new
+ }
+ }
+ case *syntax.IndexExpr:
+ Index := isubst(n.Index, smap)
+ if Index != n.Index {
+ new := *n
+ new.Index = Index
+ return &new
+ }
+ case *syntax.ListExpr:
+ var elems []syntax.Expr
+ for i, elem := range n.ElemList {
+ Elem := isubst(elem, smap)
+ if Elem != elem {
+ if elems == nil {
+ elems = make([]syntax.Expr, len(n.ElemList))
+ copy(elems, n.ElemList)
+ }
+ elems[i] = Elem
+ }
+ }
+ if elems != nil {
+ new := *n
+ new.ElemList = elems
+ return &new
+ }
+ case *syntax.ParenExpr:
+ return isubst(n.X, smap) // no need to keep parentheses
+ default:
+ // Other receiver type expressions are invalid.
+ // It's fine to ignore those here as they will
+ // be checked elsewhere.
+ }
+ return x
+}
+
+// funcType type-checks a function or method type.
+func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams []*syntax.Field, ftyp *syntax.FuncType) {
+ check.openScope(ftyp, "function")
+ check.scope.isFunc = true
+ check.recordScope(ftyp, check.scope)
+ sig.scope = check.scope
+ defer check.closeScope()
+
+ var recvTyp syntax.Expr // rewritten receiver type; valid if != nil
+ if recvPar != nil {
+ // collect generic receiver type parameters, if any
+ // - a receiver type parameter is like any other type parameter, except that it is declared implicitly
+ // - the receiver specification acts as local declaration for its type parameters, which may be blank
+ _, rname, rparams := check.unpackRecv(recvPar.Type, true)
+ if len(rparams) > 0 {
+ // Blank identifiers don't get declared and regular type-checking of the instantiated
+ // parameterized receiver type expression fails in Checker.collectParams of receiver.
+ // Identify blank type parameters and substitute each with a unique new identifier named
+ // "n_" (where n is the parameter index) and which cannot conflict with any user-defined
+ // name.
+ var smap map[*syntax.Name]*syntax.Name // substitution map from "_" to "!n" identifiers
+ for i, p := range rparams {
+ if p.Value == "_" {
+ new := *p
+ new.Value = fmt.Sprintf("%d_", i)
+ rparams[i] = &new // use n_ identifier instead of _ so it can be looked up
+ if smap == nil {
+ smap = make(map[*syntax.Name]*syntax.Name)
+ }
+ smap[p] = &new
+ }
+ }
+ if smap != nil {
+ // blank identifiers were found => use rewritten receiver type
+ recvTyp = isubst(recvPar.Type, smap)
+ }
+ // TODO(gri) rework declareTypeParams
+ sig.rparams = nil
+ for _, rparam := range rparams {
+ sig.rparams = check.declareTypeParam(sig.rparams, rparam)
+ }
+ // determine receiver type to get its type parameters
+ // and the respective type parameter bounds
+ var recvTParams []*TypeName
+ if rname != nil {
+ // recv should be a Named type (otherwise an error is reported elsewhere)
+ // Also: Don't report an error via genericType since it will be reported
+ // again when we type-check the signature.
+ // TODO(gri) maybe the receiver should be marked as invalid instead?
+ if recv := check.genericType(rname, false).Named(); recv != nil {
+ recvTParams = recv.tparams
+ }
+ }
+ // provide type parameter bounds
+ // - only do this if we have the right number (otherwise an error is reported elsewhere)
+ if len(sig.rparams) == len(recvTParams) {
+ // We have a list of *TypeNames but we need a list of Types.
+ // While creating this list, also update type parameter pointer designation
+ // for each (*TypeParam) list entry, by copying the information from the
+ // receiver base type's type parameters.
+ list := make([]Type, len(sig.rparams))
+ for i, t := range sig.rparams {
+ t.typ.(*TypeParam).ptr = recvTParams[i].typ.(*TypeParam).ptr
+ list[i] = t.typ
+ }
+ for i, tname := range sig.rparams {
+ bound := recvTParams[i].typ.(*TypeParam).bound
+ // bound is (possibly) parameterized in the context of the
+ // receiver type declaration. Substitute parameters for the
+ // current context.
+ // TODO(gri) should we assume now that bounds always exist?
+ // (no bound == empty interface)
+ if bound != nil {
+ bound = check.subst(tname.pos, bound, makeSubstMap(recvTParams, list))
+ tname.typ.(*TypeParam).bound = bound
+ }
+ }
+ }
+ }
+ }
+
+ if tparams != nil {
+ sig.tparams = check.collectTypeParams(tparams)
+ // Always type-check method type parameters but complain if they are not enabled.
+ // (A separate check is needed when type-checking interface method signatures because
+ // they don't have a receiver specification.)
+ if recvPar != nil && !check.conf.AcceptMethodTypeParams {
+ check.errorf(ftyp, "methods cannot have type parameters")
+ }
+ }
+
+ // Value (non-type) parameters' scope starts in the function body. Use a temporary scope for their
+ // declarations and then squash that scope into the parent scope (and report any redeclarations at
+ // that time).
+ scope := NewScope(check.scope, nopos, nopos, "function body (temp. scope)")
+ var recvList []*Var // TODO(gri) remove the need for making a list here
+ if recvPar != nil {
+ recvList, _ = check.collectParams(scope, []*syntax.Field{recvPar}, recvTyp, false) // use rewritten receiver type, if any
+ }
+ params, variadic := check.collectParams(scope, ftyp.ParamList, nil, true)
+ results, _ := check.collectParams(scope, ftyp.ResultList, nil, false)
+ scope.Squash(func(obj, alt Object) {
+ check.errorf(obj, "%s redeclared in this block", obj.Name())
+ check.reportAltDecl(alt)
+ })
+
+ if recvPar != nil {
+ // recv parameter list present (may be empty)
+ // spec: "The receiver is specified via an extra parameter section preceding the
+ // method name. That parameter section must declare a single parameter, the receiver."
+ var recv *Var
+ switch len(recvList) {
+ case 0:
+ // error reported by resolver
+ recv = NewParam(nopos, nil, "", Typ[Invalid]) // ignore recv below
+ default:
+ // more than one receiver
+ check.error(recvList[len(recvList)-1].Pos(), "method must have exactly one receiver")
+ fallthrough // continue with first receiver
+ case 1:
+ recv = recvList[0]
+ }
+
+ // TODO(gri) We should delay rtyp expansion to when we actually need the
+ // receiver; thus all checks here should be delayed to later.
+ rtyp, _ := deref(recv.typ)
+ rtyp = expand(rtyp)
+
+ // spec: "The receiver type must be of the form T or *T where T is a type name."
+ // (ignore invalid types - error was reported before)
+ if t := rtyp; t != Typ[Invalid] {
+ var err string
+ if T := t.Named(); T != nil {
+ // spec: "The type denoted by T is called the receiver base type; it must not
+ // be a pointer or interface type and it must be declared in the same package
+ // as the method."
+ if T.obj.pkg != check.pkg {
+ err = "type not defined in this package"
+ } else {
+ switch u := optype(T.Under()).(type) {
+ case *Basic:
+ // unsafe.Pointer is treated like a regular pointer
+ if u.kind == UnsafePointer {
+ err = "unsafe.Pointer"
+ }
+ case *Pointer, *Interface:
+ err = "pointer or interface type"
+ }
+ }
+ } else {
+ err = "basic or unnamed type"
+ }
+ if err != "" {
+ check.errorf(recv.pos, "invalid receiver %s (%s)", recv.typ, err)
+ // ok to continue
+ }
+ }
+ sig.recv = recv
+ }
+
+ sig.params = NewTuple(params...)
+ sig.results = NewTuple(results...)
+ sig.variadic = variadic
+}
+
+// goTypeName returns the Go type name for typ and
+// removes any occurences of "types." from that name.
+func goTypeName(typ Type) string {
+ return strings.ReplaceAll(fmt.Sprintf("%T", typ), "types.", "")
+}
+
+// typInternal drives type checking of types.
+// Must only be called by definedType or genericType.
+//
+func (check *Checker) typInternal(e0 syntax.Expr, def *Named) (T Type) {
+ if check.conf.Trace {
+ check.trace(e0.Pos(), "type %s", e0)
+ check.indent++
+ defer func() {
+ check.indent--
+ var under Type
+ if T != nil {
+ // Calling Under() here may lead to endless instantiations.
+ // Test case: type T[P any] *T[P]
+ // TODO(gri) investigate if that's a bug or to be expected
+ // (see also analogous comment in Checker.instantiate).
+ under = T.Underlying()
+ }
+ if T == under {
+ check.trace(e0.Pos(), "=> %s // %s", T, goTypeName(T))
+ } else {
+ check.trace(e0.Pos(), "=> %s (under = %s) // %s", T, under, goTypeName(T))
+ }
+ }()
+ }
+
+ switch e := e0.(type) {
+ case *syntax.BadExpr:
+ // ignore - error reported before
+
+ case *syntax.Name:
+ var x operand
+ check.ident(&x, e, def, true)
+
+ switch x.mode {
+ case typexpr:
+ typ := x.typ
+ def.setUnderlying(typ)
+ return typ
+ case invalid:
+ // ignore - error reported before
+ case novalue:
+ check.errorf(&x, "%s used as type", &x)
+ default:
+ check.errorf(&x, "%s is not a type", &x)
+ }
+
+ case *syntax.SelectorExpr:
+ var x operand
+ check.selector(&x, e)
+
+ switch x.mode {
+ case typexpr:
+ typ := x.typ
+ def.setUnderlying(typ)
+ return typ
+ case invalid:
+ // ignore - error reported before
+ case novalue:
+ check.errorf(&x, "%s used as type", &x)
+ default:
+ check.errorf(&x, "%s is not a type", &x)
+ }
+
+ case *syntax.IndexExpr:
+ return check.instantiatedType(e.X, unpackExpr(e.Index), def)
+
+ case *syntax.ParenExpr:
+ // Generic types must be instantiated before they can be used in any form.
+ // Consequently, generic types cannot be parenthesized.
+ return check.definedType(e.X, def)
+
+ case *syntax.ArrayType:
+ typ := new(Array)
+ def.setUnderlying(typ)
+ if e.Len != nil {
+ typ.len = check.arrayLength(e.Len)
+ } else {
+ // [...]array
+ check.errorf(e, "invalid use of [...] array (outside a composite literal)")
+ typ.len = -1
+ }
+ typ.elem = check.varType(e.Elem)
+ return typ
+
+ case *syntax.SliceType:
+ typ := new(Slice)
+ def.setUnderlying(typ)
+ typ.elem = check.varType(e.Elem)
+ return typ
+
+ case *syntax.StructType:
+ typ := new(Struct)
+ def.setUnderlying(typ)
+ check.structType(typ, e)
+ return typ
+
+ case *syntax.Operation:
+ if e.Op == syntax.Mul && e.Y == nil {
+ typ := new(Pointer)
+ def.setUnderlying(typ)
+ typ.base = check.varType(e.X)
+ return typ
+ }
+
+ case *syntax.FuncType:
+ typ := new(Signature)
+ def.setUnderlying(typ)
+ check.funcType(typ, nil, nil, e)
+ return typ
+
+ case *syntax.InterfaceType:
+ typ := new(Interface)
+ def.setUnderlying(typ)
+ if def != nil {
+ typ.obj = def.obj
+ }
+ check.interfaceType(typ, e, def)
+ return typ
+
+ case *syntax.MapType:
+ typ := new(Map)
+ def.setUnderlying(typ)
+
+ typ.key = check.varType(e.Key)
+ typ.elem = check.varType(e.Value)
+
+ // spec: "The comparison operators == and != must be fully defined
+ // for operands of the key type; thus the key type must not be a
+ // function, map, or slice."
+ //
+ // Delay this check because it requires fully setup types;
+ // it is safe to continue in any case (was issue 6667).
+ check.atEnd(func() {
+ if !Comparable(typ.key) {
+ var why string
+ if typ.key.TypeParam() != nil {
+ why = " (missing comparable constraint)"
+ }
+ check.errorf(e.Key, "invalid map key type %s%s", typ.key, why)
+ }
+ })
+
+ return typ
+
+ case *syntax.ChanType:
+ typ := new(Chan)
+ def.setUnderlying(typ)
+
+ dir := SendRecv
+ switch e.Dir {
+ case 0:
+ // nothing to do
+ case syntax.SendOnly:
+ dir = SendOnly
+ case syntax.RecvOnly:
+ dir = RecvOnly
+ default:
+ check.invalidASTf(e, "unknown channel direction %d", e.Dir)
+ // ok to continue
+ }
+
+ typ.dir = dir
+ typ.elem = check.varType(e.Elem)
+ return typ
+
+ default:
+ check.errorf(e0, "%s is not a type", e0)
+ check.use(e0)
+ }
+
+ typ := Typ[Invalid]
+ def.setUnderlying(typ)
+ return typ
+}
+
+// typeOrNil type-checks the type expression (or nil value) e
+// and returns the type of e, or nil. If e is a type, it must
+// not be an (uninstantiated) generic type.
+// If e is neither a type nor nil, typeOrNil returns Typ[Invalid].
+// TODO(gri) should we also disallow non-var types?
+func (check *Checker) typOrNil(e syntax.Expr) Type {
+ var x operand
+ check.rawExpr(&x, e, nil)
+ switch x.mode {
+ case invalid:
+ // ignore - error reported before
+ case novalue:
+ check.errorf(&x, "%s used as type", &x)
+ case typexpr:
+ check.instantiatedOperand(&x)
+ return x.typ
+ case value:
+ if x.isNil() {
+ return nil
+ }
+ fallthrough
+ default:
+ check.errorf(&x, "%s is not a type", &x)
+ }
+ return Typ[Invalid]
+}
+
+func (check *Checker) instantiatedType(x syntax.Expr, targs []syntax.Expr, def *Named) Type {
+ b := check.genericType(x, true) // TODO(gri) what about cycles?
+ if b == Typ[Invalid] {
+ return b // error already reported
+ }
+ base := b.Named()
+ if base == nil {
+ unreachable() // should have been caught by genericType
+ }
+
+ // create a new type Instance rather than instantiate the type
+ // TODO(gri) should do argument number check here rather than
+ // when instantiating the type?
+ typ := new(instance)
+ def.setUnderlying(typ)
+
+ typ.check = check
+ typ.pos = x.Pos()
+ typ.base = base
+
+ // evaluate arguments (always)
+ typ.targs = check.typeList(targs)
+ if typ.targs == nil {
+ def.setUnderlying(Typ[Invalid]) // avoid later errors due to lazy instantiation
+ return Typ[Invalid]
+ }
+
+ // determine argument positions (for error reporting)
+ typ.poslist = make([]syntax.Pos, len(targs))
+ for i, arg := range targs {
+ typ.poslist[i] = arg.Pos()
+ }
+
+ // make sure we check instantiation works at least once
+ // and that the resulting type is valid
+ check.atEnd(func() {
+ t := typ.expand()
+ check.validType(t, nil)
+ })
+
+ return typ
+}
+
+// arrayLength type-checks the array length expression e
+// and returns the constant length >= 0, or a value < 0
+// to indicate an error (and thus an unknown length).
+func (check *Checker) arrayLength(e syntax.Expr) int64 {
+ var x operand
+ check.expr(&x, e)
+ if x.mode != constant_ {
+ if x.mode != invalid {
+ check.errorf(&x, "array length %s must be constant", &x)
+ }
+ return -1
+ }
+ if isUntyped(x.typ) || isInteger(x.typ) {
+ if val := constant.ToInt(x.val); val.Kind() == constant.Int {
+ if representableConst(val, check, Typ[Int], nil) {
+ if n, ok := constant.Int64Val(val); ok && n >= 0 {
+ return n
+ }
+ check.errorf(&x, "invalid array length %s", &x)
+ return -1
+ }
+ }
+ }
+ check.errorf(&x, "array length %s must be integer", &x)
+ return -1
+}
+
+// typeList provides the list of types corresponding to the incoming expression list.
+// If an error occured, the result is nil, but all list elements were type-checked.
+func (check *Checker) typeList(list []syntax.Expr) []Type {
+ res := make([]Type, len(list)) // res != nil even if len(list) == 0
+ for i, x := range list {
+ t := check.varType(x)
+ if t == Typ[Invalid] {
+ res = nil
+ }
+ if res != nil {
+ res[i] = t
+ }
+ }
+ return res
+}
+
+// collectParams declares the parameters of list in scope and returns the corresponding
+// variable list. If type0 != nil, it is used instead of the first type in list.
+func (check *Checker) collectParams(scope *Scope, list []*syntax.Field, type0 syntax.Expr, variadicOk bool) (params []*Var, variadic bool) {
+ if list == nil {
+ return
+ }
+
+ var named, anonymous bool
+
+ var typ Type
+ var prev syntax.Expr
+ for i, field := range list {
+ ftype := field.Type
+ // type-check type of grouped fields only once
+ if ftype != prev {
+ prev = ftype
+ if i == 0 && type0 != nil {
+ ftype = type0
+ }
+ if t, _ := ftype.(*syntax.DotsType); t != nil {
+ ftype = t.Elem
+ if variadicOk && i == len(list)-1 {
+ variadic = true
+ } else {
+ check.softErrorf(t, "can only use ... with final parameter in list")
+ // ignore ... and continue
+ }
+ }
+ typ = check.varType(ftype)
+ }
+ // The parser ensures that f.Tag is nil and we don't
+ // care if a constructed AST contains a non-nil tag.
+ if field.Name != nil {
+ // named parameter
+ name := field.Name.Value
+ if name == "" {
+ check.invalidASTf(field.Name, "anonymous parameter")
+ // ok to continue
+ }
+ par := NewParam(field.Name.Pos(), check.pkg, name, typ)
+ check.declare(scope, field.Name, par, scope.pos)
+ params = append(params, par)
+ named = true
+ } else {
+ // anonymous parameter
+ par := NewParam(ftype.Pos(), check.pkg, "", typ)
+ check.recordImplicit(field, par)
+ params = append(params, par)
+ anonymous = true
+ }
+ }
+
+ if named && anonymous {
+ check.invalidASTf(list[0], "list contains both named and anonymous parameters")
+ // ok to continue
+ }
+
+ // For a variadic function, change the last parameter's type from T to []T.
+ // Since we type-checked T rather than ...T, we also need to retro-actively
+ // record the type for ...T.
+ if variadic {
+ last := params[len(params)-1]
+ last.typ = &Slice{elem: last.typ}
+ check.recordTypeAndValue(list[len(list)-1].Type, typexpr, last.typ, nil)
+ }
+
+ return
+}
+
+func (check *Checker) declareInSet(oset *objset, pos syntax.Pos, obj Object) bool {
+ if alt := oset.insert(obj); alt != nil {
+ check.errorf(pos, "%s redeclared", obj.Name())
+ check.reportAltDecl(alt)
+ return false
+ }
+ return true
+}
+
+func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType, def *Named) {
+ var tname *syntax.Name // most recent "type" name
+ var types []syntax.Expr
+ for _, f := range iface.MethodList {
+ if f.Name != nil {
+ // We have a method with name f.Name, or a type
+ // of a type list (f.Name.Value == "type").
+ name := f.Name.Value
+ if name == "_" {
+ check.errorf(f.Name, "invalid method name _")
+ continue // ignore
+ }
+
+ if name == "type" {
+ // Always collect all type list entries, even from
+ // different type lists, under the assumption that
+ // the author intended to include all types.
+ types = append(types, f.Type)
+ if tname != nil && tname != f.Name {
+ check.errorf(f.Name, "cannot have multiple type lists in an interface")
+ }
+ tname = f.Name
+ continue
+ }
+
+ typ := check.typ(f.Type)
+ sig, _ := typ.(*Signature)
+ if sig == nil {
+ if typ != Typ[Invalid] {
+ check.invalidASTf(f.Type, "%s is not a method signature", typ)
+ }
+ continue // ignore
+ }
+
+ // Always type-check method type parameters but complain if they are not enabled.
+ // (This extra check is needed here because interface method signatures don't have
+ // a receiver specification.)
+ if sig.tparams != nil && !check.conf.AcceptMethodTypeParams {
+ check.errorf(f.Type, "methods cannot have type parameters")
+ }
+
+ // use named receiver type if available (for better error messages)
+ var recvTyp Type = ityp
+ if def != nil {
+ recvTyp = def
+ }
+ sig.recv = NewVar(f.Name.Pos(), check.pkg, "", recvTyp)
+
+ m := NewFunc(f.Name.Pos(), check.pkg, name, sig)
+ check.recordDef(f.Name, m)
+ ityp.methods = append(ityp.methods, m)
+ } else {
+ // We have an embedded type. completeInterface will
+ // eventually verify that we have an interface.
+ ityp.embeddeds = append(ityp.embeddeds, check.typ(f.Type))
+ check.posMap[ityp] = append(check.posMap[ityp], f.Type.Pos())
+ }
+ }
+
+ // type constraints
+ ityp.types = NewSum(check.collectTypeConstraints(iface.Pos(), types))
+
+ if len(ityp.methods) == 0 && ityp.types == nil && len(ityp.embeddeds) == 0 {
+ // empty interface
+ ityp.allMethods = markComplete
+ return
+ }
+
+ // sort for API stability
+ sort.Sort(byUniqueMethodName(ityp.methods))
+ sort.Stable(byUniqueTypeName(ityp.embeddeds))
+
+ check.later(func() { check.completeInterface(iface.Pos(), ityp) })
+}
+
+func (check *Checker) completeInterface(pos syntax.Pos, ityp *Interface) {
+ if ityp.allMethods != nil {
+ return
+ }
+
+ // completeInterface may be called via the LookupFieldOrMethod,
+ // MissingMethod, Identical, or IdenticalIgnoreTags external API
+ // in which case check will be nil. In this case, type-checking
+ // must be finished and all interfaces should have been completed.
+ if check == nil {
+ panic("internal error: incomplete interface")
+ }
+
+ if check.conf.Trace {
+ // Types don't generally have position information.
+ // If we don't have a valid pos provided, try to use
+ // one close enough.
+ if !pos.IsKnown() && len(ityp.methods) > 0 {
+ pos = ityp.methods[0].pos
+ }
+
+ check.trace(pos, "complete %s", ityp)
+ check.indent++
+ defer func() {
+ check.indent--
+ check.trace(pos, "=> %s (methods = %v, types = %v)", ityp, ityp.allMethods, ityp.allTypes)
+ }()
+ }
+
+ // An infinitely expanding interface (due to a cycle) is detected
+ // elsewhere (Checker.validType), so here we simply assume we only
+ // have valid interfaces. Mark the interface as complete to avoid
+ // infinite recursion if the validType check occurs later for some
+ // reason.
+ ityp.allMethods = markComplete
+
+ // Methods of embedded interfaces are collected unchanged; i.e., the identity
+ // of a method I.m's Func Object of an interface I is the same as that of
+ // the method m in an interface that embeds interface I. On the other hand,
+ // if a method is embedded via multiple overlapping embedded interfaces, we
+ // don't provide a guarantee which "original m" got chosen for the embedding
+ // interface. See also issue #34421.
+ //
+ // If we don't care to provide this identity guarantee anymore, instead of
+ // reusing the original method in embeddings, we can clone the method's Func
+ // Object and give it the position of a corresponding embedded interface. Then
+ // we can get rid of the mpos map below and simply use the cloned method's
+ // position.
+
+ var seen objset
+ var methods []*Func
+ mpos := make(map[*Func]syntax.Pos) // method specification or method embedding position, for good error messages
+ addMethod := func(pos syntax.Pos, m *Func, explicit bool) {
+ switch other := seen.insert(m); {
+ case other == nil:
+ methods = append(methods, m)
+ mpos[m] = pos
+ case explicit:
+ check.errorf(pos, "duplicate method %s", m.name)
+ check.errorf(mpos[other.(*Func)], "\tother declaration of %s", m.name) // secondary error, \t indented
+ default:
+ // check method signatures after all types are computed (issue #33656)
+ check.atEnd(func() {
+ if !check.identical(m.typ, other.Type()) {
+ check.errorf(pos, "duplicate method %s", m.name)
+ check.errorf(mpos[other.(*Func)], "\tother declaration of %s", m.name) // secondary error, \t indented
+ }
+ })
+ }
+ }
+
+ for _, m := range ityp.methods {
+ addMethod(m.pos, m, true)
+ }
+
+ // collect types
+ allTypes := ityp.types
+
+ posList := check.posMap[ityp]
+ for i, typ := range ityp.embeddeds {
+ pos := posList[i] // embedding position
+ utyp := typ.Under()
+ etyp := utyp.Interface()
+ if etyp == nil {
+ if utyp != Typ[Invalid] {
+ var format string
+ if _, ok := utyp.(*TypeParam); ok {
+ format = "%s is a type parameter, not an interface"
+ } else {
+ format = "%s is not an interface"
+ }
+ check.errorf(pos, format, typ)
+ }
+ continue
+ }
+ check.completeInterface(pos, etyp)
+ for _, m := range etyp.allMethods {
+ addMethod(pos, m, false) // use embedding position pos rather than m.pos
+ }
+ allTypes = intersect(allTypes, etyp.allTypes)
+ }
+
+ if methods != nil {
+ sort.Sort(byUniqueMethodName(methods))
+ ityp.allMethods = methods
+ }
+ ityp.allTypes = allTypes
+}
+
+// intersect computes the intersection of the types x and y.
+// Note: A incomming nil type stands for the top type. A top
+// type result is returned as nil.
+func intersect(x, y Type) (r Type) {
+ defer func() {
+ if r == theTop {
+ r = nil
+ }
+ }()
+
+ switch {
+ case x == theBottom || y == theBottom:
+ return theBottom
+ case x == nil || x == theTop:
+ return y
+ case y == nil || x == theTop:
+ return x
+ }
+
+ xtypes := unpack(x)
+ ytypes := unpack(y)
+ // Compute the list rtypes which includes only
+ // types that are in both xtypes and ytypes.
+ // Quadratic algorithm, but good enough for now.
+ // TODO(gri) fix this
+ var rtypes []Type
+ for _, x := range xtypes {
+ if includes(ytypes, x) {
+ rtypes = append(rtypes, x)
+ }
+ }
+
+ if rtypes == nil {
+ return theBottom
+ }
+ return NewSum(rtypes)
+}
+
+// byUniqueTypeName named type lists can be sorted by their unique type names.
+type byUniqueTypeName []Type
+
+func (a byUniqueTypeName) Len() int { return len(a) }
+func (a byUniqueTypeName) Less(i, j int) bool { return sortName(a[i]) < sortName(a[j]) }
+func (a byUniqueTypeName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+func sortName(t Type) string {
+ if named := t.Named(); named != nil {
+ return named.obj.Id()
+ }
+ return ""
+}
+
+// byUniqueMethodName method lists can be sorted by their unique method names.
+type byUniqueMethodName []*Func
+
+func (a byUniqueMethodName) Len() int { return len(a) }
+func (a byUniqueMethodName) Less(i, j int) bool { return a[i].Id() < a[j].Id() }
+func (a byUniqueMethodName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+func (check *Checker) tag(t *syntax.BasicLit) string {
+ if t != nil {
+ if t.Kind == syntax.StringLit {
+ if val, err := strconv.Unquote(t.Value); err == nil {
+ return val
+ }
+ }
+ check.invalidASTf(t, "incorrect tag syntax: %q", t.Value)
+ }
+ return ""
+}
+
+func (check *Checker) structType(styp *Struct, e *syntax.StructType) {
+ if e.FieldList == nil {
+ return
+ }
+
+ // struct fields and tags
+ var fields []*Var
+ var tags []string
+
+ // for double-declaration checks
+ var fset objset
+
+ // current field typ and tag
+ var typ Type
+ var tag string
+ add := func(ident *syntax.Name, embedded bool, pos syntax.Pos) {
+ if tag != "" && tags == nil {
+ tags = make([]string, len(fields))
+ }
+ if tags != nil {
+ tags = append(tags, tag)
+ }
+
+ name := ident.Value
+ fld := NewField(pos, check.pkg, name, typ, embedded)
+ // spec: "Within a struct, non-blank field names must be unique."
+ if name == "_" || check.declareInSet(&fset, pos, fld) {
+ fields = append(fields, fld)
+ check.recordDef(ident, fld)
+ }
+ }
+
+ // addInvalid adds an embedded field of invalid type to the struct for
+ // fields with errors; this keeps the number of struct fields in sync
+ // with the source as long as the fields are _ or have different names
+ // (issue #25627).
+ addInvalid := func(ident *syntax.Name, pos syntax.Pos) {
+ typ = Typ[Invalid]
+ tag = ""
+ add(ident, true, pos)
+ }
+
+ var prev syntax.Expr
+ for i, f := range e.FieldList {
+ // Fields declared syntactically with the same type (e.g.: a, b, c T)
+ // share the same type expression. Only check type if it's a new type.
+ if i == 0 || f.Type != prev {
+ typ = check.varType(f.Type)
+ prev = f.Type
+ }
+ if i < len(e.TagList) {
+ tag = check.tag(e.TagList[i])
+ }
+ if f.Name != nil {
+ // named field
+ add(f.Name, false, f.Name.Pos())
+ } else {
+ // embedded field
+ // spec: "An embedded type must be specified as a (possibly parenthesized) type name T or
+ // as a pointer to a non-interface type name *T, and T itself may not be a pointer type."
+ pos := startPos(f.Type)
+ name := embeddedFieldIdent(f.Type)
+ if name == nil {
+ check.errorf(pos, "invalid embedded field type %s", f.Type)
+ name = &syntax.Name{Value: "_"} // TODO(gri) need to set position to pos
+ addInvalid(name, pos)
+ continue
+ }
+ add(name, true, pos)
+ // Because we have a name, typ must be of the form T or *T, where T is the name
+ // of a (named or alias) type, and t (= deref(typ)) must be the type of T.
+ // We must delay this check to the end because we don't want to instantiate
+ // (via t.Under()) a possibly incomplete type.
+ embeddedTyp := typ // for closure below
+ embeddedPos := pos
+ check.atEnd(func() {
+ t, isPtr := deref(embeddedTyp)
+ switch t := optype(t.Under()).(type) {
+ case *Basic:
+ if t == Typ[Invalid] {
+ // error was reported before
+ return
+ }
+ // unsafe.Pointer is treated like a regular pointer
+ if t.kind == UnsafePointer {
+ check.errorf(embeddedPos, "embedded field type cannot be unsafe.Pointer")
+ }
+ case *Pointer:
+ check.errorf(embeddedPos, "embedded field type cannot be a pointer")
+ case *Interface:
+ if isPtr {
+ check.errorf(embeddedPos, "embedded field type cannot be a pointer to an interface")
+ }
+ }
+ })
+ }
+ }
+
+ styp.fields = fields
+ styp.tags = tags
+}
+
+func embeddedFieldIdent(e syntax.Expr) *syntax.Name {
+ switch e := e.(type) {
+ case *syntax.Name:
+ return e
+ case *syntax.Operation:
+ if base := ptrBase(e); base != nil {
+ // *T is valid, but **T is not
+ if op, _ := base.(*syntax.Operation); op == nil || ptrBase(op) == nil {
+ return embeddedFieldIdent(e.X)
+ }
+ }
+ case *syntax.SelectorExpr:
+ return e.Sel
+ case *syntax.IndexExpr:
+ return embeddedFieldIdent(e.X)
+ case *syntax.ParenExpr:
+ return embeddedFieldIdent(e.X)
+ }
+ return nil // invalid embedded field
+}
+
+func (check *Checker) collectTypeConstraints(pos syntax.Pos, types []syntax.Expr) []Type {
+ list := make([]Type, 0, len(types)) // assume all types are correct
+ for _, texpr := range types {
+ if texpr == nil {
+ check.invalidASTf(pos, "missing type constraint")
+ continue
+ }
+ typ := check.varType(texpr)
+ // A type constraint may be a predeclared type or a
+ // composite type composed of only predeclared types.
+ // TODO(gri) If we enable this again it also must run
+ // at the end.
+ const restricted = false
+ var why string
+ if restricted && !check.typeConstraint(typ, &why) {
+ check.errorf(texpr, "invalid type constraint %s (%s)", typ, why)
+ continue
+ }
+ list = append(list, typ)
+ }
+
+ // Ensure that each type is only present once in the type list.
+ // Types may be interfaces, which may not be complete yet. It's
+ // ok to do this check at the end because it's not a requirement
+ // for correctness of the code.
+ check.atEnd(func() {
+ uniques := make([]Type, 0, len(list)) // assume all types are unique
+ for i, t := range list {
+ if t := t.Interface(); t != nil {
+ check.completeInterface(types[i].Pos(), t)
+ }
+ if includes(uniques, t) {
+ check.softErrorf(types[i], "duplicate type %s in type list", t)
+ }
+ uniques = append(uniques, t)
+ }
+ })
+
+ return list
+}
+
+// includes reports whether typ is in list
+func includes(list []Type, typ Type) bool {
+ for _, e := range list {
+ if Identical(typ, e) {
+ return true
+ }
+ }
+ return false
+}
+
+// typeConstraint checks that typ may be used in a type list.
+// For now this just checks for the absence of defined (*Named) types.
+func (check *Checker) typeConstraint(typ Type, why *string) bool {
+ switch t := typ.(type) {
+ case *Basic:
+ // ok
+ case *Array:
+ return check.typeConstraint(t.elem, why)
+ case *Slice:
+ return check.typeConstraint(t.elem, why)
+ case *Struct:
+ for _, f := range t.fields {
+ if !check.typeConstraint(f.typ, why) {
+ return false
+ }
+ }
+ case *Pointer:
+ return check.typeConstraint(t.base, why)
+ case *Tuple:
+ if t == nil {
+ return true
+ }
+ for _, v := range t.vars {
+ if !check.typeConstraint(v.typ, why) {
+ return false
+ }
+ }
+ case *Signature:
+ if len(t.tparams) != 0 {
+ panic("type parameter in function type")
+ }
+ return (t.recv == nil || check.typeConstraint(t.recv.typ, why)) &&
+ check.typeConstraint(t.params, why) &&
+ check.typeConstraint(t.results, why)
+ case *Interface:
+ t.assertCompleteness()
+ for _, m := range t.allMethods {
+ if !check.typeConstraint(m.typ, why) {
+ return false
+ }
+ }
+ case *Map:
+ return check.typeConstraint(t.key, why) && check.typeConstraint(t.elem, why)
+ case *Chan:
+ return check.typeConstraint(t.elem, why)
+ case *Named:
+ *why = check.sprintf("contains defined type %s", t)
+ return false
+ case *TypeParam:
+ // ok, e.g.: func f (type T interface { type T }) ()
+ default:
+ unreachable()
+ }
+ return true
+}
+
+func ptrBase(x *syntax.Operation) syntax.Expr {
+ if x.Op == syntax.Mul && x.Y == nil {
+ return x.X
+ }
+ return nil
+}
diff --git a/src/cmd/compile/internal/types2/unify.go b/src/cmd/compile/internal/types2/unify.go
new file mode 100644
index 0000000000..7c639366ef
--- /dev/null
+++ b/src/cmd/compile/internal/types2/unify.go
@@ -0,0 +1,454 @@
+// UNREVIEWED
+// 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.
+
+// This file implements type unification.
+
+package types2
+
+import "sort"
+
+// The unifier maintains two separate sets of type parameters x and y
+// which are used to resolve type parameters in the x and y arguments
+// provided to the unify call. For unidirectional unification, only
+// one of these sets (say x) is provided, and then type parameters are
+// only resolved for the x argument passed to unify, not the y argument
+// (even if that also contains possibly the same type parameters). This
+// is crucial to infer the type parameters of self-recursive calls:
+//
+// func f[type P](a P) { f(a) }
+//
+// For the call f(a) we want to infer that the type argument for P is P.
+// During unification, the parameter type P must be resolved to the type
+// parameter P ("x" side), but the argument type P must be left alone so
+// that unification resolves the type parameter P to P.
+//
+// For bidirection unification, both sets are provided. This enables
+// unification to go from argument to parameter type and vice versa.
+// For constraint type inference, we use bidirectional unification
+// where both the x and y type parameters are identical. This is done
+// by setting up one of them (using init) and then assigning its value
+// to the other.
+
+// A unifier maintains the current type parameters for x and y
+// and the respective types inferred for each type parameter.
+// A unifier is created by calling newUnifier.
+type unifier struct {
+ check *Checker
+ exact bool
+ x, y tparamsList // x and y must initialized via tparamsList.init
+ types []Type // inferred types, shared by x and y
+}
+
+// newUnifier returns a new unifier.
+// If exact is set, unification requires unified types to match
+// exactly. If exact is not set, a named type's underlying type
+// is considered if unification would fail otherwise, and the
+// direction of channels is ignored.
+func newUnifier(check *Checker, exact bool) *unifier {
+ u := &unifier{check: check, exact: exact}
+ u.x.unifier = u
+ u.y.unifier = u
+ return u
+}
+
+// unify attempts to unify x and y and reports whether it succeeded.
+func (u *unifier) unify(x, y Type) bool {
+ return u.nify(x, y, nil)
+}
+
+// A tparamsList describes a list of type parameters and the types inferred for them.
+type tparamsList struct {
+ unifier *unifier
+ tparams []*TypeName
+ // For each tparams element, there is a corresponding type slot index in indices.
+ // index < 0: unifier.types[-index] == nil
+ // index == 0: no type slot allocated yet
+ // index > 0: unifier.types[index] == typ
+ // Joined tparams elements share the same type slot and thus have the same index.
+ // By using a negative index for nil types we don't need to check unifier.types
+ // to see if we have a type or not.
+ indices []int // len(d.indices) == len(d.tparams)
+}
+
+// init initializes d with the given type parameters.
+// The type parameters must be in the order in which they appear in their declaration
+// (this ensures that the tparams indices match the respective type parameter index).
+func (d *tparamsList) init(tparams []*TypeName) {
+ if len(tparams) == 0 {
+ return
+ }
+ if debug {
+ for i, tpar := range tparams {
+ assert(i == tpar.typ.(*TypeParam).index)
+ }
+ }
+ d.tparams = tparams
+ d.indices = make([]int, len(tparams))
+}
+
+// join unifies the i'th type parameter of x with the j'th type parameter of y.
+// If both type parameters already have a type associated with them and they are
+// not joined, join fails and return false.
+func (u *unifier) join(i, j int) bool {
+ ti := u.x.indices[i]
+ tj := u.y.indices[j]
+ switch {
+ case ti == 0 && tj == 0:
+ // Neither type parameter has a type slot associated with them.
+ // Allocate a new joined nil type slot (negative index).
+ u.types = append(u.types, nil)
+ u.x.indices[i] = -len(u.types)
+ u.y.indices[j] = -len(u.types)
+ case ti == 0:
+ // The type parameter for x has no type slot yet. Use slot of y.
+ u.x.indices[i] = tj
+ case tj == 0:
+ // The type parameter for y has no type slot yet. Use slot of x.
+ u.y.indices[j] = ti
+
+ // Both type parameters have a slot: ti != 0 && tj != 0.
+ case ti == tj:
+ // Both type parameters already share the same slot. Nothing to do.
+ break
+ case ti > 0 && tj > 0:
+ // Both type parameters have (possibly different) inferred types. Cannot join.
+ return false
+ case ti > 0:
+ // Only the type parameter for x has an inferred type. Use x slot for y.
+ u.y.setIndex(j, ti)
+ // This case is handled like the default case.
+ // case tj > 0:
+ // // Only the type parameter for y has an inferred type. Use y slot for x.
+ // u.x.setIndex(i, tj)
+ default:
+ // Neither type parameter has an inferred type. Use y slot for x
+ // (or x slot for y, it doesn't matter).
+ u.x.setIndex(i, tj)
+ }
+ return true
+}
+
+// If typ is a type parameter of d, index returns the type parameter index.
+// Otherwise, the result is < 0.
+func (d *tparamsList) index(typ Type) int {
+ if t, ok := typ.(*TypeParam); ok {
+ if i := t.index; i < len(d.tparams) && d.tparams[i].typ == t {
+ return i
+ }
+ }
+ return -1
+}
+
+// setIndex sets the type slot index for the i'th type parameter
+// (and all its joined parameters) to tj. The type parameter
+// must have a (possibly nil) type slot associated with it.
+func (d *tparamsList) setIndex(i, tj int) {
+ ti := d.indices[i]
+ assert(ti != 0 && tj != 0)
+ for k, tk := range d.indices {
+ if tk == ti {
+ d.indices[k] = tj
+ }
+ }
+}
+
+// at returns the type set for the i'th type parameter; or nil.
+func (d *tparamsList) at(i int) Type {
+ if ti := d.indices[i]; ti > 0 {
+ return d.unifier.types[ti-1]
+ }
+ return nil
+}
+
+// set sets the type typ for the i'th type parameter;
+// typ must not be nil and it must not have been set before.
+func (d *tparamsList) set(i int, typ Type) {
+ assert(typ != nil)
+ u := d.unifier
+ switch ti := d.indices[i]; {
+ case ti < 0:
+ u.types[-ti-1] = typ
+ d.setIndex(i, -ti)
+ case ti == 0:
+ u.types = append(u.types, typ)
+ d.indices[i] = len(u.types)
+ default:
+ panic("type already set")
+ }
+}
+
+// types returns the list of inferred types (via unification) for the type parameters
+// described by d, and an index. If all types were inferred, the returned index is < 0.
+// Otherwise, it is the index of the first type parameter which couldn't be inferred;
+// i.e., for which list[index] is nil.
+func (d *tparamsList) types() (list []Type, index int) {
+ list = make([]Type, len(d.tparams))
+ index = -1
+ for i := range d.tparams {
+ t := d.at(i)
+ list[i] = t
+ if index < 0 && t == nil {
+ index = i
+ }
+ }
+ return
+}
+
+func (u *unifier) nifyEq(x, y Type, p *ifacePair) bool {
+ return x == y || u.nify(x, y, p)
+}
+
+// nify implements the core unification algorithm which is an
+// adapted version of Checker.identical0. For changes to that
+// code the corresponding changes should be made here.
+// Must not be called directly from outside the unifier.
+func (u *unifier) nify(x, y Type, p *ifacePair) bool {
+ // types must be expanded for comparison
+ x = expand(x)
+ y = expand(y)
+
+ if !u.exact {
+ // If exact unification is known to fail because we attempt to
+ // match a type name against an unnamed type literal, consider
+ // the underlying type of the named type.
+ // (Subtle: We use isNamed to include any type with a name (incl.
+ // basic types and type parameters. We use Named() because we only
+ // want *Named types.)
+ switch {
+ case !isNamed(x) && y != nil && y.Named() != nil:
+ return u.nify(x, y.Under(), p)
+ case x != nil && x.Named() != nil && !isNamed(y):
+ return u.nify(x.Under(), y, p)
+ }
+ }
+
+ // Cases where at least one of x or y is a type parameter.
+ switch i, j := u.x.index(x), u.y.index(y); {
+ case i >= 0 && j >= 0:
+ // both x and y are type parameters
+ if u.join(i, j) {
+ return true
+ }
+ // both x and y have an inferred type - they must match
+ return u.nifyEq(u.x.at(i), u.y.at(j), p)
+
+ case i >= 0:
+ // x is a type parameter, y is not
+ if tx := u.x.at(i); tx != nil {
+ return u.nifyEq(tx, y, p)
+ }
+ // otherwise, infer type from y
+ u.x.set(i, y)
+ return true
+
+ case j >= 0:
+ // y is a type parameter, x is not
+ if ty := u.y.at(j); ty != nil {
+ return u.nifyEq(x, ty, p)
+ }
+ // otherwise, infer type from x
+ u.y.set(j, x)
+ return true
+ }
+
+ // For type unification, do not shortcut (x == y) for identical
+ // types. Instead keep comparing them element-wise to unify the
+ // matching (and equal type parameter types). A simple test case
+ // where this matters is: func f[P any](a P) { f(a) } .
+
+ switch x := x.(type) {
+ case *Basic:
+ // Basic types are singletons except for the rune and byte
+ // aliases, thus we cannot solely rely on the x == y check
+ // above. See also comment in TypeName.IsAlias.
+ if y, ok := y.(*Basic); ok {
+ return x.kind == y.kind
+ }
+
+ case *Array:
+ // Two array types are identical if they have identical element types
+ // and the same array length.
+ if y, ok := y.(*Array); ok {
+ // If one or both array lengths are unknown (< 0) due to some error,
+ // assume they are the same to avoid spurious follow-on errors.
+ return (x.len < 0 || y.len < 0 || x.len == y.len) && u.nify(x.elem, y.elem, p)
+ }
+
+ case *Slice:
+ // Two slice types are identical if they have identical element types.
+ if y, ok := y.(*Slice); ok {
+ return u.nify(x.elem, y.elem, p)
+ }
+
+ case *Struct:
+ // Two struct types are identical if they have the same sequence of fields,
+ // and if corresponding fields have the same names, and identical types,
+ // and identical tags. Two embedded fields are considered to have the same
+ // name. Lower-case field names from different packages are always different.
+ if y, ok := y.(*Struct); ok {
+ if x.NumFields() == y.NumFields() {
+ for i, f := range x.fields {
+ g := y.fields[i]
+ if f.embedded != g.embedded ||
+ x.Tag(i) != y.Tag(i) ||
+ !f.sameId(g.pkg, g.name) ||
+ !u.nify(f.typ, g.typ, p) {
+ return false
+ }
+ }
+ return true
+ }
+ }
+
+ case *Pointer:
+ // Two pointer types are identical if they have identical base types.
+ if y, ok := y.(*Pointer); ok {
+ return u.nify(x.base, y.base, p)
+ }
+
+ case *Tuple:
+ // Two tuples types are identical if they have the same number of elements
+ // and corresponding elements have identical types.
+ if y, ok := y.(*Tuple); ok {
+ if x.Len() == y.Len() {
+ if x != nil {
+ for i, v := range x.vars {
+ w := y.vars[i]
+ if !u.nify(v.typ, w.typ, p) {
+ return false
+ }
+ }
+ }
+ return true
+ }
+ }
+
+ case *Signature:
+ // Two function types are identical if they have the same number of parameters
+ // and result values, corresponding parameter and result types are identical,
+ // and either both functions are variadic or neither is. Parameter and result
+ // names are not required to match.
+ // TODO(gri) handle type parameters or document why we can ignore them.
+ if y, ok := y.(*Signature); ok {
+ return x.variadic == y.variadic &&
+ u.nify(x.params, y.params, p) &&
+ u.nify(x.results, y.results, p)
+ }
+
+ case *Sum:
+ // This should not happen with the current internal use of sum types.
+ panic("type inference across sum types not implemented")
+
+ case *Interface:
+ // Two interface types are identical if they have the same set of methods with
+ // the same names and identical function types. Lower-case method names from
+ // different packages are always different. The order of the methods is irrelevant.
+ if y, ok := y.(*Interface); ok {
+ // If identical0 is called (indirectly) via an external API entry point
+ // (such as Identical, IdenticalIgnoreTags, etc.), check is nil. But in
+ // that case, interfaces are expected to be complete and lazy completion
+ // here is not needed.
+ if u.check != nil {
+ u.check.completeInterface(nopos, x)
+ u.check.completeInterface(nopos, y)
+ }
+ a := x.allMethods
+ b := y.allMethods
+ if len(a) == len(b) {
+ // Interface types are the only types where cycles can occur
+ // that are not "terminated" via named types; and such cycles
+ // can only be created via method parameter types that are
+ // anonymous interfaces (directly or indirectly) embedding
+ // the current interface. Example:
+ //
+ // type T interface {
+ // m() interface{T}
+ // }
+ //
+ // If two such (differently named) interfaces are compared,
+ // endless recursion occurs if the cycle is not detected.
+ //
+ // If x and y were compared before, they must be equal
+ // (if they were not, the recursion would have stopped);
+ // search the ifacePair stack for the same pair.
+ //
+ // This is a quadratic algorithm, but in practice these stacks
+ // are extremely short (bounded by the nesting depth of interface
+ // type declarations that recur via parameter types, an extremely
+ // rare occurrence). An alternative implementation might use a
+ // "visited" map, but that is probably less efficient overall.
+ q := &ifacePair{x, y, p}
+ for p != nil {
+ if p.identical(q) {
+ return true // same pair was compared before
+ }
+ p = p.prev
+ }
+ if debug {
+ assert(sort.IsSorted(byUniqueMethodName(a)))
+ assert(sort.IsSorted(byUniqueMethodName(b)))
+ }
+ for i, f := range a {
+ g := b[i]
+ if f.Id() != g.Id() || !u.nify(f.typ, g.typ, q) {
+ return false
+ }
+ }
+ return true
+ }
+ }
+
+ case *Map:
+ // Two map types are identical if they have identical key and value types.
+ if y, ok := y.(*Map); ok {
+ return u.nify(x.key, y.key, p) && u.nify(x.elem, y.elem, p)
+ }
+
+ case *Chan:
+ // Two channel types are identical if they have identical value types.
+ if y, ok := y.(*Chan); ok {
+ return (!u.exact || x.dir == y.dir) && u.nify(x.elem, y.elem, p)
+ }
+
+ case *Named:
+ // Two named types are identical if their type names originate
+ // in the same type declaration.
+ // if y, ok := y.(*Named); ok {
+ // return x.obj == y.obj
+ // }
+ if y, ok := y.(*Named); ok {
+ // TODO(gri) This is not always correct: two types may have the same names
+ // in the same package if one of them is nested in a function.
+ // Extremely unlikely but we need an always correct solution.
+ if x.obj.pkg == y.obj.pkg && x.obj.name == y.obj.name {
+ assert(len(x.targs) == len(y.targs))
+ for i, x := range x.targs {
+ if !u.nify(x, y.targs[i], p) {
+ return false
+ }
+ }
+ return true
+ }
+ }
+
+ case *TypeParam:
+ // Two type parameters (which are not part of the type parameters of the
+ // enclosing type as those are handled in the beginning of this function)
+ // are identical if they originate in the same declaration.
+ return x == y
+
+ // case *instance:
+ // unreachable since types are expanded
+
+ case nil:
+ // avoid a crash in case of nil type
+
+ default:
+ u.check.dump("### u.nify(%s, %s), u.x.tparams = %s", x, y, u.x.tparams)
+ unreachable()
+ }
+
+ return false
+}
diff --git a/src/cmd/compile/internal/types2/universe.go b/src/cmd/compile/internal/types2/universe.go
new file mode 100644
index 0000000000..c1961d7455
--- /dev/null
+++ b/src/cmd/compile/internal/types2/universe.go
@@ -0,0 +1,282 @@
+// UNREVIEWED
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file sets up the universe scope and the unsafe package.
+
+package types2
+
+import (
+ "go/constant"
+ "strings"
+)
+
+// The Universe scope contains all predeclared objects of Go.
+// It is the outermost scope of any chain of nested scopes.
+var Universe *Scope
+
+// The Unsafe package is the package returned by an importer
+// for the import path "unsafe".
+var Unsafe *Package
+
+var (
+ universeIota *Const
+ universeByte *Basic // uint8 alias, but has name "byte"
+ universeRune *Basic // int32 alias, but has name "rune"
+ universeAny *Named
+ universeError *Named
+)
+
+// Typ contains the predeclared *Basic types indexed by their
+// corresponding BasicKind.
+//
+// The *Basic type for Typ[Byte] will have the name "uint8".
+// Use Universe.Lookup("byte").Type() to obtain the specific
+// alias basic type named "byte" (and analogous for "rune").
+var Typ = []*Basic{
+ Invalid: {Invalid, 0, "invalid type", aType{}},
+
+ Bool: {Bool, IsBoolean, "bool", aType{}},
+ Int: {Int, IsInteger, "int", aType{}},
+ Int8: {Int8, IsInteger, "int8", aType{}},
+ Int16: {Int16, IsInteger, "int16", aType{}},
+ Int32: {Int32, IsInteger, "int32", aType{}},
+ Int64: {Int64, IsInteger, "int64", aType{}},
+ Uint: {Uint, IsInteger | IsUnsigned, "uint", aType{}},
+ Uint8: {Uint8, IsInteger | IsUnsigned, "uint8", aType{}},
+ Uint16: {Uint16, IsInteger | IsUnsigned, "uint16", aType{}},
+ Uint32: {Uint32, IsInteger | IsUnsigned, "uint32", aType{}},
+ Uint64: {Uint64, IsInteger | IsUnsigned, "uint64", aType{}},
+ Uintptr: {Uintptr, IsInteger | IsUnsigned, "uintptr", aType{}},
+ Float32: {Float32, IsFloat, "float32", aType{}},
+ Float64: {Float64, IsFloat, "float64", aType{}},
+ Complex64: {Complex64, IsComplex, "complex64", aType{}},
+ Complex128: {Complex128, IsComplex, "complex128", aType{}},
+ String: {String, IsString, "string", aType{}},
+ UnsafePointer: {UnsafePointer, 0, "Pointer", aType{}},
+
+ UntypedBool: {UntypedBool, IsBoolean | IsUntyped, "untyped bool", aType{}},
+ UntypedInt: {UntypedInt, IsInteger | IsUntyped, "untyped int", aType{}},
+ UntypedRune: {UntypedRune, IsInteger | IsUntyped, "untyped rune", aType{}},
+ UntypedFloat: {UntypedFloat, IsFloat | IsUntyped, "untyped float", aType{}},
+ UntypedComplex: {UntypedComplex, IsComplex | IsUntyped, "untyped complex", aType{}},
+ UntypedString: {UntypedString, IsString | IsUntyped, "untyped string", aType{}},
+ UntypedNil: {UntypedNil, IsUntyped, "untyped nil", aType{}},
+}
+
+var aliases = [...]*Basic{
+ {Byte, IsInteger | IsUnsigned, "byte", aType{}},
+ {Rune, IsInteger, "rune", aType{}},
+}
+
+func defPredeclaredTypes() {
+ for _, t := range Typ {
+ def(NewTypeName(nopos, nil, t.name, t))
+ }
+ for _, t := range aliases {
+ def(NewTypeName(nopos, nil, t.name, t))
+ }
+
+ // any
+ // (Predeclared and entered into universe scope so we do all the
+ // usual checks; but removed again from scope later since it's
+ // only visible as constraint in a type parameter list.)
+ {
+ typ := &Named{underlying: &emptyInterface}
+ def(NewTypeName(nopos, nil, "any", typ))
+ }
+
+ // Error has a nil package in its qualified name since it is in no package
+ {
+ res := NewVar(nopos, nil, "", Typ[String])
+ sig := &Signature{results: NewTuple(res)}
+ err := NewFunc(nopos, nil, "Error", sig)
+ typ := &Named{underlying: NewInterfaceType([]*Func{err}, nil).Complete()}
+ sig.recv = NewVar(nopos, nil, "", typ)
+ def(NewTypeName(nopos, nil, "error", typ))
+ }
+}
+
+var predeclaredConsts = [...]struct {
+ name string
+ kind BasicKind
+ val constant.Value
+}{
+ {"true", UntypedBool, constant.MakeBool(true)},
+ {"false", UntypedBool, constant.MakeBool(false)},
+ {"iota", UntypedInt, constant.MakeInt64(0)},
+}
+
+func defPredeclaredConsts() {
+ for _, c := range predeclaredConsts {
+ def(NewConst(nopos, nil, c.name, Typ[c.kind], c.val))
+ }
+}
+
+func defPredeclaredNil() {
+ def(&Nil{object{name: "nil", typ: Typ[UntypedNil], color_: black}})
+}
+
+// A builtinId is the id of a builtin function.
+type builtinId int
+
+const (
+ // universe scope
+ _Append builtinId = iota
+ _Cap
+ _Close
+ _Complex
+ _Copy
+ _Delete
+ _Imag
+ _Len
+ _Make
+ _New
+ _Panic
+ _Print
+ _Println
+ _Real
+ _Recover
+
+ // package unsafe
+ _Alignof
+ _Offsetof
+ _Sizeof
+
+ // testing support
+ _Assert
+ _Trace
+)
+
+var predeclaredFuncs = [...]struct {
+ name string
+ nargs int
+ variadic bool
+ kind exprKind
+}{
+ _Append: {"append", 1, true, expression},
+ _Cap: {"cap", 1, false, expression},
+ _Close: {"close", 1, false, statement},
+ _Complex: {"complex", 2, false, expression},
+ _Copy: {"copy", 2, false, statement},
+ _Delete: {"delete", 2, false, statement},
+ _Imag: {"imag", 1, false, expression},
+ _Len: {"len", 1, false, expression},
+ _Make: {"make", 1, true, expression},
+ _New: {"new", 1, false, expression},
+ _Panic: {"panic", 1, false, statement},
+ _Print: {"print", 0, true, statement},
+ _Println: {"println", 0, true, statement},
+ _Real: {"real", 1, false, expression},
+ _Recover: {"recover", 0, false, statement},
+
+ _Alignof: {"Alignof", 1, false, expression},
+ _Offsetof: {"Offsetof", 1, false, expression},
+ _Sizeof: {"Sizeof", 1, false, expression},
+
+ _Assert: {"assert", 1, false, statement},
+ _Trace: {"trace", 0, true, statement},
+}
+
+func defPredeclaredFuncs() {
+ for i := range predeclaredFuncs {
+ id := builtinId(i)
+ if id == _Assert || id == _Trace {
+ continue // only define these in testing environment
+ }
+ def(newBuiltin(id))
+ }
+}
+
+// DefPredeclaredTestFuncs defines the assert and trace built-ins.
+// These built-ins are intended for debugging and testing of this
+// package only.
+func DefPredeclaredTestFuncs() {
+ if Universe.Lookup("assert") != nil {
+ return // already defined
+ }
+ def(newBuiltin(_Assert))
+ def(newBuiltin(_Trace))
+}
+
+func defPredeclaredComparable() {
+ // The "comparable" interface can be imagined as defined like
+ //
+ // type comparable interface {
+ // == () untyped bool
+ // != () untyped bool
+ // }
+ //
+ // == and != cannot be user-declared but we can declare
+ // a magic method == and check for its presence when needed.
+
+ // Define interface { == () }. We don't care about the signature
+ // for == so leave it empty except for the receiver, which is
+ // set up later to match the usual interface method assumptions.
+ sig := new(Signature)
+ eql := NewFunc(nopos, nil, "==", sig)
+ iface := NewInterfaceType([]*Func{eql}, nil).Complete()
+
+ // set up the defined type for the interface
+ obj := NewTypeName(nopos, nil, "comparable", nil)
+ named := NewNamed(obj, iface, nil)
+ obj.color_ = black
+ sig.recv = NewVar(nopos, nil, "", named) // complete == signature
+
+ def(obj)
+}
+
+func init() {
+ Universe = NewScope(nil, nopos, nopos, "universe")
+ Unsafe = NewPackage("unsafe", "unsafe")
+ Unsafe.complete = true
+
+ defPredeclaredTypes()
+ defPredeclaredConsts()
+ defPredeclaredNil()
+ defPredeclaredFuncs()
+ defPredeclaredComparable()
+
+ universeIota = Universe.Lookup("iota").(*Const)
+ universeByte = Universe.Lookup("byte").(*TypeName).typ.(*Basic)
+ universeRune = Universe.Lookup("rune").(*TypeName).typ.(*Basic)
+ universeAny = Universe.Lookup("any").(*TypeName).typ.(*Named)
+ universeError = Universe.Lookup("error").(*TypeName).typ.(*Named)
+
+ // "any" is only visible as constraint in a type parameter list
+ delete(Universe.elems, "any")
+}
+
+// Objects with names containing blanks are internal and not entered into
+// a scope. Objects with exported names are inserted in the unsafe package
+// scope; other objects are inserted in the universe scope.
+//
+func def(obj Object) {
+ assert(obj.color() == black)
+ name := obj.Name()
+ if strings.Contains(name, " ") {
+ return // nothing to do
+ }
+ // fix Obj link for named types
+ if typ := obj.Type().Named(); typ != nil {
+ typ.obj = obj.(*TypeName)
+ }
+ // exported identifiers go into package unsafe
+ scope := Universe
+ if obj.Exported() {
+ scope = Unsafe.scope
+ // set Pkg field
+ switch obj := obj.(type) {
+ case *TypeName:
+ obj.pkg = Unsafe
+ case *Builtin:
+ obj.pkg = Unsafe
+ default:
+ unreachable()
+ }
+ }
+ if scope.Insert(obj) != nil {
+ panic("internal error: double declaration")
+ }
+}
diff --git a/src/cmd/compile/internal/types2/walk.go b/src/cmd/compile/internal/types2/walk.go
new file mode 100644
index 0000000000..18cfb28ade
--- /dev/null
+++ b/src/cmd/compile/internal/types2/walk.go
@@ -0,0 +1,322 @@
+// UNREVIEWED
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements syntax tree walking.
+// TODO(gri) A more general API should probably be in
+// the syntax package.
+
+package types2
+
+import (
+ "cmd/compile/internal/syntax"
+ "fmt"
+)
+
+// Walk traverses a syntax in pre-order: It starts by calling f(root);
+// root must not be nil. If f returns false (== "continue"), Walk calls
+// f recursively for each of the non-nil children of that node; if f
+// returns true (== "stop"), Walk does not traverse the respective node's
+// children.
+// Some nodes may be shared among multiple parent nodes (e.g., types in
+// field lists such as type T in "a, b, c T"). Such shared nodes are
+// walked multiple times.
+// TODO(gri) Revisit this design. It may make sense to walk those nodes
+// only once. A place where this matters is TestResolveIdents.
+func Walk(root syntax.Node, f func(syntax.Node) bool) {
+ w := walker{f}
+ w.node(root)
+}
+
+type walker struct {
+ f func(syntax.Node) bool
+}
+
+func (w *walker) node(n syntax.Node) {
+ if n == nil {
+ panic("invalid syntax tree: nil node")
+ }
+
+ if w.f(n) {
+ return
+ }
+
+ switch n := n.(type) {
+ // packages
+ case *syntax.File:
+ w.node(n.PkgName)
+ w.declList(n.DeclList)
+
+ // declarations
+ case *syntax.ImportDecl:
+ if n.LocalPkgName != nil {
+ w.node(n.LocalPkgName)
+ }
+ w.node(n.Path)
+
+ case *syntax.ConstDecl:
+ w.nameList(n.NameList)
+ if n.Type != nil {
+ w.node(n.Type)
+ }
+ if n.Values != nil {
+ w.node(n.Values)
+ }
+
+ case *syntax.TypeDecl:
+ w.node(n.Name)
+ w.fieldList(n.TParamList)
+ w.node(n.Type)
+
+ case *syntax.VarDecl:
+ w.nameList(n.NameList)
+ if n.Type != nil {
+ w.node(n.Type)
+ }
+ if n.Values != nil {
+ w.node(n.Values)
+ }
+
+ case *syntax.FuncDecl:
+ if n.Recv != nil {
+ w.node(n.Recv)
+ }
+ w.node(n.Name)
+ w.fieldList(n.TParamList)
+ w.node(n.Type)
+ if n.Body != nil {
+ w.node(n.Body)
+ }
+
+ // expressions
+ case *syntax.BadExpr: // nothing to do
+ case *syntax.Name:
+ case *syntax.BasicLit: // nothing to do
+
+ case *syntax.CompositeLit:
+ if n.Type != nil {
+ w.node(n.Type)
+ }
+ w.exprList(n.ElemList)
+
+ case *syntax.KeyValueExpr:
+ w.node(n.Key)
+ w.node(n.Value)
+
+ case *syntax.FuncLit:
+ w.node(n.Type)
+ w.node(n.Body)
+
+ case *syntax.ParenExpr:
+ w.node(n.X)
+
+ case *syntax.SelectorExpr:
+ w.node(n.X)
+ w.node(n.Sel)
+
+ case *syntax.IndexExpr:
+ w.node(n.X)
+ w.node(n.Index)
+
+ case *syntax.SliceExpr:
+ w.node(n.X)
+ for _, x := range n.Index {
+ if x != nil {
+ w.node(x)
+ }
+ }
+
+ case *syntax.AssertExpr:
+ w.node(n.X)
+ w.node(n.Type)
+
+ case *syntax.TypeSwitchGuard:
+ if n.Lhs != nil {
+ w.node(n.Lhs)
+ }
+ w.node(n.X)
+
+ case *syntax.Operation:
+ w.node(n.X)
+ if n.Y != nil {
+ w.node(n.Y)
+ }
+
+ case *syntax.CallExpr:
+ w.node(n.Fun)
+ w.exprList(n.ArgList)
+
+ case *syntax.ListExpr:
+ w.exprList(n.ElemList)
+
+ // types
+ case *syntax.ArrayType:
+ if n.Len != nil {
+ w.node(n.Len)
+ }
+ w.node(n.Elem)
+
+ case *syntax.SliceType:
+ w.node(n.Elem)
+
+ case *syntax.DotsType:
+ w.node(n.Elem)
+
+ case *syntax.StructType:
+ w.fieldList(n.FieldList)
+ for _, t := range n.TagList {
+ if t != nil {
+ w.node(t)
+ }
+ }
+
+ case *syntax.Field:
+ if n.Name != nil {
+ w.node(n.Name)
+ }
+ w.node(n.Type)
+
+ case *syntax.InterfaceType:
+ w.fieldList(n.MethodList)
+
+ case *syntax.FuncType:
+ w.fieldList(n.ParamList)
+ w.fieldList(n.ResultList)
+
+ case *syntax.MapType:
+ w.node(n.Key)
+ w.node(n.Value)
+
+ case *syntax.ChanType:
+ w.node(n.Elem)
+
+ // statements
+ case *syntax.EmptyStmt: // nothing to do
+
+ case *syntax.LabeledStmt:
+ w.node(n.Label)
+ w.node(n.Stmt)
+
+ case *syntax.BlockStmt:
+ w.stmtList(n.List)
+
+ case *syntax.ExprStmt:
+ w.node(n.X)
+
+ case *syntax.SendStmt:
+ w.node(n.Chan)
+ w.node(n.Value)
+
+ case *syntax.DeclStmt:
+ w.declList(n.DeclList)
+
+ case *syntax.AssignStmt:
+ w.node(n.Lhs)
+ w.node(n.Rhs)
+
+ case *syntax.BranchStmt:
+ if n.Label != nil {
+ w.node(n.Label)
+ }
+ // Target points to nodes elsewhere in the syntax tree
+
+ case *syntax.CallStmt:
+ w.node(n.Call)
+
+ case *syntax.ReturnStmt:
+ if n.Results != nil {
+ w.node(n.Results)
+ }
+
+ case *syntax.IfStmt:
+ if n.Init != nil {
+ w.node(n.Init)
+ }
+ w.node(n.Cond)
+ w.node(n.Then)
+ if n.Else != nil {
+ w.node(n.Else)
+ }
+
+ case *syntax.ForStmt:
+ if n.Init != nil {
+ w.node(n.Init)
+ }
+ if n.Cond != nil {
+ w.node(n.Cond)
+ }
+ if n.Post != nil {
+ w.node(n.Post)
+ }
+ w.node(n.Body)
+
+ case *syntax.SwitchStmt:
+ if n.Init != nil {
+ w.node(n.Init)
+ }
+ if n.Tag != nil {
+ w.node(n.Tag)
+ }
+ for _, s := range n.Body {
+ w.node(s)
+ }
+
+ case *syntax.SelectStmt:
+ for _, s := range n.Body {
+ w.node(s)
+ }
+
+ // helper nodes
+ case *syntax.RangeClause:
+ if n.Lhs != nil {
+ w.node(n.Lhs)
+ }
+ w.node(n.X)
+
+ case *syntax.CaseClause:
+ if n.Cases != nil {
+ w.node(n.Cases)
+ }
+ w.stmtList(n.Body)
+
+ case *syntax.CommClause:
+ if n.Comm != nil {
+ w.node(n.Comm)
+ }
+ w.stmtList(n.Body)
+
+ default:
+ panic(fmt.Sprintf("internal error: unknown node type %T", n))
+ }
+}
+
+func (w *walker) declList(list []syntax.Decl) {
+ for _, n := range list {
+ w.node(n)
+ }
+}
+
+func (w *walker) exprList(list []syntax.Expr) {
+ for _, n := range list {
+ w.node(n)
+ }
+}
+
+func (w *walker) stmtList(list []syntax.Stmt) {
+ for _, n := range list {
+ w.node(n)
+ }
+}
+
+func (w *walker) nameList(list []*syntax.Name) {
+ for _, n := range list {
+ w.node(n)
+ }
+}
+
+func (w *walker) fieldList(list []*syntax.Field) {
+ for _, n := range list {
+ w.node(n)
+ }
+}