From 8a9be374d7df8d1fdb04813e33a9ec26a6c99276 Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Fri, 18 Mar 2016 11:07:33 -0400 Subject: [PATCH] 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 --- go/gcimporter15/bexport.go | 11 +++++------ go/gcimporter15/bexport_test.go | 17 +++++++++++++++-- go/gcimporter15/bimport.go | 9 ++++++++- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/go/gcimporter15/bexport.go b/go/gcimporter15/bexport.go index 8eb048eb..36bcf035 100644 --- a/go/gcimporter15/bexport.go +++ b/go/gcimporter15/bexport.go @@ -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", } diff --git a/go/gcimporter15/bexport_test.go b/go/gcimporter15/bexport_test.go index fea4536f..941f7515 100644 --- a/go/gcimporter15/bexport_test.go +++ b/go/gcimporter15/bexport_test.go @@ -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) diff --git a/go/gcimporter15/bimport.go b/go/gcimporter15/bimport.go index 0ec216e9..a39e3261 100644 --- a/go/gcimporter15/bimport.go +++ b/go/gcimporter15/bimport.go @@ -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], }