diff options
author | Robert Griesemer <gri@golang.org> | 2016-10-25 14:09:18 -0700 |
---|---|---|
committer | Robert Griesemer <gri@golang.org> | 2016-10-27 17:44:45 +0000 |
commit | 03d81b5ed91dfb3a2d1041bfe80dd94e7f06a3c4 (patch) | |
tree | e4cd0b9b39f921dbdfb2e3ff0827be48106d887a | |
parent | 81038d2e2b588f9df45d20a2ca0be446b0e421b2 (diff) | |
download | go-git-03d81b5ed91dfb3a2d1041bfe80dd94e7f06a3c4.tar.gz |
cmd/compile: import/export of alias declarations
This CL completes support for alias declarations in the compiler.
Also:
- increased export format version
- updated various comments
For #16339.
Fixes #17487.
Change-Id: Ic6945fc44c0041771eaf9dcfe973f601d14de069
Reviewed-on: https://go-review.googlesource.com/32090
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
-rw-r--r-- | src/cmd/compile/internal/gc/bexport.go | 109 | ||||
-rw-r--r-- | src/cmd/compile/internal/gc/bimport.go | 26 | ||||
-rw-r--r-- | src/cmd/compile/internal/gc/go.go | 1 | ||||
-rw-r--r-- | src/cmd/compile/internal/gc/main.go | 4 | ||||
-rw-r--r-- | src/cmd/compile/internal/gc/noder.go | 8 | ||||
-rw-r--r-- | src/go/internal/gcimporter/bimport.go | 4 | ||||
-rw-r--r-- | test/alias2.go | 12 | ||||
-rw-r--r-- | test/alias3.dir/a.go | 54 | ||||
-rw-r--r-- | test/alias3.dir/b.go | 61 | ||||
-rw-r--r-- | test/alias3.dir/c.go | 66 | ||||
-rw-r--r-- | test/alias3.go | 7 |
11 files changed, 291 insertions, 61 deletions
diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go index a6312cec90..ea0f6d7aaf 100644 --- a/src/cmd/compile/internal/gc/bexport.go +++ b/src/cmd/compile/internal/gc/bexport.go @@ -9,10 +9,12 @@ 1) Export data encoding principles: The export data is a serialized description of the graph of exported -"objects": constants, types, variables, and functions. In general, -types - but also objects referred to from inlined function bodies - -can be reexported and so we need to know which package they are coming -from. Therefore, packages are also part of the export graph. +"objects": constants, types, variables, and functions. Aliases may be +directly reexported, and unaliased types may be indirectly reexported +(as part of the type of a directly exorted object). More generally, +objects referred to from inlined function bodies can be reexported. +We need to know which package declares these reexported objects, and +therefore packages are also part of the export graph. The roots of the graph are two lists of objects. The 1st list (phase 1, see Export) contains all objects that are exported at the package level. @@ -30,9 +32,9 @@ function bodies. The format of this representation is compiler specific. The graph is serialized in in-order fashion, starting with the roots. Each object in the graph is serialized by writing its fields sequentially. -If the field is a pointer to another object, that object is serialized, -recursively. Otherwise the field is written. Non-pointer fields are all -encoded as integer or string values. +If the field is a pointer to another object, that object is serialized in +place, recursively. Otherwise the field is written in place. Non-pointer +fields are all encoded as integer or string values. Some objects (packages, types) may be referred to more than once. When reaching an object that was not serialized before, an integer _index_ @@ -43,7 +45,7 @@ If the object was already serialized, the encoding is simply the object index >= 0. An importer can trivially determine if an object needs to be read in for the first time (tag < 0) and entered into the respective object table, or if the object was seen already (index >= 0), in which -case the index is used to look up the object in a table. +case the index is used to look up the object in the respective table. Before exporting or importing, the type tables are populated with the predeclared types (int, string, error, unsafe.Pointer, etc.). This way @@ -59,7 +61,7 @@ format. These strings are followed by version-specific encoding options. That format encoding is no longer used but is supported to avoid spurious errors when importing old installed package files.) -The header is followed by the package object for the exported package, +This header is followed by the package object for the exported package, two lists of objects, and the list of inlined function bodies. The encoding of objects is straight-forward: Constants, variables, and @@ -69,6 +71,8 @@ same type was imported before via another import, the importer must use the previously imported type pointer so that we have exactly one version (i.e., one pointer) for each named type (and read but discard the current type encoding). Unnamed types simply encode their respective fields. +Aliases are encoded starting with their name followed by the original +(aliased) object. In the encoding, some lists start with the list length. Some lists are terminated with an end marker (usually for lists where we may not know @@ -101,30 +105,8 @@ compatibility with both the last release of the compiler, and with the corresponding compiler at tip. That change is necessarily more involved, as it must switch based on the version number in the export data file. -It is recommended to turn on debugFormat when working on format changes -as it will help finding encoding/decoding inconsistencies quickly. - -Special care must be taken to update builtin.go when the export format -changes: builtin.go contains the export data obtained by compiling the -builtin/runtime.go and builtin/unsafe.go files; those compilations in -turn depend on importing the data in builtin.go. Thus, when the export -data format changes, the compiler must be able to import the data in -builtin.go even if its format has not yet changed. Proceed in several -steps as follows: - -- Change the exporter to use the new format, and use a different version - string as well. -- Update the importer accordingly, but accept both the old and the new - format depending on the version string. -- all.bash should pass at this point. -- Run mkbuiltin.go: this will create a new builtin.go using the new - export format. -- go test -run Builtin should pass at this point. -- Remove importer support for the old export format and (maybe) revert - the version string again (it's only needed to mark the transition). -- all.bash should still pass. - -Don't forget to set debugFormat to false. +It is recommended to turn on debugFormat temporarily when working on format +changes as it will help finding encoding/decoding inconsistencies quickly. */ package gc @@ -158,7 +140,11 @@ const debugFormat = false // default: false const forceObjFileStability = true // Current export format version. Increase with each format change. -const exportVersion = 2 +// 3: added aliasTag and export of aliases +// 2: removed unused bool in ODCL export +// 1: header format change (more regular), export package for _ struct fields +// 0: Go1.7 encoding +const exportVersion = 3 // exportInlined enables the export of inlined function bodies and related // dependencies. The compiler should work w/o any loss of functionality with @@ -364,6 +350,11 @@ func export(out *bufio.Writer, trace bool) int { if p.trace { p.tracef("\n") } + + if sym.Flags&SymAlias != 0 { + Fatalf("exporter: unexpected alias %v in inlined function body", sym) + } + p.obj(sym) objcount++ } @@ -455,16 +446,44 @@ func unidealType(typ *Type, val Val) *Type { } func (p *exporter) obj(sym *Sym) { + if sym.Flags&SymAlias != 0 { + p.tag(aliasTag) + p.pos(nil) // TODO(gri) fix position information + // Aliases can only be exported from the package that + // declares them (aliases to aliases are resolved to the + // original object, and so are uses of aliases in inlined + // exported function bodies). Thus, we only need the alias + // name without package qualification. + if sym.Pkg != localpkg { + Fatalf("exporter: export of non-local alias: %v", sym) + } + p.string(sym.Name) + sym = sym.Def.Sym // original object + // fall through to export original + // Multiple aliases to the same original will cause that + // original to be exported multiple times (issue #17636). + // TODO(gri) fix this + } + + if sym != sym.Def.Sym { + Fatalf("exporter: exported object %v is not original %v", sym, sym.Def.Sym) + } + + if sym.Flags&SymAlias != 0 { + Fatalf("exporter: original object %v marked as alias", sym) + } + // Exported objects may be from different packages because they - // may be re-exported as depencies when exporting inlined function - // bodies. Thus, exported object names must be fully qualified. + // may be re-exported via an exported alias or as dependencies in + // exported inlined function bodies. Thus, exported object names + // must be fully qualified. // - // TODO(gri) This can only happen if exportInlined is enabled - // (default), and during phase 2 of object export. Objects exported - // in phase 1 (compiler-indendepent objects) are by definition only - // the objects from the current package and not pulled in via inlined - // function bodies. In that case the package qualifier is not needed. - // Possible space optimization. + // (This can only happen for aliased objects or during phase 2 + // (exportInlined enabled) of object export. Unaliased Objects + // exported in phase 1 (compiler-indendepent objects) are by + // definition only the objects from the current package and not + // pulled in via inlined function bodies. In that case the package + // qualifier is not needed. Possible space optimization.) n := sym.Def switch n.Op { @@ -1780,6 +1799,9 @@ const ( stringTag nilTag unknownTag // not used by gc (only appears in packages with errors) + + // Aliases + aliasTag ) // Debugging support. @@ -1815,6 +1837,9 @@ var tagString = [...]string{ -stringTag: "string", -nilTag: "nil", -unknownTag: "unknown", + + // Aliases + -aliasTag: "alias", } // untype returns the "pseudo" untyped type for a Ctype (import/export use only). diff --git a/src/cmd/compile/internal/gc/bimport.go b/src/cmd/compile/internal/gc/bimport.go index 35eb5b1a39..11154ef7ba 100644 --- a/src/cmd/compile/internal/gc/bimport.go +++ b/src/cmd/compile/internal/gc/bimport.go @@ -86,10 +86,10 @@ func Import(in *bufio.Reader) { // read version specific flags - extend as necessary switch p.version { - // case 3: + // case 4: // ... // fallthrough - case 2, 1: + case 3, 2, 1: p.debugFormat = p.rawStringln(p.rawByte()) == "debug" p.trackAllTypes = p.bool() p.posInfoFormat = p.bool() @@ -307,26 +307,35 @@ func idealType(typ *Type) *Type { } func (p *importer) obj(tag int) { + var alias *Sym + if tag == aliasTag { + p.pos() + alias = importpkg.Lookup(p.string()) + alias.Flags |= SymAlias + tag = p.tagOrIndex() + } + + var sym *Sym switch tag { case constTag: p.pos() - sym := p.qualifiedName() + sym = p.qualifiedName() typ := p.typ() val := p.value(typ) importconst(sym, idealType(typ), nodlit(val)) case typeTag: - p.typ() + sym = p.typ().Sym case varTag: p.pos() - sym := p.qualifiedName() + sym = p.qualifiedName() typ := p.typ() importvar(sym, typ) case funcTag: p.pos() - sym := p.qualifiedName() + sym = p.qualifiedName() params := p.paramList() result := p.paramList() @@ -357,6 +366,11 @@ func (p *importer) obj(tag int) { default: formatErrorf("unexpected object (tag = %d)", tag) } + + if alias != nil { + alias.Def = sym.Def + importsym(alias, sym.Def.Op) + } } func (p *importer) pos() { diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go index 9ab1a8557d..8c05149618 100644 --- a/src/cmd/compile/internal/gc/go.go +++ b/src/cmd/compile/internal/gc/go.go @@ -63,6 +63,7 @@ const ( SymSiggen SymAsm SymAlgGen + SymAlias // alias, original is Sym.Def.Sym ) // The Class of a variable/function describes the "storage class" diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index a990c0896f..c5ae7d3fba 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -928,7 +928,7 @@ func mkpackage(pkgname string) { continue } - if s.Def.Sym != s { + if s.Def.Sym != s && s.Flags&SymAlias == 0 { // throw away top-level name left over // from previous import . "x" if s.Def.Name != nil && s.Def.Name.Pack != nil && !s.Def.Name.Pack.Used && nsyntaxerrors == 0 { @@ -936,8 +936,6 @@ func mkpackage(pkgname string) { s.Def.Name.Pack.Used = true } - // TODO(gri) This will also affect exported aliases. - // Need to fix this. s.Def = nil continue } diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go index 2fdea7cfc8..644abcc204 100644 --- a/src/cmd/compile/internal/gc/noder.go +++ b/src/cmd/compile/internal/gc/noder.go @@ -174,7 +174,11 @@ func (p *noder) aliasDecl(decl *syntax.AliasDecl) { } pkg.Used = true + // Resolve original entity orig := oldname(restrictlookup(qident.Sel.Value, pkg.Name.Pkg)) + if orig.Sym.Flags&SymAlias != 0 { + Fatalf("original %v marked as alias", orig.Sym) + } // An alias declaration must not refer to package unsafe. if orig.Sym.Pkg == unsafepkg { @@ -222,16 +226,16 @@ func (p *noder) aliasDecl(decl *syntax.AliasDecl) { redeclare(asym, "in alias declaration") return } + asym.Flags |= SymAlias asym.Def = orig asym.Block = block asym.Lastlineno = lineno if exportname(asym.Name) { - yyerror("cannot export alias %v: not yet implemented", asym) // TODO(gri) newname(asym) is only needed to satisfy exportsym // (and indirectly, exportlist). We should be able to just // collect the Syms, eventually. - // exportsym(newname(asym)) + exportsym(newname(asym)) } } diff --git a/src/go/internal/gcimporter/bimport.go b/src/go/internal/gcimporter/bimport.go index ad28ef7735..f7d1ddab4b 100644 --- a/src/go/internal/gcimporter/bimport.go +++ b/src/go/internal/gcimporter/bimport.go @@ -98,10 +98,10 @@ func BImportData(fset *token.FileSet, imports map[string]*types.Package, data [] // read version specific flags - extend as necessary switch p.version { - // case 3: + // case 4: // ... // fallthrough - case 2, 1: + case 3, 2, 1: p.debugFormat = p.rawStringln(p.rawByte()) == "debug" p.trackAllTypes = p.int() != 0 p.posInfoFormat = p.int() != 0 diff --git a/test/alias2.go b/test/alias2.go index a09f524611..f160d384b1 100644 --- a/test/alias2.go +++ b/test/alias2.go @@ -50,7 +50,7 @@ func f => before.f // ERROR "before is not a package" var v => after.m // ERROR "after is not a package" func f => after.m // ERROR "after is not a package" -// TODO(gri) fix error printing - should not print a qualified identifier... +// TODO(gri) fix error printing - should print correct qualified identifier... var _ => Default.ARCH // ERROR "build.Default is not a package" // aliases may not refer to package unsafe @@ -77,11 +77,11 @@ func sin1 => math.Pi // ERROR "math.Pi is not a function" // alias reference to a package marks package as used func _ => fmt.Println -// TODO(gri) aliased cannot be exported yet - fix this -const Pi => math.Pi // ERROR "cannot export alias Pi" -type Writer => io.Writer // ERROR "cannot export alias Writer" -var Def => build.Default // ERROR "cannot export alias Def" -func Sin => math.Sin // ERROR "cannot export alias Sin" +// re-exported aliases +const Pi => math.Pi +type Writer => io.Writer +var Def => build.Default +func Sin => math.Sin // type aliases denote identical types type myPackage => build.Package diff --git a/test/alias3.dir/a.go b/test/alias3.dir/a.go new file mode 100644 index 0000000000..c14f834630 --- /dev/null +++ b/test/alias3.dir/a.go @@ -0,0 +1,54 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +import ( + "bytes" + "go/build" + "io" + "math" +) + +func F(c *build.Context, w io.Writer) {} + +func Inlined() bool { var w Writer; return w == nil } + +func Check() { + if Pi != math.Pi { + panic(0) + } + + var w Writer + F(new(Context), w) + F(new(build.Context), bytes.NewBuffer(nil)) + + if &Default != &build.Default { + panic(1) + } + + if Sin(1) != math.Sin(1) { + panic(2) + } + + var _ *LimitedReader = new(LimitedReader2) +} + +// export aliases +const Pi => math.Pi + +type ( + Context => build.Context // not an interface + Writer => io.Writer // interface +) + +// different aliases may refer to the same original +type LimitedReader => io.LimitedReader +type LimitedReader2 => io.LimitedReader + +var Default => build.Default +var Default2 => build.Default + +func Sin => math.Sin +func Sin2 => math.Sin diff --git a/test/alias3.dir/b.go b/test/alias3.dir/b.go new file mode 100644 index 0000000000..d4550feca5 --- /dev/null +++ b/test/alias3.dir/b.go @@ -0,0 +1,61 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package b + +import ( + "./a" + "bytes" + "go/build" + "io" + "math" +) + +func F => a.F +func Inlined => a.Inlined + +var _ func(*Context, io.Writer) = a.F + +// check aliases +func Check() { + if Pi != math.Pi { + panic(0) + } + + var w Writer + a.F(new(Context), w) + F(new(build.Context), bytes.NewBuffer(nil)) + + if !Inlined() { + panic(1) + } + + if &Default != &build.Default { + panic(2) + } + + if Sin(1) != math.Sin(1) { + panic(3) + } + + var _ *LimitedReader = new(LimitedReader2) +} + +// re-export aliases +const Pi => a.Pi + +type ( + Context => a.Context // not an interface + Writer => a.Writer // interface +) + +// different aliases may refer to the same original +type LimitedReader => a.LimitedReader +type LimitedReader2 => a.LimitedReader2 + +var Default => a.Default +var Default2 => a.Default2 + +func Sin => a.Sin +func Sin2 => a.Sin diff --git a/test/alias3.dir/c.go b/test/alias3.dir/c.go new file mode 100644 index 0000000000..701483fac2 --- /dev/null +++ b/test/alias3.dir/c.go @@ -0,0 +1,66 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "./a" + "./b" + "bytes" + "go/build" + "math" +) + +func f => b.F +func inlined => b.Inlined + +var _ func(*context, a.Writer) = f + +func Check() { + if pi != math.Pi { + panic(0) + } + + var w writer + b.F(new(context), w) + f(new(build.Context), bytes.NewBuffer(nil)) + + if !inlined() { + panic(1) + } + + if &default_ != &build.Default { + panic(2) + } + + if sin(1) != math.Sin(1) { + panic(3) + } + + var _ *limitedReader = new(limitedReader2) +} + +// local aliases +const pi => b.Pi + +type ( + context => b.Context // not an interface + writer => b.Writer // interface +) + +// different aliases may refer to the same original +type limitedReader => b.LimitedReader +type limitedReader2 => b.LimitedReader2 + +var default_ => b.Default +var default2 => b.Default2 + +func sin => b.Sin +func sin2 => b.Sin + +func main() { + a.Check() + b.Check() + Check() +} diff --git a/test/alias3.go b/test/alias3.go new file mode 100644 index 0000000000..4830c68c68 --- /dev/null +++ b/test/alias3.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored |