cmd/compile/internal/importer: starting point for new importer

Make an exact copy of go/internal/gcimporter in
cmd/compile/internal/importer. Serves as starting
point for a types2-based importer.

Change-Id: Ib676794622bc1cb1614e396df6b391f374757cda
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go2-dev/+/744662
Reviewed-by: Robert Griesemer <gri@google.com>
This commit is contained in:
Robert Griesemer 2020-05-11 13:57:55 -07:00
parent 56162a1a5f
commit cdfaff44de
14 changed files with 1851 additions and 0 deletions

View File

@ -0,0 +1,91 @@
// 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 gcimporter
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
}

View File

@ -0,0 +1,175 @@
// 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 gcimporter implements Import for gc-generated object files.
package gcimporter // import "go/internal/gcimporter"
import (
"bufio"
"fmt"
"go/build"
"go/token"
"go/types"
"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(fset *token.FileSet, packages map[string]*types.Package, path, srcDir string, lookup func(path string) (io.ReadCloser, error)) (pkg *types.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 types.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 types.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(fset, 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 []*types.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() }

View File

@ -0,0 +1,621 @@
// 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 gcimporter
import (
"bytes"
"fmt"
"internal/testenv"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"
"time"
"go/token"
"go/types"
)
// 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) *types.Package {
t0 := time.Now()
fset := token.NewFileSet()
pkg, err := Import(fset, make(map[string]*types.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)
}
fset := token.NewFileSet()
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(fset, make(map[string]*types.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(fset, make(map[string]*types.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)
}
fset := token.NewFileSet()
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(fset, make(map[string]*types.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 := types.ObjectString(obj, types.RelativeTo(pkg))
if got != test.want {
t.Errorf("%s: got %q; want %q", test.name, got, test.want)
}
if named, _ := obj.Type().(*types.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 *types.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().(*types.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().(*types.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).(*types.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.(*types.TypeName); tname != nil {
named := tname.Type().(*types.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]*types.Package)
fset := token.NewFileSet()
_, err := Import(fset, imports, "net/http", ".", nil)
if err != nil {
t.Fatal(err)
}
mutex := imports["sync"].Scope().Lookup("Mutex").(*types.TypeName).Type()
mset := types.NewMethodSet(types.NewPointer(mutex)) // methods of *sync.Mutex
sel := mset.Lookup(nil, "Lock")
lock := sel.Obj().(*types.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
fset := token.NewFileSet()
imports := make(map[string]*types.Package)
_, err := Import(fset, imports, "go/internal/gcimporter", ".", nil)
if err != nil {
t.Fatal(err)
}
// look for go/types package
var goTypesPkg *types.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/types.Object type
obj := lookupObj(t, goTypesPkg.Scope(), "Object")
typ, ok := obj.Type().(*types.Named)
if !ok {
t.Fatalf("go/types.Object type is %v; wanted named type", typ)
}
// lookup go/types.Object.Pkg method
m, index, indirect := types.LookupFieldOrMethod(typ, false, nil, "Pkg")
if m == nil {
t.Fatalf("go/types.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]*types.Package)
fset := token.NewFileSet()
for i := 0; i < 3; i++ {
if _, err := Import(fset, 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 := types.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) *types.Package {
fset := token.NewFileSet()
pkg, err := Import(fset, make(map[string]*types.Package), path, srcDir, nil)
if err != nil {
t.Fatal(err)
}
return pkg
}
func compileAndImportPkg(t *testing.T, name string) *types.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 *types.Scope, name string) types.Object {
if obj := scope.Lookup(name); obj != nil {
return obj
}
t.Fatalf("%s not found", name)
return nil
}

View File

@ -0,0 +1,618 @@
// 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 gcimporter
import (
"bytes"
"encoding/binary"
"fmt"
"go/constant"
"go/token"
"go/types"
"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(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.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]*types.Package),
declData: declData,
pkgIndex: make(map[*types.Package]map[string]uint64),
typCache: make(map[uint64]types.Type),
fake: fakeFileSet{
fset: fset,
files: make(map[string]*token.File),
},
}
for i, pt := range predeclared {
p.typCache[uint64(i)] = pt
}
pkgList := make([]*types.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 = types.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(([]*types.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]*types.Package
declData []byte
pkgIndex map[*types.Package]map[string]uint64
typCache map[uint64]types.Type
fake fakeFileSet
interfaceList []*types.Interface
}
func (p *iimporter) doDecl(pkg *types.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) *types.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 *types.Named) types.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 *types.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(types.NewTypeName(pos, r.currPkg, name, typ))
case 'C':
typ, val := r.value()
r.declare(types.NewConst(pos, r.currPkg, name, typ, val))
case 'F':
sig := r.signature(nil)
r.declare(types.NewFunc(pos, r.currPkg, name, sig))
case 'T':
// Types can be recursive. We need to setup a stub
// declaration before recursing.
obj := types.NewTypeName(pos, r.currPkg, name, nil)
named := types.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(types.NewFunc(mpos, r.currPkg, mname, msig))
}
}
case 'V':
typ := r.typ()
r.declare(types.NewVar(pos, r.currPkg, name, typ))
default:
errorf("unexpected tag: %v", tag)
}
}
func (r *importReader) declare(obj types.Object) {
obj.Pkg().Scope().Insert(obj)
}
func (r *importReader) value() (typ types.Type, val constant.Value) {
typ = r.typ()
switch b := typ.Underlying().(*types.Basic); b.Info() & types.IsConstType {
case types.IsBoolean:
val = constant.MakeBool(r.bool())
case types.IsString:
val = constant.MakeString(r.string())
case types.IsInteger:
val = r.mpint(b)
case types.IsFloat:
val = r.mpfloat(b)
case types.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 *types.Basic) (signed bool, maxBytes uint) {
if (b.Info() & types.IsUntyped) != 0 {
return true, 64
}
switch b.Kind() {
case types.Float32, types.Complex64:
return true, 3
case types.Float64, types.Complex128:
return true, 7
}
signed = (b.Info() & types.IsUnsigned) == 0
switch b.Kind() {
case types.Int8, types.Uint8:
maxBytes = 1
case types.Int16, types.Uint16:
maxBytes = 2
case types.Int32, types.Uint32:
maxBytes = 4
default:
maxBytes = 8
}
return
}
func (r *importReader) mpint(b *types.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 *types.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() (*types.Package, string) {
name := r.string()
pkg := r.pkg()
return pkg, name
}
func (r *importReader) pos() token.Pos {
if r.p.version >= 1 {
r.posv1()
} else {
r.posv0()
}
if r.prevFile == "" && r.prevLine == 0 && r.prevColumn == 0 {
return token.NoPos
}
return r.p.fake.pos(r.prevFile, int(r.prevLine), int(r.prevColumn))
}
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() types.Type {
return r.p.typAt(r.uint64(), nil)
}
func isInterface(t types.Type) bool {
_, ok := t.(*types.Interface)
return ok
}
func (r *importReader) pkg() *types.Package { return r.p.pkgAt(r.uint64()) }
func (r *importReader) string() string { return r.p.stringAt(r.uint64()) }
func (r *importReader) doType(base *types.Named) types.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).(*types.TypeName).Type()
case pointerType:
return types.NewPointer(r.typ())
case sliceType:
return types.NewSlice(r.typ())
case arrayType:
n := r.uint64()
return types.NewArray(r.typ(), int64(n))
case chanType:
dir := chanDir(int(r.uint64()))
return types.NewChan(dir, r.typ())
case mapType:
return types.NewMap(r.typ(), r.typ())
case signatureType:
r.currPkg = r.pkg()
return r.signature(nil)
case structType:
r.currPkg = r.pkg()
fields := make([]*types.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] = types.NewField(fpos, r.currPkg, fname, ftyp, emb)
tags[i] = tag
}
return types.NewStruct(fields, tags)
case interfaceType:
r.currPkg = r.pkg()
embeddeds := make([]types.Type, r.uint64())
for i := range embeddeds {
_ = r.pos()
embeddeds[i] = r.typ()
}
methods := make([]*types.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 *types.Var
if base != nil {
recv = types.NewVar(token.NoPos, r.currPkg, "", base)
}
msig := r.signature(recv)
methods[i] = types.NewFunc(mpos, r.currPkg, mname, msig)
}
typ := types.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 *types.Var) *types.Signature {
params := r.paramList()
results := r.paramList()
variadic := params.Len() > 0 && r.bool()
return types.NewSignature(recv, params, results, variadic)
}
func (r *importReader) paramList() *types.Tuple {
xs := make([]*types.Var, r.uint64())
for i := range xs {
xs[i] = r.param()
}
return types.NewTuple(xs...)
}
func (r *importReader) param() *types.Var {
pos := r.pos()
name := r.ident()
typ := r.typ()
return types.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
}

View File

@ -0,0 +1,142 @@
// 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 gcimporter
import (
"fmt"
"go/token"
"go/types"
"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) types.ChanDir {
// tag values must match the constants in cmd/compile/internal/gc/go.go
switch d {
case 1 /* Crecv */ :
return types.RecvOnly
case 2 /* Csend */ :
return types.SendOnly
case 3 /* Cboth */ :
return types.SendRecv
default:
errorf("unexpected channel dir %d", d)
return 0
}
}
var predeclared = []types.Type{
// basic types
types.Typ[types.Bool],
types.Typ[types.Int],
types.Typ[types.Int8],
types.Typ[types.Int16],
types.Typ[types.Int32],
types.Typ[types.Int64],
types.Typ[types.Uint],
types.Typ[types.Uint8],
types.Typ[types.Uint16],
types.Typ[types.Uint32],
types.Typ[types.Uint64],
types.Typ[types.Uintptr],
types.Typ[types.Float32],
types.Typ[types.Float64],
types.Typ[types.Complex64],
types.Typ[types.Complex128],
types.Typ[types.String],
// basic type aliases
types.Universe.Lookup("byte").Type(),
types.Universe.Lookup("rune").Type(),
// error
types.Universe.Lookup("error").Type(),
// untyped types
types.Typ[types.UntypedBool],
types.Typ[types.UntypedInt],
types.Typ[types.UntypedRune],
types.Typ[types.UntypedFloat],
types.Typ[types.UntypedComplex],
types.Typ[types.UntypedString],
types.Typ[types.UntypedNil],
// package unsafe
types.Typ[types.UnsafePointer],
// invalid type
types.Typ[types.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() types.Type { return t }
func (t anyType) Under() types.Type { return t }
func (t anyType) String() string { return "any" }
// types.aType is not exported for now so we need to implemented these here.
func (anyType) Basic() *types.Basic { return nil }
func (anyType) Array() *types.Array { return nil }
func (anyType) Slice() *types.Slice { return nil }
func (anyType) Struct() *types.Struct { return nil }
func (anyType) Pointer() *types.Pointer { return nil }
func (anyType) Tuple() *types.Tuple { return nil }
func (anyType) Signature() *types.Signature { return nil }
func (anyType) Interface() *types.Interface { return nil }
func (anyType) Map() *types.Map { return nil }
func (anyType) Chan() *types.Chan { return nil }
func (anyType) Named() *types.Named { return nil }
func (anyType) TypeParam() *types.TypeParam { return nil }

View File

@ -0,0 +1,14 @@
// 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
}

View File

@ -0,0 +1,11 @@
// 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

View File

@ -0,0 +1,88 @@
// 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()

View File

@ -0,0 +1,11 @@
// 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 }

View File

@ -0,0 +1,9 @@
// 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()
}

View File

@ -0,0 +1,17 @@
// 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") }

View File

@ -0,0 +1,13 @@
// 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
}

View File

@ -0,0 +1,13 @@
// 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() {}

View File

@ -0,0 +1,28 @@
// 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
}