diff options
65 files changed, 1335 insertions, 841 deletions
@@ -1 +1 @@ -go1.8.3
\ No newline at end of file +go1.8.3.typealias
\ No newline at end of file diff --git a/doc/go_spec.html b/doc/go_spec.html index 5872eefb03..c71126d25d 100644 --- a/doc/go_spec.html +++ b/doc/go_spec.html @@ -1,6 +1,6 @@ <!--{ "Title": "The Go Programming Language Specification", - "Subtitle": "Version of November 18, 2016", + "Subtitle": "Version of January 31, 2017", "Path": "/ref/spec" }--> @@ -738,7 +738,7 @@ The method set of any other type <code>T</code> consists of all The method set of the corresponding <a href="#Pointer_types">pointer type</a> <code>*T</code> is the set of all methods declared with receiver <code>*T</code> or <code>T</code> (that is, it also contains the method set of <code>T</code>). -Further rules apply to structs containing anonymous fields, as described +Further rules apply to structs containing embedded fields, as described in the section on <a href="#Struct_types">struct types</a>. Any other type has an empty method set. In a method set, each method must have a @@ -947,16 +947,16 @@ Moreover, the inner slices must be initialized individually. <p> A struct is a sequence of named elements, called fields, each of which has a name and a type. Field names may be specified explicitly (IdentifierList) or -implicitly (AnonymousField). +implicitly (EmbeddedField). Within a struct, non-<a href="#Blank_identifier">blank</a> field names must be <a href="#Uniqueness_of_identifiers">unique</a>. </p> <pre class="ebnf"> -StructType = "struct" "{" { FieldDecl ";" } "}" . -FieldDecl = (IdentifierList Type | AnonymousField) [ Tag ] . -AnonymousField = [ "*" ] TypeName . -Tag = string_lit . +StructType = "struct" "{" { FieldDecl ";" } "}" . +FieldDecl = (IdentifierList Type | EmbeddedField) [ Tag ] . +EmbeddedField = [ "*" ] TypeName . +Tag = string_lit . </pre> <pre> @@ -974,16 +974,15 @@ struct { </pre> <p> -A field declared with a type but no explicit field name is an <i>anonymous field</i>, -also called an <i>embedded</i> field or an embedding of the type in the struct. -An embedded type must be specified as +A field declared with a type but no explicit field name is called an <i>embedded field</i>. +An embedded field must be specified as a type name <code>T</code> or as a pointer to a non-interface type name <code>*T</code>, and <code>T</code> itself may not be a pointer type. The unqualified type name acts as the field name. </p> <pre> -// A struct with four anonymous fields of type T1, *T2, P.T3 and *P.T4 +// A struct with four embedded fields of types T1, *T2, P.T3 and *P.T4 struct { T1 // field name is T1 *T2 // field name is T2 @@ -1000,15 +999,15 @@ in a struct type: <pre> struct { - T // conflicts with anonymous field *T and *P.T - *T // conflicts with anonymous field T and *P.T - *P.T // conflicts with anonymous field T and *T + T // conflicts with embedded field *T and *P.T + *T // conflicts with embedded field T and *P.T + *P.T // conflicts with embedded field T and *T } </pre> <p> A field or <a href="#Method_declarations">method</a> <code>f</code> of an -anonymous field in a struct <code>x</code> is called <i>promoted</i> if +embedded field in a struct <code>x</code> is called <i>promoted</i> if <code>x.f</code> is a legal <a href="#Selectors">selector</a> that denotes that field or method <code>f</code>. </p> @@ -1025,7 +1024,7 @@ promoted methods are included in the method set of the struct as follows: </p> <ul> <li> - If <code>S</code> contains an anonymous field <code>T</code>, + If <code>S</code> contains an embedded field <code>T</code>, the <a href="#Method_sets">method sets</a> of <code>S</code> and <code>*S</code> both include promoted methods with receiver <code>T</code>. The method set of <code>*S</code> also @@ -1033,7 +1032,7 @@ promoted methods are included in the method set of the struct as follows: </li> <li> - If <code>S</code> contains an anonymous field <code>*T</code>, + If <code>S</code> contains an embedded field <code>*T</code>, the method sets of <code>S</code> and <code>*S</code> both include promoted methods with receiver <code>T</code> or <code>*T</code>. @@ -1434,8 +1433,8 @@ literal structure and corresponding components have identical types. In detail: <li>Two struct types are identical if they have the same sequence of fields, and if corresponding fields have the same names, and identical types, and identical tags. - Two anonymous fields are considered to have the same name. Lower-case field - names from different packages are always different.</li> + <a href="#Exported_identifiers">Non-exported</a> field names from different + packages are always different.</li> <li>Two pointer types are identical if they have identical base types.</li> @@ -1445,8 +1444,9 @@ literal structure and corresponding components have identical types. In detail: Parameter and result names are not required to match.</li> <li>Two interface types are identical if they have the same set of methods - with the same names and identical function types. Lower-case method names from - different packages are always different. The order of the methods is irrelevant.</li> + with the same names and identical function types. + <a href="#Exported_identifiers">Non-exported</a> method names from different + packages are always different. The order of the methods is irrelevant.</li> <li>Two map types are identical if they have identical key and value types.</li> @@ -1891,7 +1891,7 @@ type NewMutex Mutex type PtrMutex *Mutex // The method set of *PrintableMutex contains the methods -// Lock and Unlock bound to its anonymous field Mutex. +// Lock and Unlock bound to its embedded field Mutex. type PrintableMutex struct { Mutex } @@ -2492,13 +2492,13 @@ If <code>x</code> is a package name, see the section on A selector <code>f</code> may denote a field or method <code>f</code> of a type <code>T</code>, or it may refer to a field or method <code>f</code> of a nested -<a href="#Struct_types">anonymous field</a> of <code>T</code>. -The number of anonymous fields traversed +<a href="#Struct_types">embedded field</a> of <code>T</code>. +The number of embedded fields traversed to reach <code>f</code> is called its <i>depth</i> in <code>T</code>. The depth of a field or method <code>f</code> declared in <code>T</code> is zero. The depth of a field or method <code>f</code> declared in -an anonymous field <code>A</code> in <code>T</code> is the +an embedded field <code>A</code> in <code>T</code> is the depth of <code>f</code> in <code>A</code> plus one. </p> diff --git a/src/cmd/compile/internal/gc/align.go b/src/cmd/compile/internal/gc/align.go index eee801fb8e..1dd04e349d 100644 --- a/src/cmd/compile/internal/gc/align.go +++ b/src/cmd/compile/internal/gc/align.go @@ -74,7 +74,13 @@ func widstruct(errtype *Type, t *Type, o int64, flag int) int64 { lastzero = o } o += w - if o >= Thearch.MAXWIDTH { + maxwidth := Thearch.MAXWIDTH + // On 32-bit systems, reflect tables impose an additional constraint + // that each field start offset must fit in 31 bits. + if maxwidth < 1<<32 { + maxwidth = 1<<31 - 1 + } + if o >= maxwidth { yyerror("type %L too large", errtype) o = 8 // small but nonzero } diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go index ffc5419708..5f14e0152b 100644 --- a/src/cmd/compile/internal/gc/bexport.go +++ b/src/cmd/compile/internal/gc/bexport.go @@ -140,11 +140,12 @@ const debugFormat = false // default: false const forceObjFileStability = true // Current export format version. Increase with each format change. -// 3: added aliasTag and export of aliases -// 2: removed unused bool in ODCL export +// 4: type name objects support type aliases, uses aliasTag +// 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 // 0: Go1.7 encoding -const exportVersion = 3 +const exportVersion = 4 // exportInlined enables the export of inlined function bodies and related // dependencies. The compiler should work w/o any loss of functionality with @@ -351,8 +352,8 @@ func export(out *bufio.Writer, trace bool) int { p.tracef("\n") } - if sym.Flags&SymAlias != 0 { - Fatalf("exporter: unexpected alias %v in inlined function body", sym) + if sym.isAlias() { + Fatalf("exporter: unexpected type alias %v in inlined function body", sym) } p.obj(sym) @@ -446,30 +447,6 @@ func unidealType(typ *Type, val Val) *Type { } 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 // may be re-exported via an exported alias or as dependencies in // exported inlined function bodies. Thus, exported object names @@ -509,7 +486,13 @@ func (p *exporter) obj(sym *Sym) { Fatalf("exporter: export of incomplete type %v", sym) } - p.tag(typeTag) + if sym.isAlias() { + p.tag(aliasTag) + p.pos(n) + p.qualifiedName(sym) + } else { + p.tag(typeTag) + } p.typ(t) case ONAME: @@ -868,19 +851,29 @@ func (p *exporter) methodList(t *Type) { func (p *exporter) method(m *Field) { p.pos(m.Nname) - p.fieldName(m) + p.methodName(m.Sym) p.paramList(m.Type.Params(), 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) { name := t.Sym.Name if t.Embedded != 0 { - name = "" // anonymous field - if bname := basetypeName(t.Type); bname != "" && !exportname(bname) { - // anonymous field with unexported base type name - name = "?" // unexported name to force export of package + // anonymous field - we distinguish between 3 cases: + // 1) field name matches base type name and is exported + // 2) field name matches base type name and is not exported + // 3) field name doesn't match base type name (alias name) + bname := basetypeName(t.Type) + if name == bname { + if exportname(name) { + name = "" // 1) we don't need to know the field name or package + } 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) @@ -889,16 +882,23 @@ 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 { s := t.Sym if s == nil && t.IsPtr() { s = t.Elem().Sym // deref } - // s should exist, but be conservative if s != nil { return s.Name } - return "" + return "" // unnamed type } func (p *exporter) paramList(params *Type, numbered bool) { @@ -1797,7 +1797,7 @@ const ( nilTag unknownTag // not used by gc (only appears in packages with errors) - // Aliases + // Type aliases aliasTag ) @@ -1835,7 +1835,7 @@ var tagString = [...]string{ -nilTag: "nil", -unknownTag: "unknown", - // Aliases + // Type aliases -aliasTag: "alias", } @@ -1889,7 +1889,7 @@ func predeclared() []*Type { Types[TCOMPLEX128], Types[TSTRING], - // aliases + // basic type aliases bytetype, runetype, diff --git a/src/cmd/compile/internal/gc/bimport.go b/src/cmd/compile/internal/gc/bimport.go index 94c1184138..752f65be42 100644 --- a/src/cmd/compile/internal/gc/bimport.go +++ b/src/cmd/compile/internal/gc/bimport.go @@ -86,10 +86,10 @@ func Import(in *bufio.Reader) { // read version specific flags - extend as necessary switch p.version { - // case 4: + // case 5: // ... // fallthrough - case 3, 2, 1: + case 4, 3, 2, 1: p.debugFormat = p.rawStringln(p.rawByte()) == "debug" p.trackAllTypes = p.bool() p.posInfoFormat = p.bool() @@ -317,6 +317,12 @@ func (p *importer) obj(tag int) { val := p.value(typ) importconst(sym, idealType(typ), nodlit(val)) + case aliasTag: + p.pos() + sym := p.qualifiedName() + typ := p.typ() + importalias(sym, typ) + case typeTag: p.typ() @@ -356,17 +362,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: formatErrorf("unexpected object (tag = %d)", tag) } @@ -473,14 +468,7 @@ func (p *importer) typ() *Type { result := p.paramList() nointerface := p.bool() - base := recv[0].Type - star := false - if base.IsPtr() { - base = base.Elem() - star = true - } - - n := methodname0(sym, star, base.Sym) + n := newfuncname(methodname(sym, recv[0].Type)) n.Type = functypefield(recv[0], params, result) checkwidth(n.Type) addmethod(sym, n.Type, false, nointerface) @@ -583,19 +571,22 @@ func (p *importer) fieldList() (fields []*Field) { func (p *importer) field() *Field { p.pos() - sym := p.fieldName() + sym, alias := p.fieldName() typ := p.typ() note := p.string() f := newField() 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 if s == nil && typ.IsPtr() { s = typ.Elem().Sym // deref } sym = sym.Pkg.Lookup(s.Name) f.Embedded = 1 + } else if alias { + // anonymous field: we have an explicit name because it's a type alias + f.Embedded = 1 } f.Sym = sym @@ -618,7 +609,7 @@ func (p *importer) methodList() (methods []*Field) { func (p *importer) method() *Field { p.pos() - sym := p.fieldName() + sym := p.methodName() params := p.paramList() result := p.paramList() @@ -629,18 +620,44 @@ func (p *importer) method() *Field { return f } -func (p *importer) fieldName() *Sym { +func (p *importer) fieldName() (*Sym, bool) { name := p.string() 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 - return builtinpkg.Lookup(name) + return builtinpkg.Lookup(name), false } pkg := localpkg - if name != "" && !exportname(name) { - if name == "?" { - name = "" + alias := false + switch name { + case "": + // 1) field name matches base type name and is exported: nothing to do + case "?": + // 2) field name matches base type name and is not exported: need package + name = "" + pkg = p.pkg() + case "@": + // 3) field name doesn't match base type name (alias name): need name and possibly package + name = p.string() + alias = true + fallthrough + default: + if !exportname(name) { + pkg = p.pkg() } + } + return pkg.Lookup(name), alias +} + +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() } return pkg.Lookup(name) diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go index 3cdd71df0d..856a7faced 100644 --- a/src/cmd/compile/internal/gc/dcl.go +++ b/src/cmd/compile/internal/gc/dcl.go @@ -519,10 +519,6 @@ func funchdr(n *Node) { Fatalf("funchdr: dclcontext = %d", dclcontext) } - if Ctxt.Flag_dynlink && importpkg == nil && n.Func.Nname != nil { - makefuncsym(n.Func.Nname.Sym) - } - dclcontext = PAUTO funcstart(n) @@ -695,10 +691,20 @@ func typedcl0(s *Sym) *Node { // node n, which was returned by typedcl0 // is being declared to have uncompiled type t. -// return the ODCLTYPE node to use. -func typedcl1(n *Node, t *Node, local bool) *Node { - n.Name.Param.Ntype = t - n.Local = local +// returns the ODCLTYPE node to use. +func typedcl1(n *Node, t *Node, pragma Pragma, alias bool) *Node { + if pragma != 0 && alias { + 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) } @@ -1153,19 +1159,19 @@ bad: return nil } -func methodname(n *Node, t *Node) *Node { +// methodname is a misnomer because this now returns a Sym, rather +// than an ONAME. +// TODO(mdempsky): Reconcile with methodsym. +func methodname(s *Sym, recv *Type) *Sym { star := false - if t.Op == OIND { + if recv.IsPtr() { star = true - t = t.Left + recv = recv.Elem() } - return methodname0(n.Sym, star, t.Sym) -} - -func methodname0(s *Sym, star bool, tsym *Sym) *Node { + tsym := recv.Sym if tsym == nil || isblanksym(s) { - return newfuncname(s) + return s } var p string @@ -1181,14 +1187,13 @@ func methodname0(s *Sym, star bool, tsym *Sym) *Node { s = Pkglookup(p, tsym.Pkg) } - return newfuncname(s) + return s } // Add a method, declared as a function. // - msym is the method symbol // - t is function type (with receiver) func addmethod(msym *Sym, t *Type, local, nointerface bool) { - // get field sym if msym == nil { Fatalf("no method symbol") } @@ -1309,7 +1314,7 @@ func funcsym(s *Sym) *Sym { s1 := Pkglookup(s.Name+"·f", s.Pkg) if !Ctxt.Flag_dynlink && s1.Def == nil { s1.Def = newfuncname(s1) - s1.Def.Func.Shortname = newname(s) + s1.Def.Func.Shortname = s funcsyms = append(funcsyms, s1.Def) } s.Fsym = s1 @@ -1326,8 +1331,11 @@ func makefuncsym(s *Sym) { return } s1 := funcsym(s) + if s1.Def != nil { + return + } s1.Def = newfuncname(s1) - s1.Def.Func.Shortname = newname(s) + s1.Def.Func.Shortname = s funcsyms = append(funcsyms, s1.Def) } diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go index b4c15e40b1..58b2bf8121 100644 --- a/src/cmd/compile/internal/gc/export.go +++ b/src/cmd/compile/internal/gc/export.go @@ -45,8 +45,8 @@ func exportsym(n *Node) { fmt.Printf("export symbol %v\n", n.Sym) } - // Ensure original object is on exportlist before aliases. - if n.Sym.Flags&SymAlias != 0 { + // Ensure original types are on exportlist before type aliases. + if n.Sym.isAlias() { exportlist = append(exportlist, n.Sym.Def) } @@ -83,7 +83,7 @@ func autoexport(n *Node, ctxt Class) { if (ctxt != PEXTERN && ctxt != PFUNC) || dclcontext != PEXTERN { return } - if n.Name.Param != nil && n.Name.Param.Ntype != nil && n.Name.Param.Ntype.Op == OTFUNC && n.Name.Param.Ntype.Left != nil { // method + if n.Type != nil && n.Type.IsKind(TFUNC) && n.Type.Recv() != nil { // method return } @@ -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() { b, err := bio.Create(asmhdr) if err != nil { diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go index fffce440bc..16a4e342fc 100644 --- a/src/cmd/compile/internal/gc/fmt.go +++ b/src/cmd/compile/internal/gc/fmt.go @@ -1077,6 +1077,7 @@ var opprec = []int{ OSEND: 3, OANDAND: 2, OOROR: 1, + // Statements handled by stmtfmt OAS: -1, OAS2: -1, @@ -1104,7 +1105,8 @@ var opprec = []int{ OSWITCH: -1, OXCASE: -1, OXFALL: -1, - OEND: 0, + + OEND: 0, } func (n *Node) exprfmt(s fmt.State, prec int) { diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go index a529ca40f7..63d6b414dc 100644 --- a/src/cmd/compile/internal/gc/go.go +++ b/src/cmd/compile/internal/gc/go.go @@ -63,9 +63,12 @@ const ( SymSiggen SymAsm 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" // of a variable or function. During parsing, storage classes are // called declaration contexts. @@ -87,7 +90,7 @@ const ( // of the compilers arrays. // // typedef struct -// { // must not move anything +// { // must not move anything // uchar array[8]; // pointer to data // uchar nel[4]; // number of elements // uchar cap[4]; // allocated number of elements @@ -104,7 +107,7 @@ var sizeof_Array int // runtime sizeof(Array) // of the compilers strings. // // typedef struct -// { // must not move anything +// { // must not move anything // uchar array[8]; // pointer to data // uchar nel[4]; // number of elements // } String; diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 1690944b3d..f5fb72bca5 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -340,13 +340,16 @@ func Main() { // Phase 1: const, type, and names and types of funcs. // This will gather all the information about types // and methods but doesn't depend on any of it. + // We also defer type alias declarations until phase 2 + // to avoid cycles like #18640. defercheckwidth() // Don't use range--typecheck can add closures to xtop. timings.Start("fe", "typecheck", "top1") for i := 0; i < len(xtop); i++ { - if xtop[i].Op != ODCL && xtop[i].Op != OAS && xtop[i].Op != OAS2 { - xtop[i] = typecheck(xtop[i], Etop) + n := xtop[i] + if op := n.Op; op != ODCL && op != OAS && op != OAS2 && (op != ODCLTYPE || !n.Left.Name.Param.Alias) { + xtop[i] = typecheck(n, Etop) } } @@ -356,8 +359,9 @@ func Main() { // Don't use range--typecheck can add closures to xtop. timings.Start("fe", "typecheck", "top2") for i := 0; i < len(xtop); i++ { - if xtop[i].Op == ODCL || xtop[i].Op == OAS || xtop[i].Op == OAS2 { - xtop[i] = typecheck(xtop[i], Etop) + n := xtop[i] + if op := n.Op; op == ODCL || op == OAS || op == OAS2 || op == ODCLTYPE && n.Left.Name.Param.Alias { + xtop[i] = typecheck(n, Etop) } } resumecheckwidth() @@ -367,8 +371,9 @@ func Main() { timings.Start("fe", "typecheck", "func") var fcount int64 for i := 0; i < len(xtop); i++ { - if xtop[i].Op == ODCLFUNC || xtop[i].Op == OCLOSURE { - Curfn = xtop[i] + n := xtop[i] + if op := n.Op; op == ODCLFUNC || op == OCLOSURE { + Curfn = n decldepth = 1 saveerrors() typecheckslice(Curfn.Nbody.Slice(), Etop) @@ -460,8 +465,9 @@ func Main() { timings.Start("be", "compilefuncs") fcount = 0 for i := 0; i < len(xtop); i++ { - if xtop[i].Op == ODCLFUNC { - funccompile(xtop[i]) + n := xtop[i] + if n.Op == ODCLFUNC { + funccompile(n) fcount++ } } @@ -924,7 +930,7 @@ func mkpackage(pkgname string) { continue } - if s.Def.Sym != s && s.Flags&SymAlias == 0 { + if s.isAlias() { // throw away top-level name left over // from previous import . "x" if s.Def.Name != nil && s.Def.Name.Pack != nil && !s.Def.Name.Pack.Used && nsyntaxerrors == 0 { diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go index ce18297ac3..9dbe769fa7 100644 --- a/src/cmd/compile/internal/gc/noder.go +++ b/src/cmd/compile/internal/gc/noder.go @@ -154,11 +154,7 @@ func (p *noder) importDecl(imp *syntax.ImportDecl) { func (p *noder) varDecl(decl *syntax.VarDecl) []*Node { names := p.declNames(decl.NameList) - - var typ *Node - if decl.Type != nil { - typ = p.typeExpr(decl.Type) - } + typ := p.typeExprOrNil(decl.Type) var exprs []*Node if decl.Values != nil { @@ -171,11 +167,7 @@ func (p *noder) varDecl(decl *syntax.VarDecl) []*Node { func (p *noder) constDecl(decl *syntax.ConstDecl) []*Node { names := p.declNames(decl.NameList) - - var typ *Node - if decl.Type != nil { - typ = p.typeExpr(decl.Type) - } + typ := p.typeExprOrNil(decl.Type) var exprs []*Node if decl.Values != nil { @@ -187,14 +179,11 @@ func (p *noder) constDecl(decl *syntax.ConstDecl) []*Node { func (p *noder) typeDecl(decl *syntax.TypeDecl) *Node { name := typedcl0(p.name(decl.Name)) - name.Name.Param.Pragma = Pragma(decl.Pragma) - var typ *Node - if decl.Type != nil { - typ = p.typeExpr(decl.Type) - } + // decl.Type may be nil but in that case we got a syntax error during parsing + 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 { @@ -259,19 +248,19 @@ func (p *noder) funcHeader(fun *syntax.FuncDecl) *Node { yyerror("func main must have no arguments and no return values") } } - - f.Func.Nname = newfuncname(name) } else { - // Receiver MethodName Signature - - f.Func.Shortname = newfuncname(name) - f.Func.Nname = methodname(f.Func.Shortname, t.Left.Right) + f.Func.Shortname = name + name = nblank.Sym // filled in by typecheckfunc } + f.Func.Nname = newfuncname(name) f.Func.Nname.Name.Defn = f f.Func.Nname.Name.Param.Ntype = t // TODO: check if nname already has an ntype - declare(f.Func.Nname, PFUNC) + if fun.Recv == nil { + declare(f.Func.Nname, PFUNC) + } + funchdr(f) return f } @@ -467,6 +456,13 @@ func (p *noder) typeExpr(typ syntax.Expr) *Node { return p.expr(typ) } +func (p *noder) typeExprOrNil(typ syntax.Expr) *Node { + if typ != nil { + return p.expr(typ) + } + return nil +} + func (p *noder) chanDir(dir syntax.ChanDir) ChanDir { switch dir { case 0: diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go index 08ed5604da..6d5f2aa208 100644 --- a/src/cmd/compile/internal/gc/obj.go +++ b/src/cmd/compile/internal/gc/obj.go @@ -213,7 +213,7 @@ func dumpglobls() { } for _, n := range funcsyms { - dsymptr(n.Sym, 0, n.Sym.Def.Func.Shortname.Sym, 0) + dsymptr(n.Sym, 0, n.Sym.Def.Func.Shortname, 0) ggloblsym(n.Sym, int32(Widthptr), obj.DUPOK|obj.RODATA) } diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go index 61ac67c0bc..7cd02749a5 100644 --- a/src/cmd/compile/internal/gc/reflect.go +++ b/src/cmd/compile/internal/gc/reflect.go @@ -511,7 +511,7 @@ func isExportedField(ft *Field) (bool, *Pkg) { // dnameField dumps a reflect.name for a struct field. func dnameField(s *Sym, ot int, spkg *Pkg, ft *Field) int { var name string - if ft.Sym != nil && ft.Embedded == 0 { + if ft.Sym != nil { name = ft.Sym.Name } isExported, fpkg := isExportedField(ft) @@ -1345,7 +1345,14 @@ ok: // ../../../../runtime/type.go:/structField ot = dnameField(s, ot, pkg, f) ot = dsymptr(s, ot, dtypesym(f.Type), 0) - ot = duintptr(s, ot, uint64(f.Offset)) + offsetAnon := uint64(f.Offset) << 1 + if offsetAnon>>1 != uint64(f.Offset) { + Fatalf("%v: bad field offset for %s", t, f.Sym.Name) + } + if f.Embedded != 0 { + offsetAnon |= 1 + } + ot = duintptr(s, ot, offsetAnon) } } diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go index 8848bb5955..0bd877e26a 100644 --- a/src/cmd/compile/internal/gc/syntax.go +++ b/src/cmd/compile/internal/gc/syntax.go @@ -27,7 +27,7 @@ type Node struct { // func Func *Func - // ONAME + // ONAME, OTYPE, OPACK, OLABEL, some OLITERAL Name *Name Sym *Sym // various @@ -59,8 +59,8 @@ type Node struct { 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 Typecheck uint8 // tracks state during typechecking; 2 == loop detected - Local bool - IsStatic bool // whether this Node will be converted to purely static data + 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 Initorder uint8 Used bool // for variable/label declared and not used error Isddd bool // is the argument variadic @@ -180,14 +180,14 @@ func (n *Node) SetIota(x int64) { 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 { Pack *Node // real package for import . names Pkg *Pkg // pkg for OPACK nodes Heapaddr *Node // temp holding heap address of param (could move to Param?) Defn *Node // initializing assignment 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 Vargen int32 // unique name for ONAME within a function. Function outputs are numbered starting at one. Funcdepth int32 @@ -280,15 +280,16 @@ type Param struct { Innermost *Node Outer *Node - // OTYPE pragmas + // OTYPE // // TODO: Should Func pragmas also be stored on the Name? Pragma Pragma + Alias bool // node is alias for Ntype (only used when type-checking ODCLTYPE) } // Func holds Node fields used only with function-like nodes. type Func struct { - Shortname *Node + Shortname *Sym Enter Nodes // for example, allocate and initialize memory for escaping parameters Exit Nodes Cvars Nodes // closure params @@ -382,7 +383,7 @@ const ( ODCLFUNC // func f() or func (r) f() ODCLFIELD // struct field, interface field, or func/method argument/return value. ODCLCONST // const pi = 3.14 - ODCLTYPE // type Int int + ODCLTYPE // type Int int or type Int = int ODELETE // delete(Left, Right) ODOT // Left.Sym (Left is of struct type) diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 7cfd951fde..d5aa0a8fd7 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -96,16 +96,16 @@ func typekind(t *Type) string { return fmt.Sprintf("etype=%d", et) } -// sprint_depchain prints a dependency chain of nodes into fmt. +// sprint_depchain prints a dependency chain of nodes into trace. // It is used by typecheck in the case of OLITERAL nodes // to print constant definition loops. -func sprint_depchain(fmt_ *string, stack []*Node, cur *Node, first *Node) { +func sprint_depchain(trace *string, stack []*Node, cur *Node, first *Node) { for i := len(stack) - 1; i >= 0; i-- { if n := stack[i]; n.Op == cur.Op { if n != first { - sprint_depchain(fmt_, stack[:i], n, first) + sprint_depchain(trace, stack[:i], n, first) } - *fmt_ += fmt.Sprintf("\n\t%v: %v uses %v", n.Line(), n, cur) + *trace += fmt.Sprintf("\n\t%v: %v uses %v", n.Line(), n, cur) return } } @@ -152,7 +152,6 @@ func typecheck(n *Node, top int) *Node { if n.Typecheck == 2 { // Typechecking loop. Trying printing a meaningful message, // otherwise a stack trace of typechecking. - var fmt_ string switch n.Op { // We can already diagnose variables used as types. case ONAME: @@ -160,22 +159,30 @@ func typecheck(n *Node, top int) *Node { yyerror("%v is not a type", n) } + case OTYPE: + if top&Etype == Etype { + var trace string + sprint_depchain(&trace, typecheck_tcstack, n, n) + yyerrorl(n.Lineno, "invalid recursive type alias %v%s", n, trace) + } + case OLITERAL: if top&(Erv|Etype) == Etype { yyerror("%v is not a type", n) break } - sprint_depchain(&fmt_, typecheck_tcstack, n, n) - yyerrorl(n.Lineno, "constant definition loop%s", fmt_) + var trace string + sprint_depchain(&trace, typecheck_tcstack, n, n) + yyerrorl(n.Lineno, "constant definition loop%s", trace) } if nsavederrors+nerrors == 0 { - fmt_ = "" + var trace string for i := len(typecheck_tcstack) - 1; i >= 0; i-- { x := typecheck_tcstack[i] - fmt_ += fmt.Sprintf("\n\t%v %v", x.Line(), x) + trace += fmt.Sprintf("\n\t%v %v", x.Line(), x) } - yyerror("typechecking loop involving %v%s", n, fmt_) + yyerror("typechecking loop involving %v%s", n, trace) } lineno = lno @@ -3428,7 +3435,14 @@ func typecheckfunc(n *Node) { t.SetNname(n.Func.Nname) rcvr := t.Recv() if rcvr != nil && n.Func.Shortname != nil { - addmethod(n.Func.Shortname.Sym, t, true, n.Func.Pragma&Nointerface != 0) + n.Func.Nname.Sym = methodname(n.Func.Shortname, rcvr.Type) + declare(n.Func.Nname, PFUNC) + + addmethod(n.Func.Shortname, t, true, n.Func.Pragma&Nointerface != 0) + } + + if Ctxt.Flag_dynlink && importpkg == nil && n.Func.Nname != nil { + makefuncsym(n.Func.Nname.Sym) } } @@ -3577,8 +3591,6 @@ func typecheckdeftype(n *Node) { // copy new type and clear fields // that don't come along. - // anything zeroed here must be zeroed in - // typedcl2 too. copytype(n, t) ret: @@ -3757,12 +3769,29 @@ func typecheckdef(n *Node) *Node { n.Name.Defn = typecheck(n.Name.Defn, Etop) // fills in n->type 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 { defercheckwidth() } n.Walkdef = 1 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 typecheckdeftype(n) if n.Type.Etype == TFORW && nerrors > nerrors0 { @@ -3770,7 +3799,6 @@ func typecheckdef(n *Node) *Node { // but it was reported. Silence future errors. n.Type.Broke = true } - if Curfn != nil { resumecheckwidth() } diff --git a/src/cmd/compile/internal/gc/universe.go b/src/cmd/compile/internal/gc/universe.go index 30c9c3783a..d23aebeafb 100644 --- a/src/cmd/compile/internal/gc/universe.go +++ b/src/cmd/compile/internal/gc/universe.go @@ -398,6 +398,14 @@ func lexinit1() { // errortype.Orig = makeErrorInterface() s.Def = typenod(errortype) + // We create separate byte and rune types for better error messages + // rather than just creating type alias *Sym's for the uint8 and + // int32 types. Hence, (bytetype|runtype).Sym.isAlias() is false. + // TODO(gri) Should we get rid of this special case (at the cost + // of less informative error messages involving bytes and runes)? + // (Alternatively, we could introduce an OTALIAS node representing + // type aliases, albeit at the cost of having to deal with it everywhere). + // byte alias s = Pkglookup("byte", builtinpkg) bytetype = typ(TUINT8) diff --git a/src/cmd/compile/internal/syntax/nodes.go b/src/cmd/compile/internal/syntax/nodes.go index fadba84bce..34524e5c09 100644 --- a/src/cmd/compile/internal/syntax/nodes.go +++ b/src/cmd/compile/internal/syntax/nodes.go @@ -74,6 +74,7 @@ type ( // Name Type TypeDecl struct { Name *Name + Alias bool Type Expr Group *Group // nil means not part of a group Pragma Pragma diff --git a/src/cmd/compile/internal/syntax/parser.go b/src/cmd/compile/internal/syntax/parser.go index e45ca05fd1..b6e6c77804 100644 --- a/src/cmd/compile/internal/syntax/parser.go +++ b/src/cmd/compile/internal/syntax/parser.go @@ -325,7 +325,7 @@ func (p *parser) constDecl(group *Group) Decl { return d } -// TypeSpec = identifier Type . +// TypeSpec = identifier [ "=" ] Type . func (p *parser) typeDecl(group *Group) Decl { if trace { defer p.trace("typeDecl")() @@ -335,6 +335,7 @@ func (p *parser) typeDecl(group *Group) Decl { d.init(p) d.Name = p.name() + d.Alias = p.got(_Assign) d.Type = p.tryType() if d.Type == nil { p.syntax_error("in type declaration") diff --git a/src/cmd/compile/internal/syntax/printer.go b/src/cmd/compile/internal/syntax/printer.go index 0cacf1e5d4..43876a25c2 100644 --- a/src/cmd/compile/internal/syntax/printer.go +++ b/src/cmd/compile/internal/syntax/printer.go @@ -619,7 +619,11 @@ func (p *printer) printRawNode(n Node) { if n.Group == nil { p.print(_Type, blank) } - p.print(n.Name, blank, n.Type) + p.print(n.Name, blank) + if n.Alias { + p.print(_Assign, blank) + } + p.print(n.Type) case *VarDecl: if n.Group == nil { diff --git a/src/cmd/compile/internal/syntax/printer_test.go b/src/cmd/compile/internal/syntax/printer_test.go index 5c0fc776a1..a9969e610a 100644 --- a/src/cmd/compile/internal/syntax/printer_test.go +++ b/src/cmd/compile/internal/syntax/printer_test.go @@ -22,3 +22,20 @@ func TestPrint(t *testing.T) { Fprint(os.Stdout, ast, true) fmt.Println() } + +func TestPrintString(t *testing.T) { + for _, want := range []string{ + "package p", + "package p; type _ = int; type T1 = struct{}; type ( _ = *struct{}; T2 = float32 )", + // TODO(gri) expand + } { + ast, err := ParseBytes([]byte(want), nil, nil, 0) + if err != nil { + t.Error(err) + continue + } + if got := String(ast); got != want { + t.Errorf("%q: got %q", want, got) + } + } +} diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index c51dcead2b..f97f81ecc3 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -600,6 +600,7 @@ func (t *tester) registerTests() { }) } } + return // skip API check on go1.8.typealias branch if t.goos != "nacl" && t.goos != "android" && !t.iOS() { t.tests = append(t.tests, distTest{ name: "api", diff --git a/src/cmd/doc/doc_test.go b/src/cmd/doc/doc_test.go index 1c054fd566..1244476ab2 100644 --- a/src/cmd/doc/doc_test.go +++ b/src/cmd/doc/doc_test.go @@ -71,6 +71,7 @@ var tests = []test{ `const MultiLineConst = ...`, // Multi line constant. `var MultiLineVar = map\[struct{ ... }\]struct{ ... }{ ... }`, // Multi line variable. `func MultiLineFunc\(x interface{ ... }\) \(r struct{ ... }\)`, // Multi line function. + `type T1 = T2`, // Type alias }, []string{ `const internalConstant = 2`, // No internal constants. @@ -89,6 +90,7 @@ var tests = []test{ `unexportedTypedConstant`, // No unexported typed constant. `Field`, // No fields. `Method`, // No methods. + `type T1 T2`, // Type alias does not display as type declaration. }, }, // Package dump -u @@ -265,6 +267,18 @@ var tests = []test{ `error`, // No embedded error. }, }, + // Type T1 dump (alias). + { + "type T1", + []string{p+".T1"}, + []string{ + `type T1 = T2`, + }, + []string{ + `type T1 T2`, + `type ExportedType`, + }, + }, // Type -u with unexported fields. { "type with unexported fields and -u", diff --git a/src/cmd/doc/pkg.go b/src/cmd/doc/pkg.go index daa6ed358c..32d08f21fd 100644 --- a/src/cmd/doc/pkg.go +++ b/src/cmd/doc/pkg.go @@ -258,7 +258,11 @@ func (pkg *Package) oneLineNodeDepth(node ast.Node, depth int) string { return fmt.Sprintf("func %s%s%s", recv, name, fnc) case *ast.TypeSpec: - return fmt.Sprintf("type %s %s", n.Name.Name, pkg.oneLineNodeDepth(n.Type, depth)) + sep := " " + if n.Assign.IsValid() { + sep = " = " + } + return fmt.Sprintf("type %s%s%s", n.Name.Name, sep, pkg.oneLineNodeDepth(n.Type, depth)) case *ast.FuncType: var params []string diff --git a/src/cmd/doc/testdata/pkg.go b/src/cmd/doc/testdata/pkg.go index 924daa171b..0ebea67d58 100644 --- a/src/cmd/doc/testdata/pkg.go +++ b/src/cmd/doc/testdata/pkg.go @@ -172,3 +172,7 @@ const ( ) const ConstGroup4 ExportedType = ExportedType{} + +type T2 int + +type T1 = T2 diff --git a/src/cmd/gofmt/testdata/typealias.golden b/src/cmd/gofmt/testdata/typealias.golden new file mode 100644 index 0000000000..bbbbf32121 --- /dev/null +++ b/src/cmd/gofmt/testdata/typealias.golden @@ -0,0 +1,24 @@ +package q + +import "p" + +type _ = int +type a = struct{ x int } +type b = p.B + +type ( + _ = chan<- int + aa = interface{} + bb = p.BB +) + +// TODO(gri) We may want to put the '=' into a separate column if +// we have mixed (regular and alias) type declarations in a group. +type ( + _ chan<- int + _ = chan<- int + aa0 interface{} + aaa = interface{} + bb0 p.BB + bbb = p.BB +) diff --git a/src/cmd/gofmt/testdata/typealias.input b/src/cmd/gofmt/testdata/typealias.input new file mode 100644 index 0000000000..6e49328e34 --- /dev/null +++ b/src/cmd/gofmt/testdata/typealias.input @@ -0,0 +1,24 @@ +package q + +import "p" + +type _ = int +type a = struct{ x int } +type b = p.B + +type ( + _ = chan<- int + aa = interface{} + bb = p.BB +) + +// TODO(gri) We may want to put the '=' into a separate column if +// we have mixed (regular and alias) type declarations in a group. +type ( + _ chan<- int + _ = chan<- int + aa0 interface{} + aaa = interface{} + bb0 p.BB + bbb = p.BB +) diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go index d111b005d9..d898c40c1c 100644 --- a/src/cmd/link/internal/ld/decodesym.go +++ b/src/cmd/link/internal/ld/decodesym.go @@ -255,7 +255,7 @@ func decodetypeStructFieldType(s *Symbol, i int) *Symbol { func decodetypeStructFieldOffs(arch *sys.Arch, s *Symbol, i int) int64 { off := decodetypeStructFieldArrayOff(s, i) - return int64(decodeInuxi(arch, s.P[off+2*SysArch.PtrSize:], SysArch.IntSize)) + return int64(decodeInuxi(arch, s.P[off+2*SysArch.PtrSize:], SysArch.IntSize) >> 1) } // InterfaceType.methods.length diff --git a/src/go/ast/ast.go b/src/go/ast/ast.go index a197b5a5bf..2ecc48b741 100644 --- a/src/go/ast/ast.go +++ b/src/go/ast/ast.go @@ -848,6 +848,7 @@ type ( TypeSpec struct { Doc *CommentGroup // associated documentation; or nil Name *Ident // type name + Assign token.Pos // position of '=', if any Type Expr // *Ident, *ParenExpr, *SelectorExpr, *StarExpr, or any of the *XxxTypes Comment *CommentGroup // line comments; or nil } diff --git a/src/go/build/build.go b/src/go/build/build.go index f11bc0ca6c..fdd073579a 100644 --- a/src/go/build/build.go +++ b/src/go/build/build.go @@ -290,7 +290,8 @@ func defaultContext() Context { // in all releases >= Go 1.x. Code that requires Go 1.x or later should // say "+build go1.x", and code that should only be built before Go 1.x // (perhaps it is the stub to use in that case) should say "+build !go1.x". - c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3", "go1.4", "go1.5", "go1.6", "go1.7", "go1.8"} + // NOTE: If you add to this list, also update the doc comment in doc.go. + c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3", "go1.4", "go1.5", "go1.6", "go1.7", "go1.8", "go1.8.typealias"} env := os.Getenv("CGO_ENABLED") if env == "" { diff --git a/src/go/build/doc.go b/src/go/build/doc.go index 979d0477df..5bdba4430f 100644 --- a/src/go/build/doc.go +++ b/src/go/build/doc.go @@ -105,6 +105,7 @@ // - "go1.6", from Go version 1.6 onward // - "go1.7", from Go version 1.7 onward // - "go1.8", from Go version 1.8 onward +// - "go1.8.typealias", for Go version 1.8 with aliases // - any additional words listed in ctxt.BuildTags // // If a file's name, after stripping the extension and a possible _test suffix, diff --git a/src/go/internal/gccgoimporter/importer_test.go b/src/go/internal/gccgoimporter/importer_test.go index 2b454701be..4fca828bf6 100644 --- a/src/go/internal/gccgoimporter/importer_test.go +++ b/src/go/internal/gccgoimporter/importer_test.go @@ -101,6 +101,7 @@ var importerTests = [...]importerTest{ {pkgpath: "unicode", name: "IsUpper", want: "func IsUpper(r rune) bool"}, {pkgpath: "unicode", name: "MaxRune", want: "const MaxRune untyped rune", wantval: "1114111"}, {pkgpath: "imports", wantinits: []string{"imports..import", "fmt..import", "math..import"}}, + {pkgpath: "alias", name: "IntAlias2", want: "type IntAlias2 = Int"}, } func TestGoxImporter(t *testing.T) { diff --git a/src/go/internal/gccgoimporter/parser.go b/src/go/internal/gccgoimporter/parser.go index 3b97c96d43..0d788653e3 100644 --- a/src/go/internal/gccgoimporter/parser.go +++ b/src/go/internal/gccgoimporter/parser.go @@ -370,27 +370,41 @@ func (p *parser) parseConst(pkg *types.Package) *types.Const { return types.NewConst(token.NoPos, pkg, name, typ, val) } -// TypeName = ExportedName . -func (p *parser) parseTypeName() *types.TypeName { +// NamedType = TypeName [ "=" ] Type { Method } . +// TypeName = ExportedName . +// Method = "func" "(" Param ")" Name ParamList ResultList ";" . +func (p *parser) parseNamedType(n int) types.Type { pkg, name := p.parseExportedName() scope := pkg.Scope() - if obj := scope.Lookup(name); obj != nil { - return obj.(*types.TypeName) + + if p.tok == '=' { + // type alias + p.next() + typ := p.parseType(pkg) + if obj := scope.Lookup(name); obj != nil { + typ = obj.Type() // use previously imported type + if typ == nil { + p.errorf("%v (type alias) used in cycle", obj) + } + } else { + obj = types.NewTypeName(token.NoPos, pkg, name, typ) + scope.Insert(obj) + } + p.typeMap[n] = typ + return typ } - obj := types.NewTypeName(token.NoPos, pkg, name, nil) - // a named type may be referred to before the underlying type - // is known - set it up - types.NewNamed(obj, nil, nil) - scope.Insert(obj) - return obj -} -// NamedType = TypeName Type { Method } . -// Method = "func" "(" Param ")" Name ParamList ResultList ";" . -func (p *parser) parseNamedType(n int) types.Type { - obj := p.parseTypeName() + // named type + obj := scope.Lookup(name) + if obj == nil { + // a named type may be referred to before the underlying type + // is known - set it up + tname := types.NewTypeName(token.NoPos, pkg, name, nil) + types.NewNamed(tname, nil, nil) + scope.Insert(tname) + obj = tname + } - pkg := obj.Pkg() typ := obj.Type() p.typeMap[n] = typ @@ -409,8 +423,8 @@ func (p *parser) parseNamedType(n int) types.Type { nt.SetUnderlying(underlying.Underlying()) } + // collect associated methods for p.tok == scanner.Ident { - // collect associated methods p.expectKeyword("func") p.expect('(') receiver, _ := p.parseParam(pkg) diff --git a/src/go/internal/gccgoimporter/testdata/alias.gox b/src/go/internal/gccgoimporter/testdata/alias.gox new file mode 100644 index 0000000000..ced7d84c4f --- /dev/null +++ b/src/go/internal/gccgoimporter/testdata/alias.gox @@ -0,0 +1,4 @@ +v1; +package alias; +pkgpath alias; +type <type 115 "I1" <type 116 interface { M1 (? <type 117 "IntAlias2" = <type 118 "IntAlias" = <type 119 "Int" <type -11>>>>) < type 114>; M2 () <type 1>; }>>; diff --git a/src/go/internal/gcimporter/bimport.go b/src/go/internal/gcimporter/bimport.go index a8f349052a..5badd337d9 100644 --- a/src/go/internal/gcimporter/bimport.go +++ b/src/go/internal/gcimporter/bimport.go @@ -98,10 +98,10 @@ func BImportData(fset *token.FileSet, imports map[string]*types.Package, data [] // read version specific flags - extend as necessary switch p.version { - // case 4: + // case 5: // ... // fallthrough - case 3, 2, 1: + case 4, 3, 2, 1: p.debugFormat = p.rawStringln(p.rawByte()) == "debug" p.trackAllTypes = 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. -// obj must not be a *types.Alias. func objTag(obj types.Object) int { switch obj.(type) { case *types.Const: @@ -219,7 +218,6 @@ func objTag(obj types.Object) int { return varTag case *types.Func: return funcTag - // Aliases are not exported multiple times, thus we should not see them here. default: errorf("unexpected object: %v (%T)", obj, obj) // panics panic("unreachable") @@ -237,14 +235,14 @@ func (p *importer) declare(obj types.Object) { pkg := obj.Pkg() if alt := pkg.Scope().Insert(obj); alt != nil { // 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 // functions whose inlined function bodies refer to other functions that // 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, // 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) { 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() 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: p.typ(nil) @@ -277,19 +282,6 @@ func (p *importer) obj(tag int) { sig := types.NewSignature(nil, params, result, isddd) 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: errorf("unexpected object tag %d", tag) } @@ -349,9 +341,7 @@ var ( func (p *importer) qualifiedName() (pkg *types.Package, name string) { name = p.string() - if name != "" { - pkg = p.pkg() - } + pkg = p.pkg() return } @@ -556,17 +546,17 @@ func (p *importer) fieldList(parent *types.Package) (fields []*types.Var, tags [ fields = make([]*types.Var, n) tags = make([]string, n) for i := range fields { - fields[i] = p.field(parent) - tags[i] = p.string() + fields[i], tags[i] = p.field(parent) } } return } -func (p *importer) field(parent *types.Package) *types.Var { +func (p *importer) field(parent *types.Package) (*types.Var, string) { pos := p.pos() - pkg, name := p.fieldName(parent) + pkg, name, alias := p.fieldName(parent) typ := p.typ(parent) + tag := p.string() anonymous := false if name == "" { @@ -578,12 +568,15 @@ func (p *importer) field(parent *types.Package) *types.Var { case *types.Named: name = typ.Obj().Name() default: - errorf("anonymous field expected") + errorf("named base type expected") } 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) + return types.NewField(pos, pkg, name, typ, anonymous), tag } func (p *importer) methodList(parent *types.Package) (methods []*types.Func) { @@ -598,31 +591,42 @@ func (p *importer) methodList(parent *types.Package) (methods []*types.Func) { func (p *importer) method(parent *types.Package) *types.Func { pos := p.pos() - pkg, name := p.fieldName(parent) + pkg, name, _ := p.fieldName(parent) params, isddd := p.paramList() result, _ := p.paramList() sig := types.NewSignature(nil, params, result, isddd) return types.NewFunc(pos, pkg, name, sig) } -func (p *importer) fieldName(parent *types.Package) (*types.Package, string) { - name := p.string() - pkg := parent +func (p *importer) fieldName(parent *types.Package) (pkg *types.Package, name string, alias bool) { + name = p.string() + pkg = parent if pkg == nil { // use the imported package instead pkg = p.pkgList[0] } if p.version == 0 && name == "_" { // version 0 didn't export a package for _ fields - return pkg, name - } - if name != "" && !exported(name) { - if name == "?" { - name = "" - } + return + } + switch name { + case "": + // 1) field name matches base type name and is exported: nothing to do + case "?": + // 2) field name matches base type name and is not exported: need package + name = "" pkg = p.pkg() + case "@": + // 3) field name doesn't match type name (alias) + name = p.string() + alias = true + fallthrough + default: + if !exported(name) { + pkg = p.pkg() + } } - return pkg, name + return } func (p *importer) paramList() (*types.Tuple, bool) { @@ -893,7 +897,7 @@ const ( nilTag // only used by gc (appears in exported inlined function bodies) unknownTag // not used by gc (only appears in packages with errors) - // Aliases + // Type aliases aliasTag ) @@ -917,7 +921,7 @@ var predeclared = []types.Type{ types.Typ[types.Complex128], types.Typ[types.String], - // aliases + // basic type aliases types.Universe.Lookup("byte").Type(), types.Universe.Lookup("rune").Type(), diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go index d3ef7db31e..40c4a3e58d 100644 --- a/src/go/parser/parser.go +++ b/src/go/parser/parser.go @@ -2327,7 +2327,10 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Token, _ int) ast. // (Global identifiers are resolved in a separate phase after parsing.) spec := &ast.TypeSpec{Doc: doc, Name: ident} p.declare(spec, nil, p.topScope, ast.Typ, ident) - + if p.tok == token.ASSIGN { + spec.Assign = p.pos + p.next() + } spec.Type = p.parseType() p.expectSemi() // call before accessing p.linecomment spec.Comment = p.lineComment diff --git a/src/go/parser/short_test.go b/src/go/parser/short_test.go index cdd343ea3c..6f8ef6b0f7 100644 --- a/src/go/parser/short_test.go +++ b/src/go/parser/short_test.go @@ -46,6 +46,8 @@ var valids = []string{ `package p; const (x = 0; y; z)`, // issue 9639 `package p; var _ = map[P]int{P{}:0, {}:1}`, `package p; var _ = map[*P]int{&P{}:0, {}:1}`, + `package p; type T = int`, + `package p; type (T = p.T; _ = struct{}; x = *T)`, } func TestValid(t *testing.T) { diff --git a/src/go/printer/nodes.go b/src/go/printer/nodes.go index ea432860a8..08b8711c2d 100644 --- a/src/go/printer/nodes.go +++ b/src/go/printer/nodes.go @@ -1447,6 +1447,9 @@ func (p *printer) spec(spec ast.Spec, n int, doIndent bool) { } else { p.print(vtab) } + if s.Assign.IsValid() { + p.print(token.ASSIGN, blank) + } p.expr(s.Type) p.setComment(s.Comment) diff --git a/src/go/printer/testdata/declarations.golden b/src/go/printer/testdata/declarations.golden index 82f5e0f914..d4ea545658 100644 --- a/src/go/printer/testdata/declarations.golden +++ b/src/go/printer/testdata/declarations.golden @@ -985,3 +985,18 @@ func _(struct { x int y int }) // no extra comma between } and ) + +// alias declarations + +type c0 struct{} +type c1 = C +type c2 = struct{ x int } +type c3 = p.C +type ( + s struct{} + a = A + b = A + c = foo + d = interface{} + ddd = p.Foo +) diff --git a/src/go/printer/testdata/declarations.input b/src/go/printer/testdata/declarations.input index a0a3783b84..50386eb8d5 100644 --- a/src/go/printer/testdata/declarations.input +++ b/src/go/printer/testdata/declarations.input @@ -999,3 +999,18 @@ func _(struct { x int y int }) // no extra comma between } and ) + +// alias declarations + +type c0 struct{} +type c1 = C +type c2 = struct{ x int} +type c3 = p.C +type ( + s struct{} + a = A + b = A + c = foo + d = interface{} + ddd = p.Foo +)
\ No newline at end of file diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go index 1208eb8b3a..92c6d75e70 100644 --- a/src/go/types/api_test.go +++ b/src/go/types/api_test.go @@ -1295,155 +1295,3 @@ func f(x int) { y := x; print(y) } } } } - -// Alias-related code. Keep for now. -/* -func TestAliases(t *testing.T) { - testenv.MustHaveGoBuild(t) - - const src = ` -package b - -import ( - "./testdata/alias" - a "./testdata/alias" - "math" -) - -const ( - c1 = alias.Pi1 - c2 => a.Pi1 - c3 => a.Pi2 - c4 => math.Pi -) - -var ( - v1 => alias.Default - v2 => a.Default - v3 = f1 -) - -type ( - t1 => alias.Context - t2 => a.Context -) - -func f1 => alias.Sin -func f2 => a.Sin - -func _() { - assert(c1 == alias.Pi1 && c2 == a.Pi1 && c3 == a.Pi2 && c4 == math.Pi) - assert(c2 == c2 && c2 == c3 && c3 == c4) - v1 = v2 // must be assignable - var _ *t1 = new(t2) // must be assignable - var _ t2 = alias.Default - f1(1) // must be callable - f2(1) - _ = alias.Sin(1) - _ = a.Sin(1) -} -` - - if out := compile(t, "testdata", "alias.go"); out != "" { - defer os.Remove(out) - } - - DefPredeclaredTestFuncs() // declare assert built-in for testing - mustTypecheck(t, "Aliases", src, nil) -} - -func compile(t *testing.T, dirname, filename string) string { - cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", filename) - cmd.Dir = dirname - out, err := cmd.CombinedOutput() - if err != nil { - t.Logf("%s", out) - t.Fatalf("go tool compile %s failed: %s", filename, err) - } - // filename should end with ".go" - return filepath.Join(dirname, filename[:len(filename)-2]+"o") -} - -func TestAliasDefUses(t *testing.T) { - testenv.MustHaveGoBuild(t) - - const src = ` -package p - -import( - "go/build" - "go/types" -) - -// Defs -const Invalid => types.Invalid -type Struct => types.Struct -var Default => build.Default -func Implements => types.Implements - -// Uses -const _ = Invalid -var _ types.Struct = Struct{} // types must be identical -var _ build.Context = Default -var _ = Implements(nil, nil) -` - - info := Info{ - Defs: make(map[*ast.Ident]Object), - Uses: make(map[*ast.Ident]Object), - } - mustTypecheck(t, "TestAliasDefUses", src, &info) - - // verify Defs - defs := map[string]string{ - "Invalid": "types.Invalid", - "Struct": "types.Struct", - "Default": "build.Default", - "Implements": "types.Implements", - } - - for ident, obj := range info.Defs { - if alias, ok := obj.(*Alias); ok { - if want := defs[ident.Name]; want != "" { - orig := alias.Orig() - if got := orig.Pkg().Name() + "." + orig.Name(); got != want { - t.Errorf("%v: got %v, want %v", ident, got, want) - } - delete(defs, ident.Name) // mark as found - } else { - t.Errorf("unexpected alias def of %v", ident) - } - } - } - - if len(defs) != 0 { - t.Errorf("missing aliases: %v", defs) - } - - // verify Uses - uses := map[string]string{ - "Invalid": "types.Invalid", - "Struct": "types.Struct", - "Default": "build.Default", - "Implements": "types.Implements", - } - - for ident, obj := range info.Uses { - if alias, ok := obj.(*Alias); ok { - if want := uses[ident.Name]; want != "" { - orig := alias.Orig() - if got := orig.Pkg().Name() + "." + orig.Name(); got != want { - t.Errorf("%v: got %v, want %v", ident, got, want) - } - delete(uses, ident.Name) // mark as found - } else { - t.Errorf("unexpected alias use of %v", ident) - } - } - } - - if len(uses) != 0 { - t.Errorf("missing aliases: %v", defs) - } -} -*/ diff --git a/src/go/types/call.go b/src/go/types/call.go index 8e5c5371f2..7f5823c829 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -275,8 +275,6 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) { // so we don't need a "package" mode for operands: package names // can only appear in qualified identifiers which are mapped to // selector expressions. - // (see also decl.go: checker.aliasDecl) - // TODO(gri) factor this code out and share with checker.aliasDecl if ident, ok := e.X.(*ast.Ident); ok { _, obj := check.scope.LookupParent(ident.Name, check.pos) if pname, _ := obj.(*PkgName); pname != nil { @@ -296,12 +294,6 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) { // ok to continue } check.recordUse(e.Sel, exp) - exp = original(exp) - - // avoid further errors if the imported object is an alias that's broken - if exp == nil { - goto Error - } // Simplified version of the code for *ast.Idents: // - imported objects are always fully initialized diff --git a/src/go/types/check_test.go b/src/go/types/check_test.go index f844575269..24b3365717 100644 --- a/src/go/types/check_test.go +++ b/src/go/types/check_test.go @@ -68,11 +68,11 @@ var tests = [][]string{ {"testdata/decls1.src"}, {"testdata/decls2a.src", "testdata/decls2b.src"}, {"testdata/decls3.src"}, + {"testdata/decls4.src"}, {"testdata/const0.src"}, {"testdata/const1.src"}, {"testdata/constdecl.src"}, {"testdata/vardecl.src"}, - //{"testdata/aliasdecl.src"}, {"testdata/expr0.src"}, {"testdata/expr1.src"}, {"testdata/expr2.src"}, diff --git a/src/go/types/decl.go b/src/go/types/decl.go index dced7a6d6d..7428f8f995 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -81,14 +81,10 @@ func (check *Checker) objDecl(obj Object, def *Named, path []*TypeName) { check.varDecl(obj, d.lhs, d.typ, d.init) case *TypeName: // invalid recursive types are detected via path - check.typeDecl(obj, d.typ, def, path) + check.typeDecl(obj, d.typ, def, path, d.alias) case *Func: // functions may be recursive - no need to track dependencies check.funcDecl(obj, d) - // Alias-related code. Keep for now. - // case *Alias: - // // aliases cannot be recursive - no need to track dependencies - // check.aliasDecl(obj, d) default: unreachable() } @@ -219,33 +215,42 @@ func (n *Named) setUnderlying(typ Type) { } } -func (check *Checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, path []*TypeName) { +func (check *Checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, path []*TypeName, alias bool) { assert(obj.typ == nil) // type declarations cannot use iota assert(check.iota == nil) - named := &Named{obj: obj} - def.setUnderlying(named) - obj.typ = named // make sure recursive type declarations terminate - - // determine underlying type of named - check.typExpr(typ, named, append(path, obj)) - - // The underlying type of named may be itself a named type that is - // incomplete: - // - // type ( - // A B - // B *C - // C A - // ) - // - // The type of C is the (named) type of A which is incomplete, - // and which has as its underlying type the named type B. - // Determine the (final, unnamed) underlying type by resolving - // any forward chain (they always end in an unnamed type). - named.underlying = underlying(named.underlying) + if alias { + + obj.typ = Typ[Invalid] + obj.typ = check.typExpr(typ, nil, append(path, obj)) + + } else { + + named := &Named{obj: obj} + def.setUnderlying(named) + obj.typ = named // make sure recursive type declarations terminate + + // determine underlying type of named + check.typExpr(typ, named, append(path, obj)) + + // The underlying type of named may be itself a named type that is + // incomplete: + // + // type ( + // A B + // B *C + // C A + // ) + // + // The type of C is the (named) type of A which is incomplete, + // and which has as its underlying type the named type B. + // Determine the (final, unnamed) underlying type by resolving + // any forward chain (they always end in an unnamed type). + named.underlying = underlying(named.underlying) + + } // check and add associated methods // TODO(gri) It's easy to create pathological cases where the @@ -268,21 +273,23 @@ func (check *Checker) addMethodDecls(obj *TypeName) { // spec: "If the base type is a struct type, the non-blank method // and field names must be distinct." - base := obj.typ.(*Named) - if t, _ := base.underlying.(*Struct); t != nil { - for _, fld := range t.fields { - if fld.name != "_" { - assert(mset.insert(fld) == nil) + base, _ := obj.typ.(*Named) // nil if receiver base type is type alias + if base != nil { + if t, _ := base.underlying.(*Struct); t != nil { + for _, fld := range t.fields { + if fld.name != "_" { + assert(mset.insert(fld) == nil) + } } } - } - // Checker.Files may be called multiple times; additional package files - // may add methods to already type-checked types. Add pre-existing methods - // so that we can detect redeclarations. - for _, m := range base.methods { - assert(m.name != "_") - assert(mset.insert(m) == nil) + // Checker.Files may be called multiple times; additional package files + // may add methods to already type-checked types. Add pre-existing methods + // so that we can detect redeclarations. + for _, m := range base.methods { + assert(m.name != "_") + assert(mset.insert(m) == nil) + } } // type-check methods @@ -295,7 +302,7 @@ func (check *Checker) addMethodDecls(obj *TypeName) { case *Var: check.errorf(m.pos, "field and method with the same name %s", m.name) case *Func: - check.errorf(m.pos, "method %s already declared for %s", m.name, base) + check.errorf(m.pos, "method %s already declared for %s", m.name, obj) default: unreachable() } @@ -303,9 +310,12 @@ func (check *Checker) addMethodDecls(obj *TypeName) { continue } } + + // type-check check.objDecl(m, nil, nil) + // methods with blank _ names cannot be found - don't keep them - if m.name != "_" { + if base != nil && m.name != "_" { base.methods = append(base.methods, m) } } @@ -333,106 +343,6 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) { } } -// original returns the original Object if obj is an Alias; -// otherwise it returns obj. The result is never an Alias, -// but it may be nil. -func original(obj Object) Object { - // an alias stands for the original object; use that one instead - if alias, _ := obj.(*disabledAlias); alias != nil { - obj = alias.orig - // aliases always refer to non-alias originals - if _, ok := obj.(*disabledAlias); ok { - panic("original is an alias") - } - } - return obj -} - -func (check *Checker) aliasDecl(obj *disabledAlias, decl *declInfo) { - assert(obj.typ == nil) - - // alias declarations cannot use iota - assert(check.iota == nil) - - // assume alias is invalid to start with - obj.typ = Typ[Invalid] - - // rhs must be package-qualified identifer pkg.sel (see also call.go: checker.selector) - // TODO(gri) factor this code out and share with checker.selector - rhs := decl.init - var pkg *Package - var sel *ast.Ident - if sexpr, ok := rhs.(*ast.SelectorExpr); ok { - if ident, ok := sexpr.X.(*ast.Ident); ok { - _, obj := check.scope.LookupParent(ident.Name, check.pos) - if pname, _ := obj.(*PkgName); pname != nil { - assert(pname.pkg == check.pkg) - check.recordUse(ident, pname) - pname.used = true - pkg = pname.imported - sel = sexpr.Sel - } - } - } - if pkg == nil { - check.errorf(rhs.Pos(), "invalid alias: %v is not a package-qualified identifier", rhs) - return - } - - // qualified identifier must denote an exported object - orig := pkg.scope.Lookup(sel.Name) - if orig == nil || !orig.Exported() { - if !pkg.fake { - check.errorf(rhs.Pos(), "%s is not exported by package %s", sel.Name, pkg.name) - } - return - } - check.recordUse(sel, orig) - orig = original(orig) - - // avoid further errors if the imported object is an alias that's broken - if orig == nil { - return - } - - // An alias declaration must not refer to package unsafe. - if orig.Pkg() == Unsafe { - check.errorf(rhs.Pos(), "invalid alias: %s refers to package unsafe (%v)", obj.Name(), orig) - return - } - - // The original must be of the same kind as the alias declaration. - var why string - switch obj.kind { - case token.CONST: - if _, ok := orig.(*Const); !ok { - why = "constant" - } - case token.TYPE: - if _, ok := orig.(*TypeName); !ok { - why = "type" - } - case token.VAR: - if _, ok := orig.(*Var); !ok { - why = "variable" - } - case token.FUNC: - if _, ok := orig.(*Func); !ok { - why = "function" - } - default: - unreachable() - } - if why != "" { - check.errorf(rhs.Pos(), "invalid alias: %v is not a %s", orig, why) - return - } - - // alias is valid - obj.typ = orig.Type() - obj.orig = orig -} - func (check *Checker) declStmt(decl ast.Decl) { pkg := check.pkg @@ -540,7 +450,7 @@ func (check *Checker) declStmt(decl ast.Decl) { // the innermost containing block." scopePos := s.Name.Pos() check.declare(check.scope, s.Name, obj, scopePos) - check.typeDecl(obj, s.Type, nil, nil) + check.typeDecl(obj, s.Type, nil, nil, s.Assign.IsValid()) default: check.invalidAST(s.Pos(), "const, type, or var declaration expected") diff --git a/src/go/types/example_test.go b/src/go/types/example_test.go index 8882e5063a..2a2fb3fc59 100644 --- a/src/go/types/example_test.go +++ b/src/go/types/example_test.go @@ -239,10 +239,10 @@ func fib(x int) int { // type S string: // defined at fib.go:4:6 // used at 6:23 - // type int int: + // type int: // defined at - // used at 8:12, 8:17 - // type string string: + // type string: // defined at - // used at 4:8 // var b S: diff --git a/src/go/types/lookup.go b/src/go/types/lookup.go index 3caca5519b..ee8202d9e4 100644 --- a/src/go/types/lookup.go +++ b/src/go/types/lookup.go @@ -67,24 +67,22 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o } typ, isPtr := deref(T) - named, _ := typ.(*Named) // *typ where typ is an interface has no methods. - if isPtr { - utyp := typ - if named != nil { - utyp = named.underlying - } - if _, ok := utyp.(*Interface); ok { - return - } + if isPtr && IsInterface(typ) { + return } // Start with typ as single entry at shallowest depth. - // If typ is not a named type, insert a nil type instead. - current := []embeddedType{{named, nil, isPtr, false}} - - // named types that we have seen already, allocated lazily + current := []embeddedType{{typ, nil, isPtr, false}} + + // Named types that we have seen already, allocated lazily. + // Used to avoid endless searches in case of recursive types. + // Since only Named types can be used for recursive types, we + // only need to track those. + // (If we ever allow type aliases to construct recursive types, + // we must use type identity rather than pointer equality for + // the map key comparison, as we do in consolidateMultiples.) var seen map[*Named]bool // search current depth @@ -93,11 +91,12 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o // look for (pkg, name) in all types at current depth for _, e := range current { - // The very first time only, e.typ may be nil. - // In this case, we don't have a named type and - // we simply continue with the underlying type. - if e.typ != nil { - if seen[e.typ] { + typ := e.typ + + // If we have a named type, we may have associated methods. + // Look for those first. + if named, _ := typ.(*Named); named != nil { + if seen[named] { // We have seen this type before, at a more shallow depth // (note that multiples of this type at the current depth // were consolidated before). The type at that depth shadows @@ -108,10 +107,10 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o if seen == nil { seen = make(map[*Named]bool) } - seen[e.typ] = true + seen[named] = true // look for a matching attached method - if i, m := lookupMethod(e.typ.methods, pkg, name); m != nil { + if i, m := lookupMethod(named.methods, pkg, name); m != nil { // potential match assert(m.typ != nil) index = concat(e.index, i) @@ -124,7 +123,7 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o } // continue with underlying type - typ = e.typ.underlying + typ = named.underlying } switch t := typ.(type) { @@ -147,16 +146,15 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o // we have a name collision on the same depth; in either // case we don't need to look further). // Embedded fields are always of the form T or *T where - // T is a named type. If e.typ appeared multiple times at + // T is a type name. If e.typ appeared multiple times at // this depth, f.typ appears multiple times at the next // depth. if obj == nil && f.anonymous { - // Ignore embedded basic types - only user-defined - // named types can have methods or struct fields. typ, isPtr := deref(f.typ) - if t, _ := typ.(*Named); t != nil { - next = append(next, embeddedType{t, concat(e.index, i), e.indirect || isPtr, e.multiples}) - } + // TODO(gri) optimization: ignore types that can't + // have fields or methods (only Named, Struct, and + // Interface types need to be considered). + next = append(next, embeddedType{typ, concat(e.index, i), e.indirect || isPtr, e.multiples}) } } @@ -193,12 +191,12 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o return nil, nil, false // not found } -// embeddedType represents an embedded named type +// embeddedType represents an embedded type type embeddedType struct { - typ *Named // nil means use the outer typ variable instead - index []int // embedded field indices, starting with index at depth 0 - indirect bool // if set, there was a pointer indirection on the path to this field - multiples bool // if set, typ appears multiple times at this depth + typ Type + index []int // embedded field indices, starting with index at depth 0 + indirect bool // if set, there was a pointer indirection on the path to this field + multiples bool // if set, typ appears multiple times at this depth } // consolidateMultiples collects multiple list entries with the same type @@ -209,10 +207,10 @@ func consolidateMultiples(list []embeddedType) []embeddedType { return list // at most one entry - nothing to do } - n := 0 // number of entries w/ unique type - prev := make(map[*Named]int) // index at which type was previously seen + n := 0 // number of entries w/ unique type + prev := make(map[Type]int) // index at which type was previously seen for _, e := range list { - if i, found := prev[e.typ]; found { + if i, found := lookupType(prev, e.typ); found { list[i].multiples = true // ignore this entry } else { @@ -224,6 +222,21 @@ func consolidateMultiples(list []embeddedType) []embeddedType { return list[:n] } +func lookupType(m map[Type]int, typ Type) (int, bool) { + // fast path: maybe the types are equal + if i, found := m[typ]; found { + return i, true + } + + for t, i := range m { + if Identical(t, typ) { + return i, true + } + } + + return 0, false +} + // MissingMethod returns (nil, false) if V implements T, otherwise it // returns a missing method required by T and whether it is missing or // just has the wrong type. diff --git a/src/go/types/methodset.go b/src/go/types/methodset.go index b27f2dac34..4f791d9d51 100644 --- a/src/go/types/methodset.go +++ b/src/go/types/methodset.go @@ -72,24 +72,22 @@ func NewMethodSet(T Type) *MethodSet { var base methodSet typ, isPtr := deref(T) - named, _ := typ.(*Named) // *typ where typ is an interface has no methods. - if isPtr { - utyp := typ - if named != nil { - utyp = named.underlying - } - if _, ok := utyp.(*Interface); ok { - return &emptyMethodSet - } + if isPtr && IsInterface(typ) { + return &emptyMethodSet } // Start with typ as single entry at shallowest depth. - // If typ is not a named type, insert a nil type instead. - current := []embeddedType{{named, nil, isPtr, false}} - - // named types that we have seen already, allocated lazily + current := []embeddedType{{typ, nil, isPtr, false}} + + // Named types that we have seen already, allocated lazily. + // Used to avoid endless searches in case of recursive types. + // Since only Named types can be used for recursive types, we + // only need to track those. + // (If we ever allow type aliases to construct recursive types, + // we must use type identity rather than pointer equality for + // the map key comparison, as we do in consolidateMultiples.) var seen map[*Named]bool // collect methods at current depth @@ -101,11 +99,12 @@ func NewMethodSet(T Type) *MethodSet { var mset methodSet for _, e := range current { - // The very first time only, e.typ may be nil. - // In this case, we don't have a named type and - // we simply continue with the underlying type. - if e.typ != nil { - if seen[e.typ] { + typ := e.typ + + // If we have a named type, we may have associated methods. + // Look for those first. + if named, _ := typ.(*Named); named != nil { + if seen[named] { // We have seen this type before, at a more shallow depth // (note that multiples of this type at the current depth // were consolidated before). The type at that depth shadows @@ -116,12 +115,12 @@ func NewMethodSet(T Type) *MethodSet { if seen == nil { seen = make(map[*Named]bool) } - seen[e.typ] = true + seen[named] = true - mset = mset.add(e.typ.methods, e.index, e.indirect, e.multiples) + mset = mset.add(named.methods, e.index, e.indirect, e.multiples) // continue with underlying type - typ = e.typ.underlying + typ = named.underlying } switch t := typ.(type) { @@ -130,16 +129,15 @@ func NewMethodSet(T Type) *MethodSet { fset = fset.add(f, e.multiples) // Embedded fields are always of the form T or *T where - // T is a named type. If typ appeared multiple times at + // T is a type name. If typ appeared multiple times at // this depth, f.Type appears multiple times at the next // depth. if f.anonymous { - // Ignore embedded basic types - only user-defined - // named types can have methods or struct fields. typ, isPtr := deref(f.typ) - if t, _ := typ.(*Named); t != nil { - next = append(next, embeddedType{t, concat(e.index, i), e.indirect || isPtr, e.multiples}) - } + // TODO(gri) optimization: ignore types that can't + // have fields or methods (only Named, Struct, and + // Interface types need to be considered). + next = append(next, embeddedType{typ, concat(e.index, i), e.indirect || isPtr, e.multiples}) } } diff --git a/src/go/types/object.go b/src/go/types/object.go index 6c0c5c4a24..3c44348696 100644 --- a/src/go/types/object.go +++ b/src/go/types/object.go @@ -25,7 +25,7 @@ type Object interface { Name() string // package local object name Type() Type // object type Exported() bool // reports whether the name starts with a capital letter - Id() string // object id (see Id below) + Id() string // object name if exported, qualified name if not exported (see func Id) // String returns a human-readable string of the object. String() string @@ -64,15 +64,10 @@ func Id(pkg *Package, name string) string { // inside a package and outside a package - which breaks some // tests) path := "_" - // TODO(gri): shouldn't !ast.IsExported(name) => pkg != nil be an precondition? - // if pkg == nil { - // panic("nil package in lookup of unexported name") - // } - if pkg != nil { + // pkg is nil for objects in Universe scope and possibly types + // introduced via Eval (see also comment in object.sameId) + if pkg != nil && pkg.path != "" { path = pkg.path - if path == "" { - path = "_" - } } return path + "." + name } @@ -154,7 +149,7 @@ func NewConst(pos token.Pos, pkg *Package, name string, typ Type, val constant.V func (obj *Const) Val() constant.Value { return obj.val } func (*Const) isDependency() {} // a constant may be a dependency of an initialization expression -// A TypeName represents a declared type. +// A TypeName represents a name for a (named or alias) type. type TypeName struct { object } @@ -163,6 +158,26 @@ func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName { 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 { + switch t := obj.typ.(type) { + case nil: + return false + case *Basic: + // Any user-defined type name for a basic type is an alias for a + // basic type (because basic types are pre-declared in the Universe + // scope, outside any package scope), and so is any type name with + // a different name than the name of the basic type it refers to. + // Additionaly, we need to look for "byte" and "rune" because they + // are aliases but have the same names (for better error messages). + return obj.pkg != nil || t.name != obj.name || t == universeByte || t == universeRune + case *Named: + return obj != t.obj + default: + return true + } +} + // A Variable represents a declared variable (including function parameters and results, and struct fields). type Var struct { object @@ -215,28 +230,6 @@ func (obj *Func) FullName() string { func (obj *Func) Scope() *Scope { return obj.typ.(*Signature).scope } func (*Func) isDependency() {} // a function may be a dependency of an initialization expression -// An Alias represents a declared alias. -type disabledAlias struct { - object - orig Object // aliased constant, type, variable, or function; never an alias - kind token.Token // token.CONST, token.TYPE, token.VAR, or token.FUNC (only needed during resolve phase) -} - -func disabledNewAlias(pos token.Pos, pkg *Package, name string, orig Object) *disabledAlias { - var typ Type = Typ[Invalid] - if orig != nil { - typ = orig.Type() - } - // No need to set a valid Alias.kind - that field is only used during identifier - // resolution (1st type-checker pass). We could store the field outside but it's - // easier to keep it here. - return &disabledAlias{object{nil, pos, pkg, name, typ, 0, token.NoPos}, orig, token.ILLEGAL} -} - -// Orig returns the aliased object, or nil if there was an error. -// The returned object is never an Alias. -func (obj *disabledAlias) disabledOrig() Object { return obj.orig } - // A Label represents a declared label. type Label struct { object @@ -264,7 +257,9 @@ type Nil struct { } func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) { + var tname *TypeName typ := obj.Type() + switch obj := obj.(type) { case *PkgName: fmt.Fprintf(buf, "package %s", obj.Name()) @@ -277,8 +272,8 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) { buf.WriteString("const") case *TypeName: + tname = obj buf.WriteString("type") - typ = typ.Underlying() case *Var: if obj.isField { @@ -295,10 +290,6 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) { } return - // Alias-related code. Keep for now. - // case *Alias: - // buf.WriteString("alias") - case *Label: buf.WriteString("label") typ = nil @@ -322,10 +313,27 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) { writePackage(buf, obj.Pkg(), qf) } buf.WriteString(obj.Name()) - if typ != nil { - buf.WriteByte(' ') - WriteType(buf, typ, qf) + + if typ == nil { + return + } + + if tname != nil { + // We have a type object: Don't print anything more for + // basic types since there's no more information (names + // are the same; see also comment in TypeName.IsAlias). + if _, ok := typ.(*Basic); ok { + return + } + if tname.IsAlias() { + buf.WriteString(" =") + } else { + typ = typ.Underlying() + } } + + buf.WriteByte(' ') + WriteType(buf, typ, qf) } func writePackage(buf *bytes.Buffer, pkg *Package, qf Qualifier) { @@ -353,15 +361,14 @@ func ObjectString(obj Object, qf Qualifier) string { return buf.String() } -func (obj *PkgName) String() string { return ObjectString(obj, nil) } -func (obj *Const) String() string { return ObjectString(obj, nil) } -func (obj *TypeName) String() string { return ObjectString(obj, nil) } -func (obj *Var) String() string { return ObjectString(obj, nil) } -func (obj *Func) String() string { return ObjectString(obj, nil) } -func (obj *disabledAlias) String() string { return ObjectString(obj, nil) } -func (obj *Label) String() string { return ObjectString(obj, nil) } -func (obj *Builtin) String() string { return ObjectString(obj, nil) } -func (obj *Nil) String() string { return ObjectString(obj, nil) } +func (obj *PkgName) String() string { return ObjectString(obj, nil) } +func (obj *Const) String() string { return ObjectString(obj, nil) } +func (obj *TypeName) String() string { return ObjectString(obj, nil) } +func (obj *Var) String() string { return ObjectString(obj, nil) } +func (obj *Func) String() string { return ObjectString(obj, nil) } +func (obj *Label) String() string { return ObjectString(obj, nil) } +func (obj *Builtin) String() string { return ObjectString(obj, nil) } +func (obj *Nil) String() string { return ObjectString(obj, nil) } func writeFuncName(buf *bytes.Buffer, f *Func, qf Qualifier) { if f.typ != nil { diff --git a/src/go/types/object_test.go b/src/go/types/object_test.go new file mode 100644 index 0000000000..16d7d5c723 --- /dev/null +++ b/src/go/types/object_test.go @@ -0,0 +1,43 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types + +import "testing" + +func TestIsAlias(t *testing.T) { + check := func(obj *TypeName, want bool) { + if got := obj.IsAlias(); got != want { + t.Errorf("%v: got IsAlias = %v; want %v", obj, got, want) + } + } + + // predeclared types + for _, name := range Universe.Names() { + if obj, _ := Universe.Lookup(name).(*TypeName); obj != nil { + check(obj, name == "byte" || name == "rune") + } + } + + // various other types + pkg := NewPackage("p", "p") + t1 := NewTypeName(0, pkg, "t1", nil) + n1 := NewNamed(t1, new(Struct), nil) + for _, test := range []struct { + name *TypeName + alias bool + }{ + {NewTypeName(0, nil, "t0", nil), false}, // no type yet + {NewTypeName(0, pkg, "t0", nil), false}, // no type yet + {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) + } +} diff --git a/src/go/types/predicates.go b/src/go/types/predicates.go index 21fd81e3c2..c3b87dd9cd 100644 --- a/src/go/types/predicates.go +++ b/src/go/types/predicates.go @@ -139,7 +139,7 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool { case *Basic: // Basic types are singletons except for the rune and byte // aliases, thus we cannot solely rely on the x == y check - // above. + // above. See also comment in TypeName.IsAlias. if y, ok := y.(*Basic); ok { return x.kind == y.kind } diff --git a/src/go/types/resolver.go b/src/go/types/resolver.go index 046e147456..939f70a9ca 100644 --- a/src/go/types/resolver.go +++ b/src/go/types/resolver.go @@ -14,13 +14,14 @@ import ( "unicode" ) -// A declInfo describes a package-level const, type, var, func, or alias declaration. +// A declInfo describes a package-level const, type, var, or func declaration. type declInfo struct { file *Scope // scope of file containing this declaration lhs []*Var // lhs of n:1 variable declarations, or nil typ ast.Expr // type, or nil init ast.Expr // init/orig expression, or nil fdecl *ast.FuncDecl // func declaration, or nil + alias bool // type alias declaration // The deps field tracks initialization expression dependencies. // As a special (overloaded) case, it also tracks dependencies of @@ -274,13 +275,6 @@ func (check *Checker) collectObjects() { check.declare(fileScope, nil, obj, token.NoPos) } - // Alias-related code. Keep for now. - // case *ast.AliasSpec: - // obj := NewAlias(s.Name.Pos(), pkg, s.Name.Name, nil) - // obj.typ = nil // unresolved - // obj.kind = d.Tok - // check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, init: s.Orig}) - case *ast.ValueSpec: switch d.Tok { case token.CONST: @@ -347,7 +341,7 @@ func (check *Checker) collectObjects() { case *ast.TypeSpec: obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil) - check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, typ: s.Type}) + check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, typ: s.Type, alias: s.Assign.IsValid()}) default: check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s) diff --git a/src/go/types/testdata/decls4.src b/src/go/types/testdata/decls4.src new file mode 100644 index 0000000000..5e5e2e940b --- /dev/null +++ b/src/go/types/testdata/decls4.src @@ -0,0 +1,150 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// type aliases + +package decls4 + +type ( + T0 [10]int + T1 []byte + T2 struct { + x int + } + T3 interface{ + m() T2 + } + T4 func(int, T0) chan T2 +) + +type ( + Ai = int + A0 = T0 + A1 = T1 + A2 = T2 + A3 = T3 + A4 = T4 + + A10 = [10]int + A11 = []byte + A12 = struct { + x int + } + A13 = interface{ + m() A2 + } + A14 = func(int, A0) chan A2 +) + +// check assignment compatibility due to equality of types +var ( + xi_ int + ai Ai = xi_ + + x0 T0 + a0 A0 = x0 + + x1 T1 + a1 A1 = x1 + + x2 T2 + a2 A2 = x2 + + x3 T3 + a3 A3 = x3 + + x4 T4 + a4 A4 = x4 +) + +// alias receiver types +func (Ai /* ERROR "invalid receiver" */) m1() {} +func (T0) m1() {} +func (A0) m1 /* ERROR already declared */ () {} +func (A0) m2 () {} +func (A3 /* ERROR invalid receiver */ ) m1 () {} +func (A10 /* ERROR invalid receiver */ ) m1() {} + +// x0 has methods m1, m2 declared via receiver type names T0 and A0 +var _ interface{ m1(); m2() } = x0 + +// cycles +type ( + C2 /* ERROR illegal cycle */ = C2 + C3 /* ERROR illegal cycle */ = C4 + C4 = C3 + C5 struct { + f *C6 + } + C6 = C5 + C7 /* ERROR illegal cycle */ struct { + f C8 + } + C8 = C7 +) + +// embedded fields +var ( + s0 struct { T0 } + s1 struct { A0 } = s0 /* ERROR cannot use */ // embedded field names are different +) + +// embedding and lookup of fields and methods +func _(s struct{A0}) { s.A0 = x0 } + +type eX struct{xf int} + +func (eX) xm() + +type eY = struct{eX} // field/method set of eY includes xf, xm + +type eZ = *struct{eX} // field/method set of eZ includes xf, xm + +type eA struct { + eX // eX contributes xf, xm to eA +} + +type eA2 struct { + *eX // *eX contributes xf, xm to eA +} + +type eB struct { + eY // eY contributes xf, xm to eB +} + +type eB2 struct { + *eY // *eY contributes xf, xm to eB +} + +type eC struct { + eZ // eZ contributes xf, xm to eC +} + +var ( + _ = eA{}.xf + _ = eA{}.xm + _ = eA2{}.xf + _ = eA2{}.xm + _ = eB{}.xf + _ = eB{}.xm + _ = eB2{}.xf + _ = eB2{}.xm + _ = eC{}.xf + _ = eC{}.xm +) + +// ambiguous selectors due to embedding via type aliases +type eD struct { + eY + eZ +} + +var ( + _ = eD /* ERROR ambiguous selector */ {}.xf + _ = eD /* ERROR ambiguous selector */ {}.xm +) + +var ( + _ interface{ xm() } = eD /* ERROR missing method xm */ {} +)
\ No newline at end of file diff --git a/src/go/types/typestring.go b/src/go/types/typestring.go index 47378e744c..0f8a7adc24 100644 --- a/src/go/types/typestring.go +++ b/src/go/types/typestring.go @@ -56,6 +56,7 @@ func RelativeTo(pkg *Package) Qualifier { // This flag is exported in the x/tools/go/types package. We don't // need it at the moment in the std repo and so we don't export it // anymore. We should eventually try to remove it altogether. +// TODO(gri) remove this var gcCompatibilityMode bool // TypeString returns the string representation of typ. diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index ecc0a7da02..1e906fc4d8 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -45,17 +45,6 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, path []*TypeNa delete(check.unusedDotImports[scope], pkg) } - // Alias-related code. Keep for now. - // An alias stands for the original object; use that one instead. - // TODO(gri) We should be able to factor out the Typ[Invalid] test. - // if alias, _ := obj.(*Alias); alias != nil { - // obj = original(obj) - // if obj == nil || typ == Typ[Invalid] { - // return - // } - // assert(typ == obj.Type()) - // } - switch obj := obj.(type) { case *PkgName: check.errorf(e.Pos(), "use of package %s not in selector", obj.name) @@ -661,47 +650,41 @@ func (check *Checker) structType(styp *Struct, e *ast.StructType, path []*TypeNa } } else { // anonymous field - name := anonymousFieldIdent(f.Type) + // spec: "An embedded type must be specified as a type name T or as a pointer + // to a non-interface type name *T, and T itself may not be a pointer type." pos := f.Type.Pos() + name := anonymousFieldIdent(f.Type) + if name == nil { + check.invalidAST(pos, "anonymous field type %s has no name", f.Type) + continue + } t, isPtr := deref(typ) - switch t := t.(type) { + // Because we have a name, typ must be of the form T or *T, where T is the name + // of a (named or alias) type, and t (= deref(typ)) must be the type of T. + switch t := t.Underlying().(type) { case *Basic: if t == Typ[Invalid] { // error was reported before continue } + // unsafe.Pointer is treated like a regular pointer if t.kind == UnsafePointer { check.errorf(pos, "anonymous field type cannot be unsafe.Pointer") continue } - add(f, name, true, pos) - - case *Named: - // spec: "An embedded type must be specified as a type name - // T or as a pointer to a non-interface type name *T, and T - // itself may not be a pointer type." - switch u := t.underlying.(type) { - case *Basic: - // unsafe.Pointer is treated like a regular pointer - if u.kind == UnsafePointer { - check.errorf(pos, "anonymous field type cannot be unsafe.Pointer") - continue - } - case *Pointer: - check.errorf(pos, "anonymous field type cannot be a pointer") + + case *Pointer: + check.errorf(pos, "anonymous field type cannot be a pointer") + continue + + case *Interface: + if isPtr { + check.errorf(pos, "anonymous field type cannot be a pointer to an interface") continue - case *Interface: - if isPtr { - check.errorf(pos, "anonymous field type cannot be a pointer to an interface") - continue - } } - add(f, name, true, pos) - - default: - check.invalidAST(pos, "anonymous field type %s must be named", typ) } + add(f, name, true, pos) } } @@ -714,7 +697,10 @@ func anonymousFieldIdent(e ast.Expr) *ast.Ident { case *ast.Ident: return e case *ast.StarExpr: - return anonymousFieldIdent(e.X) + // *T is valid, but **T is not + if _, ok := e.X.(*ast.StarExpr); !ok { + return anonymousFieldIdent(e.X) + } case *ast.SelectorExpr: return e.Sel } diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index 7f8c12981c..382ad6be2a 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -3731,7 +3731,7 @@ func checkSameType(t *testing.T, x, y interface{}) { func TestArrayOf(t *testing.T) { // check construction and use of type not in binary - for _, table := range []struct { + tests := []struct { n int value func(i int) interface{} comparable bool @@ -3809,7 +3809,9 @@ func TestArrayOf(t *testing.T) { comparable: true, want: "[{0 0} {1 1} {2 2} {3 3} {4 4} {5 5} {6 6} {7 7} {8 8} {9 9}]", }, - } { + } + + for _, table := range tests { at := ArrayOf(table.n, TypeOf(table.value(0))) v := New(at).Elem() vok := New(at).Elem() @@ -4175,50 +4177,58 @@ func TestStructOfExportRules(t *testing.T) { f() } - for i, test := range []struct { + tests := []struct { field StructField mustPanic bool exported bool }{ { - field: StructField{Name: "", Type: TypeOf(S1{})}, - mustPanic: false, - exported: true, + field: StructField{Name: "S1", Anonymous: true, Type: TypeOf(S1{})}, + exported: true, }, { - field: StructField{Name: "", Type: TypeOf((*S1)(nil))}, - mustPanic: false, - exported: true, + field: StructField{Name: "S1", Anonymous: true, Type: TypeOf((*S1)(nil))}, + exported: true, }, { - field: StructField{Name: "", Type: TypeOf(s2{})}, - mustPanic: false, - exported: false, + field: StructField{Name: "s2", Anonymous: true, Type: TypeOf(s2{})}, + mustPanic: true, }, { - field: StructField{Name: "", Type: TypeOf((*s2)(nil))}, - mustPanic: false, - exported: false, + field: StructField{Name: "s2", Anonymous: true, Type: TypeOf((*s2)(nil))}, + mustPanic: true, }, { - field: StructField{Name: "", Type: TypeOf(S1{}), PkgPath: "other/pkg"}, + field: StructField{Name: "Name", Type: nil, PkgPath: ""}, mustPanic: true, - exported: true, }, { - field: StructField{Name: "", Type: TypeOf((*S1)(nil)), PkgPath: "other/pkg"}, + field: StructField{Name: "", Type: TypeOf(S1{}), PkgPath: ""}, mustPanic: true, - exported: true, }, { - field: StructField{Name: "", Type: TypeOf(s2{}), PkgPath: "other/pkg"}, + field: StructField{Name: "S1", Anonymous: true, Type: TypeOf(S1{}), PkgPath: "other/pkg"}, mustPanic: true, - exported: false, }, { - field: StructField{Name: "", Type: TypeOf((*s2)(nil)), PkgPath: "other/pkg"}, + field: StructField{Name: "S1", Anonymous: true, Type: TypeOf((*S1)(nil)), PkgPath: "other/pkg"}, + mustPanic: true, + }, + { + field: StructField{Name: "s2", Anonymous: true, Type: TypeOf(s2{}), PkgPath: "other/pkg"}, + mustPanic: true, + }, + { + field: StructField{Name: "s2", Anonymous: true, Type: TypeOf((*s2)(nil)), PkgPath: "other/pkg"}, + mustPanic: true, + }, + { + field: StructField{Name: "s2", Type: TypeOf(int(0)), PkgPath: "other/pkg"}, + mustPanic: true, + }, + { + field: StructField{Name: "s2", Type: TypeOf(int(0)), PkgPath: "other/pkg"}, mustPanic: true, - exported: false, }, { field: StructField{Name: "S", Type: TypeOf(S1{})}, @@ -4226,81 +4236,68 @@ func TestStructOfExportRules(t *testing.T) { exported: true, }, { - field: StructField{Name: "S", Type: TypeOf((*S1)(nil))}, - mustPanic: false, - exported: true, + field: StructField{Name: "S", Type: TypeOf((*S1)(nil))}, + exported: true, }, { - field: StructField{Name: "S", Type: TypeOf(s2{})}, - mustPanic: false, - exported: true, + field: StructField{Name: "S", Type: TypeOf(s2{})}, + exported: true, }, { - field: StructField{Name: "S", Type: TypeOf((*s2)(nil))}, - mustPanic: false, - exported: true, + field: StructField{Name: "S", Type: TypeOf((*s2)(nil))}, + exported: true, }, { field: StructField{Name: "s", Type: TypeOf(S1{})}, mustPanic: true, - exported: false, }, { field: StructField{Name: "s", Type: TypeOf((*S1)(nil))}, mustPanic: true, - exported: false, }, { field: StructField{Name: "s", Type: TypeOf(s2{})}, mustPanic: true, - exported: false, }, { field: StructField{Name: "s", Type: TypeOf((*s2)(nil))}, mustPanic: true, - exported: false, }, { field: StructField{Name: "s", Type: TypeOf(S1{}), PkgPath: "other/pkg"}, mustPanic: true, // TODO(sbinet): creating a name with a package path - exported: false, }, { field: StructField{Name: "s", Type: TypeOf((*S1)(nil)), PkgPath: "other/pkg"}, mustPanic: true, // TODO(sbinet): creating a name with a package path - exported: false, }, { field: StructField{Name: "s", Type: TypeOf(s2{}), PkgPath: "other/pkg"}, mustPanic: true, // TODO(sbinet): creating a name with a package path - exported: false, }, { field: StructField{Name: "s", Type: TypeOf((*s2)(nil)), PkgPath: "other/pkg"}, mustPanic: true, // TODO(sbinet): creating a name with a package path - exported: false, }, { field: StructField{Name: "", Type: TypeOf(ΦType{})}, - mustPanic: false, - exported: true, + mustPanic: true, }, { field: StructField{Name: "", Type: TypeOf(φType{})}, - mustPanic: false, - exported: false, + mustPanic: true, }, { - field: StructField{Name: "Φ", Type: TypeOf(0)}, - mustPanic: false, - exported: true, + field: StructField{Name: "Φ", Type: TypeOf(0)}, + exported: true, }, { - field: StructField{Name: "φ", Type: TypeOf(0)}, - mustPanic: false, - exported: false, + field: StructField{Name: "φ", Type: TypeOf(0)}, + exported: false, }, - } { + } + + for i, test := range tests { testPanic(i, test.mustPanic, func() { typ := StructOf([]StructField{test.field}) if typ == nil { @@ -4310,7 +4307,7 @@ func TestStructOfExportRules(t *testing.T) { field := typ.Field(0) n := field.Name if n == "" { - n = field.Type.Name() + panic("field.Name must not be empty") } exported := isExported(n) if exported != test.exported { @@ -4388,7 +4385,7 @@ func TestStructOfGenericAlg(t *testing.T) { {Name: "S1", Type: st1}, }) - for _, table := range []struct { + tests := []struct { rt Type idx []int }{ @@ -4469,7 +4466,9 @@ func TestStructOfGenericAlg(t *testing.T) { ), idx: []int{2}, }, - } { + } + + for _, table := range tests { v1 := New(table.rt).Elem() v2 := New(table.rt).Elem() @@ -4571,18 +4570,21 @@ func TestStructOfWithInterface(t *testing.T) { type Iface interface { Get() int } - for i, table := range []struct { + tests := []struct { + name string typ Type val Value impl bool }{ { + name: "StructI", typ: TypeOf(StructI(want)), val: ValueOf(StructI(want)), impl: true, }, { - typ: PtrTo(TypeOf(StructI(want))), + name: "StructI", + typ: PtrTo(TypeOf(StructI(want))), val: ValueOf(func() interface{} { v := StructI(want) return &v @@ -4590,7 +4592,8 @@ func TestStructOfWithInterface(t *testing.T) { impl: true, }, { - typ: PtrTo(TypeOf(StructIPtr(want))), + name: "StructIPtr", + typ: PtrTo(TypeOf(StructIPtr(want))), val: ValueOf(func() interface{} { v := StructIPtr(want) return &v @@ -4598,6 +4601,7 @@ func TestStructOfWithInterface(t *testing.T) { impl: true, }, { + name: "StructIPtr", typ: TypeOf(StructIPtr(want)), val: ValueOf(StructIPtr(want)), impl: false, @@ -4607,13 +4611,16 @@ func TestStructOfWithInterface(t *testing.T) { // val: ValueOf(StructI(want)), // impl: true, // }, - } { + } + + for i, table := range tests { rt := StructOf( []StructField{ { - Name: "", - PkgPath: "", - Type: table.typ, + Name: table.name, + Anonymous: true, + PkgPath: "", + Type: table.typ, }, }, ) @@ -6059,6 +6066,7 @@ func TestSwapper(t *testing.T) { want: []pairPtr{{5, 6, &c}, {3, 4, &b}, {1, 2, &a}}, }, } + for i, tt := range tests { inStr := fmt.Sprint(tt.in) Swapper(tt.in)(tt.i, tt.j) @@ -6084,3 +6092,36 @@ func TestUnaddressableField(t *testing.T) { lv.Set(rv) }) } + +type Tint int + +type Tint2 = Tint + +type Talias1 struct { + byte + uint8 + int + int32 + rune +} + +type Talias2 struct { + Tint + Tint2 +} + +func TestAliasNames(t *testing.T) { + t1 := Talias1{byte: 1, uint8: 2, int: 3, int32: 4, rune: 5} + out := fmt.Sprintf("%#v", t1) + want := "reflect_test.Talias1{byte:0x1, uint8:0x2, int:3, int32:4, rune:5}" + if out != want { + t.Errorf("Talias1 print:\nhave: %s\nwant: %s", out, want) + } + + t2 := Talias2{Tint: 1, Tint2: 2} + out = fmt.Sprintf("%#v", t2) + want = "reflect_test.Talias2{Tint:1, Tint2:2}" + if out != want { + t.Errorf("Talias2 print:\nhave: %s\nwant: %s", out, want) + } +} diff --git a/src/reflect/type.go b/src/reflect/type.go index 5d3c5c612a..40859093be 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -417,9 +417,17 @@ type sliceType struct { // Struct field type structField struct { - name name // name is empty for embedded fields - typ *rtype // type of field - offset uintptr // byte offset of field within struct + name name // name is always non-empty + typ *rtype // type of field + offsetAnon uintptr // byte offset of field<<1 | isAnonymous +} + +func (f *structField) offset() uintptr { + return f.offsetAnon >> 1 +} + +func (f *structField) anon() bool { + return f.offsetAnon&1 != 0 } // structType represents a struct type. @@ -1215,16 +1223,8 @@ func (t *structType) Field(i int) (f StructField) { } p := &t.fields[i] f.Type = toType(p.typ) - if name := p.name.name(); name != "" { - f.Name = name - } else { - t := f.Type - if t.Kind() == Ptr { - t = t.Elem() - } - f.Name = t.Name() - f.Anonymous = true - } + f.Name = p.name.name() + f.Anonymous = p.anon() if !p.name.isExported() { f.PkgPath = p.name.pkgPath() if f.PkgPath == "" { @@ -1234,7 +1234,7 @@ func (t *structType) Field(i int) (f StructField) { if tag := p.name.tag(); tag != "" { f.Tag = StructTag(tag) } - f.Offset = p.offset + f.Offset = p.offset() // NOTE(rsc): This is the only allocation in the interface // presented by a reflect.Type. It would be nice to avoid, @@ -1321,19 +1321,15 @@ func (t *structType) FieldByNameFunc(match func(string) bool) (result StructFiel visited[t] = true for i := range t.fields { f := &t.fields[i] - // Find name and type for field f. - var fname string + // Find name and (for anonymous field) type for field f. + fname := f.name.name() var ntyp *rtype - if name := f.name.name(); name != "" { - fname = name - } else { + if f.anon() { // Anonymous field of type T or *T. - // Name taken from type. ntyp = f.typ if ntyp.Kind() == Ptr { ntyp = ntyp.Elem().common() } - fname = ntyp.Name() } // Does it match? @@ -1390,14 +1386,12 @@ func (t *structType) FieldByName(name string) (f StructField, present bool) { if name != "" { for i := range t.fields { tf := &t.fields[i] - tfname := tf.name.name() - if tfname == "" { - hasAnon = true - continue - } - if tfname == name { + if tf.name.name() == name { return t.Field(i), true } + if tf.anon() { + hasAnon = true + } } } if !hasAnon { @@ -1695,7 +1689,7 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool { if cmpTags && tf.name.tag() != vf.name.tag() { return false } - if tf.offset != vf.offset { + if tf.offsetAnon != vf.offsetAnon { return false } if !tf.name.isExported() { @@ -2404,6 +2398,9 @@ func StructOf(fields []StructField) Type { lastzero := uintptr(0) repr = append(repr, "struct {"...) for i, field := range fields { + if field.Name == "" { + panic("reflect.StructOf: field " + strconv.Itoa(i) + " has no name") + } if field.Type == nil { panic("reflect.StructOf: field " + strconv.Itoa(i) + " has no type") } @@ -2416,13 +2413,11 @@ func StructOf(fields []StructField) Type { hasPtr = true } - name := "" // Update string and hash - if f.name.nameLen() > 0 { - hash = fnv1(hash, []byte(f.name.name())...) - repr = append(repr, (" " + f.name.name())...) - name = f.name.name() - } else { + name := f.name.name() + hash = fnv1(hash, []byte(name)...) + repr = append(repr, (" " + name)...) + if f.anon() { // Embedded field if f.typ.Kind() == Ptr { // Embedded ** and *interface{} are illegal @@ -2430,11 +2425,7 @@ func StructOf(fields []StructField) Type { if k := elem.Kind(); k == Ptr || k == Interface { panic("reflect.StructOf: illegal anonymous field type " + ft.String()) } - name = elem.String() - } else { - name = ft.String() } - // TODO(sbinet) check for syntactically impossible type names? switch f.typ.Kind() { case Interface: @@ -2566,11 +2557,12 @@ func StructOf(fields []StructField) Type { comparable = comparable && (ft.alg.equal != nil) hashable = hashable && (ft.alg.hash != nil) - f.offset = align(size, uintptr(ft.align)) + offset := align(size, uintptr(ft.align)) if ft.align > typalign { typalign = ft.align } - size = f.offset + ft.size + size = offset + ft.size + f.offsetAnon |= offset << 1 if ft.size == 0 { lastzero = size @@ -2762,7 +2754,7 @@ func StructOf(fields []StructField) Type { typ.alg.hash = func(p unsafe.Pointer, seed uintptr) uintptr { o := seed for _, ft := range typ.fields { - pi := unsafe.Pointer(uintptr(p) + ft.offset) + pi := unsafe.Pointer(uintptr(p) + ft.offset()) o = ft.typ.alg.hash(pi, o) } return o @@ -2772,8 +2764,8 @@ func StructOf(fields []StructField) Type { if comparable { typ.alg.equal = func(p, q unsafe.Pointer) bool { for _, ft := range typ.fields { - pi := unsafe.Pointer(uintptr(p) + ft.offset) - qi := unsafe.Pointer(uintptr(q) + ft.offset) + pi := unsafe.Pointer(uintptr(p) + ft.offset()) + qi := unsafe.Pointer(uintptr(q) + ft.offset()) if !ft.typ.alg.equal(pi, qi) { return false } @@ -2795,25 +2787,27 @@ func StructOf(fields []StructField) Type { } func runtimeStructField(field StructField) structField { - exported := field.PkgPath == "" - if field.Name == "" { - t := field.Type.(*rtype) - if t.Kind() == Ptr { - t = t.Elem().(*rtype) - } - exported = t.nameOff(t.str).isExported() - } else if exported { - b0 := field.Name[0] - if ('a' <= b0 && b0 <= 'z') || b0 == '_' { - panic("reflect.StructOf: field \"" + field.Name + "\" is unexported but has no PkgPath") - } + if field.PkgPath != "" { + panic("reflect.StructOf: StructOf does not allow unexported fields") + } + + // Best-effort check for misuse. + // Since PkgPath is empty, not much harm done if Unicode lowercase slips through. + c := field.Name[0] + if 'a' <= c && c <= 'z' || c == '_' { + panic("reflect.StructOf: field \"" + field.Name + "\" is unexported but missing PkgPath") + } + + offsetAnon := uintptr(0) + if field.Anonymous { + offsetAnon |= 1 } - _ = resolveReflectType(field.Type.common()) + resolveReflectType(field.Type.common()) // install in runtime return structField{ - name: newName(field.Name, string(field.Tag), field.PkgPath, exported), - typ: field.Type.common(), - offset: 0, + name: newName(field.Name, string(field.Tag), "", true), + typ: field.Type.common(), + offsetAnon: offsetAnon, } } @@ -2836,7 +2830,7 @@ func typeptrdata(t *rtype) uintptr { } } f := st.fields[field] - return f.offset + f.typ.ptrdata + return f.offset() + f.typ.ptrdata default: panic("reflect.typeptrdata: unexpected type, " + t.String()) @@ -3210,7 +3204,7 @@ func addTypeBits(bv *bitVector, offset uintptr, t *rtype) { tt := (*structType)(unsafe.Pointer(t)) for i := range tt.fields { f := &tt.fields[i] - addTypeBits(bv, offset+f.offset, f.typ) + addTypeBits(bv, offset+f.offset(), f.typ) } } } diff --git a/src/reflect/value.go b/src/reflect/value.go index 387b9f3c13..125f04eaee 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -769,7 +769,7 @@ func (v Value) Field(i int) Value { fl := v.flag&(flagStickyRO|flagIndir|flagAddr) | flag(typ.Kind()) // Using an unexported field forces flagRO. if !field.name.isExported() { - if field.name.name() == "" { + if field.anon() { fl |= flagEmbedRO } else { fl |= flagStickyRO @@ -780,7 +780,7 @@ func (v Value) Field(i int) Value { // In the former case, we want v.ptr + offset. // In the latter case, we must have field.offset = 0, // so v.ptr + field.offset is still okay. - ptr := unsafe.Pointer(uintptr(v.ptr) + field.offset) + ptr := unsafe.Pointer(uintptr(v.ptr) + field.offset()) return Value{typ, ptr, fl} } diff --git a/src/runtime/cgocall.go b/src/runtime/cgocall.go index 69e29ef976..879e786231 100644 --- a/src/runtime/cgocall.go +++ b/src/runtime/cgocall.go @@ -531,7 +531,7 @@ func cgoCheckArg(t *_type, p unsafe.Pointer, indir, top bool, msg string) { return } for _, f := range st.fields { - cgoCheckArg(f.typ, add(p, f.offset), true, top, msg) + cgoCheckArg(f.typ, add(p, f.offset()), true, top, msg) } case kindPtr, kindUnsafePointer: if indir { diff --git a/src/runtime/type.go b/src/runtime/type.go index 3ecc54c72c..10442eff69 100644 --- a/src/runtime/type.go +++ b/src/runtime/type.go @@ -390,9 +390,13 @@ type ptrtype struct { } type structfield struct { - name name - typ *_type - offset uintptr + name name + typ *_type + offsetAnon uintptr +} + +func (f *structfield) offset() uintptr { + return f.offsetAnon >> 1 } type structtype struct { @@ -650,7 +654,7 @@ func typesEqual(t, v *_type) bool { if tf.name.tag() != vf.name.tag() { return false } - if tf.offset != vf.offset { + if tf.offsetAnon != vf.offsetAnon { return false } } diff --git a/test/alias2.go b/test/alias2.go new file mode 100644 index 0000000000..32c3654995 --- /dev/null +++ b/test/alias2.go @@ -0,0 +1,104 @@ +// errorcheck + +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test basic restrictions on type aliases. + +package p + +import ( + "reflect" + . "reflect" +) + +type T0 struct{} + +// Valid type alias declarations. + +type _ = T0 +type _ = int +type _ = struct{} +type _ = reflect.Value +type _ = Value + +type ( + A0 = T0 + A1 = int + A2 = struct{} + A3 = reflect.Value + A4 = Value + A5 = Value + + N0 A0 +) + +// Methods can be declared on the original named type and the alias. +func (T0) m1() {} // GCCGO_ERROR "previous" +func (*T0) m1() {} // ERROR "method redeclared: T0\.m1|redefinition of .m1." +func (A0) m1() {} // ERROR "T0\.m1 redeclared in this block|redefinition of .m1." +func (A0) m1() {} // ERROR "T0\.m1 redeclared in this block|redefinition of .m1." +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|incompatible type" +var _ N0 = A0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment|incompatible type" + +var _ A5 = Value{} + +var _ interface { + m1() + m2() +} = T0{} + +var _ interface { + m1() + m2() +} = A0{} + +func _() { + type _ = T0 + type _ = int + type _ = struct{} + type _ = reflect.Value + type _ = Value + + type ( + A0 = T0 + A1 = int + A2 = struct{} + 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|incompatible type" + var _ N0 = A0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment|incompatible type" + + var _ A5 = Value{} // ERROR "cannot use reflect\.Value literal \(type reflect.Value\) as type A5 in assignment|incompatible type" +} + +// Invalid type alias declarations. + +type _ = reflect.ValueOf // ERROR "reflect.ValueOf is not a type|expected type" + +func (A1) m() {} // ERROR "cannot define new methods on non-local type int|may not define methods on non-local type" +func (A2) m() {} // ERROR "invalid receiver type" +func (A3) m() {} // ERROR "cannot define new methods on non-local type reflect.Value|may not define methods on non-local type" +func (A4) m() {} // ERROR "reflect.Value.m redeclared in this block" "cannot define new methods on non-local type reflect.Value|may not define methods on non-local type" + +type B1 = struct{} + +func (B1) m() {} // ERROR "m redeclared in this block" "invalid receiver type" + +// TODO(gri) expand diff --git a/test/alias3.dir/a.go b/test/alias3.dir/a.go new file mode 100644 index 0000000000..09b3408d16 --- /dev/null +++ b/test/alias3.dir/a.go @@ -0,0 +1,42 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +import "go/build" + +type ( + Float64 = float64 + Rune = rune +) + +type ( + Int int + IntAlias = Int + IntAlias2 = IntAlias + S struct { + Int + IntAlias + IntAlias2 + } +) + +type ( + Context = build.Context +) + +type ( + I1 interface { + M1(IntAlias2) Float64 + M2() Context + } + + I2 = interface { + M1(Int) float64 + M2() build.Context + } +) + +var i1 I1 +var i2 I2 = i1 diff --git a/test/alias3.dir/b.go b/test/alias3.dir/b.go new file mode 100644 index 0000000000..8a86cc0643 --- /dev/null +++ b/test/alias3.dir/b.go @@ -0,0 +1,26 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package b + +import ( + "./a" + . "go/build" +) + +func F(x float64) a.Float64 { + return x +} + +type MyContext = Context // = build.Context + +var C a.Context = Default + +type S struct{} + +func (S) M1(x a.IntAlias) float64 { return a.Float64(x) } +func (S) M2() Context { return Default } + +var _ a.I1 = S{} +var _ a.I2 = S{} diff --git a/test/alias3.dir/c.go b/test/alias3.dir/c.go new file mode 100644 index 0000000000..161d5934c2 --- /dev/null +++ b/test/alias3.dir/c.go @@ -0,0 +1,25 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "./a" + "./b" +) + +func main() { + var _ float64 = b.F(0) + var _ a.Rune = int32(0) + + // embedded types can have different names but the same types + var s a.S + s.Int = 1 + s.IntAlias = s.Int + s.IntAlias2 = s.Int + + // aliases denote identical types across packages + var c a.Context = b.C + var _ b.MyContext = c +} diff --git a/test/alias3.go b/test/alias3.go new file mode 100644 index 0000000000..c3732c311b --- /dev/null +++ b/test/alias3.go @@ -0,0 +1,7 @@ +// rundir + +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ignored diff --git a/test/fixedbugs/issue18640.go b/test/fixedbugs/issue18640.go new file mode 100644 index 0000000000..c4f948b706 --- /dev/null +++ b/test/fixedbugs/issue18640.go @@ -0,0 +1,26 @@ +// compile + +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type ( + a = b + b struct { + *a + } + + c struct { + *d + } + d = c + + e = f + f = g + g = []h + h i + i = j + j = e +) diff --git a/test/fixedbugs/issue18655.go b/test/fixedbugs/issue18655.go new file mode 100644 index 0000000000..abc2600280 --- /dev/null +++ b/test/fixedbugs/issue18655.go @@ -0,0 +1,22 @@ +// errorcheck + +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type T struct{} +type A = T +type B = T + +func (T) m() {} +func (T) m() {} // ERROR "redeclared" +func (A) m() {} // ERROR "redeclared" +func (A) m() {} // ERROR "redeclared" +func (B) m() {} // ERROR "redeclared" +func (B) m() {} // ERROR "redeclared" + +func (*T) m() {} // ERROR "redeclared" +func (*A) m() {} // ERROR "redeclared" +func (*B) m() {} // ERROR "redeclared" |