]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: import/export of alias declarations
authorRobert Griesemer <gri@golang.org>
Tue, 25 Oct 2016 21:09:18 +0000 (14:09 -0700)
committerRobert Griesemer <gri@golang.org>
Thu, 27 Oct 2016 17:44:45 +0000 (17:44 +0000)
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>
src/cmd/compile/internal/gc/bexport.go
src/cmd/compile/internal/gc/bimport.go
src/cmd/compile/internal/gc/go.go
src/cmd/compile/internal/gc/main.go
src/cmd/compile/internal/gc/noder.go
src/go/internal/gcimporter/bimport.go
test/alias2.go
test/alias3.dir/a.go [new file with mode: 0644]
test/alias3.dir/b.go [new file with mode: 0644]
test/alias3.dir/c.go [new file with mode: 0644]
test/alias3.go [new file with mode: 0644]

index a6312cec90a30e993579b51889003ccc2d9adff5..ea0f6d7aaf73bfde92e1214b589a7529d7553614 100644 (file)
@@ -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).
index 35eb5b1a399ef7987c22cec34607d6f694a68b46..11154ef7ba26fc5f01b5b1b220f4a594d3873cea 100644 (file)
@@ -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() {
index 9ab1a8557de7254f83fb7395818c90b1d99aa0c4..8c05149618aeaa1585c5d26bacd4199c4c88c7c3 100644 (file)
@@ -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"
index a990c0896fbf03d4c48fdd954f7db3c17063ebc1..c5ae7d3fba37a9b5f9ec6f832b42edbf7885c559 100644 (file)
@@ -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
                        }
index 2fdea7cfc8302553f9b7fe1de1a81f2215ac41d7..644abcc204e3532f251997bf1053b497c0f7bb4e 100644 (file)
@@ -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))
        }
 }
 
index ad28ef7735e3b2504c8cd379e159d8a865efbfd7..f7d1ddab4b884adcefd112f00f9cad428b575eff 100644 (file)
@@ -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
index a09f524611fda6da82645b8e829eda7cf76714c4..f160d384b14abca6c7d3140198c85a8f348da609 100644 (file)
@@ -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 (file)
index 0000000..c14f834
--- /dev/null
@@ -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 (file)
index 0000000..d4550fe
--- /dev/null
@@ -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 (file)
index 0000000..701483f
--- /dev/null
@@ -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 (file)
index 0000000..4830c68
--- /dev/null
@@ -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