Skip to content

Commit 22d1494

Browse files
committed
internal/gcimporter: add support for reading unified IR export data
This does not include writing export data in the unified IR format. Most of this change is an import of code from the Go implementation, with minor tweaks and gaskets added. This does not (yet) address the registry issue mentioned in golang/go#52163. Updates golang/go#52163. Change-Id: I98030e6c9ff35c6ff678b8a7ce9b653b18e65e17 Reviewed-on: https://go-review.googlesource.com/c/tools/+/412821 TryBot-Result: Gopher Robot <[email protected]> Run-TryBot: David Chase <[email protected]> Reviewed-by: Matthew Dempsky <[email protected]> gopls-CI: kokoro <[email protected]>
1 parent c3af7c2 commit 22d1494

18 files changed

+1968
-18
lines changed

go/gcexportdata/gcexportdata.go

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,29 @@ func Read(in io.Reader, fset *token.FileSet, imports map[string]*types.Package,
116116
// The indexed export format starts with an 'i'; the older
117117
// binary export format starts with a 'c', 'd', or 'v'
118118
// (from "version"). Select appropriate importer.
119-
if len(data) > 0 && data[0] == 'i' {
120-
_, pkg, err := gcimporter.IImportData(fset, imports, data[1:], path)
121-
return pkg, err
122-
}
119+
if len(data) > 0 {
120+
switch data[0] {
121+
case 'i':
122+
_, pkg, err := gcimporter.IImportData(fset, imports, data[1:], path)
123+
return pkg, err
124+
125+
case 'v', 'c', 'd':
126+
_, pkg, err := gcimporter.BImportData(fset, imports, data, path)
127+
return pkg, err
123128

124-
_, pkg, err := gcimporter.BImportData(fset, imports, data, path)
125-
return pkg, err
129+
case 'u':
130+
_, pkg, err := gcimporter.UImportData(fset, imports, data[1:], path)
131+
return pkg, err
132+
133+
default:
134+
l := len(data)
135+
if l > 10 {
136+
l = 10
137+
}
138+
return nil, fmt.Errorf("unexpected export data with prefix %q for path %s", string(data[:l]), path)
139+
}
140+
}
141+
return nil, fmt.Errorf("empty export data for %s", path)
126142
}
127143

128144
// Write writes encoded type information for the specified package to out.

go/internal/gcimporter/gcimporter.go

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,9 @@ func Import(packages map[string]*types.Package, path, srcDir string, lookup func
181181
defer rc.Close()
182182

183183
var hdr string
184+
var size int64
184185
buf := bufio.NewReader(rc)
185-
if hdr, _, err = FindExportData(buf); err != nil {
186+
if hdr, size, err = FindExportData(buf); err != nil {
186187
return
187188
}
188189

@@ -210,10 +211,27 @@ func Import(packages map[string]*types.Package, path, srcDir string, lookup func
210211
// The indexed export format starts with an 'i'; the older
211212
// binary export format starts with a 'c', 'd', or 'v'
212213
// (from "version"). Select appropriate importer.
213-
if len(data) > 0 && data[0] == 'i' {
214-
_, pkg, err = IImportData(fset, packages, data[1:], id)
215-
} else {
216-
_, pkg, err = BImportData(fset, packages, data, id)
214+
if len(data) > 0 {
215+
switch data[0] {
216+
case 'i':
217+
_, pkg, err := IImportData(fset, packages, data[1:], id)
218+
return pkg, err
219+
220+
case 'v', 'c', 'd':
221+
_, pkg, err := BImportData(fset, packages, data, id)
222+
return pkg, err
223+
224+
case 'u':
225+
_, pkg, err := UImportData(fset, packages, data[1:size], id)
226+
return pkg, err
227+
228+
default:
229+
l := len(data)
230+
if l > 10 {
231+
l = 10
232+
}
233+
return nil, fmt.Errorf("unexpected export data with prefix %q for path %s", string(data[:l]), id)
234+
}
217235
}
218236

219237
default:

go/internal/gcimporter/gcimporter_test.go

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ func needsCompiler(t *testing.T, compiler string) {
4545
// compile runs the compiler on filename, with dirname as the working directory,
4646
// and writes the output file to outdirname.
4747
func compile(t *testing.T, dirname, filename, outdirname string) string {
48+
return compilePkg(t, dirname, filename, outdirname, "p")
49+
}
50+
51+
func compilePkg(t *testing.T, dirname, filename, outdirname, pkg string) string {
4852
testenv.NeedsGoBuild(t)
4953

5054
// filename must end with ".go"
@@ -53,12 +57,12 @@ func compile(t *testing.T, dirname, filename, outdirname string) string {
5357
}
5458
basename := filepath.Base(filename)
5559
outname := filepath.Join(outdirname, basename[:len(basename)-2]+"o")
56-
cmd := exec.Command("go", "tool", "compile", "-p=p", "-o", outname, filename)
60+
cmd := exec.Command("go", "tool", "compile", "-p="+pkg, "-o", outname, filename)
5761
cmd.Dir = dirname
5862
out, err := cmd.CombinedOutput()
5963
if err != nil {
6064
t.Logf("%s", out)
61-
t.Fatalf("go tool compile %s failed: %s", filename, err)
65+
t.Fatalf("(cd %v && %v) failed: %s", cmd.Dir, cmd, err)
6266
}
6367
return outname
6468
}
@@ -140,7 +144,11 @@ func TestImportTestdata(t *testing.T) {
140144
// For now, we just test the presence of a few packages
141145
// that we know are there for sure.
142146
got := fmt.Sprint(pkg.Imports())
143-
for _, want := range []string{"go/ast", "go/token"} {
147+
wants := []string{"go/ast", "go/token"}
148+
if unifiedIR {
149+
wants = []string{"go/ast"}
150+
}
151+
for _, want := range wants {
144152
if !strings.Contains(got, want) {
145153
t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want)
146154
}
@@ -364,6 +372,14 @@ func verifyInterfaceMethodRecvs(t *testing.T, named *types.Named, level int) {
364372
return // not an interface
365373
}
366374

375+
// The unified IR importer always sets interface method receiver
376+
// parameters to point to the Interface type, rather than the Named.
377+
// See #49906.
378+
var want types.Type = named
379+
if unifiedIR {
380+
want = iface
381+
}
382+
367383
// check explicitly declared methods
368384
for i := 0; i < iface.NumExplicitMethods(); i++ {
369385
m := iface.ExplicitMethod(i)
@@ -372,8 +388,8 @@ func verifyInterfaceMethodRecvs(t *testing.T, named *types.Named, level int) {
372388
t.Errorf("%s: missing receiver type", m)
373389
continue
374390
}
375-
if recv.Type() != named {
376-
t.Errorf("%s: got recv type %s; want %s", m, recv.Type(), named)
391+
if recv.Type() != want {
392+
t.Errorf("%s: got recv type %s; want %s", m, recv.Type(), want)
377393
}
378394
}
379395

@@ -451,7 +467,7 @@ func TestIssue13566(t *testing.T) {
451467
if err != nil {
452468
t.Fatal(err)
453469
}
454-
compile(t, "testdata", "a.go", testoutdir)
470+
compilePkg(t, "testdata", "a.go", testoutdir, apkg(testoutdir))
455471
compile(t, testoutdir, bpath, testoutdir)
456472

457473
// import must succeed (test for issue at hand)
@@ -611,13 +627,22 @@ func TestIssue51836(t *testing.T) {
611627
if err != nil {
612628
t.Fatal(err)
613629
}
614-
compile(t, dir, "a.go", testoutdir)
630+
compilePkg(t, dir, "a.go", testoutdir, apkg(testoutdir))
615631
compile(t, testoutdir, bpath, testoutdir)
616632

617633
// import must succeed (test for issue at hand)
618634
_ = importPkg(t, "./testdata/aa", tmpdir)
619635
}
620636

637+
// apkg returns the package "a" prefixed by (as a package) testoutdir
638+
func apkg(testoutdir string) string {
639+
apkg := testoutdir + "/a"
640+
if os.PathSeparator != '/' {
641+
apkg = strings.ReplaceAll(apkg, string(os.PathSeparator), "/")
642+
}
643+
return apkg
644+
}
645+
621646
func importPkg(t *testing.T, path, srcDir string) *types.Package {
622647
pkg, err := Import(make(map[string]*types.Package), path, srcDir, nil)
623648
if err != nil {

go/internal/gcimporter/unified_no.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright 2022 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build !(go1.18 && goexperiment.unified)
6+
// +build !go1.18 !goexperiment.unified
7+
8+
package gcimporter
9+
10+
const unifiedIR = false

go/internal/gcimporter/unified_yes.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright 2022 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build go1.18 && goexperiment.unified
6+
// +build go1.18,goexperiment.unified
7+
8+
package gcimporter
9+
10+
const unifiedIR = true

go/internal/gcimporter/ureader_no.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2022 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build !go1.18
6+
// +build !go1.18
7+
8+
package gcimporter
9+
10+
import (
11+
"fmt"
12+
"go/token"
13+
"go/types"
14+
)
15+
16+
func UImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) {
17+
err = fmt.Errorf("go/tools compiled with a Go version earlier than 1.18 cannot read unified IR export data")
18+
return
19+
}

0 commit comments

Comments
 (0)