1
0
mirror of https://github.com/golang/go synced 2024-11-18 14:04:45 -07:00

go/gcimporter15: support invalid types and constants in binary export data

Although invalid types and unknown constant values should never appear
in .a files (since gc will stop with an error before writing export
data), they can now be faithfully encoded and decoded.  This makes the
protocol robust for IDE-like applications that must deal with incomplete
or incorrect programs.

(Corresponding std lib CL: https://go-review.googlesource.com/20828)

Change-Id: I539ffd951b90f01705a7f23ec778c623c729d9a0
Reviewed-on: https://go-review.googlesource.com/20827
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Alan Donovan 2016-03-18 11:07:33 -04:00
parent 758728c4b2
commit 8a9be374d7
3 changed files with 28 additions and 9 deletions

View File

@ -32,9 +32,6 @@ const (
)
// BExportData returns binary export data for pkg.
//
// It is not safe to call this function on a package containing errors.
// TODO(adonovan): add InvalidType to the protocol and lift this restriction.
func BExportData(pkg *types.Package) []byte {
p := exporter{
pkgIndex: make(map[*types.Package]int),
@ -209,9 +206,6 @@ func (p *exporter) typ(t types.Type) {
if t == nil {
log.Fatalf("nil type")
}
if t == types.Typ[types.Invalid] {
log.Fatal("BExportData invoked on package with errors")
}
// Possible optimization: Anonymous pointer types *T where
// T is a named type are common. We could canonicalize all
@ -489,6 +483,10 @@ func (p *exporter) value(x constant.Value) {
p.tag(stringTag)
p.string(constant.StringVal(x))
case constant.Unknown:
// (Package contains type errors.)
p.tag(unknownTag)
default:
log.Fatalf("unexpected value %v (%T)", x, x)
}
@ -700,4 +698,5 @@ var tagString = [...]string{
-fractionTag: "fraction",
-complexTag: "complex",
-stringTag: "string",
-unknownTag: "unknown",
}

View File

@ -30,11 +30,25 @@ func TestBExportData_stdlib(t *testing.T) {
// Load, parse and type-check the program.
ctxt := build.Default // copy
ctxt.GOPATH = "" // disable GOPATH
conf := loader.Config{Build: &ctxt}
conf := loader.Config{
Build: &ctxt,
AllowErrors: true,
}
for _, path := range buildutil.AllPackages(conf.Build) {
conf.Import(path)
}
// Create a package containing type and value errors to ensure
// they are properly encoded/decoded.
f, err := conf.ParseFile("haserrors/haserrors.go", `package haserrors
const UnknownValue = "" + 0
type UnknownType undefined
`)
if err != nil {
t.Fatal(err)
}
conf.CreateFromFiles("haserrors", f)
prog, err := conf.Load()
if err != nil {
t.Fatalf("Load failed: %v", err)
@ -49,7 +63,6 @@ func TestBExportData_stdlib(t *testing.T) {
if info.Files == nil {
continue // empty directory
}
exportdata := gcimporter.BExportData(pkg)
imports := make(map[string]*types.Package)

View File

@ -471,6 +471,9 @@ func (p *importer) value() constant.Value {
return constant.BinaryOp(re, token.ADD, constant.MakeImag(im))
case stringTag:
return constant.MakeString(p.string())
case unknownTag:
// (Encoded package contains type errors.)
return constant.MakeUnknown()
default:
panic(fmt.Sprintf("unexpected value tag %d", tag))
}
@ -644,6 +647,7 @@ const (
fractionTag // not used by gc
complexTag
stringTag
unknownTag // only appears in packages with errors
)
var predeclared = []types.Type{
@ -685,7 +689,10 @@ var predeclared = []types.Type{
// package unsafe
types.Typ[types.UnsafePointer],
// any type, for builtin export data
// invalid type
types.Typ[types.Invalid], // only appears in packages with errors
// TODO(mdempsky): Provide an actual Type value to represent "any"?
// (Why exactly does gc emit the "any" type?)
types.Typ[types.Invalid],
}