aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--VERSION2
-rw-r--r--doc/go_spec.html50
-rw-r--r--src/cmd/compile/internal/gc/align.go8
-rw-r--r--src/cmd/compile/internal/gc/bexport.go82
-rw-r--r--src/cmd/compile/internal/gc/bimport.go77
-rw-r--r--src/cmd/compile/internal/gc/dcl.go48
-rw-r--r--src/cmd/compile/internal/gc/export.go27
-rw-r--r--src/cmd/compile/internal/gc/fmt.go4
-rw-r--r--src/cmd/compile/internal/gc/go.go9
-rw-r--r--src/cmd/compile/internal/gc/main.go24
-rw-r--r--src/cmd/compile/internal/gc/noder.go42
-rw-r--r--src/cmd/compile/internal/gc/obj.go2
-rw-r--r--src/cmd/compile/internal/gc/reflect.go11
-rw-r--r--src/cmd/compile/internal/gc/syntax.go17
-rw-r--r--src/cmd/compile/internal/gc/typecheck.go58
-rw-r--r--src/cmd/compile/internal/gc/universe.go8
-rw-r--r--src/cmd/compile/internal/syntax/nodes.go1
-rw-r--r--src/cmd/compile/internal/syntax/parser.go3
-rw-r--r--src/cmd/compile/internal/syntax/printer.go6
-rw-r--r--src/cmd/compile/internal/syntax/printer_test.go17
-rw-r--r--src/cmd/dist/test.go1
-rw-r--r--src/cmd/doc/doc_test.go14
-rw-r--r--src/cmd/doc/pkg.go6
-rw-r--r--src/cmd/doc/testdata/pkg.go4
-rw-r--r--src/cmd/gofmt/testdata/typealias.golden24
-rw-r--r--src/cmd/gofmt/testdata/typealias.input24
-rw-r--r--src/cmd/link/internal/ld/decodesym.go2
-rw-r--r--src/go/ast/ast.go1
-rw-r--r--src/go/build/build.go3
-rw-r--r--src/go/build/doc.go1
-rw-r--r--src/go/internal/gccgoimporter/importer_test.go1
-rw-r--r--src/go/internal/gccgoimporter/parser.go48
-rw-r--r--src/go/internal/gccgoimporter/testdata/alias.gox4
-rw-r--r--src/go/internal/gcimporter/bimport.go88
-rw-r--r--src/go/parser/parser.go5
-rw-r--r--src/go/parser/short_test.go2
-rw-r--r--src/go/printer/nodes.go3
-rw-r--r--src/go/printer/testdata/declarations.golden15
-rw-r--r--src/go/printer/testdata/declarations.input15
-rw-r--r--src/go/types/api_test.go152
-rw-r--r--src/go/types/call.go8
-rw-r--r--src/go/types/check_test.go2
-rw-r--r--src/go/types/decl.go194
-rw-r--r--src/go/types/example_test.go4
-rw-r--r--src/go/types/lookup.go83
-rw-r--r--src/go/types/methodset.go52
-rw-r--r--src/go/types/object.go105
-rw-r--r--src/go/types/object_test.go43
-rw-r--r--src/go/types/predicates.go2
-rw-r--r--src/go/types/resolver.go12
-rw-r--r--src/go/types/testdata/decls4.src150
-rw-r--r--src/go/types/typestring.go1
-rw-r--r--src/go/types/typexpr.go62
-rw-r--r--src/reflect/all_test.go163
-rw-r--r--src/reflect/type.go116
-rw-r--r--src/reflect/value.go4
-rw-r--r--src/runtime/cgocall.go2
-rw-r--r--src/runtime/type.go12
-rw-r--r--test/alias2.go104
-rw-r--r--test/alias3.dir/a.go42
-rw-r--r--test/alias3.dir/b.go26
-rw-r--r--test/alias3.dir/c.go25
-rw-r--r--test/alias3.go7
-rw-r--r--test/fixedbugs/issue18640.go26
-rw-r--r--test/fixedbugs/issue18655.go22
65 files changed, 1335 insertions, 841 deletions
diff --git a/VERSION b/VERSION
index 3e144cd980..7b931138d7 100644
--- a/VERSION
+++ b/VERSION
@@ -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"