Commit 07029254 authored by Matthew Dempsky's avatar Matthew Dempsky

cmd/compile: add package height to export data

A package's height is defined as the length of the longest import path
between itself and a leaf package (i.e., package with no imports).

We can't rely on knowing the path of the package being compiled, so
package height is useful for defining a package ordering.

Updates #24693.

Change-Id: I965162c440b6c5397db91b621ea0be7fa63881f1
Reviewed-on: https://go-review.googlesource.com/105038
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarRobert Griesemer <gri@golang.org>
parent fe77a541
...@@ -135,13 +135,14 @@ import ( ...@@ -135,13 +135,14 @@ import (
const debugFormat = false // default: false const debugFormat = false // default: false
// Current export format version. Increase with each format change. // Current export format version. Increase with each format change.
// 6: package height (CL 105038)
// 5: improved position encoding efficiency (issue 20080, CL 41619) // 5: improved position encoding efficiency (issue 20080, CL 41619)
// 4: type name objects support type aliases, uses aliasTag // 4: type name objects support type aliases, uses aliasTag
// 3: Go1.8 encoding (same as version 2, aliasTag defined but never used) // 3: Go1.8 encoding (same as version 2, aliasTag defined but never used)
// 2: removed unused bool in ODCL export (compiler only) // 2: removed unused bool in ODCL export (compiler only)
// 1: header format change (more regular), export package for _ struct fields // 1: header format change (more regular), export package for _ struct fields
// 0: Go1.7 encoding // 0: Go1.7 encoding
const exportVersion = 5 const exportVersion = 6
// exportInlined enables the export of inlined function bodies and related // exportInlined enables the export of inlined function bodies and related
// dependencies. The compiler should work w/o any loss of functionality with // dependencies. The compiler should work w/o any loss of functionality with
...@@ -428,6 +429,7 @@ func (p *exporter) pkg(pkg *types.Pkg) { ...@@ -428,6 +429,7 @@ func (p *exporter) pkg(pkg *types.Pkg) {
p.tag(packageTag) p.tag(packageTag)
p.string(pkg.Name) p.string(pkg.Name)
p.path(pkg.Path) p.path(pkg.Path)
p.int(pkg.Height)
} }
func unidealType(typ *types.Type, val Val) *types.Type { func unidealType(typ *types.Type, val Val) *types.Type {
......
...@@ -96,10 +96,10 @@ func Import(imp *types.Pkg, in *bufio.Reader) { ...@@ -96,10 +96,10 @@ func Import(imp *types.Pkg, in *bufio.Reader) {
// read version specific flags - extend as necessary // read version specific flags - extend as necessary
switch p.version { switch p.version {
// case 6: // case 7:
// ... // ...
// fallthrough // fallthrough
case 5, 4, 3, 2, 1: case 6, 5, 4, 3, 2, 1:
p.debugFormat = p.rawStringln(p.rawByte()) == "debug" p.debugFormat = p.rawStringln(p.rawByte()) == "debug"
p.trackAllTypes = p.bool() p.trackAllTypes = p.bool()
p.posInfoFormat = p.bool() p.posInfoFormat = p.bool()
...@@ -281,6 +281,10 @@ func (p *importer) pkg() *types.Pkg { ...@@ -281,6 +281,10 @@ func (p *importer) pkg() *types.Pkg {
} else { } else {
path = p.string() path = p.string()
} }
var height int
if p.version >= 6 {
height = p.int()
}
// we should never see an empty package name // we should never see an empty package name
if name == "" { if name == "" {
...@@ -298,6 +302,18 @@ func (p *importer) pkg() *types.Pkg { ...@@ -298,6 +302,18 @@ func (p *importer) pkg() *types.Pkg {
p.formatErrorf("package path %q for pkg index %d", path, len(p.pkgList)) p.formatErrorf("package path %q for pkg index %d", path, len(p.pkgList))
} }
if p.version >= 6 {
if height < 0 || height >= types.MaxPkgHeight {
p.formatErrorf("bad package height %v for package %s", height, name)
}
// reexported packages should always have a lower height than
// the main package
if len(p.pkgList) != 0 && height >= p.imp.Height {
p.formatErrorf("package %q (height %d) reexports package %q (height %d)", p.imp.Path, p.imp.Height, path, height)
}
}
// add package to pkgList // add package to pkgList
pkg := p.imp pkg := p.imp
if path != "" { if path != "" {
...@@ -313,6 +329,7 @@ func (p *importer) pkg() *types.Pkg { ...@@ -313,6 +329,7 @@ func (p *importer) pkg() *types.Pkg {
yyerror("import %q: package depends on %q (import cycle)", p.imp.Path, path) yyerror("import %q: package depends on %q (import cycle)", p.imp.Path, path)
errorexit() errorexit()
} }
pkg.Height = height
p.pkgList = append(p.pkgList, pkg) p.pkgList = append(p.pkgList, pkg)
return pkg return pkg
......
...@@ -141,6 +141,11 @@ func Main(archInit func(*Arch)) { ...@@ -141,6 +141,11 @@ func Main(archInit func(*Arch)) {
localpkg = types.NewPkg("", "") localpkg = types.NewPkg("", "")
localpkg.Prefix = "\"\"" localpkg.Prefix = "\"\""
// We won't know localpkg's height until after import
// processing. In the mean time, set to MaxPkgHeight to ensure
// height comparisons at least work until then.
localpkg.Height = types.MaxPkgHeight
// pseudo-package, for scoping // pseudo-package, for scoping
builtinpkg = types.NewPkg("go.builtin", "") // TODO(gri) name this package go.builtin? builtinpkg = types.NewPkg("go.builtin", "") // TODO(gri) name this package go.builtin?
builtinpkg.Prefix = "go.builtin" // not go%2ebuiltin builtinpkg.Prefix = "go.builtin" // not go%2ebuiltin
...@@ -925,6 +930,10 @@ func loadsys() { ...@@ -925,6 +930,10 @@ func loadsys() {
inimport = false inimport = false
} }
// myheight tracks the local package's height based on packages
// imported so far.
var myheight int
func importfile(f *Val) *types.Pkg { func importfile(f *Val) *types.Pkg {
path_, ok := f.U.(string) path_, ok := f.U.(string)
if !ok { if !ok {
...@@ -1117,6 +1126,10 @@ func importfile(f *Val) *types.Pkg { ...@@ -1117,6 +1126,10 @@ func importfile(f *Val) *types.Pkg {
errorexit() errorexit()
} }
if importpkg.Height >= myheight {
myheight = importpkg.Height + 1
}
return importpkg return importpkg
} }
......
...@@ -65,6 +65,8 @@ func parseFiles(filenames []string) uint { ...@@ -65,6 +65,8 @@ func parseFiles(filenames []string) uint {
testdclstack() testdclstack()
} }
localpkg.Height = myheight
return lines return lines
} }
......
...@@ -15,14 +15,24 @@ import ( ...@@ -15,14 +15,24 @@ import (
// pkgMap maps a package path to a package. // pkgMap maps a package path to a package.
var pkgMap = make(map[string]*Pkg) var pkgMap = make(map[string]*Pkg)
// MaxPkgHeight is a height greater than any likely package height.
const MaxPkgHeight = 1e9
type Pkg struct { type Pkg struct {
Path string // string literal used in import statement, e.g. "runtime/internal/sys" Path string // string literal used in import statement, e.g. "runtime/internal/sys"
Name string // package name, e.g. "sys" Name string // package name, e.g. "sys"
Pathsym *obj.LSym Prefix string // escaped path for use in symbol table
Prefix string // escaped path for use in symbol table Syms map[string]*Sym
Imported bool // export data of this package was parsed Pathsym *obj.LSym
Direct bool // imported directly
Syms map[string]*Sym // Height is the package's height in the import graph. Leaf
// packages (i.e., packages with no imports) have height 0,
// and all other packages have height 1 plus the maximum
// height of their imported packages.
Height int
Imported bool // export data of this package was parsed
Direct bool // imported directly
} }
// NewPkg returns a new Pkg for the given package path and name. // NewPkg returns a new Pkg for the given package path and name.
......
...@@ -102,10 +102,10 @@ func BImportData(fset *token.FileSet, imports map[string]*types.Package, data [] ...@@ -102,10 +102,10 @@ func BImportData(fset *token.FileSet, imports map[string]*types.Package, data []
// read version specific flags - extend as necessary // read version specific flags - extend as necessary
switch p.version { switch p.version {
// case 6: // case 7:
// ... // ...
// fallthrough // fallthrough
case 5, 4, 3, 2, 1: case 6, 5, 4, 3, 2, 1:
p.debugFormat = p.rawStringln(p.rawByte()) == "debug" p.debugFormat = p.rawStringln(p.rawByte()) == "debug"
p.trackAllTypes = p.int() != 0 p.trackAllTypes = p.int() != 0
p.posInfoFormat = p.int() != 0 p.posInfoFormat = p.int() != 0
...@@ -182,6 +182,9 @@ func (p *importer) pkg() *types.Package { ...@@ -182,6 +182,9 @@ func (p *importer) pkg() *types.Package {
} else { } else {
path = p.string() path = p.string()
} }
if p.version >= 6 {
p.int() // package height; unused by go/types
}
// we should never see an empty package name // we should never see an empty package name
if name == "" { if name == "" {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment