From 2e5116bd999be18691d860e47cb87f1446cf70fe Mon Sep 17 00:00:00 2001
From: Robert Griesemer
Date: Fri, 16 Dec 2016 15:10:07 -0800
Subject: [PATCH 01/26] [dev.typealias] go/ast, go/parser, go/printer,
go/types: initial type alias support
Parsing and printing support for type aliases complete.
go/types recognizes them an issues an "unimplemented" error for now.
For #18130.
Change-Id: I9f2f7b1971b527276b698d9347bcd094ef0012ee
Reviewed-on: https://go-review.googlesource.com/34986
Run-TryBot: Robert Griesemer
Reviewed-by: Matthew Dempsky
TryBot-Result: Gobot Gobot
---
src/go/ast/ast.go | 1 +
src/go/parser/parser.go | 5 ++++-
src/go/parser/short_test.go | 2 ++
src/go/printer/nodes.go | 3 +++
src/go/printer/testdata/declarations.golden | 15 +++++++++++++++
src/go/printer/testdata/declarations.input | 15 +++++++++++++++
src/go/types/decl.go | 3 +++
src/go/types/resolver.go | 3 +++
src/go/types/testdata/decls0.src | 8 ++++++++
9 files changed, 54 insertions(+), 1 deletion(-)
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/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 11f26d45ea..5a408cd571 100644
--- a/src/go/printer/nodes.go
+++ b/src/go/printer/nodes.go
@@ -1445,6 +1445,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/decl.go b/src/go/types/decl.go
index dced7a6d6d..2472aa3434 100644
--- a/src/go/types/decl.go
+++ b/src/go/types/decl.go
@@ -534,6 +534,9 @@ func (check *Checker) declStmt(decl ast.Decl) {
}
case *ast.TypeSpec:
+ if s.Assign.IsValid() {
+ check.errorf(s.Assign, "type alias declarations not yet implemented")
+ }
obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
// spec: "The scope of a type identifier declared inside a function
// begins at the identifier in the TypeSpec and ends at the end of
diff --git a/src/go/types/resolver.go b/src/go/types/resolver.go
index 046e147456..d37f93de45 100644
--- a/src/go/types/resolver.go
+++ b/src/go/types/resolver.go
@@ -346,6 +346,9 @@ func (check *Checker) collectObjects() {
}
case *ast.TypeSpec:
+ if s.Assign.IsValid() {
+ check.errorf(s.Assign, "type alias declarations not yet implemented")
+ }
obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, typ: s.Type})
diff --git a/src/go/types/testdata/decls0.src b/src/go/types/testdata/decls0.src
index d4df386b13..3ed1b976e5 100644
--- a/src/go/types/testdata/decls0.src
+++ b/src/go/types/testdata/decls0.src
@@ -208,3 +208,11 @@ func (BlankT) _() {}
func (BlankT) _(int) {}
func (BlankT) _() int { return 0 }
func (BlankT) _(int) int { return 0}
+
+// type alias declarations
+// TODO(gri) complete this
+type (
+ __ = /* ERROR not yet implemented */ int
+ a0 = /* ERROR not yet implemented */ int
+ a1 = /* ERROR not yet implemented */ struct{}
+)
From e0a05c274aa5a3917c5e53f72537e38bb05c10d6 Mon Sep 17 00:00:00 2001
From: Robert Griesemer
Date: Fri, 16 Dec 2016 15:56:05 -0800
Subject: [PATCH 02/26] [dev.typealias] cmd/gofmt: added test cases for alias
type declarations
For #18130.
Change-Id: I95e84130df40db5241e0cc25c36873c3281199ff
Reviewed-on: https://go-review.googlesource.com/34987
Reviewed-by: Matthew Dempsky
---
src/cmd/gofmt/testdata/typealias.golden | 24 ++++++++++++++++++++++++
src/cmd/gofmt/testdata/typealias.input | 24 ++++++++++++++++++++++++
2 files changed, 48 insertions(+)
create mode 100644 src/cmd/gofmt/testdata/typealias.golden
create mode 100644 src/cmd/gofmt/testdata/typealias.input
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
+)
From 3e119404372fd0d47de1458802b68522f593bf36 Mon Sep 17 00:00:00 2001
From: Robert Griesemer
Date: Fri, 16 Dec 2016 16:28:30 -0800
Subject: [PATCH 03/26] [dev.typealias] cmd/compile: recognize type aliases but
complain for now (not yet supported)
Added test file.
For #18130.
Change-Id: Ifcfd7cd1acf9dd6a2f4f3d85979d232bb6b8c6b1
Reviewed-on: https://go-review.googlesource.com/34988
Run-TryBot: Robert Griesemer
Reviewed-by: Matthew Dempsky
TryBot-Result: Gobot Gobot
---
src/cmd/compile/internal/gc/noder.go | 4 ++
src/cmd/compile/internal/syntax/nodes.go | 1 +
src/cmd/compile/internal/syntax/parser.go | 3 +-
src/cmd/compile/internal/syntax/printer.go | 6 +-
.../compile/internal/syntax/printer_test.go | 17 ++++++
test/alias2.go | 58 +++++++++++++++++++
6 files changed, 87 insertions(+), 2 deletions(-)
create mode 100644 test/alias2.go
diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go
index a501cb67b6..3f6fe20b6b 100644
--- a/src/cmd/compile/internal/gc/noder.go
+++ b/src/cmd/compile/internal/gc/noder.go
@@ -185,6 +185,10 @@ func (p *noder) constDecl(decl *syntax.ConstDecl) []*Node {
}
func (p *noder) typeDecl(decl *syntax.TypeDecl) *Node {
+ if decl.Alias {
+ yyerror("type alias declarations unimplemented")
+ }
+
name := typedcl0(p.name(decl.Name))
name.Name.Param.Pragma = Pragma(decl.Pragma)
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 121dfb75e5..1185507238 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/test/alias2.go b/test/alias2.go
new file mode 100644
index 0000000000..25df7c287d
--- /dev/null
+++ b/test/alias2.go
@@ -0,0 +1,58 @@
+// 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.
+
+// The compiler doesn't implement type aliases yet,
+// so for now we get the same error (unimplemented)
+// everywhere, OR-ed into the ERROR checks.
+// TODO(gri) remove the need for "unimplemented"
+
+package p
+
+import (
+ "reflect"
+ . "reflect"
+)
+
+// Valid type alias declarations.
+
+type _ = int // ERROR "unimplemented"
+type _ = struct{} // ERROR "unimplemented"
+type _ = reflect.Value // ERROR "unimplemented"
+type _ = Value // ERROR "unimplemented"
+
+type (
+ a1 = int // ERROR "unimplemented"
+ a2 = struct{} // ERROR "unimplemented"
+ a3 = reflect.Value // ERROR "unimplemented"
+ a4 = Value // ERROR "unimplemented"
+)
+
+func _() {
+ type _ = int // ERROR "unimplemented"
+ type _ = struct{} // ERROR "unimplemented"
+ type _ = reflect.Value // ERROR "unimplemented"
+ type _ = Value // ERROR "unimplemented"
+
+ type (
+ a1 = int // ERROR "unimplemented"
+ a2 = struct{} // ERROR "unimplemented"
+ a3 = reflect.Value // ERROR "unimplemented"
+ a4 = Value // ERROR "unimplemented"
+ )
+}
+
+// Invalid type alias declarations.
+
+type _ = reflect.ValueOf // ERROR "reflect.ValueOf is not a type|unimplemented"
+
+type b1 = struct{} // ERROR "unimplemented"
+func (b1) m() {} // disabled ERROR "invalid receiver type"
+
+// TODO(gri) expand
+// It appears that type-checking exits after some more severe errors, so we may
+// need more test files.
From a917097b5e5fd42bb4e6f4884a58544330d34984 Mon Sep 17 00:00:00 2001
From: Russ Cox
Date: Mon, 9 Jan 2017 19:41:19 -0500
Subject: [PATCH 04/26] [dev.typealias] go/build: add go1.9 build tag
It's earlier than usual but this will help us put the type alias-aware
code into x/tools without breaking clients on go1.6, go1.7,
or (eventually) go1.8.
Change-Id: I43e7ea804922de07d153c7e356cf95e2a11fc592
Reviewed-on: https://go-review.googlesource.com/35050
Run-TryBot: Russ Cox
Reviewed-by: Robert Griesemer
Reviewed-by: Brad Fitzpatrick
---
src/go/build/build.go | 3 ++-
src/go/build/doc.go | 1 +
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/go/build/build.go b/src/go/build/build.go
index da12d50bb1..31456ea343 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.9"}
env := os.Getenv("CGO_ENABLED")
if env == "" {
diff --git a/src/go/build/doc.go b/src/go/build/doc.go
index 979d0477df..422e1a5ffd 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.9", from Go version 1.9 onward
// - any additional words listed in ctxt.BuildTags
//
// If a file's name, after stripping the extension and a possible _test suffix,
From 80d8b69e95a4514f6567d3b314aa3434ec924363 Mon Sep 17 00:00:00 2001
From: Robert Griesemer
Date: Mon, 19 Dec 2016 17:55:47 -0800
Subject: [PATCH 05/26] [dev.typealias] go/types: implement type aliases
Now a TypeName is just that: a name for a type (not just Named and Basic types
as before). If it happens to be an alias, its type won't be a Named or Basic type,
or it won't have the same name. We can determine this externally.
It may be useful to provide a helper predicate to make that test easily accessible,
but we can get to that if there's an actual need.
The field/method lookup code has become more general an simpler, which is a good sign.
The changes in methodset.go are symmetric to the changes in lookup.go.
Known issue: Cycles created via alias types are not properly detected at the moment.
For #18130.
Change-Id: I90a3206be13116f89c221b5ab4d0f577eec6c78a
Reviewed-on: https://go-review.googlesource.com/35091
Run-TryBot: Robert Griesemer
TryBot-Result: Gobot Gobot
Reviewed-by: Alan Donovan
---
src/go/types/call.go | 3 -
src/go/types/check_test.go | 2 +-
src/go/types/decl.go | 193 ++++++++-----------------------
src/go/types/lookup.go | 81 +++++++------
src/go/types/methodset.go | 50 ++++----
src/go/types/object.go | 48 ++------
src/go/types/resolver.go | 15 +--
src/go/types/testdata/decls0.src | 8 --
src/go/types/testdata/decls4.src | 149 ++++++++++++++++++++++++
src/go/types/typestring.go | 1 +
src/go/types/typexpr.go | 60 ++++------
11 files changed, 310 insertions(+), 300 deletions(-)
create mode 100644 src/go/types/testdata/decls4.src
diff --git a/src/go/types/call.go b/src/go/types/call.go
index 8e5c5371f2..194b1fea10 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,7 +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 {
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 2472aa3434..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
+ if alias {
- // determine underlying type of named
- check.typExpr(typ, named, append(path, obj))
+ obj.typ = Typ[Invalid]
+ obj.typ = check.typExpr(typ, nil, 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)
+ } 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
@@ -534,16 +444,13 @@ func (check *Checker) declStmt(decl ast.Decl) {
}
case *ast.TypeSpec:
- if s.Assign.IsValid() {
- check.errorf(s.Assign, "type alias declarations not yet implemented")
- }
obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
// spec: "The scope of a type identifier declared inside a function
// begins at the identifier in the TypeSpec and ends at the end of
// 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/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}}
+ current := []embeddedType{{typ, nil, isPtr, false}}
- // named types that we have seen already, allocated lazily
+ // 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}}
+ current := []embeddedType{{typ, nil, isPtr, false}}
- // named types that we have seen already, allocated lazily
+ // 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..57b82c5983 100644
--- a/src/go/types/object.go
+++ b/src/go/types/object.go
@@ -154,7 +154,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
}
@@ -215,28 +215,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
@@ -295,10 +273,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,6 +296,9 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
writePackage(buf, obj.Pkg(), qf)
}
buf.WriteString(obj.Name())
+
+ // TODO(gri) indicate type alias if we have one
+
if typ != nil {
buf.WriteByte(' ')
WriteType(buf, typ, qf)
@@ -353,15 +330,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/resolver.go b/src/go/types/resolver.go
index d37f93de45..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:
@@ -346,11 +340,8 @@ func (check *Checker) collectObjects() {
}
case *ast.TypeSpec:
- if s.Assign.IsValid() {
- check.errorf(s.Assign, "type alias declarations not yet implemented")
- }
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/decls0.src b/src/go/types/testdata/decls0.src
index 3ed1b976e5..d4df386b13 100644
--- a/src/go/types/testdata/decls0.src
+++ b/src/go/types/testdata/decls0.src
@@ -208,11 +208,3 @@ func (BlankT) _() {}
func (BlankT) _(int) {}
func (BlankT) _() int { return 0 }
func (BlankT) _(int) int { return 0}
-
-// type alias declarations
-// TODO(gri) complete this
-type (
- __ = /* ERROR not yet implemented */ int
- a0 = /* ERROR not yet implemented */ int
- a1 = /* ERROR not yet implemented */ struct{}
-)
diff --git a/src/go/types/testdata/decls4.src b/src/go/types/testdata/decls4.src
new file mode 100644
index 0000000000..6030edb7cb
--- /dev/null
+++ b/src/go/types/testdata/decls4.src
@@ -0,0 +1,149 @@
+// 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 (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
}
From c80748e3894b5623681fc5f1059ffdbd2cff6b7c Mon Sep 17 00:00:00 2001
From: Robert Griesemer
Date: Thu, 22 Dec 2016 09:48:43 -0800
Subject: [PATCH 06/26] [dev.typealias] go/types: remove some more vestiges of
prior alias implementation
For #18130.
Change-Id: Ibec8efd158d32746978242910dc71e5ed23e9d91
Reviewed-on: https://go-review.googlesource.com/35092
Run-TryBot: Robert Griesemer
TryBot-Result: Gobot Gobot
Reviewed-by: Alan Donovan
---
src/go/types/api_test.go | 152 ---------------------------------------
src/go/types/call.go | 5 --
2 files changed, 157 deletions(-)
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 194b1fea10..7f5823c829 100644
--- a/src/go/types/call.go
+++ b/src/go/types/call.go
@@ -295,11 +295,6 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
}
check.recordUse(e.Sel, 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
switch exp := exp.(type) {
From aa1f0681bc34da2088fec08773eacebc3aee7391 Mon Sep 17 00:00:00 2001
From: Robert Griesemer
Date: Thu, 22 Dec 2016 11:27:48 -0800
Subject: [PATCH 07/26] [dev.typealias] go/types: improved Object printing
- added internal isAlias predicated and test
- use it for improved Object printing
- when printing a basic type object, don't repeat type name
(i.e., print "type int" rather than "type int int")
- added another test to testdata/decls4.src
For #18130.
Change-Id: Ice9517c0065a2cc465c6d12f87cd27c01ef801e6
Reviewed-on: https://go-review.googlesource.com/35093
Run-TryBot: Robert Griesemer
TryBot-Result: Gobot Gobot
Reviewed-by: Alan Donovan
---
src/go/types/example_test.go | 4 +--
src/go/types/object.go | 52 ++++++++++++++++++++++++++++----
src/go/types/object_test.go | 36 ++++++++++++++++++++++
src/go/types/predicates.go | 2 +-
src/go/types/testdata/decls4.src | 1 +
5 files changed, 86 insertions(+), 9 deletions(-)
create mode 100644 src/go/types/object_test.go
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/object.go b/src/go/types/object.go
index 57b82c5983..9a1740825f 100644
--- a/src/go/types/object.go
+++ b/src/go/types/object.go
@@ -163,6 +163,30 @@ func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName {
return &TypeName{object{nil, pos, pkg, name, typ, 0, token.NoPos}}
}
+func (obj *TypeName) isAlias() bool {
+ switch t := obj.typ.(type) {
+ case nil:
+ return false
+ case *Basic:
+ // It would seem that we should be able to look for different names here;
+ // but the names of universeByte/Rune are "byte" and "rune", respectively.
+ // We do this so that we get better error messages. However, general alias
+ // types don't have that name information and thus behave differently when
+ // reporting errors (we won't see the alias name, only the original name).
+ // Maybe we should remove the special handling for the predeclared types
+ // as well to be consistent (at the cost of slightly less clear error
+ // messages when byte/rune are involved).
+ // This also plays out in the implementation of the Identical(Type, Type)
+ // predicate.
+ // TODO(gri) consider possible clean up
+ return t == universeByte || t == universeRune
+ case *Named:
+ 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
@@ -242,7 +266,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())
@@ -255,8 +281,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 {
@@ -297,12 +323,26 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
}
buf.WriteString(obj.Name())
- // TODO(gri) indicate type alias if we have one
-
- 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) {
diff --git a/src/go/types/object_test.go b/src/go/types/object_test.go
new file mode 100644
index 0000000000..c9fc7b7258
--- /dev/null
+++ b/src/go/types/object_test.go
@@ -0,0 +1,36 @@
+// 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
+ t0 := NewTypeName(0, nil, "t0", nil)
+ check(t0, false) // no type yet
+
+ t1 := NewTypeName(0, nil, "t1", nil)
+ n1 := NewNamed(t1, new(Struct), nil)
+ check(t1, false) // type name refers to named type and vice versa
+
+ t2 := NewTypeName(0, nil, "t2", new(Interface))
+ check(t2, true) // type name refers to unnamed type
+
+ t3 := NewTypeName(0, nil, "t3", n1)
+ check(t3, true) // type name refers to named type with different type name (true alias)
+}
diff --git a/src/go/types/predicates.go b/src/go/types/predicates.go
index 21fd81e3c2..707fb9619d 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/testdata/decls4.src b/src/go/types/testdata/decls4.src
index 6030edb7cb..5e5e2e940b 100644
--- a/src/go/types/testdata/decls4.src
+++ b/src/go/types/testdata/decls4.src
@@ -63,6 +63,7 @@ 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
From 5ceec42dc0db9342bc4f37503844b46cf2689c65 Mon Sep 17 00:00:00 2001
From: Robert Griesemer
Date: Thu, 22 Dec 2016 14:42:05 -0800
Subject: [PATCH 08/26] [dev.typealias] go/types: export TypeName.IsAlias so
clients can use it
For #18130.
Change-Id: I634eaaeaa11e92fc31219d70419fdb4a7aa6e0b4
Reviewed-on: https://go-review.googlesource.com/35099
Run-TryBot: Robert Griesemer
Reviewed-by: Alan Donovan
TryBot-Result: Gobot Gobot
---
src/go/types/object.go | 6 +++---
src/go/types/object_test.go | 4 ++--
src/go/types/predicates.go | 2 +-
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/go/types/object.go b/src/go/types/object.go
index 9a1740825f..f4f628f876 100644
--- a/src/go/types/object.go
+++ b/src/go/types/object.go
@@ -163,7 +163,7 @@ func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName {
return &TypeName{object{nil, pos, pkg, name, typ, 0, token.NoPos}}
}
-func (obj *TypeName) isAlias() bool {
+func (obj *TypeName) IsAlias() bool {
switch t := obj.typ.(type) {
case nil:
return false
@@ -330,11 +330,11 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
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).
+ // are the same; see also comment in TypeName.IsAlias).
if _, ok := typ.(*Basic); ok {
return
}
- if tname.isAlias() {
+ if tname.IsAlias() {
buf.WriteString(" =")
} else {
typ = typ.Underlying()
diff --git a/src/go/types/object_test.go b/src/go/types/object_test.go
index c9fc7b7258..70656ae022 100644
--- a/src/go/types/object_test.go
+++ b/src/go/types/object_test.go
@@ -8,8 +8,8 @@ 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)
+ if got := obj.IsAlias(); got != want {
+ t.Errorf("%v: got IsAlias = %v; want %v", obj, got, want)
}
}
diff --git a/src/go/types/predicates.go b/src/go/types/predicates.go
index 707fb9619d..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. See also comment in TypeName.isAlias.
+ // above. See also comment in TypeName.IsAlias.
if y, ok := y.(*Basic); ok {
return x.kind == y.kind
}
From 49de5f035169526675b9d5897753d257bf2c7965 Mon Sep 17 00:00:00 2001
From: Robert Griesemer
Date: Thu, 22 Dec 2016 13:04:04 -0800
Subject: [PATCH 09/26] [dev.typealias] cmd/compile, go/importer: define export
format and implement importing of type aliases
This defines the (tentative) export/import format for type aliases.
The compiler doesn't support type aliases yet, so while the code is present
it is guarded with a flag.
The export format for embedded (anonymous) fields now has three modes (mode 3 is new):
1) The original type name and the anonymous field name are the same, and the name is exported:
we don't need the field name and write "" instead
2) The original type name and the anonymous field name are the same, and the name is not exported:
we don't need the field name and write "?" instead, indicating that there is package info
3) The original type name and the anonymous field name are different:
we do need the field name and write "@" followed by the field name (and possible package info)
For #18130.
Change-Id: I790dad826757233fa71396a210f966c6256b75d3
Reviewed-on: https://go-review.googlesource.com/35100
Reviewed-by: Alan Donovan
---
src/cmd/compile/internal/gc/bexport.go | 52 +++++++++++++++------
src/cmd/compile/internal/gc/bimport.go | 61 +++++++++++++++++--------
src/go/internal/gcimporter/bimport.go | 62 +++++++++++++-------------
3 files changed, 114 insertions(+), 61 deletions(-)
diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go
index ffc5419708..b7529163b9 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
@@ -509,7 +510,14 @@ func (p *exporter) obj(sym *Sym) {
Fatalf("exporter: export of incomplete type %v", sym)
}
- p.tag(typeTag)
+ const alias = false // TODO(gri) fix this
+ if alias {
+ p.tag(aliasTag)
+ p.pos(n)
+ p.qualifiedName(sym)
+ } else {
+ p.tag(typeTag)
+ }
p.typ(t)
case ONAME:
@@ -868,19 +876,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 name is exported
+ // 2) field name matches base type name and name is not exported
+ // 3) field name doesn't match base type name (type name is alias)
+ bname := basetypeName(t.Type)
+ if name == bname {
+ if exportname(name) {
+ name = "" // 1) we don't need to know the name
+ } else {
+ name = "?" // 2) use unexported name to force package export
+ }
+ } else {
+ // 3) indicate alias and export name as is
+ // (this requires an extra "@" but this is a rare case)
+ p.string("@")
}
}
p.string(name)
@@ -889,6 +907,14 @@ func (p *exporter) fieldName(t *Field) {
}
}
+// methodName is like qualifiedName but it doesn't record the package for exported names.
+func (p *exporter) methodName(sym *Sym) {
+ p.string(sym.Name)
+ if !exportname(sym.Name) {
+ p.pkg(sym.Pkg)
+ }
+}
+
func basetypeName(t *Type) string {
s := t.Sym
if s == nil && t.IsPtr() {
@@ -1797,7 +1823,7 @@ const (
nilTag
unknownTag // not used by gc (only appears in packages with errors)
- // Aliases
+ // Type aliases
aliasTag
)
@@ -1835,7 +1861,7 @@ var tagString = [...]string{
-nilTag: "nil",
-unknownTag: "unknown",
- // Aliases
+ // Type aliases
-aliasTag: "alias",
}
@@ -1889,7 +1915,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 1d668412a1..853c4bd2a4 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()
@@ -315,6 +315,12 @@ func (p *importer) obj(tag int) {
val := p.value(typ)
importconst(sym, idealType(typ), nodlit(val))
+ case aliasTag:
+ // TODO(gri) hook up type alias
+ p.pos()
+ p.qualifiedName()
+ p.typ()
+
case typeTag:
p.typ()
@@ -354,17 +360,6 @@ func (p *importer) obj(tag int) {
}
}
- case aliasTag:
- p.pos()
- alias := importpkg.Lookup(p.string())
- orig := p.qualifiedName()
-
- // Although the protocol allows the alias to precede the original,
- // this never happens in files produced by gc.
- alias.Flags |= SymAlias
- alias.Def = orig.Def
- importsym(alias, orig.Def.Op)
-
default:
formatErrorf("unexpected object (tag = %d)", tag)
}
@@ -594,6 +589,8 @@ func (p *importer) field() *Field {
}
sym = sym.Pkg.Lookup(s.Name)
f.Embedded = 1
+ } else if sym.Flags&SymAlias != 0 {
+ f.Embedded = 1
}
f.Sym = sym
@@ -616,7 +613,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()
@@ -630,15 +627,43 @@ func (p *importer) method() *Field {
func (p *importer) fieldName() *Sym {
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)
}
pkg := localpkg
- if name != "" && !exportname(name) {
- if name == "?" {
- name = ""
+ var flag SymFlags
+ switch name {
+ case "":
+ // field name is exported - nothing to do
+ case "?":
+ // field name is not exported - need package
+ name = ""
+ pkg = p.pkg()
+ case "@":
+ // field name doesn't match type name (alias)
+ name = p.string()
+ flag = SymAlias
+ fallthrough
+ default:
+ if !exportname(name) {
+ pkg = p.pkg()
}
+ }
+ sym := pkg.Lookup(name)
+ sym.Flags |= flag
+ return sym
+}
+
+func (p *importer) methodName() *Sym {
+ name := p.string()
+ if p.version == 0 && name == "_" {
+ // version 0 didn't export a package for _ method names
+ // but used the builtin package instead
+ return builtinpkg.Lookup(name)
+ }
+ pkg := localpkg
+ if !exportname(name) {
pkg = p.pkg()
}
return pkg.Lookup(name)
diff --git a/src/go/internal/gcimporter/bimport.go b/src/go/internal/gcimporter/bimport.go
index a8f349052a..55019df39d 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)
}
@@ -556,17 +548,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)
typ := p.typ(parent)
+ tag := p.string()
anonymous := false
if name == "" {
@@ -583,7 +575,7 @@ func (p *importer) field(parent *types.Package) *types.Var {
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) {
@@ -616,11 +608,21 @@ func (p *importer) fieldName(parent *types.Package) (*types.Package, string) {
// version 0 didn't export a package for _ fields
return pkg, name
}
- if name != "" && !exported(name) {
- if name == "?" {
- name = ""
- }
+ switch name {
+ case "":
+ // field name is exported - nothing to do
+ case "?":
+ // field name is not exported - need package
+ name = ""
pkg = p.pkg()
+ case "@":
+ // field name doesn't match type name (alias)
+ name = p.string()
+ fallthrough
+ default:
+ if !exported(name) {
+ pkg = p.pkg()
+ }
}
return pkg, name
}
@@ -893,7 +895,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 +919,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(),
From f011e0c6c378427f32bbf09f24ba211f7bd96b9c Mon Sep 17 00:00:00 2001
From: Robert Griesemer
Date: Tue, 27 Dec 2016 16:53:33 -0800
Subject: [PATCH 10/26] [dev.typealias] cmd/compile, go/types, go/importer:
various alias related fixes
cmd/compile:
- remove crud from prior alias implementation
- better comments in places
go/types:
- fix TypeName.IsAlias predicate
- more tests
go/importer (go/internal/gcimporter15):
- handle "@" format for anonymous fields using aliases
(currently tested indirectly via x/tools/gcimporter15 tests)
For #18130.
Change-Id: I23a6d4e3a4c2a5c1ae589513da73fde7cad5f386
Reviewed-on: https://go-review.googlesource.com/35101
Reviewed-by: Alan Donovan
---
src/cmd/compile/internal/gc/bexport.go | 37 +++++---------------------
src/cmd/compile/internal/gc/bimport.go | 9 ++++---
src/go/internal/gcimporter/bimport.go | 30 +++++++++++----------
src/go/types/object.go | 20 ++++++--------
src/go/types/object_test.go | 29 ++++++++++++--------
5 files changed, 53 insertions(+), 72 deletions(-)
diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go
index b7529163b9..4125e83b3a 100644
--- a/src/cmd/compile/internal/gc/bexport.go
+++ b/src/cmd/compile/internal/gc/bexport.go
@@ -447,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
@@ -885,15 +861,15 @@ func (p *exporter) fieldName(t *Field) {
name := t.Sym.Name
if t.Embedded != 0 {
// anonymous field - we distinguish between 3 cases:
- // 1) field name matches base type name and name is exported
- // 2) field name matches base type name and name is not exported
- // 3) field name doesn't match base type name (type name is alias)
+ // 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 name
+ name = "" // 1) we don't need to know the field name or package
} else {
- name = "?" // 2) use unexported name to force package export
+ name = "?" // 2) use unexported name "?" to force package export
}
} else {
// 3) indicate alias and export name as is
@@ -920,11 +896,10 @@ func basetypeName(t *Type) string {
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) {
diff --git a/src/cmd/compile/internal/gc/bimport.go b/src/cmd/compile/internal/gc/bimport.go
index 853c4bd2a4..6b34770e08 100644
--- a/src/cmd/compile/internal/gc/bimport.go
+++ b/src/cmd/compile/internal/gc/bimport.go
@@ -582,7 +582,7 @@ func (p *importer) field() *Field {
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
@@ -590,6 +590,7 @@ func (p *importer) field() *Field {
sym = sym.Pkg.Lookup(s.Name)
f.Embedded = 1
} else if sym.Flags&SymAlias != 0 {
+ // anonymous field: we have an explicit name because it's an alias
f.Embedded = 1
}
@@ -635,13 +636,13 @@ func (p *importer) fieldName() *Sym {
var flag SymFlags
switch name {
case "":
- // field name is exported - nothing to do
+ // 1) field name matches base type name and is exported: nothing to do
case "?":
- // field name is not exported - need package
+ // 2) field name matches base type name and is not exported: need package
name = ""
pkg = p.pkg()
case "@":
- // field name doesn't match type name (alias)
+ // 3) field name doesn't match base type name (alias name): need name and possibly package
name = p.string()
flag = SymAlias
fallthrough
diff --git a/src/go/internal/gcimporter/bimport.go b/src/go/internal/gcimporter/bimport.go
index 55019df39d..5badd337d9 100644
--- a/src/go/internal/gcimporter/bimport.go
+++ b/src/go/internal/gcimporter/bimport.go
@@ -341,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,7 +554,7 @@ func (p *importer) fieldList(parent *types.Package) (fields []*types.Var, tags [
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()
@@ -570,9 +568,12 @@ func (p *importer) field(parent *types.Package) (*types.Var, string) {
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), tag
@@ -590,41 +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
+ return
}
switch name {
case "":
- // field name is exported - nothing to do
+ // 1) field name matches base type name and is exported: nothing to do
case "?":
- // field name is not exported - need package
+ // 2) field name matches base type name and is not exported: need package
name = ""
pkg = p.pkg()
case "@":
- // field name doesn't match type name (alias)
+ // 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) {
diff --git a/src/go/types/object.go b/src/go/types/object.go
index f4f628f876..1668ba396b 100644
--- a/src/go/types/object.go
+++ b/src/go/types/object.go
@@ -163,23 +163,19 @@ func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName {
return &TypeName{object{nil, pos, pkg, name, typ, 0, token.NoPos}}
}
+// 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:
- // It would seem that we should be able to look for different names here;
- // but the names of universeByte/Rune are "byte" and "rune", respectively.
- // We do this so that we get better error messages. However, general alias
- // types don't have that name information and thus behave differently when
- // reporting errors (we won't see the alias name, only the original name).
- // Maybe we should remove the special handling for the predeclared types
- // as well to be consistent (at the cost of slightly less clear error
- // messages when byte/rune are involved).
- // This also plays out in the implementation of the Identical(Type, Type)
- // predicate.
- // TODO(gri) consider possible clean up
- return t == universeByte || t == universeRune
+ // 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:
diff --git a/src/go/types/object_test.go b/src/go/types/object_test.go
index 70656ae022..16d7d5c723 100644
--- a/src/go/types/object_test.go
+++ b/src/go/types/object_test.go
@@ -21,16 +21,23 @@ func TestIsAlias(t *testing.T) {
}
// various other types
- t0 := NewTypeName(0, nil, "t0", nil)
- check(t0, false) // no type yet
-
- t1 := NewTypeName(0, nil, "t1", nil)
+ pkg := NewPackage("p", "p")
+ t1 := NewTypeName(0, pkg, "t1", nil)
n1 := NewNamed(t1, new(Struct), nil)
- check(t1, false) // type name refers to named type and vice versa
-
- t2 := NewTypeName(0, nil, "t2", new(Interface))
- check(t2, true) // type name refers to unnamed type
-
- t3 := NewTypeName(0, nil, "t3", n1)
- check(t3, true) // type name refers to named type with different type name (true alias)
+ 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)
+ }
}
From ac8421f9a58c2c4df9072d1702783baa62eb99f3 Mon Sep 17 00:00:00 2001
From: Robert Griesemer
Date: Thu, 29 Dec 2016 15:49:01 -0800
Subject: [PATCH 11/26] [dev.typealias] cmd/compile: various minor cleanups
Also: Don't allow type pragmas with type alias declarations.
For #18130.
Change-Id: Ie54ea5fefcd677ad87ced03466bbfd783771e974
Reviewed-on: https://go-review.googlesource.com/35102
Reviewed-by: Matthew Dempsky
---
src/cmd/compile/internal/gc/main.go | 20 +++++++++++-------
src/cmd/compile/internal/gc/noder.go | 31 ++++++++++++++--------------
2 files changed, 28 insertions(+), 23 deletions(-)
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index 75f58a731c..b0b31dd30d 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -344,8 +344,9 @@ func Main() {
// 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 {
+ xtop[i] = typecheck(n, Etop)
}
}
@@ -355,8 +356,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 {
+ xtop[i] = typecheck(n, Etop)
}
}
resumecheckwidth()
@@ -366,8 +368,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)
@@ -459,8 +462,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++
}
}
diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go
index 3f6fe20b6b..8d830ad62d 100644
--- a/src/cmd/compile/internal/gc/noder.go
+++ b/src/cmd/compile/internal/gc/noder.go
@@ -153,11 +153,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 {
@@ -170,11 +166,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 {
@@ -190,12 +182,14 @@ 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)
+ pragma := Pragma(decl.Pragma)
+ if pragma != 0 && decl.Alias {
+ yyerror("cannot specify directive with type alias")
+ pragma = 0
}
+ name.Name.Param.Pragma = pragma
+
+ typ := p.typeExprOrNil(decl.Type)
return typedcl1(name, typ, true)
}
@@ -470,6 +464,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:
From b2386dffa1f646f06c230f9b317cb3640fef11d4 Mon Sep 17 00:00:00 2001
From: Robert Griesemer
Date: Wed, 11 Jan 2017 11:24:35 -0800
Subject: [PATCH 12/26] [dev.typealias] cmd/compile: type-check type alias
declarations
Known issues:
- needs many more tests
- duplicate method declarations via type alias names are not detected
- type alias cycle error messages need to be improved
- need to review setup of byte/rune type aliases
For #18130.
Change-Id: Icc2fefad6214e5e56539a9dcb3fe537bf58029f8
Reviewed-on: https://go-review.googlesource.com/35121
Run-TryBot: Robert Griesemer
TryBot-Result: Gobot Gobot
Reviewed-by: Matthew Dempsky
---
src/cmd/compile/internal/gc/bexport.go | 7 +-
src/cmd/compile/internal/gc/bimport.go | 24 +++---
src/cmd/compile/internal/gc/dcl.go | 18 ++++-
src/cmd/compile/internal/gc/export.go | 25 ++++++-
src/cmd/compile/internal/gc/go.go | 9 ++-
src/cmd/compile/internal/gc/main.go | 2 +-
src/cmd/compile/internal/gc/noder.go | 13 +---
src/cmd/compile/internal/gc/syntax.go | 15 ++--
src/cmd/compile/internal/gc/typecheck.go | 22 +++++-
test/alias2.go | 93 +++++++++++++++++-------
10 files changed, 153 insertions(+), 75 deletions(-)
diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go
index 4125e83b3a..5f14e0152b 100644
--- a/src/cmd/compile/internal/gc/bexport.go
+++ b/src/cmd/compile/internal/gc/bexport.go
@@ -352,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)
@@ -486,8 +486,7 @@ func (p *exporter) obj(sym *Sym) {
Fatalf("exporter: export of incomplete type %v", sym)
}
- const alias = false // TODO(gri) fix this
- if alias {
+ if sym.isAlias() {
p.tag(aliasTag)
p.pos(n)
p.qualifiedName(sym)
diff --git a/src/cmd/compile/internal/gc/bimport.go b/src/cmd/compile/internal/gc/bimport.go
index 6b34770e08..3c1f7100c3 100644
--- a/src/cmd/compile/internal/gc/bimport.go
+++ b/src/cmd/compile/internal/gc/bimport.go
@@ -316,10 +316,10 @@ func (p *importer) obj(tag int) {
importconst(sym, idealType(typ), nodlit(val))
case aliasTag:
- // TODO(gri) hook up type alias
p.pos()
- p.qualifiedName()
- p.typ()
+ sym := p.qualifiedName()
+ typ := p.typ()
+ importalias(sym, typ)
case typeTag:
p.typ()
@@ -576,7 +576,7 @@ 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()
@@ -589,8 +589,8 @@ func (p *importer) field() *Field {
}
sym = sym.Pkg.Lookup(s.Name)
f.Embedded = 1
- } else if sym.Flags&SymAlias != 0 {
- // anonymous field: we have an explicit name because it's an alias
+ } else if alias {
+ // anonymous field: we have an explicit name because it's a type alias
f.Embedded = 1
}
@@ -625,15 +625,15 @@ 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 _ field names
// but used the builtin package instead
- return builtinpkg.Lookup(name)
+ return builtinpkg.Lookup(name), false
}
pkg := localpkg
- var flag SymFlags
+ alias := false
switch name {
case "":
// 1) field name matches base type name and is exported: nothing to do
@@ -644,16 +644,14 @@ func (p *importer) fieldName() *Sym {
case "@":
// 3) field name doesn't match base type name (alias name): need name and possibly package
name = p.string()
- flag = SymAlias
+ alias = true
fallthrough
default:
if !exportname(name) {
pkg = p.pkg()
}
}
- sym := pkg.Lookup(name)
- sym.Flags |= flag
- return sym
+ return pkg.Lookup(name), alias
}
func (p *importer) methodName() *Sym {
diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go
index 3cdd71df0d..5a1c5e12a0 100644
--- a/src/cmd/compile/internal/gc/dcl.go
+++ b/src/cmd/compile/internal/gc/dcl.go
@@ -695,10 +695,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)
}
diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go
index b4c15e40b1..5556984dcb 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)
}
@@ -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/go.go b/src/cmd/compile/internal/gc/go.go
index ff33e9c1c4..070fb5f54b 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 b0b31dd30d..a861a3556b 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -927,7 +927,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 8d830ad62d..699015488a 100644
--- a/src/cmd/compile/internal/gc/noder.go
+++ b/src/cmd/compile/internal/gc/noder.go
@@ -177,21 +177,12 @@ func (p *noder) constDecl(decl *syntax.ConstDecl) []*Node {
}
func (p *noder) typeDecl(decl *syntax.TypeDecl) *Node {
- if decl.Alias {
- yyerror("type alias declarations unimplemented")
- }
-
name := typedcl0(p.name(decl.Name))
- pragma := Pragma(decl.Pragma)
- if pragma != 0 && decl.Alias {
- yyerror("cannot specify directive with type alias")
- pragma = 0
- }
- name.Name.Param.Pragma = pragma
+ // decl.Type may be nil but in that case we got a syntax error during parsing
typ := p.typeExprOrNil(decl.Type)
- return typedcl1(name, typ, true)
+ return typedcl1(name, typ, Pragma(decl.Pragma), decl.Alias)
}
func (p *noder) declNames(names []*syntax.Name) []*Node {
diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go
index 8848bb5955..7a52dc612f 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,10 +280,11 @@ 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
}
// Func holds Node fields used only with function-like nodes.
@@ -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 5ec1c9e2f2..46c71d69c4 100644
--- a/src/cmd/compile/internal/gc/typecheck.go
+++ b/src/cmd/compile/internal/gc/typecheck.go
@@ -3578,8 +3578,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:
@@ -3758,12 +3756,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 {
@@ -3771,7 +3786,6 @@ func typecheckdef(n *Node) *Node {
// but it was reported. Silence future errors.
n.Type.Broke = true
}
-
if Curfn != nil {
resumecheckwidth()
}
diff --git a/test/alias2.go b/test/alias2.go
index 25df7c287d..fb0a97feb2 100644
--- a/test/alias2.go
+++ b/test/alias2.go
@@ -6,11 +6,6 @@
// Test basic restrictions on type aliases.
-// The compiler doesn't implement type aliases yet,
-// so for now we get the same error (unimplemented)
-// everywhere, OR-ed into the ERROR checks.
-// TODO(gri) remove the need for "unimplemented"
-
package p
import (
@@ -18,41 +13,87 @@ import (
. "reflect"
)
+type T0 struct{}
+
// Valid type alias declarations.
-type _ = int // ERROR "unimplemented"
-type _ = struct{} // ERROR "unimplemented"
-type _ = reflect.Value // ERROR "unimplemented"
-type _ = Value // ERROR "unimplemented"
+type _ = T0
+type _ = int
+type _ = struct{}
+type _ = reflect.Value
+type _ = Value
type (
- a1 = int // ERROR "unimplemented"
- a2 = struct{} // ERROR "unimplemented"
- a3 = reflect.Value // ERROR "unimplemented"
- a4 = Value // ERROR "unimplemented"
+ 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() {}
+func (A0) m1() {} // TODO(gri) this should be an error
+func (A0) m2() {}
+
+// Type aliases and the original type name can be used interchangeably.
+var _ A0 = T0{}
+var _ T0 = A0{}
+
+// But aliases and original types cannot be used with new types based on them.
+var _ N0 = T0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment"
+var _ N0 = A0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment"
+
+var _ A5 = Value{}
+
+var _ interface {
+ m1()
+ m2()
+} = T0{}
+
+var _ interface {
+ m1()
+ m2()
+} = A0{}
+
func _() {
- type _ = int // ERROR "unimplemented"
- type _ = struct{} // ERROR "unimplemented"
- type _ = reflect.Value // ERROR "unimplemented"
- type _ = Value // ERROR "unimplemented"
+ type _ = T0
+ type _ = int
+ type _ = struct{}
+ type _ = reflect.Value
+ type _ = Value
type (
- a1 = int // ERROR "unimplemented"
- a2 = struct{} // ERROR "unimplemented"
- a3 = reflect.Value // ERROR "unimplemented"
- a4 = Value // ERROR "unimplemented"
+ 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"
+ var _ N0 = A0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment"
+
+ var _ A5 = Value{} // ERROR "cannot use reflect\.Value literal \(type reflect.Value\) as type A5 in assignment"
}
// Invalid type alias declarations.
-type _ = reflect.ValueOf // ERROR "reflect.ValueOf is not a type|unimplemented"
+type _ = reflect.ValueOf // ERROR "reflect.ValueOf is not a type"
-type b1 = struct{} // ERROR "unimplemented"
-func (b1) m() {} // disabled ERROR "invalid receiver type"
+func (A1) m() {} // ERROR "cannot define new methods on non-local type int"
+
+type B1 = struct{}
+
+func (B1) m() {} // ERROR "invalid receiver type"
// TODO(gri) expand
-// It appears that type-checking exits after some more severe errors, so we may
-// need more test files.
From 5c160b28baceb263fbd95ea0c95f5083e191c114 Mon Sep 17 00:00:00 2001
From: Robert Griesemer
Date: Thu, 12 Jan 2017 14:35:20 -0800
Subject: [PATCH 13/26] [dev.typealias] cmd/compile: improved error message for
cyles involving type aliases
Known issue: #18640 (requires a bit more work, I believe).
For #18130.
Change-Id: I53dc26012070e0c79f63b7c76266732190a83d47
Reviewed-on: https://go-review.googlesource.com/35129
Reviewed-by: Matthew Dempsky
---
src/cmd/compile/internal/gc/fmt.go | 4 +++-
src/cmd/compile/internal/gc/typecheck.go | 27 +++++++++++++++---------
2 files changed, 20 insertions(+), 11 deletions(-)
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/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go
index 46c71d69c4..f18bcfad78 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
From cc2dcce3d748b5585a1da5a9aa2e7ab8b8be00cd Mon Sep 17 00:00:00 2001
From: Robert Griesemer
Date: Thu, 12 Jan 2017 15:21:21 -0800
Subject: [PATCH 14/26] [dev.typealias] cmd/compile: a few better comments
related to alias types
For #18130.
Change-Id: I50bded3af0db673fc92b20c41a86b9cae614acd9
Reviewed-on: https://go-review.googlesource.com/35191
Reviewed-by: Matthew Dempsky
---
src/cmd/compile/internal/gc/syntax.go | 2 +-
src/cmd/compile/internal/gc/universe.go | 8 ++++++++
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go
index 7a52dc612f..1955a0e42f 100644
--- a/src/cmd/compile/internal/gc/syntax.go
+++ b/src/cmd/compile/internal/gc/syntax.go
@@ -284,7 +284,7 @@ type Param struct {
//
// TODO: Should Func pragmas also be stored on the Name?
Pragma Pragma
- Alias bool // node is alias for Ntype
+ Alias bool // node is alias for Ntype (only used when type-checking ODCLTYPE)
}
// Func holds Node fields used only with function-like nodes.
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)
From d7cabd40dd92e5364969273229373d515bee14e8 Mon Sep 17 00:00:00 2001
From: Robert Griesemer
Date: Tue, 17 Jan 2017 11:40:04 -0800
Subject: [PATCH 15/26] [dev.typealias] go/types: clarified doc string
Also: removed internal TODO and added better comment
Fixes #18644.
Change-Id: I3e3763d3afdad6937173cdd32fc661618fb60820
Reviewed-on: https://go-review.googlesource.com/35245
Run-TryBot: Robert Griesemer
TryBot-Result: Gobot Gobot
Reviewed-by: Josh Bleecher Snyder
---
src/go/types/object.go | 13 ++++---------
1 file changed, 4 insertions(+), 9 deletions(-)
diff --git a/src/go/types/object.go b/src/go/types/object.go
index 1668ba396b..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
}
From 5802cfd900c238baeb835bff62bad61c4f4c9852 Mon Sep 17 00:00:00 2001
From: Robert Griesemer
Date: Fri, 13 Jan 2017 17:23:01 -0800
Subject: [PATCH 16/26] [dev.typealias] cmd/compile: export/import test cases
for type aliases
Plus a few minor changes.
For #18130.
Change-Id: Ica6503fe9c888cc05c15b46178423f620c087491
Reviewed-on: https://go-review.googlesource.com/35233
Reviewed-by: Alan Donovan
---
src/cmd/compile/internal/gc/dcl.go | 9 +++----
test/alias2.go | 11 +++++---
test/alias3.dir/a.go | 42 ++++++++++++++++++++++++++++++
test/alias3.dir/b.go | 26 ++++++++++++++++++
test/alias3.dir/c.go | 25 ++++++++++++++++++
test/alias3.go | 7 +++++
6 files changed, 112 insertions(+), 8 deletions(-)
create mode 100644 test/alias3.dir/a.go
create mode 100644 test/alias3.dir/b.go
create mode 100644 test/alias3.dir/c.go
create mode 100644 test/alias3.go
diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go
index 5a1c5e12a0..f934a650bd 100644
--- a/src/cmd/compile/internal/gc/dcl.go
+++ b/src/cmd/compile/internal/gc/dcl.go
@@ -1163,14 +1163,14 @@ bad:
return nil
}
-func methodname(n *Node, t *Node) *Node {
+func methodname(n, recv *Node) *Node {
star := false
- if t.Op == OIND {
+ if recv.Op == OIND {
star = true
- t = t.Left
+ recv = recv.Left
}
- return methodname0(n.Sym, star, t.Sym)
+ return methodname0(n.Sym, star, recv.Sym)
}
func methodname0(s *Sym, star bool, tsym *Sym) *Node {
@@ -1198,7 +1198,6 @@ func methodname0(s *Sym, star bool, tsym *Sym) *Node {
// - 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")
}
diff --git a/test/alias2.go b/test/alias2.go
index fb0a97feb2..f404d0dd3b 100644
--- a/test/alias2.go
+++ b/test/alias2.go
@@ -35,9 +35,11 @@ type (
)
// Methods can be declared on the original named type and the alias.
-func (T0) m1() {}
-func (A0) m1() {} // TODO(gri) this should be an error
-func (A0) m2() {}
+func (T0) m1() {}
+func (*T0) m1() {} // ERROR "method redeclared: T0\.m1"
+func (A0) m1() {} // TODO(gri) this should be an error
+func (A0) m1() {} // ERROR "A0\.m1 redeclared in this block"
+func (A0) m2() {}
// Type aliases and the original type name can be used interchangeably.
var _ A0 = T0{}
@@ -91,6 +93,9 @@ func _() {
type _ = reflect.ValueOf // ERROR "reflect.ValueOf is not a type"
func (A1) m() {} // ERROR "cannot define new methods on non-local type int"
+func (A2) m() {} // ERROR "invalid receiver type struct {}"
+func (A3) m() {} // ERROR "cannot define new methods on non-local type reflect.Value"
+func (A4) m() {} // ERROR "cannot define new methods on non-local type reflect.Value"
type B1 = struct{}
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
From a7c884efc14368750e30067367b6eab57ed06c0e Mon Sep 17 00:00:00 2001
From: Robert Griesemer
Date: Tue, 17 Jan 2017 15:06:04 -0800
Subject: [PATCH 17/26] [dev.typealias] go/internal/gccgoimporter: support for
type aliases
For #18130.
Change-Id: Iac182a6c5bc62633eb02191d9da6166d3b254c4c
Reviewed-on: https://go-review.googlesource.com/35268
Run-TryBot: Robert Griesemer
TryBot-Result: Gobot Gobot
Reviewed-by: Alan Donovan
---
.../internal/gccgoimporter/importer_test.go | 1 +
src/go/internal/gccgoimporter/parser.go | 52 ++++++++++++-------
.../internal/gccgoimporter/testdata/alias.gox | 4 ++
3 files changed, 38 insertions(+), 19 deletions(-)
create mode 100644 src/go/internal/gccgoimporter/testdata/alias.gox
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 {
- pkg, name := p.parseExportedName()
- scope := pkg.Scope()
- if obj := scope.Lookup(name); obj != nil {
- return obj.(*types.TypeName)
- }
- 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 } .
+// NamedType = TypeName [ "=" ] Type { Method } .
+// TypeName = ExportedName .
// Method = "func" "(" Param ")" Name ParamList ResultList ";" .
func (p *parser) parseNamedType(n int) types.Type {
- obj := p.parseTypeName()
+ pkg, name := p.parseExportedName()
+ scope := pkg.Scope()
+
+ 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
+ }
+
+ // 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 114>; M2 () ; }>>;
From 5d92916770ef57aeb2ae2cb556285d5e093c3aa0 Mon Sep 17 00:00:00 2001
From: Matthew Dempsky
Date: Mon, 23 Jan 2017 13:40:25 -0800
Subject: [PATCH 18/26] [dev.typealias] cmd/compile: change Func.Shortname to
*Sym
A Func's Shortname is just an identifier. No need for an entire ONAME
Node.
Change-Id: Ie4d397e8d694c907fdf924ce57bd96bdb4aaabca
Reviewed-on: https://go-review.googlesource.com/35574
Run-TryBot: Matthew Dempsky
TryBot-Result: Gobot Gobot
Reviewed-by: Robert Griesemer
---
src/cmd/compile/internal/gc/dcl.go | 8 ++++----
src/cmd/compile/internal/gc/noder.go | 2 +-
src/cmd/compile/internal/gc/obj.go | 2 +-
src/cmd/compile/internal/gc/syntax.go | 2 +-
src/cmd/compile/internal/gc/typecheck.go | 2 +-
5 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go
index f934a650bd..a5c50f06dc 100644
--- a/src/cmd/compile/internal/gc/dcl.go
+++ b/src/cmd/compile/internal/gc/dcl.go
@@ -1163,14 +1163,14 @@ bad:
return nil
}
-func methodname(n, recv *Node) *Node {
+func methodname(s *Sym, recv *Node) *Node {
star := false
if recv.Op == OIND {
star = true
recv = recv.Left
}
- return methodname0(n.Sym, star, recv.Sym)
+ return methodname0(s, star, recv.Sym)
}
func methodname0(s *Sym, star bool, tsym *Sym) *Node {
@@ -1318,7 +1318,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
@@ -1336,7 +1336,7 @@ func makefuncsym(s *Sym) {
}
s1 := funcsym(s)
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/noder.go b/src/cmd/compile/internal/gc/noder.go
index 699015488a..0c5957f987 100644
--- a/src/cmd/compile/internal/gc/noder.go
+++ b/src/cmd/compile/internal/gc/noder.go
@@ -252,7 +252,7 @@ func (p *noder) funcHeader(fun *syntax.FuncDecl) *Node {
} else {
// Receiver MethodName Signature
- f.Func.Shortname = newfuncname(name)
+ f.Func.Shortname = name
f.Func.Nname = methodname(f.Func.Shortname, t.Left.Right)
}
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/syntax.go b/src/cmd/compile/internal/gc/syntax.go
index 1955a0e42f..0bd877e26a 100644
--- a/src/cmd/compile/internal/gc/syntax.go
+++ b/src/cmd/compile/internal/gc/syntax.go
@@ -289,7 +289,7 @@ type Param struct {
// 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
diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go
index f18bcfad78..d751610763 100644
--- a/src/cmd/compile/internal/gc/typecheck.go
+++ b/src/cmd/compile/internal/gc/typecheck.go
@@ -3436,7 +3436,7 @@ 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)
+ addmethod(n.Func.Shortname, t, true, n.Func.Pragma&Nointerface != 0)
}
}
From 9259f3073afe0830ab1484bfee46bfa1f322e7e7 Mon Sep 17 00:00:00 2001
From: Ian Lance Taylor
Date: Tue, 24 Jan 2017 12:43:52 -0800
Subject: [PATCH 19/26] [dev.typealias] test: match gccgo error messages on
alias2.go
For #18130.
Change-Id: I9561ee2b8a9f7b11f0851f281a899f78b9e9703e
Reviewed-on: https://go-review.googlesource.com/35640
Reviewed-by: Robert Griesemer
---
test/alias2.go | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/test/alias2.go b/test/alias2.go
index f404d0dd3b..58eedf0c8c 100644
--- a/test/alias2.go
+++ b/test/alias2.go
@@ -35,10 +35,10 @@ type (
)
// Methods can be declared on the original named type and the alias.
-func (T0) m1() {}
-func (*T0) m1() {} // ERROR "method redeclared: T0\.m1"
-func (A0) m1() {} // TODO(gri) this should be an error
-func (A0) m1() {} // ERROR "A0\.m1 redeclared in this block"
+func (T0) m1() {} // GCCGO_ERROR "previous"
+func (*T0) m1() {} // ERROR "method redeclared: T0\.m1|redefinition of .m1."
+func (A0) m1() {} // TODO(gri) this should be an error // GCCGO_ERROR "redefinition of .m1."
+func (A0) m1() {} // ERROR "A0\.m1 redeclared in this block|redefinition of .m1."
func (A0) m2() {}
// Type aliases and the original type name can be used interchangeably.
@@ -46,8 +46,8 @@ var _ A0 = T0{}
var _ T0 = A0{}
// But aliases and original types cannot be used with new types based on them.
-var _ N0 = T0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment"
-var _ N0 = A0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment"
+var _ 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{}
@@ -82,20 +82,20 @@ func _() {
var _ A0 = T0{}
var _ T0 = A0{}
- var _ N0 = T0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment"
- var _ N0 = A0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment"
+ var _ 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"
+ 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"
+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"
-func (A2) m() {} // ERROR "invalid receiver type struct {}"
-func (A3) m() {} // ERROR "cannot define new methods on non-local type reflect.Value"
-func (A4) m() {} // ERROR "cannot define new methods on non-local type reflect.Value"
+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 "cannot define new methods on non-local type reflect.Value|may not define methods on non-local type"
type B1 = struct{}
From de2e5459aecb531a67dad274b789ffeb61dca020 Mon Sep 17 00:00:00 2001
From: Matthew Dempsky
Date: Mon, 23 Jan 2017 14:24:24 -0800
Subject: [PATCH 20/26] [dev.typealias] cmd/compile: declare methods after
resolving receiver type
For #18130.
Fixes #18655.
Change-Id: I58e2f076b9d8273f128cc033bba9edcd06c81567
Reviewed-on: https://go-review.googlesource.com/35575
Run-TryBot: Matthew Dempsky
TryBot-Result: Gobot Gobot
Reviewed-by: Robert Griesemer
---
src/cmd/compile/internal/gc/bimport.go | 9 +--------
src/cmd/compile/internal/gc/dcl.go | 25 ++++++++++++------------
src/cmd/compile/internal/gc/export.go | 2 +-
src/cmd/compile/internal/gc/noder.go | 12 ++++++------
src/cmd/compile/internal/gc/typecheck.go | 7 +++++++
test/alias2.go | 8 ++++----
test/fixedbugs/issue18655.go | 22 +++++++++++++++++++++
7 files changed, 53 insertions(+), 32 deletions(-)
create mode 100644 test/fixedbugs/issue18655.go
diff --git a/src/cmd/compile/internal/gc/bimport.go b/src/cmd/compile/internal/gc/bimport.go
index 3c1f7100c3..1ee9e76737 100644
--- a/src/cmd/compile/internal/gc/bimport.go
+++ b/src/cmd/compile/internal/gc/bimport.go
@@ -466,14 +466,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)
diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go
index a5c50f06dc..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)
@@ -1163,19 +1159,19 @@ bad:
return nil
}
-func methodname(s *Sym, recv *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 recv.Op == OIND {
+ if recv.IsPtr() {
star = true
- recv = recv.Left
+ recv = recv.Elem()
}
- return methodname0(s, star, recv.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
@@ -1191,7 +1187,7 @@ 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.
@@ -1335,6 +1331,9 @@ func makefuncsym(s *Sym) {
return
}
s1 := funcsym(s)
+ if s1.Def != nil {
+ return
+ }
s1.Def = newfuncname(s1)
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 5556984dcb..58b2bf8121 100644
--- a/src/cmd/compile/internal/gc/export.go
+++ b/src/cmd/compile/internal/gc/export.go
@@ -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
}
diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go
index 0c5957f987..1d69151cc4 100644
--- a/src/cmd/compile/internal/gc/noder.go
+++ b/src/cmd/compile/internal/gc/noder.go
@@ -247,19 +247,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 = name
- f.Func.Nname = methodname(f.Func.Shortname, t.Left.Right)
+ 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
}
diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go
index d751610763..1379bb56d4 100644
--- a/src/cmd/compile/internal/gc/typecheck.go
+++ b/src/cmd/compile/internal/gc/typecheck.go
@@ -3436,8 +3436,15 @@ func typecheckfunc(n *Node) {
t.SetNname(n.Func.Nname)
rcvr := t.Recv()
if rcvr != nil && n.Func.Shortname != nil {
+ 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)
+ }
}
// The result of stringtoarraylit MUST be assigned back to n, e.g.
diff --git a/test/alias2.go b/test/alias2.go
index 58eedf0c8c..32c3654995 100644
--- a/test/alias2.go
+++ b/test/alias2.go
@@ -37,8 +37,8 @@ type (
// 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() {} // TODO(gri) this should be an error // GCCGO_ERROR "redefinition of .m1."
-func (A0) m1() {} // ERROR "A0\.m1 redeclared in this block|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.
@@ -95,10 +95,10 @@ 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 "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 "invalid receiver type"
+func (B1) m() {} // ERROR "m redeclared in this block" "invalid receiver type"
// TODO(gri) expand
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"
From 9657e0b0777f3af3b48908cc39e5ab6d06022422 Mon Sep 17 00:00:00 2001
From: Russ Cox
Date: Tue, 24 Jan 2017 14:53:31 -0500
Subject: [PATCH 21/26] [dev.typealias] cmd/doc: update for type alias
For #18130.
Change-Id: I06b05a2b45a2aa6764053fc51e05883063572dad
Reviewed-on: https://go-review.googlesource.com/35670
Run-TryBot: Russ Cox
Reviewed-by: Robert Griesemer
---
src/cmd/doc/doc_test.go | 14 ++++++++++++++
src/cmd/doc/pkg.go | 6 +++++-
src/cmd/doc/testdata/pkg.go | 4 ++++
3 files changed, 23 insertions(+), 1 deletion(-)
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
From 43c70943861ce39b44e5bd577a8c3c2ef18538db Mon Sep 17 00:00:00 2001
From: Russ Cox
Date: Wed, 25 Jan 2017 09:50:36 -0500
Subject: [PATCH 22/26] [dev.typealias] reflect: fix StructOf use of
StructField to match StructField docs
The runtime internal structField interprets name=="" as meaning anonymous,
but the exported reflect.StructField has always set Name, even for anonymous
fields, and also set Anonymous=true.
The initial implementation of StructOf confused the internal and public
meanings of the StructField, expecting the runtime representation of
anonymous fields instead of the exported reflect API representation.
It also did not document this fact, so that users had no way to know how
to create an anonymous field.
This CL changes StructOf to use the previously documented interpretation
of reflect.StructField instead of an undocumented one.
The implementation of StructOf also, in some cases, allowed creating
structs with unexported fields (if you knew how to ask) but set the
PkgPath incorrectly on those fields. Rather than try to fix that, this CL
changes StructOf to reject attempts to create unexported fields.
(I think that may be the right design choice, not just a temporary limitation.
In any event, it's not the topic for today's work.)
For #17766.
Fixes #18780.
Change-Id: I585a4e324dc5a90551f49d21ae04d2de9ea04b6c
Reviewed-on: https://go-review.googlesource.com/35731
Run-TryBot: Russ Cox
Reviewed-by: Robert Griesemer
---
src/reflect/all_test.go | 136 +++++++++++++++++++++-------------------
src/reflect/type.go | 33 +++++-----
2 files changed, 91 insertions(+), 78 deletions(-)
diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go
index 033a18171d..e057b0cfcc 100644
--- a/src/reflect/all_test.go
+++ b/src/reflect/all_test.go
@@ -3689,7 +3689,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
@@ -3767,7 +3767,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()
@@ -4133,50 +4135,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: "", Type: TypeOf((*s2)(nil))},
- mustPanic: false,
- exported: false,
- },
- {
- field: StructField{Name: "", Type: TypeOf(S1{}), PkgPath: "other/pkg"},
+ field: StructField{Name: "s2", Anonymous: true, Type: TypeOf(s2{})},
mustPanic: true,
- exported: true,
},
{
- field: StructField{Name: "", Type: TypeOf((*S1)(nil)), PkgPath: "other/pkg"},
+ field: StructField{Name: "s2", Anonymous: true, Type: TypeOf((*s2)(nil))},
mustPanic: true,
- exported: true,
},
{
- field: StructField{Name: "", Type: TypeOf(s2{}), PkgPath: "other/pkg"},
+ field: StructField{Name: "Name", Type: nil, PkgPath: ""},
mustPanic: true,
- exported: false,
},
{
- field: StructField{Name: "", Type: TypeOf((*s2)(nil)), PkgPath: "other/pkg"},
+ field: StructField{Name: "", Type: TypeOf(S1{}), PkgPath: ""},
+ mustPanic: true,
+ },
+ {
+ field: StructField{Name: "S1", Anonymous: true, Type: TypeOf(S1{}), PkgPath: "other/pkg"},
+ mustPanic: true,
+ },
+ {
+ 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{})},
@@ -4184,81 +4194,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 {
@@ -4346,7 +4343,7 @@ func TestStructOfGenericAlg(t *testing.T) {
{Name: "S1", Type: st1},
})
- for _, table := range []struct {
+ tests := []struct {
rt Type
idx []int
}{
@@ -4427,7 +4424,9 @@ func TestStructOfGenericAlg(t *testing.T) {
),
idx: []int{2},
},
- } {
+ }
+
+ for _, table := range tests {
v1 := New(table.rt).Elem()
v2 := New(table.rt).Elem()
@@ -4529,18 +4528,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
@@ -4548,7 +4550,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
@@ -4556,6 +4559,7 @@ func TestStructOfWithInterface(t *testing.T) {
impl: true,
},
{
+ name: "StructIPtr",
typ: TypeOf(StructIPtr(want)),
val: ValueOf(StructIPtr(want)),
impl: false,
@@ -4565,13 +4569,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,
},
},
)
@@ -5951,6 +5958,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)
diff --git a/src/reflect/type.go b/src/reflect/type.go
index 9d6e7a6846..e0be36d970 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -2403,6 +2403,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")
}
@@ -2794,23 +2797,25 @@ 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")
}
- _ = resolveReflectType(field.Type.common())
+ // 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")
+ }
+
+ name := field.Name
+ if field.Anonymous {
+ name = ""
+ }
+
+ resolveReflectType(field.Type.common()) // install in runtime
return structField{
- name: newName(field.Name, string(field.Tag), field.PkgPath, exported),
+ name: newName(name, string(field.Tag), "", true),
typ: field.Type.common(),
offset: 0,
}
From 9bbb07ddec63e5e747f1cd9dbf82b7504b29dd09 Mon Sep 17 00:00:00 2001
From: Russ Cox
Date: Wed, 25 Jan 2017 10:19:33 -0500
Subject: [PATCH 23/26] [dev.typealias] cmd/compile, reflect: fix struct field
names for embedded byte, rune
Will also fix type aliases.
Fixes #17766.
For #18130.
Change-Id: I9e1584d47128782152e06abd0a30ef423d5c30d2
Reviewed-on: https://go-review.googlesource.com/35732
Run-TryBot: Russ Cox
Reviewed-by: Robert Griesemer
---
src/cmd/compile/internal/gc/align.go | 8 ++-
src/cmd/compile/internal/gc/reflect.go | 11 +++-
src/cmd/link/internal/ld/decodesym.go | 2 +-
src/reflect/all_test.go | 19 +++++-
src/reflect/type.go | 89 +++++++++++---------------
src/reflect/value.go | 4 +-
src/runtime/cgocall.go | 2 +-
src/runtime/type.go | 12 ++--
8 files changed, 85 insertions(+), 62 deletions(-)
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/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/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/reflect/all_test.go b/src/reflect/all_test.go
index e057b0cfcc..ed3ad33835 100644
--- a/src/reflect/all_test.go
+++ b/src/reflect/all_test.go
@@ -4265,7 +4265,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 {
@@ -5984,3 +5984,20 @@ func TestUnaddressableField(t *testing.T) {
lv.Set(rv)
})
}
+
+type Talias1 struct {
+ byte
+ uint8
+ int
+ int32
+ rune
+}
+
+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)
+ }
+}
diff --git a/src/reflect/type.go b/src/reflect/type.go
index e0be36d970..fbfda3a363 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 {
@@ -1694,7 +1688,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() {
@@ -2418,13 +2412,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
@@ -2432,11 +2424,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:
@@ -2568,11 +2556,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
@@ -2764,7 +2753,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
@@ -2774,8 +2763,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
}
@@ -2808,16 +2797,16 @@ func runtimeStructField(field StructField) structField {
panic("reflect.StructOf: field \"" + field.Name + "\" is unexported but missing PkgPath")
}
- name := field.Name
+ offsetAnon := uintptr(0)
if field.Anonymous {
- name = ""
+ offsetAnon |= 1
}
resolveReflectType(field.Type.common()) // install in runtime
return structField{
- name: newName(name, string(field.Tag), "", true),
- typ: field.Type.common(),
- offset: 0,
+ name: newName(field.Name, string(field.Tag), "", true),
+ typ: field.Type.common(),
+ offsetAnon: offsetAnon,
}
}
@@ -2840,7 +2829,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())
@@ -3214,7 +3203,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 042414ffe7..a1bfb6d489 100644
--- a/src/reflect/value.go
+++ b/src/reflect/value.go
@@ -755,7 +755,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
@@ -766,7 +766,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
}
}
From 49b7af8a308c24b1e3f6e83ded9e97513316c8d5 Mon Sep 17 00:00:00 2001
From: Russ Cox
Date: Tue, 24 Jan 2017 14:59:22 -0500
Subject: [PATCH 24/26] [dev.typealias] reflect: add test for type aliases
For #18130.
Change-Id: Idd77cb391178c185227cfd779c70fec16351f825
Reviewed-on: https://go-review.googlesource.com/35733
Run-TryBot: Russ Cox
Reviewed-by: Robert Griesemer
Reviewed-by: Ian Lance Taylor
---
src/reflect/all_test.go | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go
index ed3ad33835..1fed972eea 100644
--- a/src/reflect/all_test.go
+++ b/src/reflect/all_test.go
@@ -5985,6 +5985,11 @@ func TestUnaddressableField(t *testing.T) {
})
}
+
+type Tint int
+
+type Tint2 = Tint
+
type Talias1 struct {
byte
uint8
@@ -5993,6 +5998,11 @@ type Talias1 struct {
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)
@@ -6000,4 +6010,12 @@ func TestAliasNames(t *testing.T) {
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)
+ }
}
+
From 9ecc3ee2523f2db87b5b2d79efdd04abda93fb6e Mon Sep 17 00:00:00 2001
From: Matthew Dempsky
Date: Thu, 26 Jan 2017 09:00:56 -0800
Subject: [PATCH 25/26] [dev.typealias] cmd/compile: avoid false positive
cycles from type aliases
For #18130.
Fixes #18640.
Change-Id: I26cf1d1b78cca6ef207cc4333f30a9011ef347c9
Reviewed-on: https://go-review.googlesource.com/35831
Run-TryBot: Matthew Dempsky
TryBot-Result: Gobot Gobot
Reviewed-by: Robert Griesemer
---
src/cmd/compile/internal/gc/main.go | 6 ++++--
test/fixedbugs/issue18640.go | 26 ++++++++++++++++++++++++++
2 files changed, 30 insertions(+), 2 deletions(-)
create mode 100644 test/fixedbugs/issue18640.go
diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go
index a861a3556b..11f0547d5e 100644
--- a/src/cmd/compile/internal/gc/main.go
+++ b/src/cmd/compile/internal/gc/main.go
@@ -339,13 +339,15 @@ 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++ {
n := xtop[i]
- if op := n.Op; op != ODCL && op != OAS && op != OAS2 {
+ if op := n.Op; op != ODCL && op != OAS && op != OAS2 && (op != ODCLTYPE || !n.Left.Name.Param.Alias) {
xtop[i] = typecheck(n, Etop)
}
}
@@ -357,7 +359,7 @@ func Main() {
timings.Start("fe", "typecheck", "top2")
for i := 0; i < len(xtop); i++ {
n := xtop[i]
- if op := n.Op; op == ODCL || op == OAS || op == OAS2 {
+ if op := n.Op; op == ODCL || op == OAS || op == OAS2 || op == ODCLTYPE && n.Left.Name.Param.Alias {
xtop[i] = typecheck(n, Etop)
}
}
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
+)
From f8b4123613a2cb0c453726033a03a1968205ccae Mon Sep 17 00:00:00 2001
From: Robert Griesemer
Date: Tue, 10 Jan 2017 16:19:14 -0800
Subject: [PATCH 26/26] [dev.typealias] spec: use term 'embedded field' rather
than 'anonymous field'
First steps towards defining type aliases in the spec.
This is a nomenclature clarification, not a language change.
The spec used all three terms 'embedded type', 'anonymous field',
and 'embedded field'. Users where using the terms inconsistently.
The notion of an 'anonymous' field was always misleading since they
always had a de-facto name. With type aliases that name becomes even
more important because we may have different names for the same type.
Use the term 'embedded field' consistently and remove competing
terminology.
For #18130.
Change-Id: I2083bbc85788cab0b2e2cb1ff58b2f979491f001
Reviewed-on: https://go-review.googlesource.com/35108
Reviewed-by: Alan Donovan
Reviewed-by: Russ Cox
Reviewed-by: Rob Pike
---
doc/go_spec.html | 50 ++++++++++++++++++++++++------------------------
1 file changed, 25 insertions(+), 25 deletions(-)
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 @@
@@ -738,7 +738,7 @@ The method set of any other type T consists of all
The method set of the corresponding pointer type *T
is the set of all methods declared with receiver *T or T
(that is, it also contains the method set of T).
-Further rules apply to structs containing anonymous fields, as described
+Further rules apply to structs containing embedded fields, as described
in the section on struct types.
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.
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-blank field names must
be unique.
-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 .
@@ -974,16 +974,15 @@ struct {
-A field declared with a type but no explicit field name is an anonymous field,
-also called an embedded 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 embedded field.
+An embedded field 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. The unqualified type name acts as the field name.
-// 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:
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
}
A field or method f of an
-anonymous field in a struct x is called promoted if
+embedded field in a struct x is called promoted if
x.f is a legal selector that denotes
that field or method f.
@@ -1025,7 +1024,7 @@ promoted methods are included in the method set of the struct as follows:
-
- If
S contains an anonymous field T,
+ If S contains an embedded field T,
the method sets of S
and *S both include promoted methods with receiver
T. The method set of *S also
@@ -1033,7 +1032,7 @@ promoted methods are included in the method set of the struct as follows:
-
- If
S contains an anonymous field *T,
+ If S contains an embedded field *T,
the method sets of S and *S both
include promoted methods with receiver T or
*T.
@@ -1434,8 +1433,8 @@ literal structure and corresponding components have identical types. In detail:
- 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.
+ Non-exported field names from different
+ packages are always different.
- Two pointer types are identical if they have identical base types.
@@ -1445,8 +1444,9 @@ literal structure and corresponding components have identical types. In detail:
Parameter and result names are not required to match.
- 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.
+ with the same names and identical function types.
+ Non-exported method names from different
+ packages are always different. The order of the methods is irrelevant.
- Two map types are identical if they have identical key and value types.
@@ -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 x is a package name, see the section on
A selector f may denote a field or method f of
a type T, or it may refer
to a field or method f of a nested
-anonymous field of T.
-The number of anonymous fields traversed
+embedded field of T.
+The number of embedded fields traversed
to reach f is called its depth in T.
The depth of a field or method f
declared in T is zero.
The depth of a field or method f declared in
-an anonymous field A in T is the
+an embedded field A in T is the
depth of f in A plus one.