Commit f011e0c6 authored by Robert Griesemer's avatar Robert Griesemer

[dev.typealias] cmd/compile, go/types, go/importer: various alias related fixes

cmd/compile:
- remove crud from prior alias implementation
- better comments in places

go/types:
- fix TypeName.IsAlias predicate
- more tests

go/importer (go/internal/gcimporter15):
- handle "@" format for anonymous fields using aliases
  (currently tested indirectly via x/tools/gcimporter15 tests)

For #18130.

Change-Id: I23a6d4e3a4c2a5c1ae589513da73fde7cad5f386
Reviewed-on: https://go-review.googlesource.com/35101Reviewed-by: 's avatarAlan Donovan <adonovan@google.com>
parent 49de5f03
...@@ -447,30 +447,6 @@ func unidealType(typ *Type, val Val) *Type { ...@@ -447,30 +447,6 @@ func unidealType(typ *Type, val Val) *Type {
} }
func (p *exporter) obj(sym *Sym) { 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)
orig := sym.Def.Sym
if orig.Flags&SymAlias != 0 {
Fatalf("exporter: original object %v marked as alias", sym)
}
p.qualifiedName(orig)
return
}
if sym != sym.Def.Sym {
Fatalf("exporter: exported object %v is not original %v", sym, sym.Def.Sym)
}
// Exported objects may be from different packages because they // Exported objects may be from different packages because they
// may be re-exported via an exported alias or as dependencies in // may be re-exported via an exported alias or as dependencies in
// exported inlined function bodies. Thus, exported object names // exported inlined function bodies. Thus, exported object names
...@@ -885,15 +861,15 @@ func (p *exporter) fieldName(t *Field) { ...@@ -885,15 +861,15 @@ func (p *exporter) fieldName(t *Field) {
name := t.Sym.Name name := t.Sym.Name
if t.Embedded != 0 { if t.Embedded != 0 {
// anonymous field - we distinguish between 3 cases: // anonymous field - we distinguish between 3 cases:
// 1) field name matches base type name and name is exported // 1) field name matches base type name and is exported
// 2) field name matches base type name and name is not exported // 2) field name matches base type name and is not exported
// 3) field name doesn't match base type name (type name is alias) // 3) field name doesn't match base type name (alias name)
bname := basetypeName(t.Type) bname := basetypeName(t.Type)
if name == bname { if name == bname {
if exportname(name) { if exportname(name) {
name = "" // 1) we don't need to know the name name = "" // 1) we don't need to know the field name or package
} else { } else {
name = "?" // 2) use unexported name to force package export name = "?" // 2) use unexported name "?" to force package export
} }
} else { } else {
// 3) indicate alias and export name as is // 3) indicate alias and export name as is
...@@ -920,11 +896,10 @@ func basetypeName(t *Type) string { ...@@ -920,11 +896,10 @@ func basetypeName(t *Type) string {
if s == nil && t.IsPtr() { if s == nil && t.IsPtr() {
s = t.Elem().Sym // deref s = t.Elem().Sym // deref
} }
// s should exist, but be conservative
if s != nil { if s != nil {
return s.Name return s.Name
} }
return "" return "" // unnamed type
} }
func (p *exporter) paramList(params *Type, numbered bool) { func (p *exporter) paramList(params *Type, numbered bool) {
......
...@@ -582,7 +582,7 @@ func (p *importer) field() *Field { ...@@ -582,7 +582,7 @@ func (p *importer) field() *Field {
f := newField() f := newField()
if sym.Name == "" { if sym.Name == "" {
// anonymous field - typ must be T or *T and T must be a type name // anonymous field: typ must be T or *T and T must be a type name
s := typ.Sym s := typ.Sym
if s == nil && typ.IsPtr() { if s == nil && typ.IsPtr() {
s = typ.Elem().Sym // deref s = typ.Elem().Sym // deref
...@@ -590,6 +590,7 @@ func (p *importer) field() *Field { ...@@ -590,6 +590,7 @@ 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 { } else if sym.Flags&SymAlias != 0 {
// anonymous field: we have an explicit name because it's an alias
f.Embedded = 1 f.Embedded = 1
} }
...@@ -635,13 +636,13 @@ func (p *importer) fieldName() *Sym { ...@@ -635,13 +636,13 @@ func (p *importer) fieldName() *Sym {
var flag SymFlags var flag SymFlags
switch name { switch name {
case "": case "":
// field name is exported - nothing to do // 1) field name matches base type name and is exported: nothing to do
case "?": case "?":
// field name is not exported - need package // 2) field name matches base type name and is not exported: need package
name = "" name = ""
pkg = p.pkg() pkg = p.pkg()
case "@": case "@":
// field name doesn't match type name (alias) // 3) field name doesn't match base type name (alias name): need name and possibly package
name = p.string() name = p.string()
flag = SymAlias flag = SymAlias
fallthrough fallthrough
......
...@@ -341,9 +341,7 @@ var ( ...@@ -341,9 +341,7 @@ var (
func (p *importer) qualifiedName() (pkg *types.Package, name string) { func (p *importer) qualifiedName() (pkg *types.Package, name string) {
name = p.string() name = p.string()
if name != "" { pkg = p.pkg()
pkg = p.pkg()
}
return return
} }
...@@ -556,7 +554,7 @@ func (p *importer) fieldList(parent *types.Package) (fields []*types.Var, tags [ ...@@ -556,7 +554,7 @@ func (p *importer) fieldList(parent *types.Package) (fields []*types.Var, tags [
func (p *importer) field(parent *types.Package) (*types.Var, string) { func (p *importer) field(parent *types.Package) (*types.Var, string) {
pos := p.pos() pos := p.pos()
pkg, name := p.fieldName(parent) pkg, name, alias := p.fieldName(parent)
typ := p.typ(parent) typ := p.typ(parent)
tag := p.string() tag := p.string()
...@@ -570,9 +568,12 @@ func (p *importer) field(parent *types.Package) (*types.Var, string) { ...@@ -570,9 +568,12 @@ func (p *importer) field(parent *types.Package) (*types.Var, string) {
case *types.Named: case *types.Named:
name = typ.Obj().Name() name = typ.Obj().Name()
default: default:
errorf("anonymous field expected") errorf("named base type expected")
} }
anonymous = true anonymous = true
} else if alias {
// anonymous field: we have an explicit name because it's an alias
anonymous = true
} }
return types.NewField(pos, pkg, name, typ, anonymous), tag return types.NewField(pos, pkg, name, typ, anonymous), tag
...@@ -590,41 +591,42 @@ func (p *importer) methodList(parent *types.Package) (methods []*types.Func) { ...@@ -590,41 +591,42 @@ func (p *importer) methodList(parent *types.Package) (methods []*types.Func) {
func (p *importer) method(parent *types.Package) *types.Func { func (p *importer) method(parent *types.Package) *types.Func {
pos := p.pos() pos := p.pos()
pkg, name := p.fieldName(parent) pkg, name, _ := p.fieldName(parent)
params, isddd := p.paramList() params, isddd := p.paramList()
result, _ := p.paramList() result, _ := p.paramList()
sig := types.NewSignature(nil, params, result, isddd) sig := types.NewSignature(nil, params, result, isddd)
return types.NewFunc(pos, pkg, name, sig) return types.NewFunc(pos, pkg, name, sig)
} }
func (p *importer) fieldName(parent *types.Package) (*types.Package, string) { func (p *importer) fieldName(parent *types.Package) (pkg *types.Package, name string, alias bool) {
name := p.string() name = p.string()
pkg := parent pkg = parent
if pkg == nil { if pkg == nil {
// use the imported package instead // use the imported package instead
pkg = p.pkgList[0] pkg = p.pkgList[0]
} }
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 _ fields
return pkg, name return
} }
switch name { switch name {
case "": case "":
// field name is exported - nothing to do // 1) field name matches base type name and is exported: nothing to do
case "?": case "?":
// field name is not exported - need package // 2) field name matches base type name and is not exported: need package
name = "" name = ""
pkg = p.pkg() pkg = p.pkg()
case "@": case "@":
// field name doesn't match type name (alias) // 3) field name doesn't match type name (alias)
name = p.string() name = p.string()
alias = true
fallthrough fallthrough
default: default:
if !exported(name) { if !exported(name) {
pkg = p.pkg() pkg = p.pkg()
} }
} }
return pkg, name return
} }
func (p *importer) paramList() (*types.Tuple, bool) { func (p *importer) paramList() (*types.Tuple, bool) {
......
...@@ -163,23 +163,19 @@ func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName { ...@@ -163,23 +163,19 @@ func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName {
return &TypeName{object{nil, pos, pkg, name, typ, 0, token.NoPos}} return &TypeName{object{nil, pos, pkg, name, typ, 0, token.NoPos}}
} }
// IsAlias reports whether obj is an alias name for a type.
func (obj *TypeName) IsAlias() bool { func (obj *TypeName) IsAlias() bool {
switch t := obj.typ.(type) { switch t := obj.typ.(type) {
case nil: case nil:
return false return false
case *Basic: case *Basic:
// It would seem that we should be able to look for different names here; // Any user-defined type name for a basic type is an alias for a
// but the names of universeByte/Rune are "byte" and "rune", respectively. // basic type (because basic types are pre-declared in the Universe
// We do this so that we get better error messages. However, general alias // scope, outside any package scope), and so is any type name with
// types don't have that name information and thus behave differently when // a different name than the name of the basic type it refers to.
// reporting errors (we won't see the alias name, only the original name). // Additionaly, we need to look for "byte" and "rune" because they
// Maybe we should remove the special handling for the predeclared types // are aliases but have the same names (for better error messages).
// as well to be consistent (at the cost of slightly less clear error return obj.pkg != nil || t.name != obj.name || t == universeByte || t == universeRune
// messages when byte/rune are involved).
// This also plays out in the implementation of the Identical(Type, Type)
// predicate.
// TODO(gri) consider possible clean up
return t == universeByte || t == universeRune
case *Named: case *Named:
return obj != t.obj return obj != t.obj
default: default:
......
...@@ -21,16 +21,23 @@ func TestIsAlias(t *testing.T) { ...@@ -21,16 +21,23 @@ func TestIsAlias(t *testing.T) {
} }
// various other types // various other types
t0 := NewTypeName(0, nil, "t0", nil) pkg := NewPackage("p", "p")
check(t0, false) // no type yet t1 := NewTypeName(0, pkg, "t1", nil)
t1 := NewTypeName(0, nil, "t1", nil)
n1 := NewNamed(t1, new(Struct), nil) n1 := NewNamed(t1, new(Struct), nil)
check(t1, false) // type name refers to named type and vice versa for _, test := range []struct {
name *TypeName
t2 := NewTypeName(0, nil, "t2", new(Interface)) alias bool
check(t2, true) // type name refers to unnamed type }{
{NewTypeName(0, nil, "t0", nil), false}, // no type yet
t3 := NewTypeName(0, nil, "t3", n1) {NewTypeName(0, pkg, "t0", nil), false}, // no type yet
check(t3, true) // type name refers to named type with different type name (true alias) {t1, false}, // type name refers to named type and vice versa
{NewTypeName(0, nil, "t2", new(Interface)), true}, // type name refers to unnamed type
{NewTypeName(0, pkg, "t3", n1), true}, // type name refers to named type with different type name
{NewTypeName(0, nil, "t4", Typ[Int32]), true}, // type name refers to basic type with different name
{NewTypeName(0, nil, "int32", Typ[Int32]), false}, // type name refers to basic type with same name
{NewTypeName(0, pkg, "int32", Typ[Int32]), true}, // type name is declared in user-defined package (outside Universe)
{NewTypeName(0, nil, "rune", Typ[Rune]), true}, // type name refers to basic type rune which is an alias already
} {
check(test.name, test.alias)
}
} }
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