Commit 49de5f03 authored by Robert Griesemer's avatar Robert Griesemer

[dev.typealias] cmd/compile, go/importer: define export format and implement…

[dev.typealias] cmd/compile, go/importer: define export format and implement importing of type aliases

This defines the (tentative) export/import format for type aliases.

The compiler doesn't support type aliases yet, so while the code is present
it is guarded with a flag.

The export format for embedded (anonymous) fields now has three modes (mode 3 is new):
1) The original type name and the anonymous field name are the same, and the name is exported:
   we don't need the field name and write "" instead
2) The original type name and the anonymous field name are the same, and the name is not exported:
   we don't need the field name and write "?" instead, indicating that there is package info
3) The original type name and the anonymous field name are different:
   we do need the field name and write "@" followed by the field name (and possible package info)

For #18130.

Change-Id: I790dad826757233fa71396a210f966c6256b75d3
Reviewed-on: https://go-review.googlesource.com/35100Reviewed-by: 's avatarAlan Donovan <adonovan@google.com>
parent 5ceec42d
...@@ -140,11 +140,12 @@ const debugFormat = false // default: false ...@@ -140,11 +140,12 @@ const debugFormat = false // default: false
const forceObjFileStability = true const forceObjFileStability = true
// Current export format version. Increase with each format change. // Current export format version. Increase with each format change.
// 3: added aliasTag and export of aliases // 4: type name objects support type aliases, uses aliasTag
// 2: removed unused bool in ODCL export // 3: Go1.8 encoding (same as version 2, aliasTag defined but never used)
// 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 = 3 const exportVersion = 4
// 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
...@@ -509,7 +510,14 @@ func (p *exporter) obj(sym *Sym) { ...@@ -509,7 +510,14 @@ func (p *exporter) obj(sym *Sym) {
Fatalf("exporter: export of incomplete type %v", sym) Fatalf("exporter: export of incomplete type %v", sym)
} }
p.tag(typeTag) const alias = false // TODO(gri) fix this
if alias {
p.tag(aliasTag)
p.pos(n)
p.qualifiedName(sym)
} else {
p.tag(typeTag)
}
p.typ(t) p.typ(t)
case ONAME: case ONAME:
...@@ -868,19 +876,29 @@ func (p *exporter) methodList(t *Type) { ...@@ -868,19 +876,29 @@ func (p *exporter) methodList(t *Type) {
func (p *exporter) method(m *Field) { func (p *exporter) method(m *Field) {
p.pos(m.Nname) p.pos(m.Nname)
p.fieldName(m) p.methodName(m.Sym)
p.paramList(m.Type.Params(), false) p.paramList(m.Type.Params(), false)
p.paramList(m.Type.Results(), false) p.paramList(m.Type.Results(), false)
} }
// fieldName is like qualifiedName but it doesn't record the package for exported names.
func (p *exporter) fieldName(t *Field) { func (p *exporter) fieldName(t *Field) {
name := t.Sym.Name name := t.Sym.Name
if t.Embedded != 0 { if t.Embedded != 0 {
name = "" // anonymous field // anonymous field - we distinguish between 3 cases:
if bname := basetypeName(t.Type); bname != "" && !exportname(bname) { // 1) field name matches base type name and name is exported
// anonymous field with unexported base type name // 2) field name matches base type name and name is not exported
name = "?" // unexported name to force export of package // 3) field name doesn't match base type name (type name is alias)
bname := basetypeName(t.Type)
if name == bname {
if exportname(name) {
name = "" // 1) we don't need to know the name
} else {
name = "?" // 2) use unexported name to force package export
}
} else {
// 3) indicate alias and export name as is
// (this requires an extra "@" but this is a rare case)
p.string("@")
} }
} }
p.string(name) p.string(name)
...@@ -889,6 +907,14 @@ func (p *exporter) fieldName(t *Field) { ...@@ -889,6 +907,14 @@ func (p *exporter) fieldName(t *Field) {
} }
} }
// methodName is like qualifiedName but it doesn't record the package for exported names.
func (p *exporter) methodName(sym *Sym) {
p.string(sym.Name)
if !exportname(sym.Name) {
p.pkg(sym.Pkg)
}
}
func basetypeName(t *Type) string { func basetypeName(t *Type) string {
s := t.Sym s := t.Sym
if s == nil && t.IsPtr() { if s == nil && t.IsPtr() {
...@@ -1797,7 +1823,7 @@ const ( ...@@ -1797,7 +1823,7 @@ const (
nilTag nilTag
unknownTag // not used by gc (only appears in packages with errors) unknownTag // not used by gc (only appears in packages with errors)
// Aliases // Type aliases
aliasTag aliasTag
) )
...@@ -1835,7 +1861,7 @@ var tagString = [...]string{ ...@@ -1835,7 +1861,7 @@ var tagString = [...]string{
-nilTag: "nil", -nilTag: "nil",
-unknownTag: "unknown", -unknownTag: "unknown",
// Aliases // Type aliases
-aliasTag: "alias", -aliasTag: "alias",
} }
...@@ -1889,7 +1915,7 @@ func predeclared() []*Type { ...@@ -1889,7 +1915,7 @@ func predeclared() []*Type {
Types[TCOMPLEX128], Types[TCOMPLEX128],
Types[TSTRING], Types[TSTRING],
// aliases // basic type aliases
bytetype, bytetype,
runetype, runetype,
......
...@@ -86,10 +86,10 @@ func Import(in *bufio.Reader) { ...@@ -86,10 +86,10 @@ func Import(in *bufio.Reader) {
// read version specific flags - extend as necessary // read version specific flags - extend as necessary
switch p.version { switch p.version {
// case 4: // case 5:
// ... // ...
// fallthrough // fallthrough
case 3, 2, 1: case 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()
...@@ -315,6 +315,12 @@ func (p *importer) obj(tag int) { ...@@ -315,6 +315,12 @@ func (p *importer) obj(tag int) {
val := p.value(typ) val := p.value(typ)
importconst(sym, idealType(typ), nodlit(val)) importconst(sym, idealType(typ), nodlit(val))
case aliasTag:
// TODO(gri) hook up type alias
p.pos()
p.qualifiedName()
p.typ()
case typeTag: case typeTag:
p.typ() p.typ()
...@@ -354,17 +360,6 @@ func (p *importer) obj(tag int) { ...@@ -354,17 +360,6 @@ func (p *importer) obj(tag int) {
} }
} }
case aliasTag:
p.pos()
alias := importpkg.Lookup(p.string())
orig := p.qualifiedName()
// Although the protocol allows the alias to precede the original,
// this never happens in files produced by gc.
alias.Flags |= SymAlias
alias.Def = orig.Def
importsym(alias, orig.Def.Op)
default: default:
formatErrorf("unexpected object (tag = %d)", tag) formatErrorf("unexpected object (tag = %d)", tag)
} }
...@@ -594,6 +589,8 @@ func (p *importer) field() *Field { ...@@ -594,6 +589,8 @@ func (p *importer) field() *Field {
} }
sym = sym.Pkg.Lookup(s.Name) sym = sym.Pkg.Lookup(s.Name)
f.Embedded = 1 f.Embedded = 1
} else if sym.Flags&SymAlias != 0 {
f.Embedded = 1
} }
f.Sym = sym f.Sym = sym
...@@ -616,7 +613,7 @@ func (p *importer) methodList() (methods []*Field) { ...@@ -616,7 +613,7 @@ func (p *importer) methodList() (methods []*Field) {
func (p *importer) method() *Field { func (p *importer) method() *Field {
p.pos() p.pos()
sym := p.fieldName() sym := p.methodName()
params := p.paramList() params := p.paramList()
result := p.paramList() result := p.paramList()
...@@ -630,15 +627,43 @@ func (p *importer) method() *Field { ...@@ -630,15 +627,43 @@ func (p *importer) method() *Field {
func (p *importer) fieldName() *Sym { func (p *importer) fieldName() *Sym {
name := p.string() name := p.string()
if p.version == 0 && name == "_" { if p.version == 0 && name == "_" {
// version 0 didn't export a package for _ fields // version 0 didn't export a package for _ field names
// but used the builtin package instead // but used the builtin package instead
return builtinpkg.Lookup(name) return builtinpkg.Lookup(name)
} }
pkg := localpkg pkg := localpkg
if name != "" && !exportname(name) { var flag SymFlags
if name == "?" { switch name {
name = "" case "":
// field name is exported - nothing to do
case "?":
// field name is not exported - need package
name = ""
pkg = p.pkg()
case "@":
// field name doesn't match type name (alias)
name = p.string()
flag = SymAlias
fallthrough
default:
if !exportname(name) {
pkg = p.pkg()
} }
}
sym := pkg.Lookup(name)
sym.Flags |= flag
return sym
}
func (p *importer) methodName() *Sym {
name := p.string()
if p.version == 0 && name == "_" {
// version 0 didn't export a package for _ method names
// but used the builtin package instead
return builtinpkg.Lookup(name)
}
pkg := localpkg
if !exportname(name) {
pkg = p.pkg() pkg = p.pkg()
} }
return pkg.Lookup(name) return pkg.Lookup(name)
......
...@@ -98,10 +98,10 @@ func BImportData(fset *token.FileSet, imports map[string]*types.Package, data [] ...@@ -98,10 +98,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 4: // case 5:
// ... // ...
// fallthrough // fallthrough
case 3, 2, 1: case 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
...@@ -208,7 +208,6 @@ func (p *importer) pkg() *types.Package { ...@@ -208,7 +208,6 @@ func (p *importer) pkg() *types.Package {
} }
// objTag returns the tag value for each object kind. // objTag returns the tag value for each object kind.
// obj must not be a *types.Alias.
func objTag(obj types.Object) int { func objTag(obj types.Object) int {
switch obj.(type) { switch obj.(type) {
case *types.Const: case *types.Const:
...@@ -219,7 +218,6 @@ func objTag(obj types.Object) int { ...@@ -219,7 +218,6 @@ func objTag(obj types.Object) int {
return varTag return varTag
case *types.Func: case *types.Func:
return funcTag return funcTag
// Aliases are not exported multiple times, thus we should not see them here.
default: default:
errorf("unexpected object: %v (%T)", obj, obj) // panics errorf("unexpected object: %v (%T)", obj, obj) // panics
panic("unreachable") panic("unreachable")
...@@ -237,14 +235,14 @@ func (p *importer) declare(obj types.Object) { ...@@ -237,14 +235,14 @@ func (p *importer) declare(obj types.Object) {
pkg := obj.Pkg() pkg := obj.Pkg()
if alt := pkg.Scope().Insert(obj); alt != nil { if alt := pkg.Scope().Insert(obj); alt != nil {
// This can only trigger if we import a (non-type) object a second time. // This can only trigger if we import a (non-type) object a second time.
// Excluding aliases, this cannot happen because 1) we only import a package // Excluding type aliases, this cannot happen because 1) we only import a package
// once; and b) we ignore compiler-specific export data which may contain // once; and b) we ignore compiler-specific export data which may contain
// functions whose inlined function bodies refer to other functions that // functions whose inlined function bodies refer to other functions that
// were already imported. // were already imported.
// However, aliases require reexporting the original object, so we need // However, type aliases require reexporting the original type, so we need
// to allow it (see also the comment in cmd/compile/internal/gc/bimport.go, // to allow it (see also the comment in cmd/compile/internal/gc/bimport.go,
// method importer.obj, switch case importing functions). // method importer.obj, switch case importing functions).
// Note that the original itself cannot be an alias. // TODO(gri) review/update this comment once the gc compiler handles type aliases.
if !sameObj(obj, alt) { if !sameObj(obj, alt) {
errorf("inconsistent import:\n\t%v\npreviously imported as:\n\t%v\n", obj, alt) errorf("inconsistent import:\n\t%v\npreviously imported as:\n\t%v\n", obj, alt)
} }
...@@ -260,6 +258,13 @@ func (p *importer) obj(tag int) { ...@@ -260,6 +258,13 @@ func (p *importer) obj(tag int) {
val := p.value() val := p.value()
p.declare(types.NewConst(pos, pkg, name, typ, val)) p.declare(types.NewConst(pos, pkg, name, typ, val))
case aliasTag:
// TODO(gri) verify type alias hookup is correct
pos := p.pos()
pkg, name := p.qualifiedName()
typ := p.typ(nil)
p.declare(types.NewTypeName(pos, pkg, name, typ))
case typeTag: case typeTag:
p.typ(nil) p.typ(nil)
...@@ -277,19 +282,6 @@ func (p *importer) obj(tag int) { ...@@ -277,19 +282,6 @@ func (p *importer) obj(tag int) {
sig := types.NewSignature(nil, params, result, isddd) sig := types.NewSignature(nil, params, result, isddd)
p.declare(types.NewFunc(pos, pkg, name, sig)) p.declare(types.NewFunc(pos, pkg, name, sig))
case aliasTag:
pos := p.pos()
name := p.string()
var orig types.Object
if pkg, name := p.qualifiedName(); pkg != nil {
orig = pkg.Scope().Lookup(name)
}
// Alias-related code. Keep for now.
_ = pos
_ = name
_ = orig
// p.declare(types.NewAlias(pos, p.pkgList[0], name, orig))
default: default:
errorf("unexpected object tag %d", tag) errorf("unexpected object tag %d", tag)
} }
...@@ -556,17 +548,17 @@ func (p *importer) fieldList(parent *types.Package) (fields []*types.Var, tags [ ...@@ -556,17 +548,17 @@ func (p *importer) fieldList(parent *types.Package) (fields []*types.Var, tags [
fields = make([]*types.Var, n) fields = make([]*types.Var, n)
tags = make([]string, n) tags = make([]string, n)
for i := range fields { for i := range fields {
fields[i] = p.field(parent) fields[i], tags[i] = p.field(parent)
tags[i] = p.string()
} }
} }
return return
} }
func (p *importer) field(parent *types.Package) *types.Var { func (p *importer) field(parent *types.Package) (*types.Var, string) {
pos := p.pos() pos := p.pos()
pkg, name := p.fieldName(parent) pkg, name := p.fieldName(parent)
typ := p.typ(parent) typ := p.typ(parent)
tag := p.string()
anonymous := false anonymous := false
if name == "" { if name == "" {
...@@ -583,7 +575,7 @@ func (p *importer) field(parent *types.Package) *types.Var { ...@@ -583,7 +575,7 @@ func (p *importer) field(parent *types.Package) *types.Var {
anonymous = true anonymous = true
} }
return types.NewField(pos, pkg, name, typ, anonymous) return types.NewField(pos, pkg, name, typ, anonymous), tag
} }
func (p *importer) methodList(parent *types.Package) (methods []*types.Func) { func (p *importer) methodList(parent *types.Package) (methods []*types.Func) {
...@@ -616,11 +608,21 @@ func (p *importer) fieldName(parent *types.Package) (*types.Package, string) { ...@@ -616,11 +608,21 @@ func (p *importer) fieldName(parent *types.Package) (*types.Package, string) {
// version 0 didn't export a package for _ fields // version 0 didn't export a package for _ fields
return pkg, name return pkg, name
} }
if name != "" && !exported(name) { switch name {
if name == "?" { case "":
name = "" // field name is exported - nothing to do
} case "?":
// field name is not exported - need package
name = ""
pkg = p.pkg() pkg = p.pkg()
case "@":
// field name doesn't match type name (alias)
name = p.string()
fallthrough
default:
if !exported(name) {
pkg = p.pkg()
}
} }
return pkg, name return pkg, name
} }
...@@ -893,7 +895,7 @@ const ( ...@@ -893,7 +895,7 @@ const (
nilTag // only used by gc (appears in exported inlined function bodies) nilTag // only used by gc (appears in exported inlined function bodies)
unknownTag // not used by gc (only appears in packages with errors) unknownTag // not used by gc (only appears in packages with errors)
// Aliases // Type aliases
aliasTag aliasTag
) )
...@@ -917,7 +919,7 @@ var predeclared = []types.Type{ ...@@ -917,7 +919,7 @@ var predeclared = []types.Type{
types.Typ[types.Complex128], types.Typ[types.Complex128],
types.Typ[types.String], types.Typ[types.String],
// aliases // basic type aliases
types.Universe.Lookup("byte").Type(), types.Universe.Lookup("byte").Type(),
types.Universe.Lookup("rune").Type(), types.Universe.Lookup("rune").Type(),
......
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