Commit b2386dff authored by Robert Griesemer's avatar Robert Griesemer

[dev.typealias] cmd/compile: type-check type alias declarations

Known issues:
- needs many more tests
- duplicate method declarations via type alias names are not detected
- type alias cycle error messages need to be improved
- need to review setup of byte/rune type aliases

For #18130.

Change-Id: Icc2fefad6214e5e56539a9dcb3fe537bf58029f8
Reviewed-on: https://go-review.googlesource.com/35121
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarMatthew Dempsky <mdempsky@google.com>
parent ac8421f9
...@@ -352,8 +352,8 @@ func export(out *bufio.Writer, trace bool) int { ...@@ -352,8 +352,8 @@ func export(out *bufio.Writer, trace bool) int {
p.tracef("\n") p.tracef("\n")
} }
if sym.Flags&SymAlias != 0 { if sym.isAlias() {
Fatalf("exporter: unexpected alias %v in inlined function body", sym) Fatalf("exporter: unexpected type alias %v in inlined function body", sym)
} }
p.obj(sym) p.obj(sym)
...@@ -486,8 +486,7 @@ func (p *exporter) obj(sym *Sym) { ...@@ -486,8 +486,7 @@ func (p *exporter) obj(sym *Sym) {
Fatalf("exporter: export of incomplete type %v", sym) Fatalf("exporter: export of incomplete type %v", sym)
} }
const alias = false // TODO(gri) fix this if sym.isAlias() {
if alias {
p.tag(aliasTag) p.tag(aliasTag)
p.pos(n) p.pos(n)
p.qualifiedName(sym) p.qualifiedName(sym)
......
...@@ -316,10 +316,10 @@ func (p *importer) obj(tag int) { ...@@ -316,10 +316,10 @@ func (p *importer) obj(tag int) {
importconst(sym, idealType(typ), nodlit(val)) importconst(sym, idealType(typ), nodlit(val))
case aliasTag: case aliasTag:
// TODO(gri) hook up type alias
p.pos() p.pos()
p.qualifiedName() sym := p.qualifiedName()
p.typ() typ := p.typ()
importalias(sym, typ)
case typeTag: case typeTag:
p.typ() p.typ()
...@@ -576,7 +576,7 @@ func (p *importer) fieldList() (fields []*Field) { ...@@ -576,7 +576,7 @@ func (p *importer) fieldList() (fields []*Field) {
func (p *importer) field() *Field { func (p *importer) field() *Field {
p.pos() p.pos()
sym := p.fieldName() sym, alias := p.fieldName()
typ := p.typ() typ := p.typ()
note := p.string() note := p.string()
...@@ -589,8 +589,8 @@ func (p *importer) field() *Field { ...@@ -589,8 +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 { } else if alias {
// anonymous field: we have an explicit name because it's an alias // anonymous field: we have an explicit name because it's a type alias
f.Embedded = 1 f.Embedded = 1
} }
...@@ -625,15 +625,15 @@ func (p *importer) method() *Field { ...@@ -625,15 +625,15 @@ func (p *importer) method() *Field {
return f return f
} }
func (p *importer) fieldName() *Sym { func (p *importer) fieldName() (*Sym, bool) {
name := p.string() name := p.string()
if p.version == 0 && name == "_" { if p.version == 0 && name == "_" {
// version 0 didn't export a package for _ field names // 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), false
} }
pkg := localpkg pkg := localpkg
var flag SymFlags alias := false
switch name { switch name {
case "": case "":
// 1) field name matches base type name and is exported: nothing to do // 1) field name matches base type name and is exported: nothing to do
...@@ -644,16 +644,14 @@ func (p *importer) fieldName() *Sym { ...@@ -644,16 +644,14 @@ func (p *importer) fieldName() *Sym {
case "@": case "@":
// 3) field name doesn't match base type name (alias name): need name and possibly package // 3) field name doesn't match base type name (alias name): need name and possibly package
name = p.string() name = p.string()
flag = SymAlias alias = true
fallthrough fallthrough
default: default:
if !exportname(name) { if !exportname(name) {
pkg = p.pkg() pkg = p.pkg()
} }
} }
sym := pkg.Lookup(name) return pkg.Lookup(name), alias
sym.Flags |= flag
return sym
} }
func (p *importer) methodName() *Sym { func (p *importer) methodName() *Sym {
......
...@@ -695,10 +695,20 @@ func typedcl0(s *Sym) *Node { ...@@ -695,10 +695,20 @@ func typedcl0(s *Sym) *Node {
// node n, which was returned by typedcl0 // node n, which was returned by typedcl0
// is being declared to have uncompiled type t. // is being declared to have uncompiled type t.
// return the ODCLTYPE node to use. // returns the ODCLTYPE node to use.
func typedcl1(n *Node, t *Node, local bool) *Node { func typedcl1(n *Node, t *Node, pragma Pragma, alias bool) *Node {
n.Name.Param.Ntype = t if pragma != 0 && alias {
n.Local = local yyerror("cannot specify directive with type alias")
pragma = 0
}
n.Local = true
p := n.Name.Param
p.Ntype = t
p.Pragma = pragma
p.Alias = alias
return nod(ODCLTYPE, n, nil) return nod(ODCLTYPE, n, nil)
} }
......
...@@ -45,8 +45,8 @@ func exportsym(n *Node) { ...@@ -45,8 +45,8 @@ func exportsym(n *Node) {
fmt.Printf("export symbol %v\n", n.Sym) fmt.Printf("export symbol %v\n", n.Sym)
} }
// Ensure original object is on exportlist before aliases. // Ensure original types are on exportlist before type aliases.
if n.Sym.Flags&SymAlias != 0 { if n.Sym.isAlias() {
exportlist = append(exportlist, n.Sym.Def) exportlist = append(exportlist, n.Sym.Def)
} }
...@@ -348,6 +348,27 @@ func importvar(s *Sym, t *Type) { ...@@ -348,6 +348,27 @@ func importvar(s *Sym, t *Type) {
} }
} }
// importalias declares symbol s as an imported type alias with type t.
func importalias(s *Sym, t *Type) {
importsym(s, OTYPE)
if s.Def != nil && s.Def.Op == OTYPE {
if eqtype(t, s.Def.Type) {
return
}
yyerror("inconsistent definition for type alias %v during import\n\t%v (in %q)\n\t%v (in %q)", s, s.Def.Type, s.Importdef.Path, t, importpkg.Path)
}
n := newname(s)
n.Op = OTYPE
s.Importdef = importpkg
n.Type = t
declare(n, PEXTERN)
if Debug['E'] != 0 {
fmt.Printf("import type %v = %L\n", s, t)
}
}
func dumpasmhdr() { func dumpasmhdr() {
b, err := bio.Create(asmhdr) b, err := bio.Create(asmhdr)
if err != nil { if err != nil {
......
...@@ -63,9 +63,12 @@ const ( ...@@ -63,9 +63,12 @@ const (
SymSiggen SymSiggen
SymAsm SymAsm
SymAlgGen SymAlgGen
SymAlias // alias, original is Sym.Def.Sym
) )
func (sym *Sym) isAlias() bool {
return sym.Def != nil && sym.Def.Sym != sym
}
// The Class of a variable/function describes the "storage class" // The Class of a variable/function describes the "storage class"
// of a variable or function. During parsing, storage classes are // of a variable or function. During parsing, storage classes are
// called declaration contexts. // called declaration contexts.
...@@ -87,7 +90,7 @@ const ( ...@@ -87,7 +90,7 @@ const (
// of the compilers arrays. // of the compilers arrays.
// //
// typedef struct // typedef struct
// { // must not move anything // { // must not move anything
// uchar array[8]; // pointer to data // uchar array[8]; // pointer to data
// uchar nel[4]; // number of elements // uchar nel[4]; // number of elements
// uchar cap[4]; // allocated number of elements // uchar cap[4]; // allocated number of elements
...@@ -104,7 +107,7 @@ var sizeof_Array int // runtime sizeof(Array) ...@@ -104,7 +107,7 @@ var sizeof_Array int // runtime sizeof(Array)
// of the compilers strings. // of the compilers strings.
// //
// typedef struct // typedef struct
// { // must not move anything // { // must not move anything
// uchar array[8]; // pointer to data // uchar array[8]; // pointer to data
// uchar nel[4]; // number of elements // uchar nel[4]; // number of elements
// } String; // } String;
......
...@@ -927,7 +927,7 @@ func mkpackage(pkgname string) { ...@@ -927,7 +927,7 @@ func mkpackage(pkgname string) {
continue continue
} }
if s.Def.Sym != s && s.Flags&SymAlias == 0 { if s.isAlias() {
// throw away top-level name left over // throw away top-level name left over
// from previous import . "x" // from previous import . "x"
if s.Def.Name != nil && s.Def.Name.Pack != nil && !s.Def.Name.Pack.Used && nsyntaxerrors == 0 { if s.Def.Name != nil && s.Def.Name.Pack != nil && !s.Def.Name.Pack.Used && nsyntaxerrors == 0 {
......
...@@ -177,21 +177,12 @@ func (p *noder) constDecl(decl *syntax.ConstDecl) []*Node { ...@@ -177,21 +177,12 @@ func (p *noder) constDecl(decl *syntax.ConstDecl) []*Node {
} }
func (p *noder) typeDecl(decl *syntax.TypeDecl) *Node { func (p *noder) typeDecl(decl *syntax.TypeDecl) *Node {
if decl.Alias {
yyerror("type alias declarations unimplemented")
}
name := typedcl0(p.name(decl.Name)) name := typedcl0(p.name(decl.Name))
pragma := Pragma(decl.Pragma)
if pragma != 0 && decl.Alias {
yyerror("cannot specify directive with type alias")
pragma = 0
}
name.Name.Param.Pragma = pragma
// decl.Type may be nil but in that case we got a syntax error during parsing
typ := p.typeExprOrNil(decl.Type) typ := p.typeExprOrNil(decl.Type)
return typedcl1(name, typ, true) return typedcl1(name, typ, Pragma(decl.Pragma), decl.Alias)
} }
func (p *noder) declNames(names []*syntax.Name) []*Node { func (p *noder) declNames(names []*syntax.Name) []*Node {
......
...@@ -27,7 +27,7 @@ type Node struct { ...@@ -27,7 +27,7 @@ type Node struct {
// func // func
Func *Func Func *Func
// ONAME // ONAME, OTYPE, OPACK, OLABEL, some OLITERAL
Name *Name Name *Name
Sym *Sym // various Sym *Sym // various
...@@ -59,8 +59,8 @@ type Node struct { ...@@ -59,8 +59,8 @@ type Node struct {
Noescape bool // func arguments do not escape; TODO(rsc): move Noescape to Func struct (see CL 7360) Noescape bool // func arguments do not escape; TODO(rsc): move Noescape to Func struct (see CL 7360)
Walkdef uint8 // tracks state during typecheckdef; 2 == loop detected Walkdef uint8 // tracks state during typecheckdef; 2 == loop detected
Typecheck uint8 // tracks state during typechecking; 2 == loop detected Typecheck uint8 // tracks state during typechecking; 2 == loop detected
Local bool Local bool // type created in this file (see also Type.Local); TODO(gri): move this into flags
IsStatic bool // whether this Node will be converted to purely static data IsStatic bool // whether this Node will be converted to purely static data
Initorder uint8 Initorder uint8
Used bool // for variable/label declared and not used error Used bool // for variable/label declared and not used error
Isddd bool // is the argument variadic Isddd bool // is the argument variadic
...@@ -180,14 +180,14 @@ func (n *Node) SetIota(x int64) { ...@@ -180,14 +180,14 @@ func (n *Node) SetIota(x int64) {
n.Xoffset = x n.Xoffset = x
} }
// Name holds Node fields used only by named nodes (ONAME, OPACK, OLABEL, some OLITERAL). // Name holds Node fields used only by named nodes (ONAME, OTYPE, OPACK, OLABEL, some OLITERAL).
type Name struct { type Name struct {
Pack *Node // real package for import . names Pack *Node // real package for import . names
Pkg *Pkg // pkg for OPACK nodes Pkg *Pkg // pkg for OPACK nodes
Heapaddr *Node // temp holding heap address of param (could move to Param?) Heapaddr *Node // temp holding heap address of param (could move to Param?)
Defn *Node // initializing assignment Defn *Node // initializing assignment
Curfn *Node // function for local variables Curfn *Node // function for local variables
Param *Param // additional fields for ONAME Param *Param // additional fields for ONAME, OTYPE
Decldepth int32 // declaration loop depth, increased for every loop or label Decldepth int32 // declaration loop depth, increased for every loop or label
Vargen int32 // unique name for ONAME within a function. Function outputs are numbered starting at one. Vargen int32 // unique name for ONAME within a function. Function outputs are numbered starting at one.
Funcdepth int32 Funcdepth int32
...@@ -280,10 +280,11 @@ type Param struct { ...@@ -280,10 +280,11 @@ type Param struct {
Innermost *Node Innermost *Node
Outer *Node Outer *Node
// OTYPE pragmas // OTYPE
// //
// TODO: Should Func pragmas also be stored on the Name? // TODO: Should Func pragmas also be stored on the Name?
Pragma Pragma Pragma Pragma
Alias bool // node is alias for Ntype
} }
// Func holds Node fields used only with function-like nodes. // Func holds Node fields used only with function-like nodes.
...@@ -382,7 +383,7 @@ const ( ...@@ -382,7 +383,7 @@ const (
ODCLFUNC // func f() or func (r) f() ODCLFUNC // func f() or func (r) f()
ODCLFIELD // struct field, interface field, or func/method argument/return value. ODCLFIELD // struct field, interface field, or func/method argument/return value.
ODCLCONST // const pi = 3.14 ODCLCONST // const pi = 3.14
ODCLTYPE // type Int int ODCLTYPE // type Int int or type Int = int
ODELETE // delete(Left, Right) ODELETE // delete(Left, Right)
ODOT // Left.Sym (Left is of struct type) ODOT // Left.Sym (Left is of struct type)
......
...@@ -3578,8 +3578,6 @@ func typecheckdeftype(n *Node) { ...@@ -3578,8 +3578,6 @@ func typecheckdeftype(n *Node) {
// copy new type and clear fields // copy new type and clear fields
// that don't come along. // that don't come along.
// anything zeroed here must be zeroed in
// typedcl2 too.
copytype(n, t) copytype(n, t)
ret: ret:
...@@ -3758,12 +3756,29 @@ func typecheckdef(n *Node) *Node { ...@@ -3758,12 +3756,29 @@ func typecheckdef(n *Node) *Node {
n.Name.Defn = typecheck(n.Name.Defn, Etop) // fills in n->type n.Name.Defn = typecheck(n.Name.Defn, Etop) // fills in n->type
case OTYPE: case OTYPE:
if p := n.Name.Param; p.Alias {
// Type alias declaration: Simply use the rhs type - no need
// to create a new type.
// If we have a syntax error, p.Ntype may be nil.
if p.Ntype != nil {
p.Ntype = typecheck(p.Ntype, Etype)
n.Type = p.Ntype.Type
if n.Type == nil {
n.Diag = true
goto ret
}
n.Sym.Def = p.Ntype
}
break
}
// regular type declaration
if Curfn != nil { if Curfn != nil {
defercheckwidth() defercheckwidth()
} }
n.Walkdef = 1 n.Walkdef = 1
n.Type = typ(TFORW) n.Type = typ(TFORW)
n.Type.Sym = n.Sym n.Type.Sym = n.Sym // TODO(gri) this also happens in typecheckdeftype(n) - where should it happen?
nerrors0 := nerrors nerrors0 := nerrors
typecheckdeftype(n) typecheckdeftype(n)
if n.Type.Etype == TFORW && nerrors > nerrors0 { if n.Type.Etype == TFORW && nerrors > nerrors0 {
...@@ -3771,7 +3786,6 @@ func typecheckdef(n *Node) *Node { ...@@ -3771,7 +3786,6 @@ func typecheckdef(n *Node) *Node {
// but it was reported. Silence future errors. // but it was reported. Silence future errors.
n.Type.Broke = true n.Type.Broke = true
} }
if Curfn != nil { if Curfn != nil {
resumecheckwidth() resumecheckwidth()
} }
......
...@@ -6,11 +6,6 @@ ...@@ -6,11 +6,6 @@
// Test basic restrictions on type aliases. // Test basic restrictions on type aliases.
// The compiler doesn't implement type aliases yet,
// so for now we get the same error (unimplemented)
// everywhere, OR-ed into the ERROR checks.
// TODO(gri) remove the need for "unimplemented"
package p package p
import ( import (
...@@ -18,41 +13,87 @@ import ( ...@@ -18,41 +13,87 @@ import (
. "reflect" . "reflect"
) )
type T0 struct{}
// Valid type alias declarations. // Valid type alias declarations.
type _ = int // ERROR "unimplemented" type _ = T0
type _ = struct{} // ERROR "unimplemented" type _ = int
type _ = reflect.Value // ERROR "unimplemented" type _ = struct{}
type _ = Value // ERROR "unimplemented" type _ = reflect.Value
type _ = Value
type ( type (
a1 = int // ERROR "unimplemented" A0 = T0
a2 = struct{} // ERROR "unimplemented" A1 = int
a3 = reflect.Value // ERROR "unimplemented" A2 = struct{}
a4 = Value // ERROR "unimplemented" A3 = reflect.Value
A4 = Value
A5 = Value
N0 A0
) )
// Methods can be declared on the original named type and the alias.
func (T0) m1() {}
func (A0) m1() {} // TODO(gri) this should be an error
func (A0) m2() {}
// Type aliases and the original type name can be used interchangeably.
var _ A0 = T0{}
var _ T0 = A0{}
// But aliases and original types cannot be used with new types based on them.
var _ N0 = T0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment"
var _ N0 = A0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment"
var _ A5 = Value{}
var _ interface {
m1()
m2()
} = T0{}
var _ interface {
m1()
m2()
} = A0{}
func _() { func _() {
type _ = int // ERROR "unimplemented" type _ = T0
type _ = struct{} // ERROR "unimplemented" type _ = int
type _ = reflect.Value // ERROR "unimplemented" type _ = struct{}
type _ = Value // ERROR "unimplemented" type _ = reflect.Value
type _ = Value
type ( type (
a1 = int // ERROR "unimplemented" A0 = T0
a2 = struct{} // ERROR "unimplemented" A1 = int
a3 = reflect.Value // ERROR "unimplemented" A2 = struct{}
a4 = Value // ERROR "unimplemented" A3 = reflect.Value
A4 = Value
A5 Value
N0 A0
) )
var _ A0 = T0{}
var _ T0 = A0{}
var _ N0 = T0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment"
var _ N0 = A0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment"
var _ A5 = Value{} // ERROR "cannot use reflect\.Value literal \(type reflect.Value\) as type A5 in assignment"
} }
// Invalid type alias declarations. // Invalid type alias declarations.
type _ = reflect.ValueOf // ERROR "reflect.ValueOf is not a type|unimplemented" type _ = reflect.ValueOf // ERROR "reflect.ValueOf is not a type"
func (A1) m() {} // ERROR "cannot define new methods on non-local type int"
type B1 = struct{}
type b1 = struct{} // ERROR "unimplemented" func (B1) m() {} // ERROR "invalid receiver type"
func (b1) m() {} // disabled ERROR "invalid receiver type"
// TODO(gri) expand // TODO(gri) expand
// It appears that type-checking exits after some more severe errors, so we may
// need more test files.
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