mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
[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/35100 Reviewed-by: Alan Donovan <adonovan@google.com>
This commit is contained in:
parent
5ceec42dc0
commit
49de5f0351
3 changed files with 114 additions and 61 deletions
|
|
@ -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) {
|
||||||
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) {
|
||||||
|
|
||||||
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) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 (
|
||||||
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{
|
||||||
-nilTag: "nil",
|
-nilTag: "nil",
|
||||||
-unknownTag: "unknown",
|
-unknownTag: "unknown",
|
||||||
|
|
||||||
// Aliases
|
// Type aliases
|
||||||
-aliasTag: "alias",
|
-aliasTag: "alias",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
||||||
// 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) {
|
||||||
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) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
}
|
}
|
||||||
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) {
|
||||||
|
|
||||||
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 {
|
||||||
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 []
|
||||||
|
|
||||||
// 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 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
||||||
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) {
|
||||||
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) {
|
||||||
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) {
|
||||||
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 [
|
||||||
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 {
|
||||||
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) {
|
||||||
// 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 (
|
||||||
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{
|
||||||
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(),
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue