mirror of
https://github.com/golang/go
synced 2024-11-02 11:50:30 +00:00
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>
This commit is contained in:
parent
81038d2e2b
commit
03d81b5ed9
11 changed files with 291 additions and 61 deletions
|
@ -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).
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
54
test/alias3.dir/a.go
Normal file
54
test/alias3.dir/a.go
Normal file
|
@ -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
|
61
test/alias3.dir/b.go
Normal file
61
test/alias3.dir/b.go
Normal file
|
@ -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
|
66
test/alias3.dir/c.go
Normal file
66
test/alias3.dir/c.go
Normal file
|
@ -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()
|
||||
}
|
7
test/alias3.go
Normal file
7
test/alias3.go
Normal file
|
@ -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
|
Loading…
Reference in a new issue