mirror of https://github.com/golang/go.git
go/internal/gcimporter: fix reexporting compiler data
The current versions of the indexed export format encode constant values based on their type. However, IExportData was encoding constants values based on their own kind instead. While cmd/compile internally keeps these matched, go/types does not guarantee the same (see also golang/go#43891). This CL fixes this issue, and also extends testing to check reading in the compiler's export data and that imported packages can be exported again. Change-Id: I08f1845f371edd87773df0ef85bcd4e28f5db2f5 Reviewed-on: https://go-review.googlesource.com/c/tools/+/292351 gopls-CI: kokoro <noreply+kokoro@google.com> Trust: Matthew Dempsky <mdempsky@google.com> Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
parent
7fde01ffd3
commit
b79f76fe3e
|
|
@ -12,6 +12,7 @@ import (
|
|||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
|
@ -117,7 +118,8 @@ type UnknownType undefined
|
|||
|
||||
func fileLine(fset *token.FileSet, obj types.Object) string {
|
||||
posn := fset.Position(obj.Pos())
|
||||
return fmt.Sprintf("%s:%d", posn.Filename, posn.Line)
|
||||
filename := filepath.Clean(strings.ReplaceAll(posn.Filename, "$GOROOT", build.Default.GOROOT))
|
||||
return fmt.Sprintf("%s:%d", filename, posn.Line)
|
||||
}
|
||||
|
||||
// equalObj reports how x and y differ. They are assumed to belong to
|
||||
|
|
|
|||
|
|
@ -472,10 +472,10 @@ func (w *exportWriter) param(obj types.Object) {
|
|||
func (w *exportWriter) value(typ types.Type, v constant.Value) {
|
||||
w.typ(typ, nil)
|
||||
|
||||
switch v.Kind() {
|
||||
case constant.Bool:
|
||||
switch b := typ.Underlying().(*types.Basic); b.Info() & types.IsConstType {
|
||||
case types.IsBoolean:
|
||||
w.bool(constant.BoolVal(v))
|
||||
case constant.Int:
|
||||
case types.IsInteger:
|
||||
var i big.Int
|
||||
if i64, exact := constant.Int64Val(v); exact {
|
||||
i.SetInt64(i64)
|
||||
|
|
@ -485,25 +485,27 @@ func (w *exportWriter) value(typ types.Type, v constant.Value) {
|
|||
i.SetString(v.ExactString(), 10)
|
||||
}
|
||||
w.mpint(&i, typ)
|
||||
case constant.Float:
|
||||
case types.IsFloat:
|
||||
f := constantToFloat(v)
|
||||
w.mpfloat(f, typ)
|
||||
case constant.Complex:
|
||||
case types.IsComplex:
|
||||
w.mpfloat(constantToFloat(constant.Real(v)), typ)
|
||||
w.mpfloat(constantToFloat(constant.Imag(v)), typ)
|
||||
case constant.String:
|
||||
case types.IsString:
|
||||
w.string(constant.StringVal(v))
|
||||
case constant.Unknown:
|
||||
// package contains type errors
|
||||
default:
|
||||
panic(internalErrorf("unexpected value %v (%T)", v, v))
|
||||
if b.Kind() == types.Invalid {
|
||||
// package contains type errors
|
||||
break
|
||||
}
|
||||
panic(internalErrorf("unexpected type %v (%v)", typ, typ.Underlying()))
|
||||
}
|
||||
}
|
||||
|
||||
// constantToFloat converts a constant.Value with kind constant.Float to a
|
||||
// big.Float.
|
||||
func constantToFloat(x constant.Value) *big.Float {
|
||||
assert(x.Kind() == constant.Float)
|
||||
x = constant.ToFloat(x)
|
||||
// Use the same floating-point precision (512) as cmd/compile
|
||||
// (see Mpprec in cmd/compile/internal/gc/mpfloat.go).
|
||||
const mpprec = 512
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
package gcimporter_test
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
|
|
@ -17,7 +18,9 @@ import (
|
|||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"sort"
|
||||
|
|
@ -29,6 +32,27 @@ import (
|
|||
"golang.org/x/tools/go/loader"
|
||||
)
|
||||
|
||||
func readExportFile(filename string) ([]byte, error) {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
buf := bufio.NewReader(f)
|
||||
if _, err := gcimporter.FindExportData(buf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ch, err := buf.ReadByte(); err != nil {
|
||||
return nil, err
|
||||
} else if ch != 'i' {
|
||||
return nil, fmt.Errorf("unexpected byte: %v", ch)
|
||||
}
|
||||
|
||||
return ioutil.ReadAll(buf)
|
||||
}
|
||||
|
||||
func iexport(fset *token.FileSet, pkg *types.Package) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
if err := gcimporter.IExportData(&buf, fset, pkg); err != nil {
|
||||
|
|
@ -54,6 +78,9 @@ func TestIExportData_stdlib(t *testing.T) {
|
|||
conf := loader.Config{
|
||||
Build: &ctxt,
|
||||
AllowErrors: true,
|
||||
TypeChecker: types.Config{
|
||||
Sizes: types.SizesFor(ctxt.Compiler, ctxt.GOARCH),
|
||||
},
|
||||
}
|
||||
for _, path := range buildutil.AllPackages(conf.Build) {
|
||||
conf.Import(path)
|
||||
|
|
@ -96,6 +123,16 @@ type UnknownType undefined
|
|||
} else {
|
||||
testPkgData(t, conf.Fset, pkg, exportdata)
|
||||
}
|
||||
|
||||
if pkg.Name() == "main" || pkg.Name() == "haserrors" {
|
||||
// skip; no export data
|
||||
} else if bp, err := ctxt.Import(pkg.Path(), "", build.FindOnly); err != nil {
|
||||
t.Log("warning:", err)
|
||||
} else if exportdata, err := readExportFile(bp.PkgObj); err != nil {
|
||||
t.Log("warning:", err)
|
||||
} else {
|
||||
testPkgData(t, conf.Fset, pkg, exportdata)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -111,6 +148,10 @@ func testPkgData(t *testing.T, fset *token.FileSet, pkg *types.Package, exportda
|
|||
}
|
||||
|
||||
func testPkg(t *testing.T, fset *token.FileSet, pkg *types.Package, fset2 *token.FileSet, pkg2 *types.Package) {
|
||||
if _, err := iexport(fset2, pkg2); err != nil {
|
||||
t.Errorf("reexport %q: %v", pkg.Path(), err)
|
||||
}
|
||||
|
||||
// Compare the packages' corresponding members.
|
||||
for _, name := range pkg.Scope().Names() {
|
||||
if !ast.IsExported(name) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue