mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
[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>
This commit is contained in:
parent
6ff16fe3ee
commit
ca36ba83ab
90 changed files with 24300 additions and 3 deletions
|
|
@ -42,6 +42,10 @@ var knownFormats = map[string]string{
|
||||||
"*cmd/compile/internal/ssa.Value %s": "",
|
"*cmd/compile/internal/ssa.Value %s": "",
|
||||||
"*cmd/compile/internal/ssa.Value %v": "",
|
"*cmd/compile/internal/ssa.Value %v": "",
|
||||||
"*cmd/compile/internal/ssa.sparseTreeMapEntry %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 %p": "",
|
||||||
"*cmd/compile/internal/types.Field %v": "",
|
"*cmd/compile/internal/types.Field %v": "",
|
||||||
"*cmd/compile/internal/types.Sym %0S": "",
|
"*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 %p": "",
|
||||||
"*cmd/compile/internal/types.Type %s": "",
|
"*cmd/compile/internal/types.Type %s": "",
|
||||||
"*cmd/compile/internal/types.Type %v": "",
|
"*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.Addr %v": "",
|
||||||
"*cmd/internal/obj.LSym %v": "",
|
"*cmd/internal/obj.LSym %v": "",
|
||||||
"*math/big.Float %f": "",
|
"*math/big.Float %f": "",
|
||||||
|
|
@ -67,6 +90,8 @@ var knownFormats = map[string]string{
|
||||||
"[16]byte %x": "",
|
"[16]byte %x": "",
|
||||||
"[]*cmd/compile/internal/ssa.Block %v": "",
|
"[]*cmd/compile/internal/ssa.Block %v": "",
|
||||||
"[]*cmd/compile/internal/ssa.Value %v": "",
|
"[]*cmd/compile/internal/ssa.Value %v": "",
|
||||||
|
"[]*cmd/compile/internal/types2.Func %v": "",
|
||||||
|
"[]*cmd/compile/internal/types2.TypeName %s": "",
|
||||||
"[][]string %q": "",
|
"[][]string %q": "",
|
||||||
"[]byte %s": "",
|
"[]byte %s": "",
|
||||||
"[]byte %x": "",
|
"[]byte %x": "",
|
||||||
|
|
@ -75,6 +100,8 @@ var knownFormats = map[string]string{
|
||||||
"[]cmd/compile/internal/ssa.posetNode %v": "",
|
"[]cmd/compile/internal/ssa.posetNode %v": "",
|
||||||
"[]cmd/compile/internal/ssa.posetUndo %v": "",
|
"[]cmd/compile/internal/ssa.posetUndo %v": "",
|
||||||
"[]cmd/compile/internal/syntax.token %s": "",
|
"[]cmd/compile/internal/syntax.token %s": "",
|
||||||
|
"[]cmd/compile/internal/types2.Type %s": "",
|
||||||
|
"[]int %v": "",
|
||||||
"[]string %v": "",
|
"[]string %v": "",
|
||||||
"[]uint32 %v": "",
|
"[]uint32 %v": "",
|
||||||
"bool %v": "",
|
"bool %v": "",
|
||||||
|
|
@ -100,6 +127,7 @@ var knownFormats = map[string]string{
|
||||||
"cmd/compile/internal/gc.fmtMode %d": "",
|
"cmd/compile/internal/gc.fmtMode %d": "",
|
||||||
"cmd/compile/internal/gc.initKind %d": "",
|
"cmd/compile/internal/gc.initKind %d": "",
|
||||||
"cmd/compile/internal/gc.itag %v": "",
|
"cmd/compile/internal/gc.itag %v": "",
|
||||||
|
"cmd/compile/internal/importer.itag %v": "",
|
||||||
"cmd/compile/internal/ssa.BranchPrediction %d": "",
|
"cmd/compile/internal/ssa.BranchPrediction %d": "",
|
||||||
"cmd/compile/internal/ssa.Edge %v": "",
|
"cmd/compile/internal/ssa.Edge %v": "",
|
||||||
"cmd/compile/internal/ssa.GCNode %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.regMask %d": "",
|
||||||
"cmd/compile/internal/ssa.register %d": "",
|
"cmd/compile/internal/ssa.register %d": "",
|
||||||
"cmd/compile/internal/ssa.relation %s": "",
|
"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 %q": "",
|
||||||
"cmd/compile/internal/syntax.Error %v": "",
|
"cmd/compile/internal/syntax.Error %v": "",
|
||||||
"cmd/compile/internal/syntax.Expr %#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.LitKind %d": "",
|
||||||
"cmd/compile/internal/syntax.Node %T": "",
|
"cmd/compile/internal/syntax.Node %T": "",
|
||||||
"cmd/compile/internal/syntax.Operator %s": "",
|
"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 %d": "",
|
||||||
"cmd/compile/internal/types.EType %s": "",
|
"cmd/compile/internal/types.EType %s": "",
|
||||||
"cmd/compile/internal/types.EType %v": "",
|
"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": "",
|
"cmd/internal/obj.ABI %v": "",
|
||||||
|
"error %s": "",
|
||||||
"error %v": "",
|
"error %v": "",
|
||||||
"float64 %.2f": "",
|
"float64 %.2f": "",
|
||||||
"float64 %.3f": "",
|
"float64 %.3f": "",
|
||||||
"float64 %.6g": "",
|
"float64 %.6g": "",
|
||||||
"float64 %g": "",
|
"float64 %g": "",
|
||||||
|
"go/constant.Value %s": "",
|
||||||
"int %#x": "",
|
"int %#x": "",
|
||||||
"int %-12d": "",
|
"int %-12d": "",
|
||||||
"int %-6d": "",
|
"int %-6d": "",
|
||||||
|
|
@ -176,6 +218,7 @@ var knownFormats = map[string]string{
|
||||||
"interface{} %v": "",
|
"interface{} %v": "",
|
||||||
"map[*cmd/compile/internal/gc.Node]*cmd/compile/internal/ssa.Value %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/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[cmd/compile/internal/ssa.ID]uint32 %v": "",
|
||||||
"map[int64]uint32 %v": "",
|
"map[int64]uint32 %v": "",
|
||||||
"math/big.Accuracy %s": "",
|
"math/big.Accuracy %s": "",
|
||||||
|
|
@ -186,6 +229,7 @@ var knownFormats = map[string]string{
|
||||||
"string %-*s": "",
|
"string %-*s": "",
|
||||||
"string %-16s": "",
|
"string %-16s": "",
|
||||||
"string %-6s": "",
|
"string %-6s": "",
|
||||||
|
"string %T": "",
|
||||||
"string %q": "",
|
"string %q": "",
|
||||||
"string %s": "",
|
"string %s": "",
|
||||||
"string %v": "",
|
"string %v": "",
|
||||||
|
|
@ -205,6 +249,7 @@ var knownFormats = map[string]string{
|
||||||
"uint64 %08x": "",
|
"uint64 %08x": "",
|
||||||
"uint64 %b": "",
|
"uint64 %b": "",
|
||||||
"uint64 %d": "",
|
"uint64 %d": "",
|
||||||
|
"uint64 %v": "",
|
||||||
"uint64 %x": "",
|
"uint64 %x": "",
|
||||||
"uint8 %#x": "",
|
"uint8 %#x": "",
|
||||||
"uint8 %d": "",
|
"uint8 %d": "",
|
||||||
|
|
|
||||||
92
src/cmd/compile/internal/importer/exportdata.go
Normal file
92
src/cmd/compile/internal/importer/exportdata.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
175
src/cmd/compile/internal/importer/gcimporter.go
Normal file
175
src/cmd/compile/internal/importer/gcimporter.go
Normal file
|
|
@ -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() }
|
||||||
612
src/cmd/compile/internal/importer/gcimporter_test.go
Normal file
612
src/cmd/compile/internal/importer/gcimporter_test.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
616
src/cmd/compile/internal/importer/iimport.go
Normal file
616
src/cmd/compile/internal/importer/iimport.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
144
src/cmd/compile/internal/importer/support.go
Normal file
144
src/cmd/compile/internal/importer/support.go
Normal file
|
|
@ -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 }
|
||||||
15
src/cmd/compile/internal/importer/testdata/a.go
vendored
Normal file
15
src/cmd/compile/internal/importer/testdata/a.go
vendored
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
12
src/cmd/compile/internal/importer/testdata/b.go
vendored
Normal file
12
src/cmd/compile/internal/importer/testdata/b.go
vendored
Normal file
|
|
@ -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
|
||||||
89
src/cmd/compile/internal/importer/testdata/exports.go
vendored
Normal file
89
src/cmd/compile/internal/importer/testdata/exports.go
vendored
Normal file
|
|
@ -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()
|
||||||
12
src/cmd/compile/internal/importer/testdata/issue15920.go
vendored
Normal file
12
src/cmd/compile/internal/importer/testdata/issue15920.go
vendored
Normal file
|
|
@ -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 }
|
||||||
10
src/cmd/compile/internal/importer/testdata/issue20046.go
vendored
Normal file
10
src/cmd/compile/internal/importer/testdata/issue20046.go
vendored
Normal file
|
|
@ -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()
|
||||||
|
}
|
||||||
18
src/cmd/compile/internal/importer/testdata/issue25301.go
vendored
Normal file
18
src/cmd/compile/internal/importer/testdata/issue25301.go
vendored
Normal file
|
|
@ -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") }
|
||||||
14
src/cmd/compile/internal/importer/testdata/issue25596.go
vendored
Normal file
14
src/cmd/compile/internal/importer/testdata/issue25596.go
vendored
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
14
src/cmd/compile/internal/importer/testdata/p.go
vendored
Normal file
14
src/cmd/compile/internal/importer/testdata/p.go
vendored
Normal file
|
|
@ -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() {}
|
||||||
29
src/cmd/compile/internal/importer/testdata/versions/test.go
vendored
Normal file
29
src/cmd/compile/internal/importer/testdata/versions/test.go
vendored
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
426
src/cmd/compile/internal/types2/api.go
Normal file
426
src/cmd/compile/internal/types2/api.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
1741
src/cmd/compile/internal/types2/api_test.go
Normal file
1741
src/cmd/compile/internal/types2/api_test.go
Normal file
File diff suppressed because it is too large
Load diff
359
src/cmd/compile/internal/types2/assignments.go
Normal file
359
src/cmd/compile/internal/types2/assignments.go
Normal file
|
|
@ -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 :=")
|
||||||
|
}
|
||||||
|
}
|
||||||
777
src/cmd/compile/internal/types2/builtins.go
Normal file
777
src/cmd/compile/internal/types2/builtins.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
219
src/cmd/compile/internal/types2/builtins_test.go
Normal file
219
src/cmd/compile/internal/types2/builtins_test.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
808
src/cmd/compile/internal/types2/call.go
Normal file
808
src/cmd/compile/internal/types2/call.go
Normal file
|
|
@ -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 = ©
|
||||||
|
}
|
||||||
|
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 = ©
|
||||||
|
}
|
||||||
|
// 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]
|
||||||
|
}
|
||||||
|
}
|
||||||
449
src/cmd/compile/internal/types2/check.go
Normal file
449
src/cmd/compile/internal/types2/check.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
269
src/cmd/compile/internal/types2/check_test.go
Normal file
269
src/cmd/compile/internal/types2/check_test.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
164
src/cmd/compile/internal/types2/conversions.go
Normal file
164
src/cmd/compile/internal/types2/conversions.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
981
src/cmd/compile/internal/types2/decl.go
Normal file
981
src/cmd/compile/internal/types2/decl.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
159
src/cmd/compile/internal/types2/errors.go
Normal file
159
src/cmd/compile/internal/types2/errors.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
26
src/cmd/compile/internal/types2/errors_test.go
Normal file
26
src/cmd/compile/internal/types2/errors_test.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
324
src/cmd/compile/internal/types2/example_test.go
Normal file
324
src/cmd/compile/internal/types2/example_test.go
Normal file
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
216
src/cmd/compile/internal/types2/examples/functions.go2
Normal file
216
src/cmd/compile/internal/types2/examples/functions.go2
Normal file
|
|
@ -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 */ ()
|
||||||
|
}
|
||||||
97
src/cmd/compile/internal/types2/examples/methods.go2
Normal file
97
src/cmd/compile/internal/types2/examples/methods.go2
Normal file
|
|
@ -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]) _() {}
|
||||||
261
src/cmd/compile/internal/types2/examples/types.go2
Normal file
261
src/cmd/compile/internal/types2/examples/types.go2
Normal file
|
|
@ -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 */
|
||||||
|
}
|
||||||
1906
src/cmd/compile/internal/types2/expr.go
Normal file
1906
src/cmd/compile/internal/types2/expr.go
Normal file
File diff suppressed because it is too large
Load diff
287
src/cmd/compile/internal/types2/exprstring.go
Normal file
287
src/cmd/compile/internal/types2/exprstring.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
97
src/cmd/compile/internal/types2/exprstring_test.go
Normal file
97
src/cmd/compile/internal/types2/exprstring_test.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
92
src/cmd/compile/internal/types2/fixedbugs/issue39634.go2
Normal file
92
src/cmd/compile/internal/types2/fixedbugs/issue39634.go2
Normal file
|
|
@ -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 */ ) }
|
||||||
16
src/cmd/compile/internal/types2/fixedbugs/issue39664.go2
Normal file
16
src/cmd/compile/internal/types2/fixedbugs/issue39664.go2
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
28
src/cmd/compile/internal/types2/fixedbugs/issue39680.go2
Normal file
28
src/cmd/compile/internal/types2/fixedbugs/issue39680.go2
Normal file
|
|
@ -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"})
|
||||||
|
}
|
||||||
15
src/cmd/compile/internal/types2/fixedbugs/issue39693.go2
Normal file
15
src/cmd/compile/internal/types2/fixedbugs/issue39693.go2
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
30
src/cmd/compile/internal/types2/fixedbugs/issue39699.go2
Normal file
30
src/cmd/compile/internal/types2/fixedbugs/issue39699.go2
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
12
src/cmd/compile/internal/types2/fixedbugs/issue39711.go2
Normal file
12
src/cmd/compile/internal/types2/fixedbugs/issue39711.go2
Normal file
|
|
@ -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 }
|
||||||
|
}
|
||||||
10
src/cmd/compile/internal/types2/fixedbugs/issue39723.go2
Normal file
10
src/cmd/compile/internal/types2/fixedbugs/issue39723.go2
Normal file
|
|
@ -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 */ ]()
|
||||||
17
src/cmd/compile/internal/types2/fixedbugs/issue39725.go2
Normal file
17
src/cmd/compile/internal/types2/fixedbugs/issue39725.go2
Normal file
|
|
@ -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) {})
|
||||||
|
}
|
||||||
21
src/cmd/compile/internal/types2/fixedbugs/issue39754.go2
Normal file
21
src/cmd/compile/internal/types2/fixedbugs/issue39754.go2
Normal file
|
|
@ -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]]()
|
||||||
|
}
|
||||||
24
src/cmd/compile/internal/types2/fixedbugs/issue39755.go2
Normal file
24
src/cmd/compile/internal/types2/fixedbugs/issue39755.go2
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
21
src/cmd/compile/internal/types2/fixedbugs/issue39768.go2
Normal file
21
src/cmd/compile/internal/types2/fixedbugs/issue39768.go2
Normal file
|
|
@ -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]
|
||||||
51
src/cmd/compile/internal/types2/fixedbugs/issue39938.go2
Normal file
51
src/cmd/compile/internal/types2/fixedbugs/issue39938.go2
Normal file
|
|
@ -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]]]]]
|
||||||
|
}
|
||||||
10
src/cmd/compile/internal/types2/fixedbugs/issue39948.go2
Normal file
10
src/cmd/compile/internal/types2/fixedbugs/issue39948.go2
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
17
src/cmd/compile/internal/types2/fixedbugs/issue39976.go2
Normal file
17
src/cmd/compile/internal/types2/fixedbugs/issue39976.go2
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
37
src/cmd/compile/internal/types2/fixedbugs/issue39982.go2
Normal file
37
src/cmd/compile/internal/types2/fixedbugs/issue39982.go2
Normal file
|
|
@ -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]])),
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/cmd/compile/internal/types2/fixedbugs/issue40038.go2
Normal file
16
src/cmd/compile/internal/types2/fixedbugs/issue40038.go2
Normal file
|
|
@ -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]]
|
||||||
|
}
|
||||||
16
src/cmd/compile/internal/types2/fixedbugs/issue40056.go2
Normal file
16
src/cmd/compile/internal/types2/fixedbugs/issue40056.go2
Normal file
|
|
@ -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()
|
||||||
18
src/cmd/compile/internal/types2/fixedbugs/issue40057.go2
Normal file
18
src/cmd/compile/internal/types2/fixedbugs/issue40057.go2
Normal file
|
|
@ -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()
|
||||||
13
src/cmd/compile/internal/types2/fixedbugs/issue40301.go2
Normal file
13
src/cmd/compile/internal/types2/fixedbugs/issue40301.go2
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
16
src/cmd/compile/internal/types2/fixedbugs/issue40684.go2
Normal file
16
src/cmd/compile/internal/types2/fixedbugs/issue40684.go2
Normal file
|
|
@ -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 */ ]
|
||||||
|
}
|
||||||
92
src/cmd/compile/internal/types2/fixedbugs/issue41124.go2
Normal file
92
src/cmd/compile/internal/types2/fixedbugs/issue41124.go2
Normal file
|
|
@ -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 */ :
|
||||||
|
}
|
||||||
|
}
|
||||||
41
src/cmd/compile/internal/types2/gccgosizes.go
Normal file
41
src/cmd/compile/internal/types2/gccgosizes.go
Normal file
|
|
@ -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},
|
||||||
|
}
|
||||||
220
src/cmd/compile/internal/types2/hilbert_test.go
Normal file
220
src/cmd/compile/internal/types2/hilbert_test.go
Normal file
|
|
@ -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")
|
||||||
|
}
|
||||||
36
src/cmd/compile/internal/types2/importer_test.go
Normal file
36
src/cmd/compile/internal/types2/importer_test.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
359
src/cmd/compile/internal/types2/infer.go
Normal file
359
src/cmd/compile/internal/types2/infer.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
298
src/cmd/compile/internal/types2/initorder.go
Normal file
298
src/cmd/compile/internal/types2/initorder.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
533
src/cmd/compile/internal/types2/issues_test.go
Normal file
533
src/cmd/compile/internal/types2/issues_test.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
260
src/cmd/compile/internal/types2/labels.go
Normal file
260
src/cmd/compile/internal/types2/labels.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
493
src/cmd/compile/internal/types2/lookup.go
Normal file
493
src/cmd/compile/internal/types2/lookup.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
261
src/cmd/compile/internal/types2/methodset.go
Normal file
261
src/cmd/compile/internal/types2/methodset.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
492
src/cmd/compile/internal/types2/object.go
Normal file
492
src/cmd/compile/internal/types2/object.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
89
src/cmd/compile/internal/types2/object_test.go
Normal file
89
src/cmd/compile/internal/types2/object_test.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
32
src/cmd/compile/internal/types2/objset.go
Normal file
32
src/cmd/compile/internal/types2/objset.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
315
src/cmd/compile/internal/types2/operand.go
Normal file
315
src/cmd/compile/internal/types2/operand.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
65
src/cmd/compile/internal/types2/package.go
Normal file
65
src/cmd/compile/internal/types2/package.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
364
src/cmd/compile/internal/types2/pos.go
Normal file
364
src/cmd/compile/internal/types2/pos.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
413
src/cmd/compile/internal/types2/predicates.go
Normal file
413
src/cmd/compile/internal/types2/predicates.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
714
src/cmd/compile/internal/types2/resolver.go
Normal file
714
src/cmd/compile/internal/types2/resolver.go
Normal file
|
|
@ -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 "."
|
||||||
|
}
|
||||||
223
src/cmd/compile/internal/types2/resolver_test.go
Normal file
223
src/cmd/compile/internal/types2/resolver_test.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
180
src/cmd/compile/internal/types2/return.go
Normal file
180
src/cmd/compile/internal/types2/return.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
149
src/cmd/compile/internal/types2/sanitize.go
Normal file
149
src/cmd/compile/internal/types2/sanitize.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
217
src/cmd/compile/internal/types2/scope.go
Normal file
217
src/cmd/compile/internal/types2/scope.go
Normal file
|
|
@ -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()
|
||||||
|
}
|
||||||
144
src/cmd/compile/internal/types2/selection.go
Normal file
144
src/cmd/compile/internal/types2/selection.go
Normal file
|
|
@ -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()
|
||||||
|
}
|
||||||
97
src/cmd/compile/internal/types2/self_test.go
Normal file
97
src/cmd/compile/internal/types2/self_test.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
266
src/cmd/compile/internal/types2/sizes.go
Normal file
266
src/cmd/compile/internal/types2/sizes.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
108
src/cmd/compile/internal/types2/sizes_test.go
Normal file
108
src/cmd/compile/internal/types2/sizes_test.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
320
src/cmd/compile/internal/types2/stdlib_test.go
Normal file
320
src/cmd/compile/internal/types2/stdlib_test.go
Normal file
|
|
@ -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()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
919
src/cmd/compile/internal/types2/stmt.go
Normal file
919
src/cmd/compile/internal/types2/stmt.go
Normal file
|
|
@ -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, ""
|
||||||
|
}
|
||||||
554
src/cmd/compile/internal/types2/subst.go
Normal file
554
src/cmd/compile/internal/types2/subst.go
Normal file
|
|
@ -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 = ©
|
||||||
|
}
|
||||||
|
// 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 ©
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 ©
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
1061
src/cmd/compile/internal/types2/type.go
Normal file
1061
src/cmd/compile/internal/types2/type.go
Normal file
File diff suppressed because it is too large
Load diff
480
src/cmd/compile/internal/types2/typestring.go
Normal file
480
src/cmd/compile/internal/types2/typestring.go
Normal file
|
|
@ -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:])
|
||||||
|
}
|
||||||
221
src/cmd/compile/internal/types2/typestring_test.go
Normal file
221
src/cmd/compile/internal/types2/typestring_test.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1280
src/cmd/compile/internal/types2/typexpr.go
Normal file
1280
src/cmd/compile/internal/types2/typexpr.go
Normal file
File diff suppressed because it is too large
Load diff
454
src/cmd/compile/internal/types2/unify.go
Normal file
454
src/cmd/compile/internal/types2/unify.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
282
src/cmd/compile/internal/types2/universe.go
Normal file
282
src/cmd/compile/internal/types2/universe.go
Normal file
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
322
src/cmd/compile/internal/types2/walk.go
Normal file
322
src/cmd/compile/internal/types2/walk.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue