Skip to content

Commit 18096c5

Browse files
committed
go/internal/gcimporter: add support for the Go 1.18 export data version
Add support for accepting the Go 1.18 export data version (2) to the x/tools gcimporter, while preserving support for generic code at version 1. For now, the exporter still outputs version 1. For golang/go#49040 Change-Id: Ic4547e385ced72b88212d150bf16acaf200a010e Reviewed-on: https://go-review.googlesource.com/c/tools/+/358034 Trust: Robert Findley <[email protected]> Trust: Dan Scales <[email protected]> Run-TryBot: Robert Findley <[email protected]> gopls-CI: kokoro <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Dan Scales <[email protected]> Reviewed-by: Robert Griesemer <[email protected]>
1 parent affba50 commit 18096c5

File tree

7 files changed

+111
-45
lines changed

7 files changed

+111
-45
lines changed

go/internal/gcimporter/iexport.go

+19-10
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@ const bundleVersion = 0
3535
// The package path of the top-level package will not be recorded,
3636
// so that calls to IImportData can override with a provided package path.
3737
func IExportData(out io.Writer, fset *token.FileSet, pkg *types.Package) error {
38-
return iexportCommon(out, fset, false, []*types.Package{pkg})
38+
return iexportCommon(out, fset, false, iexportVersion, []*types.Package{pkg})
3939
}
4040

4141
// IExportBundle writes an indexed export bundle for pkgs to out.
4242
func IExportBundle(out io.Writer, fset *token.FileSet, pkgs []*types.Package) error {
43-
return iexportCommon(out, fset, true, pkgs)
43+
return iexportCommon(out, fset, true, iexportVersion, pkgs)
4444
}
4545

46-
func iexportCommon(out io.Writer, fset *token.FileSet, bundle bool, pkgs []*types.Package) (err error) {
46+
func iexportCommon(out io.Writer, fset *token.FileSet, bundle bool, version int, pkgs []*types.Package) (err error) {
4747
if !debug {
4848
defer func() {
4949
if e := recover(); e != nil {
@@ -59,6 +59,7 @@ func iexportCommon(out io.Writer, fset *token.FileSet, bundle bool, pkgs []*type
5959

6060
p := iexporter{
6161
fset: fset,
62+
version: version,
6263
allPkgs: map[*types.Package]bool{},
6364
stringIndex: map[string]uint64{},
6465
declIndex: map[types.Object]uint64{},
@@ -122,7 +123,7 @@ func iexportCommon(out io.Writer, fset *token.FileSet, bundle bool, pkgs []*type
122123
if bundle {
123124
hdr.uint64(bundleVersion)
124125
}
125-
hdr.uint64(iexportVersion)
126+
hdr.uint64(uint64(p.version))
126127
hdr.uint64(uint64(p.strings.Len()))
127128
hdr.uint64(dataLen)
128129

@@ -200,8 +201,9 @@ func (p *iexporter) indexName(obj types.Object) (res string) {
200201
}
201202

202203
type iexporter struct {
203-
fset *token.FileSet
204-
out *bytes.Buffer
204+
fset *token.FileSet
205+
out *bytes.Buffer
206+
version int
205207

206208
localpkg *types.Package
207209

@@ -224,7 +226,7 @@ type iexporter struct {
224226
}
225227

226228
func (p *iexporter) trace(format string, args ...interface{}) {
227-
if !debug {
229+
if !trace {
228230
// Call sites should also be guarded, but having this check here allows
229231
// easily enabling/disabling debug trace statements.
230232
return
@@ -330,7 +332,15 @@ func (p *iexporter) doDecl(obj types.Object) {
330332
if tparam, ok := t.(*typeparams.TypeParam); ok {
331333
w.tag('P')
332334
w.pos(obj.Pos())
333-
w.typ(tparam.Constraint(), obj.Pkg())
335+
constraint := tparam.Constraint()
336+
if p.version >= iexportVersionGo1_18 {
337+
implicit := false
338+
if iface, _ := constraint.(*types.Interface); iface != nil {
339+
implicit = typeparams.IsImplicit(iface)
340+
}
341+
w.bool(implicit)
342+
}
343+
w.typ(constraint, obj.Pkg())
334344
break
335345
}
336346

@@ -397,7 +407,7 @@ func (w *exportWriter) tag(tag byte) {
397407
}
398408

399409
func (w *exportWriter) pos(pos token.Pos) {
400-
if iexportVersion >= iexportVersionPosCol {
410+
if w.p.version >= iexportVersionPosCol {
401411
w.posV1(pos)
402412
} else {
403413
w.posV0(pos)
@@ -484,7 +494,6 @@ func (w *exportWriter) qualifiedIdent(obj types.Object) {
484494

485495
// Ensure any referenced declarations are written out too.
486496
w.p.pushDecl(obj)
487-
w.p.trace("writing ident %s for %s", name, obj)
488497
w.string(name)
489498
w.pkg(obj.Pkg())
490499
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2021 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+
package gcimporter
6+
7+
// Temporarily expose version-related functionality so that we can test at
8+
// specific export data versions.
9+
10+
var IExportCommon = iexportCommon
11+
12+
const (
13+
IExportVersion = iexportVersion
14+
IExportVersionGenerics = iexportVersionGenerics
15+
IExportVersionGo1_18 = iexportVersionGo1_18
16+
)

go/internal/gcimporter/iexport_go118_test.go

+30-17
Original file line numberDiff line numberDiff line change
@@ -50,26 +50,39 @@ func testExportSrc(t *testing.T, src []byte) {
5050
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
5151
}
5252

53-
fset := token.NewFileSet()
54-
f, err := parser.ParseFile(fset, "g.go", src, 0)
55-
if err != nil {
56-
t.Fatal(err)
57-
}
58-
conf := types.Config{
59-
Importer: importer.Default(),
60-
}
61-
pkg, err := conf.Check("", fset, []*ast.File{f}, nil)
62-
if err != nil {
63-
t.Fatal(err)
53+
// Test at both stages of the 1.18 export data format change.
54+
tests := []struct {
55+
name string
56+
version int
57+
}{
58+
{"legacy generics", gcimporter.IExportVersionGenerics},
59+
{"go1.18", gcimporter.IExportVersionGo1_18},
6460
}
6561

66-
// export
67-
var b bytes.Buffer
68-
if err := gcimporter.IExportData(&b, fset, pkg); err != nil {
69-
t.Fatal(err)
70-
}
62+
for _, test := range tests {
63+
t.Run(test.name, func(t *testing.T) {
64+
fset := token.NewFileSet()
65+
f, err := parser.ParseFile(fset, "g.go", src, 0)
66+
if err != nil {
67+
t.Fatal(err)
68+
}
69+
conf := types.Config{
70+
Importer: importer.Default(),
71+
}
72+
pkg, err := conf.Check("", fset, []*ast.File{f}, nil)
73+
if err != nil {
74+
t.Fatal(err)
75+
}
76+
77+
// export
78+
data, err := iexport(fset, test.version, pkg)
79+
if err != nil {
80+
t.Fatal(err)
81+
}
7182

72-
testPkgData(t, fset, pkg, b.Bytes())
83+
testPkgData(t, fset, test.version, pkg, data)
84+
})
85+
}
7386
}
7487

7588
func TestImportTypeparamTests(t *testing.T) {

go/internal/gcimporter/iexport_test.go

+13-12
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,9 @@ func readExportFile(filename string) ([]byte, error) {
5555
return ioutil.ReadAll(buf)
5656
}
5757

58-
func iexport(fset *token.FileSet, pkg *types.Package) ([]byte, error) {
58+
func iexport(fset *token.FileSet, version int, pkg *types.Package) ([]byte, error) {
5959
var buf bytes.Buffer
60-
if err := gcimporter.IExportData(&buf, fset, pkg); err != nil {
60+
if err := gcimporter.IExportCommon(&buf, fset, false, version, []*types.Package{pkg}); err != nil {
6161
return nil, err
6262
}
6363
return buf.Bytes(), nil
@@ -132,11 +132,12 @@ type UnknownType undefined
132132
return sorted[i].Path() < sorted[j].Path()
133133
})
134134

135+
version := gcimporter.IExportVersion
135136
for _, pkg := range sorted {
136-
if exportdata, err := iexport(conf.Fset, pkg); err != nil {
137+
if exportdata, err := iexport(conf.Fset, version, pkg); err != nil {
137138
t.Error(err)
138139
} else {
139-
testPkgData(t, conf.Fset, pkg, exportdata)
140+
testPkgData(t, conf.Fset, version, pkg, exportdata)
140141
}
141142

142143
if pkg.Name() == "main" || pkg.Name() == "haserrors" {
@@ -146,7 +147,7 @@ type UnknownType undefined
146147
} else if exportdata, err := readExportFile(bp.PkgObj); err != nil {
147148
t.Log("warning:", err)
148149
} else {
149-
testPkgData(t, conf.Fset, pkg, exportdata)
150+
testPkgData(t, conf.Fset, version, pkg, exportdata)
150151
}
151152
}
152153

@@ -162,23 +163,23 @@ type UnknownType undefined
162163
}
163164

164165
for i, pkg := range sorted {
165-
testPkg(t, conf.Fset, pkg, fset2, pkgs2[i])
166+
testPkg(t, conf.Fset, version, pkg, fset2, pkgs2[i])
166167
}
167168
}
168169

169-
func testPkgData(t *testing.T, fset *token.FileSet, pkg *types.Package, exportdata []byte) {
170+
func testPkgData(t *testing.T, fset *token.FileSet, version int, pkg *types.Package, exportdata []byte) {
170171
imports := make(map[string]*types.Package)
171172
fset2 := token.NewFileSet()
172173
_, pkg2, err := gcimporter.IImportData(fset2, imports, exportdata, pkg.Path())
173174
if err != nil {
174175
t.Errorf("IImportData(%s): %v", pkg.Path(), err)
175176
}
176177

177-
testPkg(t, fset, pkg, fset2, pkg2)
178+
testPkg(t, fset, version, pkg, fset2, pkg2)
178179
}
179180

180-
func testPkg(t *testing.T, fset *token.FileSet, pkg *types.Package, fset2 *token.FileSet, pkg2 *types.Package) {
181-
if _, err := iexport(fset2, pkg2); err != nil {
181+
func testPkg(t *testing.T, fset *token.FileSet, version int, pkg *types.Package, fset2 *token.FileSet, pkg2 *types.Package) {
182+
if _, err := iexport(fset2, version, pkg2); err != nil {
182183
t.Errorf("reexport %q: %v", pkg.Path(), err)
183184
}
184185

@@ -226,7 +227,7 @@ func TestIExportData_long(t *testing.T) {
226227
}
227228

228229
// export
229-
exportdata, err := iexport(fset1, pkg)
230+
exportdata, err := iexport(fset1, gcimporter.IExportVersion, pkg)
230231
if err != nil {
231232
t.Fatal(err)
232233
}
@@ -269,7 +270,7 @@ func TestIExportData_typealiases(t *testing.T) {
269270

270271
// export
271272
// use a nil fileset here to confirm that it doesn't panic
272-
exportdata, err := iexport(nil, pkg1)
273+
exportdata, err := iexport(nil, gcimporter.IExportVersion, pkg1)
273274
if err != nil {
274275
t.Fatal(err)
275276
}

go/internal/gcimporter/iimport.go

+24-6
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,20 @@ func (r *intReader) uint64() uint64 {
4545
}
4646

4747
// Keep this in sync with constants in iexport.go.
48+
//
49+
// Temporarily, the x/tools importer accepts generic code at both version 1 and
50+
// 2. However, version 2 contains some breaking changes on top of version 1:
51+
// - the 'implicit' bit is added to exported constraints
52+
// - a 'kind' byte is added to constant values (not yet done)
53+
//
54+
// Once we've completed the bump to version 2 in the standard library, we'll
55+
// remove support for generics here at version 1.
4856
const (
4957
iexportVersionGo1_11 = 0
5058
iexportVersionPosCol = 1
59+
iexportVersionGo1_18 = 2
5160
// TODO: before release, change this back to 2.
5261
iexportVersionGenerics = iexportVersionPosCol
53-
54-
iexportVersionCurrent = iexportVersionGenerics
5562
)
5663

5764
type ident struct {
@@ -124,9 +131,9 @@ func iimportCommon(fset *token.FileSet, imports map[string]*types.Package, data
124131

125132
version = int64(r.uint64())
126133
switch version {
127-
case /* iexportVersionGenerics, */ iexportVersionPosCol, iexportVersionGo1_11:
134+
case iexportVersionGo1_18, iexportVersionPosCol, iexportVersionGo1_11:
128135
default:
129-
if version > iexportVersionGenerics {
136+
if version > iexportVersionGo1_18 {
130137
errorf("unstable iexport format version %d, just rebuild compiler and std library", version)
131138
} else {
132139
errorf("unknown iexport format version %d", version)
@@ -437,8 +444,19 @@ func (r *importReader) obj(name string) {
437444
// bound, save the partial type in tparamIndex before reading the bounds.
438445
id := ident{r.currPkg.Name(), name}
439446
r.p.tparamIndex[id] = t
440-
441-
typeparams.SetTypeParamConstraint(t, r.typ())
447+
var implicit bool
448+
if r.p.exportVersion >= iexportVersionGo1_18 {
449+
implicit = r.bool()
450+
}
451+
constraint := r.typ()
452+
if implicit {
453+
iface, _ := constraint.(*types.Interface)
454+
if iface == nil {
455+
errorf("non-interface constraint marked implicit")
456+
}
457+
typeparams.MarkImplicit(iface)
458+
}
459+
typeparams.SetTypeParamConstraint(t, constraint)
442460

443461
case 'V':
444462
typ := r.typ()

internal/typeparams/typeparams_go117.go

+4
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,10 @@ func IsImplicit(*types.Interface) bool {
137137
return false
138138
}
139139

140+
// MarkImplicit does nothing, because this Go version does not have implicit
141+
// interfaces.
142+
func MarkImplicit(*types.Interface) {}
143+
140144
// ForNamed returns an empty type parameter list, as type parameters are not
141145
// supported at this Go version.
142146
func ForNamed(*types.Named) *TypeParamList {

internal/typeparams/typeparams_go118.go

+5
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,11 @@ func IsImplicit(iface *types.Interface) bool {
130130
return iface.IsImplicit()
131131
}
132132

133+
// MarkImplicit calls iface.MarkImplicit().
134+
func MarkImplicit(iface *types.Interface) {
135+
iface.MarkImplicit()
136+
}
137+
133138
// ForNamed extracts the (possibly empty) type parameter object list from
134139
// named.
135140
func ForNamed(named *types.Named) *TypeParamList {

0 commit comments

Comments
 (0)