mirror of
https://github.com/golang/go
synced 2024-09-15 22:20:06 +00:00
all: merge dev.typealias into master
For #18130.f8b4123613
[dev.typealias] spec: use term 'embedded field' rather than 'anonymous field'9ecc3ee252
[dev.typealias] cmd/compile: avoid false positive cycles from type aliases49b7af8a30
[dev.typealias] reflect: add test for type aliases9bbb07ddec
[dev.typealias] cmd/compile, reflect: fix struct field names for embedded byte, rune43c7094386
[dev.typealias] reflect: fix StructOf use of StructField to match StructField docs9657e0b077
[dev.typealias] cmd/doc: update for type aliasde2e5459ae
[dev.typealias] cmd/compile: declare methods after resolving receiver type9259f3073a
[dev.typealias] test: match gccgo error messages on alias2.go5d92916770
[dev.typealias] cmd/compile: change Func.Shortname to *Syma7c884efc1
[dev.typealias] go/internal/gccgoimporter: support for type aliases5802cfd900
[dev.typealias] cmd/compile: export/import test cases for type aliasesd7cabd40dd
[dev.typealias] go/types: clarified doc stringcc2dcce3d7
[dev.typealias] cmd/compile: a few better comments related to alias types5c160b28ba
[dev.typealias] cmd/compile: improved error message for cyles involving type aliasesb2386dffa1
[dev.typealias] cmd/compile: type-check type alias declarationsac8421f9a5
[dev.typealias] cmd/compile: various minor cleanupsf011e0c6c3
[dev.typealias] cmd/compile, go/types, go/importer: various alias related fixes49de5f0351
[dev.typealias] cmd/compile, go/importer: define export format and implement importing of type aliases5ceec42dc0
[dev.typealias] go/types: export TypeName.IsAlias so clients can use itaa1f0681bc
[dev.typealias] go/types: improved Object printingc80748e389
[dev.typealias] go/types: remove some more vestiges of prior alias implementation80d8b69e95
[dev.typealias] go/types: implement type aliasesa917097b5e
[dev.typealias] go/build: add go1.9 build tag3e11940437
[dev.typealias] cmd/compile: recognize type aliases but complain for now (not yet supported)e0a05c274a
[dev.typealias] cmd/gofmt: added test cases for alias type declarations2e5116bd99
[dev.typealias] go/ast, go/parser, go/printer, go/types: initial type alias support Change-Id: Ia65f2e011fd7195f18e1dce67d4d49b80a261203
This commit is contained in:
commit
c47df7ae17
|
@ -1,6 +1,6 @@
|
|||
<!--{
|
||||
"Title": "The Go Programming Language Specification",
|
||||
"Subtitle": "Version of November 18, 2016",
|
||||
"Subtitle": "Version of January 31, 2017",
|
||||
"Path": "/ref/spec"
|
||||
}-->
|
||||
|
||||
|
@ -738,7 +738,7 @@ The method set of any other type <code>T</code> consists of all
|
|||
The method set of the corresponding <a href="#Pointer_types">pointer type</a> <code>*T</code>
|
||||
is the set of all methods declared with receiver <code>*T</code> or <code>T</code>
|
||||
(that is, it also contains the method set of <code>T</code>).
|
||||
Further rules apply to structs containing anonymous fields, as described
|
||||
Further rules apply to structs containing embedded fields, as described
|
||||
in the section on <a href="#Struct_types">struct types</a>.
|
||||
Any other type has an empty method set.
|
||||
In a method set, each method must have a
|
||||
|
@ -947,16 +947,16 @@ Moreover, the inner slices must be initialized individually.
|
|||
<p>
|
||||
A struct is a sequence of named elements, called fields, each of which has a
|
||||
name and a type. Field names may be specified explicitly (IdentifierList) or
|
||||
implicitly (AnonymousField).
|
||||
implicitly (EmbeddedField).
|
||||
Within a struct, non-<a href="#Blank_identifier">blank</a> field names must
|
||||
be <a href="#Uniqueness_of_identifiers">unique</a>.
|
||||
</p>
|
||||
|
||||
<pre class="ebnf">
|
||||
StructType = "struct" "{" { FieldDecl ";" } "}" .
|
||||
FieldDecl = (IdentifierList Type | AnonymousField) [ Tag ] .
|
||||
AnonymousField = [ "*" ] TypeName .
|
||||
Tag = string_lit .
|
||||
StructType = "struct" "{" { FieldDecl ";" } "}" .
|
||||
FieldDecl = (IdentifierList Type | EmbeddedField) [ Tag ] .
|
||||
EmbeddedField = [ "*" ] TypeName .
|
||||
Tag = string_lit .
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
|
@ -974,16 +974,15 @@ struct {
|
|||
</pre>
|
||||
|
||||
<p>
|
||||
A field declared with a type but no explicit field name is an <i>anonymous field</i>,
|
||||
also called an <i>embedded</i> field or an embedding of the type in the struct.
|
||||
An embedded type must be specified as
|
||||
A field declared with a type but no explicit field name is called an <i>embedded field</i>.
|
||||
An embedded field must be specified as
|
||||
a type name <code>T</code> or as a pointer to a non-interface type name <code>*T</code>,
|
||||
and <code>T</code> itself may not be
|
||||
a pointer type. The unqualified type name acts as the field name.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
// A struct with four anonymous fields of type T1, *T2, P.T3 and *P.T4
|
||||
// A struct with four embedded fields of types T1, *T2, P.T3 and *P.T4
|
||||
struct {
|
||||
T1 // field name is T1
|
||||
*T2 // field name is T2
|
||||
|
@ -1000,15 +999,15 @@ in a struct type:
|
|||
|
||||
<pre>
|
||||
struct {
|
||||
T // conflicts with anonymous field *T and *P.T
|
||||
*T // conflicts with anonymous field T and *P.T
|
||||
*P.T // conflicts with anonymous field T and *T
|
||||
T // conflicts with embedded field *T and *P.T
|
||||
*T // conflicts with embedded field T and *P.T
|
||||
*P.T // conflicts with embedded field T and *T
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
A field or <a href="#Method_declarations">method</a> <code>f</code> of an
|
||||
anonymous field in a struct <code>x</code> is called <i>promoted</i> if
|
||||
embedded field in a struct <code>x</code> is called <i>promoted</i> if
|
||||
<code>x.f</code> is a legal <a href="#Selectors">selector</a> that denotes
|
||||
that field or method <code>f</code>.
|
||||
</p>
|
||||
|
@ -1025,7 +1024,7 @@ promoted methods are included in the method set of the struct as follows:
|
|||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
If <code>S</code> contains an anonymous field <code>T</code>,
|
||||
If <code>S</code> contains an embedded field <code>T</code>,
|
||||
the <a href="#Method_sets">method sets</a> of <code>S</code>
|
||||
and <code>*S</code> both include promoted methods with receiver
|
||||
<code>T</code>. The method set of <code>*S</code> also
|
||||
|
@ -1033,7 +1032,7 @@ promoted methods are included in the method set of the struct as follows:
|
|||
</li>
|
||||
|
||||
<li>
|
||||
If <code>S</code> contains an anonymous field <code>*T</code>,
|
||||
If <code>S</code> contains an embedded field <code>*T</code>,
|
||||
the method sets of <code>S</code> and <code>*S</code> both
|
||||
include promoted methods with receiver <code>T</code> or
|
||||
<code>*T</code>.
|
||||
|
@ -1434,8 +1433,8 @@ literal structure and corresponding components have identical types. In detail:
|
|||
<li>Two struct types are identical if they have the same sequence of fields,
|
||||
and if corresponding fields have the same names, and identical types,
|
||||
and identical tags.
|
||||
Two anonymous fields are considered to have the same name. Lower-case field
|
||||
names from different packages are always different.</li>
|
||||
<a href="#Exported_identifiers">Non-exported</a> field names from different
|
||||
packages are always different.</li>
|
||||
|
||||
<li>Two pointer types are identical if they have identical base types.</li>
|
||||
|
||||
|
@ -1445,8 +1444,9 @@ literal structure and corresponding components have identical types. In detail:
|
|||
Parameter and result names are not required to match.</li>
|
||||
|
||||
<li>Two interface types are identical if they have the same set of methods
|
||||
with the same names and identical function types. Lower-case method names from
|
||||
different packages are always different. The order of the methods is irrelevant.</li>
|
||||
with the same names and identical function types.
|
||||
<a href="#Exported_identifiers">Non-exported</a> method names from different
|
||||
packages are always different. The order of the methods is irrelevant.</li>
|
||||
|
||||
<li>Two map types are identical if they have identical key and value types.</li>
|
||||
|
||||
|
@ -1891,7 +1891,7 @@ type NewMutex Mutex
|
|||
type PtrMutex *Mutex
|
||||
|
||||
// The method set of *PrintableMutex contains the methods
|
||||
// Lock and Unlock bound to its anonymous field Mutex.
|
||||
// Lock and Unlock bound to its embedded field Mutex.
|
||||
type PrintableMutex struct {
|
||||
Mutex
|
||||
}
|
||||
|
@ -2492,13 +2492,13 @@ If <code>x</code> is a package name, see the section on
|
|||
A selector <code>f</code> may denote a field or method <code>f</code> of
|
||||
a type <code>T</code>, or it may refer
|
||||
to a field or method <code>f</code> of a nested
|
||||
<a href="#Struct_types">anonymous field</a> of <code>T</code>.
|
||||
The number of anonymous fields traversed
|
||||
<a href="#Struct_types">embedded field</a> of <code>T</code>.
|
||||
The number of embedded fields traversed
|
||||
to reach <code>f</code> is called its <i>depth</i> in <code>T</code>.
|
||||
The depth of a field or method <code>f</code>
|
||||
declared in <code>T</code> is zero.
|
||||
The depth of a field or method <code>f</code> declared in
|
||||
an anonymous field <code>A</code> in <code>T</code> is the
|
||||
an embedded field <code>A</code> in <code>T</code> is the
|
||||
depth of <code>f</code> in <code>A</code> plus one.
|
||||
</p>
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -140,11 +140,12 @@ const debugFormat = false // default: false
|
|||
const forceObjFileStability = true
|
||||
|
||||
// Current export format version. Increase with each format change.
|
||||
// 3: added aliasTag and export of aliases
|
||||
// 2: removed unused bool in ODCL export
|
||||
// 4: type name objects support type aliases, uses aliasTag
|
||||
// 3: Go1.8 encoding (same as version 2, aliasTag defined but never used)
|
||||
// 2: removed unused bool in ODCL export (compiler only)
|
||||
// 1: header format change (more regular), export package for _ struct fields
|
||||
// 0: Go1.7 encoding
|
||||
const exportVersion = 3
|
||||
const exportVersion = 4
|
||||
|
||||
// exportInlined enables the export of inlined function bodies and related
|
||||
// dependencies. The compiler should work w/o any loss of functionality with
|
||||
|
@ -351,8 +352,8 @@ func export(out *bufio.Writer, trace bool) int {
|
|||
p.tracef("\n")
|
||||
}
|
||||
|
||||
if sym.Flags&SymAlias != 0 {
|
||||
Fatalf("exporter: unexpected alias %v in inlined function body", sym)
|
||||
if sym.isAlias() {
|
||||
Fatalf("exporter: unexpected type alias %v in inlined function body", sym)
|
||||
}
|
||||
|
||||
p.obj(sym)
|
||||
|
@ -446,30 +447,6 @@ func unidealType(typ *Type, val Val) *Type {
|
|||
}
|
||||
|
||||
func (p *exporter) obj(sym *Sym) {
|
||||
if sym.Flags&SymAlias != 0 {
|
||||
p.tag(aliasTag)
|
||||
p.pos(nil) // TODO(gri) fix position information
|
||||
// Aliases can only be exported from the package that
|
||||
// declares them (aliases to aliases are resolved to the
|
||||
// original object, and so are uses of aliases in inlined
|
||||
// exported function bodies). Thus, we only need the alias
|
||||
// name without package qualification.
|
||||
if sym.Pkg != localpkg {
|
||||
Fatalf("exporter: export of non-local alias: %v", sym)
|
||||
}
|
||||
p.string(sym.Name)
|
||||
orig := sym.Def.Sym
|
||||
if orig.Flags&SymAlias != 0 {
|
||||
Fatalf("exporter: original object %v marked as alias", sym)
|
||||
}
|
||||
p.qualifiedName(orig)
|
||||
return
|
||||
}
|
||||
|
||||
if sym != sym.Def.Sym {
|
||||
Fatalf("exporter: exported object %v is not original %v", sym, sym.Def.Sym)
|
||||
}
|
||||
|
||||
// Exported objects may be from different packages because they
|
||||
// may be re-exported via an exported alias or as dependencies in
|
||||
// exported inlined function bodies. Thus, exported object names
|
||||
|
@ -509,7 +486,13 @@ func (p *exporter) obj(sym *Sym) {
|
|||
Fatalf("exporter: export of incomplete type %v", sym)
|
||||
}
|
||||
|
||||
p.tag(typeTag)
|
||||
if sym.isAlias() {
|
||||
p.tag(aliasTag)
|
||||
p.pos(n)
|
||||
p.qualifiedName(sym)
|
||||
} else {
|
||||
p.tag(typeTag)
|
||||
}
|
||||
p.typ(t)
|
||||
|
||||
case ONAME:
|
||||
|
@ -868,19 +851,29 @@ func (p *exporter) methodList(t *Type) {
|
|||
|
||||
func (p *exporter) method(m *Field) {
|
||||
p.pos(m.Nname)
|
||||
p.fieldName(m)
|
||||
p.methodName(m.Sym)
|
||||
p.paramList(m.Type.Params(), false)
|
||||
p.paramList(m.Type.Results(), false)
|
||||
}
|
||||
|
||||
// fieldName is like qualifiedName but it doesn't record the package for exported names.
|
||||
func (p *exporter) fieldName(t *Field) {
|
||||
name := t.Sym.Name
|
||||
if t.Embedded != 0 {
|
||||
name = "" // anonymous field
|
||||
if bname := basetypeName(t.Type); bname != "" && !exportname(bname) {
|
||||
// anonymous field with unexported base type name
|
||||
name = "?" // unexported name to force export of package
|
||||
// anonymous field - we distinguish between 3 cases:
|
||||
// 1) field name matches base type name and is exported
|
||||
// 2) field name matches base type name and is not exported
|
||||
// 3) field name doesn't match base type name (alias name)
|
||||
bname := basetypeName(t.Type)
|
||||
if name == bname {
|
||||
if exportname(name) {
|
||||
name = "" // 1) we don't need to know the field name or package
|
||||
} else {
|
||||
name = "?" // 2) use unexported name "?" to force package export
|
||||
}
|
||||
} else {
|
||||
// 3) indicate alias and export name as is
|
||||
// (this requires an extra "@" but this is a rare case)
|
||||
p.string("@")
|
||||
}
|
||||
}
|
||||
p.string(name)
|
||||
|
@ -889,16 +882,23 @@ func (p *exporter) fieldName(t *Field) {
|
|||
}
|
||||
}
|
||||
|
||||
// methodName is like qualifiedName but it doesn't record the package for exported names.
|
||||
func (p *exporter) methodName(sym *Sym) {
|
||||
p.string(sym.Name)
|
||||
if !exportname(sym.Name) {
|
||||
p.pkg(sym.Pkg)
|
||||
}
|
||||
}
|
||||
|
||||
func basetypeName(t *Type) string {
|
||||
s := t.Sym
|
||||
if s == nil && t.IsPtr() {
|
||||
s = t.Elem().Sym // deref
|
||||
}
|
||||
// s should exist, but be conservative
|
||||
if s != nil {
|
||||
return s.Name
|
||||
}
|
||||
return ""
|
||||
return "" // unnamed type
|
||||
}
|
||||
|
||||
func (p *exporter) paramList(params *Type, numbered bool) {
|
||||
|
@ -1797,7 +1797,7 @@ const (
|
|||
nilTag
|
||||
unknownTag // not used by gc (only appears in packages with errors)
|
||||
|
||||
// Aliases
|
||||
// Type aliases
|
||||
aliasTag
|
||||
)
|
||||
|
||||
|
@ -1835,7 +1835,7 @@ var tagString = [...]string{
|
|||
-nilTag: "nil",
|
||||
-unknownTag: "unknown",
|
||||
|
||||
// Aliases
|
||||
// Type aliases
|
||||
-aliasTag: "alias",
|
||||
}
|
||||
|
||||
|
@ -1889,7 +1889,7 @@ func predeclared() []*Type {
|
|||
Types[TCOMPLEX128],
|
||||
Types[TSTRING],
|
||||
|
||||
// aliases
|
||||
// basic type aliases
|
||||
bytetype,
|
||||
runetype,
|
||||
|
||||
|
|
|
@ -86,10 +86,10 @@ func Import(in *bufio.Reader) {
|
|||
|
||||
// read version specific flags - extend as necessary
|
||||
switch p.version {
|
||||
// case 4:
|
||||
// case 5:
|
||||
// ...
|
||||
// fallthrough
|
||||
case 3, 2, 1:
|
||||
case 4, 3, 2, 1:
|
||||
p.debugFormat = p.rawStringln(p.rawByte()) == "debug"
|
||||
p.trackAllTypes = p.bool()
|
||||
p.posInfoFormat = p.bool()
|
||||
|
@ -317,6 +317,12 @@ func (p *importer) obj(tag int) {
|
|||
val := p.value(typ)
|
||||
importconst(sym, idealType(typ), nodlit(val))
|
||||
|
||||
case aliasTag:
|
||||
p.pos()
|
||||
sym := p.qualifiedName()
|
||||
typ := p.typ()
|
||||
importalias(sym, typ)
|
||||
|
||||
case typeTag:
|
||||
p.typ()
|
||||
|
||||
|
@ -356,17 +362,6 @@ func (p *importer) obj(tag int) {
|
|||
}
|
||||
}
|
||||
|
||||
case aliasTag:
|
||||
p.pos()
|
||||
alias := importpkg.Lookup(p.string())
|
||||
orig := p.qualifiedName()
|
||||
|
||||
// Although the protocol allows the alias to precede the original,
|
||||
// this never happens in files produced by gc.
|
||||
alias.Flags |= SymAlias
|
||||
alias.Def = orig.Def
|
||||
importsym(alias, orig.Def.Op)
|
||||
|
||||
default:
|
||||
formatErrorf("unexpected object (tag = %d)", tag)
|
||||
}
|
||||
|
@ -473,14 +468,7 @@ func (p *importer) typ() *Type {
|
|||
result := p.paramList()
|
||||
nointerface := p.bool()
|
||||
|
||||
base := recv[0].Type
|
||||
star := false
|
||||
if base.IsPtr() {
|
||||
base = base.Elem()
|
||||
star = true
|
||||
}
|
||||
|
||||
n := methodname0(sym, star, base.Sym)
|
||||
n := newfuncname(methodname(sym, recv[0].Type))
|
||||
n.Type = functypefield(recv[0], params, result)
|
||||
checkwidth(n.Type)
|
||||
addmethod(sym, n.Type, false, nointerface)
|
||||
|
@ -583,19 +571,22 @@ func (p *importer) fieldList() (fields []*Field) {
|
|||
|
||||
func (p *importer) field() *Field {
|
||||
p.pos()
|
||||
sym := p.fieldName()
|
||||
sym, alias := p.fieldName()
|
||||
typ := p.typ()
|
||||
note := p.string()
|
||||
|
||||
f := newField()
|
||||
if sym.Name == "" {
|
||||
// anonymous field - typ must be T or *T and T must be a type name
|
||||
// anonymous field: typ must be T or *T and T must be a type name
|
||||
s := typ.Sym
|
||||
if s == nil && typ.IsPtr() {
|
||||
s = typ.Elem().Sym // deref
|
||||
}
|
||||
sym = sym.Pkg.Lookup(s.Name)
|
||||
f.Embedded = 1
|
||||
} else if alias {
|
||||
// anonymous field: we have an explicit name because it's a type alias
|
||||
f.Embedded = 1
|
||||
}
|
||||
|
||||
f.Sym = sym
|
||||
|
@ -618,7 +609,7 @@ func (p *importer) methodList() (methods []*Field) {
|
|||
|
||||
func (p *importer) method() *Field {
|
||||
p.pos()
|
||||
sym := p.fieldName()
|
||||
sym := p.methodName()
|
||||
params := p.paramList()
|
||||
result := p.paramList()
|
||||
|
||||
|
@ -629,18 +620,44 @@ func (p *importer) method() *Field {
|
|||
return f
|
||||
}
|
||||
|
||||
func (p *importer) fieldName() *Sym {
|
||||
func (p *importer) fieldName() (*Sym, bool) {
|
||||
name := p.string()
|
||||
if p.version == 0 && name == "_" {
|
||||
// version 0 didn't export a package for _ fields
|
||||
// version 0 didn't export a package for _ field names
|
||||
// but used the builtin package instead
|
||||
return builtinpkg.Lookup(name), false
|
||||
}
|
||||
pkg := localpkg
|
||||
alias := false
|
||||
switch name {
|
||||
case "":
|
||||
// 1) field name matches base type name and is exported: nothing to do
|
||||
case "?":
|
||||
// 2) field name matches base type name and is not exported: need package
|
||||
name = ""
|
||||
pkg = p.pkg()
|
||||
case "@":
|
||||
// 3) field name doesn't match base type name (alias name): need name and possibly package
|
||||
name = p.string()
|
||||
alias = true
|
||||
fallthrough
|
||||
default:
|
||||
if !exportname(name) {
|
||||
pkg = p.pkg()
|
||||
}
|
||||
}
|
||||
return pkg.Lookup(name), alias
|
||||
}
|
||||
|
||||
func (p *importer) methodName() *Sym {
|
||||
name := p.string()
|
||||
if p.version == 0 && name == "_" {
|
||||
// version 0 didn't export a package for _ method names
|
||||
// but used the builtin package instead
|
||||
return builtinpkg.Lookup(name)
|
||||
}
|
||||
pkg := localpkg
|
||||
if name != "" && !exportname(name) {
|
||||
if name == "?" {
|
||||
name = ""
|
||||
}
|
||||
if !exportname(name) {
|
||||
pkg = p.pkg()
|
||||
}
|
||||
return pkg.Lookup(name)
|
||||
|
|
|
@ -519,10 +519,6 @@ func funchdr(n *Node) {
|
|||
Fatalf("funchdr: dclcontext = %d", dclcontext)
|
||||
}
|
||||
|
||||
if Ctxt.Flag_dynlink && importpkg == nil && n.Func.Nname != nil {
|
||||
makefuncsym(n.Func.Nname.Sym)
|
||||
}
|
||||
|
||||
dclcontext = PAUTO
|
||||
funcstart(n)
|
||||
|
||||
|
@ -695,10 +691,20 @@ func typedcl0(s *Sym) *Node {
|
|||
|
||||
// node n, which was returned by typedcl0
|
||||
// is being declared to have uncompiled type t.
|
||||
// return the ODCLTYPE node to use.
|
||||
func typedcl1(n *Node, t *Node, local bool) *Node {
|
||||
n.Name.Param.Ntype = t
|
||||
n.Local = local
|
||||
// returns the ODCLTYPE node to use.
|
||||
func typedcl1(n *Node, t *Node, pragma Pragma, alias bool) *Node {
|
||||
if pragma != 0 && alias {
|
||||
yyerror("cannot specify directive with type alias")
|
||||
pragma = 0
|
||||
}
|
||||
|
||||
n.Local = true
|
||||
|
||||
p := n.Name.Param
|
||||
p.Ntype = t
|
||||
p.Pragma = pragma
|
||||
p.Alias = alias
|
||||
|
||||
return nod(ODCLTYPE, n, nil)
|
||||
}
|
||||
|
||||
|
@ -1153,19 +1159,19 @@ bad:
|
|||
return nil
|
||||
}
|
||||
|
||||
func methodname(n *Node, t *Node) *Node {
|
||||
// methodname is a misnomer because this now returns a Sym, rather
|
||||
// than an ONAME.
|
||||
// TODO(mdempsky): Reconcile with methodsym.
|
||||
func methodname(s *Sym, recv *Type) *Sym {
|
||||
star := false
|
||||
if t.Op == OIND {
|
||||
if recv.IsPtr() {
|
||||
star = true
|
||||
t = t.Left
|
||||
recv = recv.Elem()
|
||||
}
|
||||
|
||||
return methodname0(n.Sym, star, t.Sym)
|
||||
}
|
||||
|
||||
func methodname0(s *Sym, star bool, tsym *Sym) *Node {
|
||||
tsym := recv.Sym
|
||||
if tsym == nil || isblanksym(s) {
|
||||
return newfuncname(s)
|
||||
return s
|
||||
}
|
||||
|
||||
var p string
|
||||
|
@ -1181,14 +1187,13 @@ func methodname0(s *Sym, star bool, tsym *Sym) *Node {
|
|||
s = Pkglookup(p, tsym.Pkg)
|
||||
}
|
||||
|
||||
return newfuncname(s)
|
||||
return s
|
||||
}
|
||||
|
||||
// Add a method, declared as a function.
|
||||
// - msym is the method symbol
|
||||
// - t is function type (with receiver)
|
||||
func addmethod(msym *Sym, t *Type, local, nointerface bool) {
|
||||
// get field sym
|
||||
if msym == nil {
|
||||
Fatalf("no method symbol")
|
||||
}
|
||||
|
@ -1309,7 +1314,7 @@ func funcsym(s *Sym) *Sym {
|
|||
s1 := Pkglookup(s.Name+"·f", s.Pkg)
|
||||
if !Ctxt.Flag_dynlink && s1.Def == nil {
|
||||
s1.Def = newfuncname(s1)
|
||||
s1.Def.Func.Shortname = newname(s)
|
||||
s1.Def.Func.Shortname = s
|
||||
funcsyms = append(funcsyms, s1.Def)
|
||||
}
|
||||
s.Fsym = s1
|
||||
|
@ -1326,8 +1331,11 @@ func makefuncsym(s *Sym) {
|
|||
return
|
||||
}
|
||||
s1 := funcsym(s)
|
||||
if s1.Def != nil {
|
||||
return
|
||||
}
|
||||
s1.Def = newfuncname(s1)
|
||||
s1.Def.Func.Shortname = newname(s)
|
||||
s1.Def.Func.Shortname = s
|
||||
funcsyms = append(funcsyms, s1.Def)
|
||||
}
|
||||
|
||||
|
|
|
@ -45,8 +45,8 @@ func exportsym(n *Node) {
|
|||
fmt.Printf("export symbol %v\n", n.Sym)
|
||||
}
|
||||
|
||||
// Ensure original object is on exportlist before aliases.
|
||||
if n.Sym.Flags&SymAlias != 0 {
|
||||
// Ensure original types are on exportlist before type aliases.
|
||||
if n.Sym.isAlias() {
|
||||
exportlist = append(exportlist, n.Sym.Def)
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ func autoexport(n *Node, ctxt Class) {
|
|||
if (ctxt != PEXTERN && ctxt != PFUNC) || dclcontext != PEXTERN {
|
||||
return
|
||||
}
|
||||
if n.Name.Param != nil && n.Name.Param.Ntype != nil && n.Name.Param.Ntype.Op == OTFUNC && n.Name.Param.Ntype.Left != nil { // method
|
||||
if n.Type != nil && n.Type.IsKind(TFUNC) && n.Type.Recv() != nil { // method
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -348,6 +348,27 @@ func importvar(s *Sym, t *Type) {
|
|||
}
|
||||
}
|
||||
|
||||
// importalias declares symbol s as an imported type alias with type t.
|
||||
func importalias(s *Sym, t *Type) {
|
||||
importsym(s, OTYPE)
|
||||
if s.Def != nil && s.Def.Op == OTYPE {
|
||||
if eqtype(t, s.Def.Type) {
|
||||
return
|
||||
}
|
||||
yyerror("inconsistent definition for type alias %v during import\n\t%v (in %q)\n\t%v (in %q)", s, s.Def.Type, s.Importdef.Path, t, importpkg.Path)
|
||||
}
|
||||
|
||||
n := newname(s)
|
||||
n.Op = OTYPE
|
||||
s.Importdef = importpkg
|
||||
n.Type = t
|
||||
declare(n, PEXTERN)
|
||||
|
||||
if Debug['E'] != 0 {
|
||||
fmt.Printf("import type %v = %L\n", s, t)
|
||||
}
|
||||
}
|
||||
|
||||
func dumpasmhdr() {
|
||||
b, err := bio.Create(asmhdr)
|
||||
if err != nil {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -340,13 +340,16 @@ func Main() {
|
|||
// Phase 1: const, type, and names and types of funcs.
|
||||
// This will gather all the information about types
|
||||
// and methods but doesn't depend on any of it.
|
||||
// We also defer type alias declarations until phase 2
|
||||
// to avoid cycles like #18640.
|
||||
defercheckwidth()
|
||||
|
||||
// Don't use range--typecheck can add closures to xtop.
|
||||
timings.Start("fe", "typecheck", "top1")
|
||||
for i := 0; i < len(xtop); i++ {
|
||||
if xtop[i].Op != ODCL && xtop[i].Op != OAS && xtop[i].Op != OAS2 {
|
||||
xtop[i] = typecheck(xtop[i], Etop)
|
||||
n := xtop[i]
|
||||
if op := n.Op; op != ODCL && op != OAS && op != OAS2 && (op != ODCLTYPE || !n.Left.Name.Param.Alias) {
|
||||
xtop[i] = typecheck(n, Etop)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -356,8 +359,9 @@ func Main() {
|
|||
// Don't use range--typecheck can add closures to xtop.
|
||||
timings.Start("fe", "typecheck", "top2")
|
||||
for i := 0; i < len(xtop); i++ {
|
||||
if xtop[i].Op == ODCL || xtop[i].Op == OAS || xtop[i].Op == OAS2 {
|
||||
xtop[i] = typecheck(xtop[i], Etop)
|
||||
n := xtop[i]
|
||||
if op := n.Op; op == ODCL || op == OAS || op == OAS2 || op == ODCLTYPE && n.Left.Name.Param.Alias {
|
||||
xtop[i] = typecheck(n, Etop)
|
||||
}
|
||||
}
|
||||
resumecheckwidth()
|
||||
|
@ -367,8 +371,9 @@ func Main() {
|
|||
timings.Start("fe", "typecheck", "func")
|
||||
var fcount int64
|
||||
for i := 0; i < len(xtop); i++ {
|
||||
if xtop[i].Op == ODCLFUNC || xtop[i].Op == OCLOSURE {
|
||||
Curfn = xtop[i]
|
||||
n := xtop[i]
|
||||
if op := n.Op; op == ODCLFUNC || op == OCLOSURE {
|
||||
Curfn = n
|
||||
decldepth = 1
|
||||
saveerrors()
|
||||
typecheckslice(Curfn.Nbody.Slice(), Etop)
|
||||
|
@ -460,8 +465,9 @@ func Main() {
|
|||
timings.Start("be", "compilefuncs")
|
||||
fcount = 0
|
||||
for i := 0; i < len(xtop); i++ {
|
||||
if xtop[i].Op == ODCLFUNC {
|
||||
funccompile(xtop[i])
|
||||
n := xtop[i]
|
||||
if n.Op == ODCLFUNC {
|
||||
funccompile(n)
|
||||
fcount++
|
||||
}
|
||||
}
|
||||
|
@ -924,7 +930,7 @@ func mkpackage(pkgname string) {
|
|||
continue
|
||||
}
|
||||
|
||||
if s.Def.Sym != s && s.Flags&SymAlias == 0 {
|
||||
if s.isAlias() {
|
||||
// throw away top-level name left over
|
||||
// from previous import . "x"
|
||||
if s.Def.Name != nil && s.Def.Name.Pack != nil && !s.Def.Name.Pack.Used && nsyntaxerrors == 0 {
|
||||
|
|
|
@ -154,11 +154,7 @@ func (p *noder) importDecl(imp *syntax.ImportDecl) {
|
|||
|
||||
func (p *noder) varDecl(decl *syntax.VarDecl) []*Node {
|
||||
names := p.declNames(decl.NameList)
|
||||
|
||||
var typ *Node
|
||||
if decl.Type != nil {
|
||||
typ = p.typeExpr(decl.Type)
|
||||
}
|
||||
typ := p.typeExprOrNil(decl.Type)
|
||||
|
||||
var exprs []*Node
|
||||
if decl.Values != nil {
|
||||
|
@ -171,11 +167,7 @@ func (p *noder) varDecl(decl *syntax.VarDecl) []*Node {
|
|||
|
||||
func (p *noder) constDecl(decl *syntax.ConstDecl) []*Node {
|
||||
names := p.declNames(decl.NameList)
|
||||
|
||||
var typ *Node
|
||||
if decl.Type != nil {
|
||||
typ = p.typeExpr(decl.Type)
|
||||
}
|
||||
typ := p.typeExprOrNil(decl.Type)
|
||||
|
||||
var exprs []*Node
|
||||
if decl.Values != nil {
|
||||
|
@ -187,14 +179,11 @@ func (p *noder) constDecl(decl *syntax.ConstDecl) []*Node {
|
|||
|
||||
func (p *noder) typeDecl(decl *syntax.TypeDecl) *Node {
|
||||
name := typedcl0(p.name(decl.Name))
|
||||
name.Name.Param.Pragma = Pragma(decl.Pragma)
|
||||
|
||||
var typ *Node
|
||||
if decl.Type != nil {
|
||||
typ = p.typeExpr(decl.Type)
|
||||
}
|
||||
// decl.Type may be nil but in that case we got a syntax error during parsing
|
||||
typ := p.typeExprOrNil(decl.Type)
|
||||
|
||||
return typedcl1(name, typ, true)
|
||||
return typedcl1(name, typ, Pragma(decl.Pragma), decl.Alias)
|
||||
}
|
||||
|
||||
func (p *noder) declNames(names []*syntax.Name) []*Node {
|
||||
|
@ -259,19 +248,19 @@ func (p *noder) funcHeader(fun *syntax.FuncDecl) *Node {
|
|||
yyerror("func main must have no arguments and no return values")
|
||||
}
|
||||
}
|
||||
|
||||
f.Func.Nname = newfuncname(name)
|
||||
} else {
|
||||
// Receiver MethodName Signature
|
||||
|
||||
f.Func.Shortname = newfuncname(name)
|
||||
f.Func.Nname = methodname(f.Func.Shortname, t.Left.Right)
|
||||
f.Func.Shortname = name
|
||||
name = nblank.Sym // filled in by typecheckfunc
|
||||
}
|
||||
|
||||
f.Func.Nname = newfuncname(name)
|
||||
f.Func.Nname.Name.Defn = f
|
||||
f.Func.Nname.Name.Param.Ntype = t // TODO: check if nname already has an ntype
|
||||
|
||||
declare(f.Func.Nname, PFUNC)
|
||||
if fun.Recv == nil {
|
||||
declare(f.Func.Nname, PFUNC)
|
||||
}
|
||||
|
||||
funchdr(f)
|
||||
return f
|
||||
}
|
||||
|
@ -467,6 +456,13 @@ func (p *noder) typeExpr(typ syntax.Expr) *Node {
|
|||
return p.expr(typ)
|
||||
}
|
||||
|
||||
func (p *noder) typeExprOrNil(typ syntax.Expr) *Node {
|
||||
if typ != nil {
|
||||
return p.expr(typ)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *noder) chanDir(dir syntax.ChanDir) ChanDir {
|
||||
switch dir {
|
||||
case 0:
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ type Node struct {
|
|||
// func
|
||||
Func *Func
|
||||
|
||||
// ONAME
|
||||
// ONAME, OTYPE, OPACK, OLABEL, some OLITERAL
|
||||
Name *Name
|
||||
|
||||
Sym *Sym // various
|
||||
|
@ -59,8 +59,8 @@ type Node struct {
|
|||
Noescape bool // func arguments do not escape; TODO(rsc): move Noescape to Func struct (see CL 7360)
|
||||
Walkdef uint8 // tracks state during typecheckdef; 2 == loop detected
|
||||
Typecheck uint8 // tracks state during typechecking; 2 == loop detected
|
||||
Local bool
|
||||
IsStatic bool // whether this Node will be converted to purely static data
|
||||
Local bool // type created in this file (see also Type.Local); TODO(gri): move this into flags
|
||||
IsStatic bool // whether this Node will be converted to purely static data
|
||||
Initorder uint8
|
||||
Used bool // for variable/label declared and not used error
|
||||
Isddd bool // is the argument variadic
|
||||
|
@ -180,14 +180,14 @@ func (n *Node) SetIota(x int64) {
|
|||
n.Xoffset = x
|
||||
}
|
||||
|
||||
// Name holds Node fields used only by named nodes (ONAME, OPACK, OLABEL, some OLITERAL).
|
||||
// Name holds Node fields used only by named nodes (ONAME, OTYPE, OPACK, OLABEL, some OLITERAL).
|
||||
type Name struct {
|
||||
Pack *Node // real package for import . names
|
||||
Pkg *Pkg // pkg for OPACK nodes
|
||||
Heapaddr *Node // temp holding heap address of param (could move to Param?)
|
||||
Defn *Node // initializing assignment
|
||||
Curfn *Node // function for local variables
|
||||
Param *Param // additional fields for ONAME
|
||||
Param *Param // additional fields for ONAME, OTYPE
|
||||
Decldepth int32 // declaration loop depth, increased for every loop or label
|
||||
Vargen int32 // unique name for ONAME within a function. Function outputs are numbered starting at one.
|
||||
Funcdepth int32
|
||||
|
@ -280,15 +280,16 @@ type Param struct {
|
|||
Innermost *Node
|
||||
Outer *Node
|
||||
|
||||
// OTYPE pragmas
|
||||
// OTYPE
|
||||
//
|
||||
// TODO: Should Func pragmas also be stored on the Name?
|
||||
Pragma Pragma
|
||||
Alias bool // node is alias for Ntype (only used when type-checking ODCLTYPE)
|
||||
}
|
||||
|
||||
// Func holds Node fields used only with function-like nodes.
|
||||
type Func struct {
|
||||
Shortname *Node
|
||||
Shortname *Sym
|
||||
Enter Nodes // for example, allocate and initialize memory for escaping parameters
|
||||
Exit Nodes
|
||||
Cvars Nodes // closure params
|
||||
|
@ -382,7 +383,7 @@ const (
|
|||
ODCLFUNC // func f() or func (r) f()
|
||||
ODCLFIELD // struct field, interface field, or func/method argument/return value.
|
||||
ODCLCONST // const pi = 3.14
|
||||
ODCLTYPE // type Int int
|
||||
ODCLTYPE // type Int int or type Int = int
|
||||
|
||||
ODELETE // delete(Left, Right)
|
||||
ODOT // Left.Sym (Left is of struct type)
|
||||
|
|
|
@ -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
|
||||
|
@ -3429,7 +3436,14 @@ func typecheckfunc(n *Node) {
|
|||
t.SetNname(n.Func.Nname)
|
||||
rcvr := t.Recv()
|
||||
if rcvr != nil && n.Func.Shortname != nil {
|
||||
addmethod(n.Func.Shortname.Sym, t, true, n.Func.Pragma&Nointerface != 0)
|
||||
n.Func.Nname.Sym = methodname(n.Func.Shortname, rcvr.Type)
|
||||
declare(n.Func.Nname, PFUNC)
|
||||
|
||||
addmethod(n.Func.Shortname, t, true, n.Func.Pragma&Nointerface != 0)
|
||||
}
|
||||
|
||||
if Ctxt.Flag_dynlink && importpkg == nil && n.Func.Nname != nil {
|
||||
makefuncsym(n.Func.Nname.Sym)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3578,8 +3592,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 +3770,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 +3800,6 @@ func typecheckdef(n *Node) *Node {
|
|||
// but it was reported. Silence future errors.
|
||||
n.Type.Broke = true
|
||||
}
|
||||
|
||||
if Curfn != nil {
|
||||
resumecheckwidth()
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
4
src/cmd/doc/testdata/pkg.go
vendored
4
src/cmd/doc/testdata/pkg.go
vendored
|
@ -172,3 +172,7 @@ const (
|
|||
)
|
||||
|
||||
const ConstGroup4 ExportedType = ExportedType{}
|
||||
|
||||
type T2 int
|
||||
|
||||
type T1 = T2
|
||||
|
|
24
src/cmd/gofmt/testdata/typealias.golden
vendored
Normal file
24
src/cmd/gofmt/testdata/typealias.golden
vendored
Normal file
|
@ -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
|
||||
)
|
24
src/cmd/gofmt/testdata/typealias.input
vendored
Normal file
24
src/cmd/gofmt/testdata/typealias.input
vendored
Normal file
|
@ -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
|
||||
)
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 == "" {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
4
src/go/internal/gccgoimporter/testdata/alias.gox
vendored
Normal file
4
src/go/internal/gccgoimporter/testdata/alias.gox
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
v1;
|
||||
package alias;
|
||||
pkgpath alias;
|
||||
type <type 115 "I1" <type 116 interface { M1 (? <type 117 "IntAlias2" = <type 118 "IntAlias" = <type 119 "Int" <type -11>>>>) < type 114>; M2 () <type 1>; }>>;
|
|
@ -98,10 +98,10 @@ func BImportData(fset *token.FileSet, imports map[string]*types.Package, data []
|
|||
|
||||
// read version specific flags - extend as necessary
|
||||
switch p.version {
|
||||
// case 4:
|
||||
// case 5:
|
||||
// ...
|
||||
// fallthrough
|
||||
case 3, 2, 1:
|
||||
case 4, 3, 2, 1:
|
||||
p.debugFormat = p.rawStringln(p.rawByte()) == "debug"
|
||||
p.trackAllTypes = p.int() != 0
|
||||
p.posInfoFormat = p.int() != 0
|
||||
|
@ -208,7 +208,6 @@ func (p *importer) pkg() *types.Package {
|
|||
}
|
||||
|
||||
// objTag returns the tag value for each object kind.
|
||||
// obj must not be a *types.Alias.
|
||||
func objTag(obj types.Object) int {
|
||||
switch obj.(type) {
|
||||
case *types.Const:
|
||||
|
@ -219,7 +218,6 @@ func objTag(obj types.Object) int {
|
|||
return varTag
|
||||
case *types.Func:
|
||||
return funcTag
|
||||
// Aliases are not exported multiple times, thus we should not see them here.
|
||||
default:
|
||||
errorf("unexpected object: %v (%T)", obj, obj) // panics
|
||||
panic("unreachable")
|
||||
|
@ -237,14 +235,14 @@ func (p *importer) declare(obj types.Object) {
|
|||
pkg := obj.Pkg()
|
||||
if alt := pkg.Scope().Insert(obj); alt != nil {
|
||||
// This can only trigger if we import a (non-type) object a second time.
|
||||
// Excluding aliases, this cannot happen because 1) we only import a package
|
||||
// Excluding type aliases, this cannot happen because 1) we only import a package
|
||||
// once; and b) we ignore compiler-specific export data which may contain
|
||||
// functions whose inlined function bodies refer to other functions that
|
||||
// were already imported.
|
||||
// However, aliases require reexporting the original object, so we need
|
||||
// However, type aliases require reexporting the original type, so we need
|
||||
// to allow it (see also the comment in cmd/compile/internal/gc/bimport.go,
|
||||
// method importer.obj, switch case importing functions).
|
||||
// Note that the original itself cannot be an alias.
|
||||
// TODO(gri) review/update this comment once the gc compiler handles type aliases.
|
||||
if !sameObj(obj, alt) {
|
||||
errorf("inconsistent import:\n\t%v\npreviously imported as:\n\t%v\n", obj, alt)
|
||||
}
|
||||
|
@ -260,6 +258,13 @@ func (p *importer) obj(tag int) {
|
|||
val := p.value()
|
||||
p.declare(types.NewConst(pos, pkg, name, typ, val))
|
||||
|
||||
case aliasTag:
|
||||
// TODO(gri) verify type alias hookup is correct
|
||||
pos := p.pos()
|
||||
pkg, name := p.qualifiedName()
|
||||
typ := p.typ(nil)
|
||||
p.declare(types.NewTypeName(pos, pkg, name, typ))
|
||||
|
||||
case typeTag:
|
||||
p.typ(nil)
|
||||
|
||||
|
@ -277,19 +282,6 @@ func (p *importer) obj(tag int) {
|
|||
sig := types.NewSignature(nil, params, result, isddd)
|
||||
p.declare(types.NewFunc(pos, pkg, name, sig))
|
||||
|
||||
case aliasTag:
|
||||
pos := p.pos()
|
||||
name := p.string()
|
||||
var orig types.Object
|
||||
if pkg, name := p.qualifiedName(); pkg != nil {
|
||||
orig = pkg.Scope().Lookup(name)
|
||||
}
|
||||
// Alias-related code. Keep for now.
|
||||
_ = pos
|
||||
_ = name
|
||||
_ = orig
|
||||
// p.declare(types.NewAlias(pos, p.pkgList[0], name, orig))
|
||||
|
||||
default:
|
||||
errorf("unexpected object tag %d", tag)
|
||||
}
|
||||
|
@ -349,9 +341,7 @@ var (
|
|||
|
||||
func (p *importer) qualifiedName() (pkg *types.Package, name string) {
|
||||
name = p.string()
|
||||
if name != "" {
|
||||
pkg = p.pkg()
|
||||
}
|
||||
pkg = p.pkg()
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -556,17 +546,17 @@ func (p *importer) fieldList(parent *types.Package) (fields []*types.Var, tags [
|
|||
fields = make([]*types.Var, n)
|
||||
tags = make([]string, n)
|
||||
for i := range fields {
|
||||
fields[i] = p.field(parent)
|
||||
tags[i] = p.string()
|
||||
fields[i], tags[i] = p.field(parent)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *importer) field(parent *types.Package) *types.Var {
|
||||
func (p *importer) field(parent *types.Package) (*types.Var, string) {
|
||||
pos := p.pos()
|
||||
pkg, name := p.fieldName(parent)
|
||||
pkg, name, alias := p.fieldName(parent)
|
||||
typ := p.typ(parent)
|
||||
tag := p.string()
|
||||
|
||||
anonymous := false
|
||||
if name == "" {
|
||||
|
@ -578,12 +568,15 @@ func (p *importer) field(parent *types.Package) *types.Var {
|
|||
case *types.Named:
|
||||
name = typ.Obj().Name()
|
||||
default:
|
||||
errorf("anonymous field expected")
|
||||
errorf("named base type expected")
|
||||
}
|
||||
anonymous = true
|
||||
} else if alias {
|
||||
// anonymous field: we have an explicit name because it's an alias
|
||||
anonymous = true
|
||||
}
|
||||
|
||||
return types.NewField(pos, pkg, name, typ, anonymous)
|
||||
return types.NewField(pos, pkg, name, typ, anonymous), tag
|
||||
}
|
||||
|
||||
func (p *importer) methodList(parent *types.Package) (methods []*types.Func) {
|
||||
|
@ -598,31 +591,42 @@ func (p *importer) methodList(parent *types.Package) (methods []*types.Func) {
|
|||
|
||||
func (p *importer) method(parent *types.Package) *types.Func {
|
||||
pos := p.pos()
|
||||
pkg, name := p.fieldName(parent)
|
||||
pkg, name, _ := p.fieldName(parent)
|
||||
params, isddd := p.paramList()
|
||||
result, _ := p.paramList()
|
||||
sig := types.NewSignature(nil, params, result, isddd)
|
||||
return types.NewFunc(pos, pkg, name, sig)
|
||||
}
|
||||
|
||||
func (p *importer) fieldName(parent *types.Package) (*types.Package, string) {
|
||||
name := p.string()
|
||||
pkg := parent
|
||||
func (p *importer) fieldName(parent *types.Package) (pkg *types.Package, name string, alias bool) {
|
||||
name = p.string()
|
||||
pkg = parent
|
||||
if pkg == nil {
|
||||
// use the imported package instead
|
||||
pkg = p.pkgList[0]
|
||||
}
|
||||
if p.version == 0 && name == "_" {
|
||||
// version 0 didn't export a package for _ fields
|
||||
return pkg, name
|
||||
return
|
||||
}
|
||||
if name != "" && !exported(name) {
|
||||
if name == "?" {
|
||||
name = ""
|
||||
}
|
||||
switch name {
|
||||
case "":
|
||||
// 1) field name matches base type name and is exported: nothing to do
|
||||
case "?":
|
||||
// 2) field name matches base type name and is not exported: need package
|
||||
name = ""
|
||||
pkg = p.pkg()
|
||||
case "@":
|
||||
// 3) field name doesn't match type name (alias)
|
||||
name = p.string()
|
||||
alias = true
|
||||
fallthrough
|
||||
default:
|
||||
if !exported(name) {
|
||||
pkg = p.pkg()
|
||||
}
|
||||
}
|
||||
return pkg, name
|
||||
return
|
||||
}
|
||||
|
||||
func (p *importer) paramList() (*types.Tuple, bool) {
|
||||
|
@ -893,7 +897,7 @@ const (
|
|||
nilTag // only used by gc (appears in exported inlined function bodies)
|
||||
unknownTag // not used by gc (only appears in packages with errors)
|
||||
|
||||
// Aliases
|
||||
// Type aliases
|
||||
aliasTag
|
||||
)
|
||||
|
||||
|
@ -917,7 +921,7 @@ var predeclared = []types.Type{
|
|||
types.Typ[types.Complex128],
|
||||
types.Typ[types.String],
|
||||
|
||||
// aliases
|
||||
// basic type aliases
|
||||
types.Universe.Lookup("byte").Type(),
|
||||
types.Universe.Lookup("rune").Type(),
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -1447,6 +1447,9 @@ func (p *printer) spec(spec ast.Spec, n int, doIndent bool) {
|
|||
} else {
|
||||
p.print(vtab)
|
||||
}
|
||||
if s.Assign.IsValid() {
|
||||
p.print(token.ASSIGN, blank)
|
||||
}
|
||||
p.expr(s.Type)
|
||||
p.setComment(s.Comment)
|
||||
|
||||
|
|
15
src/go/printer/testdata/declarations.golden
vendored
15
src/go/printer/testdata/declarations.golden
vendored
|
@ -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
|
||||
)
|
||||
|
|
15
src/go/printer/testdata/declarations.input
vendored
15
src/go/printer/testdata/declarations.input
vendored
|
@ -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
|
||||
)
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -275,8 +275,6 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
|
|||
// so we don't need a "package" mode for operands: package names
|
||||
// can only appear in qualified identifiers which are mapped to
|
||||
// selector expressions.
|
||||
// (see also decl.go: checker.aliasDecl)
|
||||
// TODO(gri) factor this code out and share with checker.aliasDecl
|
||||
if ident, ok := e.X.(*ast.Ident); ok {
|
||||
_, obj := check.scope.LookupParent(ident.Name, check.pos)
|
||||
if pname, _ := obj.(*PkgName); pname != nil {
|
||||
|
@ -296,12 +294,6 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
|
|||
// ok to continue
|
||||
}
|
||||
check.recordUse(e.Sel, exp)
|
||||
exp = original(exp)
|
||||
|
||||
// avoid further errors if the imported object is an alias that's broken
|
||||
if exp == nil {
|
||||
goto Error
|
||||
}
|
||||
|
||||
// Simplified version of the code for *ast.Idents:
|
||||
// - imported objects are always fully initialized
|
||||
|
|
|
@ -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"},
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -540,7 +450,7 @@ func (check *Checker) declStmt(decl ast.Decl) {
|
|||
// the innermost containing block."
|
||||
scopePos := s.Name.Pos()
|
||||
check.declare(check.scope, s.Name, obj, scopePos)
|
||||
check.typeDecl(obj, s.Type, nil, nil)
|
||||
check.typeDecl(obj, s.Type, nil, nil, s.Assign.IsValid())
|
||||
|
||||
default:
|
||||
check.invalidAST(s.Pos(), "const, type, or var declaration expected")
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ type Object interface {
|
|||
Name() string // package local object name
|
||||
Type() Type // object type
|
||||
Exported() bool // reports whether the name starts with a capital letter
|
||||
Id() string // object id (see Id below)
|
||||
Id() string // object name if exported, qualified name if not exported (see func Id)
|
||||
|
||||
// String returns a human-readable string of the object.
|
||||
String() string
|
||||
|
@ -64,15 +64,10 @@ func Id(pkg *Package, name string) string {
|
|||
// inside a package and outside a package - which breaks some
|
||||
// tests)
|
||||
path := "_"
|
||||
// TODO(gri): shouldn't !ast.IsExported(name) => pkg != nil be an precondition?
|
||||
// if pkg == nil {
|
||||
// panic("nil package in lookup of unexported name")
|
||||
// }
|
||||
if pkg != nil {
|
||||
// pkg is nil for objects in Universe scope and possibly types
|
||||
// introduced via Eval (see also comment in object.sameId)
|
||||
if pkg != nil && pkg.path != "" {
|
||||
path = pkg.path
|
||||
if path == "" {
|
||||
path = "_"
|
||||
}
|
||||
}
|
||||
return path + "." + name
|
||||
}
|
||||
|
@ -154,7 +149,7 @@ func NewConst(pos token.Pos, pkg *Package, name string, typ Type, val constant.V
|
|||
func (obj *Const) Val() constant.Value { return obj.val }
|
||||
func (*Const) isDependency() {} // a constant may be a dependency of an initialization expression
|
||||
|
||||
// A TypeName represents a declared type.
|
||||
// A TypeName represents a name for a (named or alias) type.
|
||||
type TypeName struct {
|
||||
object
|
||||
}
|
||||
|
@ -163,6 +158,26 @@ func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName {
|
|||
return &TypeName{object{nil, pos, pkg, name, typ, 0, token.NoPos}}
|
||||
}
|
||||
|
||||
// IsAlias reports whether obj is an alias name for a type.
|
||||
func (obj *TypeName) IsAlias() bool {
|
||||
switch t := obj.typ.(type) {
|
||||
case nil:
|
||||
return false
|
||||
case *Basic:
|
||||
// Any user-defined type name for a basic type is an alias for a
|
||||
// basic type (because basic types are pre-declared in the Universe
|
||||
// scope, outside any package scope), and so is any type name with
|
||||
// a different name than the name of the basic type it refers to.
|
||||
// Additionaly, we need to look for "byte" and "rune" because they
|
||||
// are aliases but have the same names (for better error messages).
|
||||
return obj.pkg != nil || t.name != obj.name || t == universeByte || t == universeRune
|
||||
case *Named:
|
||||
return obj != t.obj
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// A Variable represents a declared variable (including function parameters and results, and struct fields).
|
||||
type Var struct {
|
||||
object
|
||||
|
@ -215,28 +230,6 @@ func (obj *Func) FullName() string {
|
|||
func (obj *Func) Scope() *Scope { return obj.typ.(*Signature).scope }
|
||||
func (*Func) isDependency() {} // a function may be a dependency of an initialization expression
|
||||
|
||||
// An Alias represents a declared alias.
|
||||
type disabledAlias struct {
|
||||
object
|
||||
orig Object // aliased constant, type, variable, or function; never an alias
|
||||
kind token.Token // token.CONST, token.TYPE, token.VAR, or token.FUNC (only needed during resolve phase)
|
||||
}
|
||||
|
||||
func disabledNewAlias(pos token.Pos, pkg *Package, name string, orig Object) *disabledAlias {
|
||||
var typ Type = Typ[Invalid]
|
||||
if orig != nil {
|
||||
typ = orig.Type()
|
||||
}
|
||||
// No need to set a valid Alias.kind - that field is only used during identifier
|
||||
// resolution (1st type-checker pass). We could store the field outside but it's
|
||||
// easier to keep it here.
|
||||
return &disabledAlias{object{nil, pos, pkg, name, typ, 0, token.NoPos}, orig, token.ILLEGAL}
|
||||
}
|
||||
|
||||
// Orig returns the aliased object, or nil if there was an error.
|
||||
// The returned object is never an Alias.
|
||||
func (obj *disabledAlias) disabledOrig() Object { return obj.orig }
|
||||
|
||||
// A Label represents a declared label.
|
||||
type Label struct {
|
||||
object
|
||||
|
@ -264,7 +257,9 @@ type Nil struct {
|
|||
}
|
||||
|
||||
func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
|
||||
var tname *TypeName
|
||||
typ := obj.Type()
|
||||
|
||||
switch obj := obj.(type) {
|
||||
case *PkgName:
|
||||
fmt.Fprintf(buf, "package %s", obj.Name())
|
||||
|
@ -277,8 +272,8 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
|
|||
buf.WriteString("const")
|
||||
|
||||
case *TypeName:
|
||||
tname = obj
|
||||
buf.WriteString("type")
|
||||
typ = typ.Underlying()
|
||||
|
||||
case *Var:
|
||||
if obj.isField {
|
||||
|
@ -295,10 +290,6 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
|
|||
}
|
||||
return
|
||||
|
||||
// Alias-related code. Keep for now.
|
||||
// case *Alias:
|
||||
// buf.WriteString("alias")
|
||||
|
||||
case *Label:
|
||||
buf.WriteString("label")
|
||||
typ = nil
|
||||
|
@ -322,10 +313,27 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
|
|||
writePackage(buf, obj.Pkg(), qf)
|
||||
}
|
||||
buf.WriteString(obj.Name())
|
||||
if typ != nil {
|
||||
buf.WriteByte(' ')
|
||||
WriteType(buf, typ, qf)
|
||||
|
||||
if typ == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if tname != nil {
|
||||
// We have a type object: Don't print anything more for
|
||||
// basic types since there's no more information (names
|
||||
// are the same; see also comment in TypeName.IsAlias).
|
||||
if _, ok := typ.(*Basic); ok {
|
||||
return
|
||||
}
|
||||
if tname.IsAlias() {
|
||||
buf.WriteString(" =")
|
||||
} else {
|
||||
typ = typ.Underlying()
|
||||
}
|
||||
}
|
||||
|
||||
buf.WriteByte(' ')
|
||||
WriteType(buf, typ, qf)
|
||||
}
|
||||
|
||||
func writePackage(buf *bytes.Buffer, pkg *Package, qf Qualifier) {
|
||||
|
@ -353,15 +361,14 @@ func ObjectString(obj Object, qf Qualifier) string {
|
|||
return buf.String()
|
||||
}
|
||||
|
||||
func (obj *PkgName) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *Const) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *TypeName) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *Var) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *Func) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *disabledAlias) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *Label) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *Builtin) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *Nil) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *PkgName) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *Const) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *TypeName) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *Var) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *Func) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *Label) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *Builtin) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *Nil) String() string { return ObjectString(obj, nil) }
|
||||
|
||||
func writeFuncName(buf *bytes.Buffer, f *Func, qf Qualifier) {
|
||||
if f.typ != nil {
|
||||
|
|
43
src/go/types/object_test.go
Normal file
43
src/go/types/object_test.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package types
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestIsAlias(t *testing.T) {
|
||||
check := func(obj *TypeName, want bool) {
|
||||
if got := obj.IsAlias(); got != want {
|
||||
t.Errorf("%v: got IsAlias = %v; want %v", obj, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// predeclared types
|
||||
for _, name := range Universe.Names() {
|
||||
if obj, _ := Universe.Lookup(name).(*TypeName); obj != nil {
|
||||
check(obj, name == "byte" || name == "rune")
|
||||
}
|
||||
}
|
||||
|
||||
// various other types
|
||||
pkg := NewPackage("p", "p")
|
||||
t1 := NewTypeName(0, pkg, "t1", nil)
|
||||
n1 := NewNamed(t1, new(Struct), nil)
|
||||
for _, test := range []struct {
|
||||
name *TypeName
|
||||
alias bool
|
||||
}{
|
||||
{NewTypeName(0, nil, "t0", nil), false}, // no type yet
|
||||
{NewTypeName(0, pkg, "t0", nil), false}, // no type yet
|
||||
{t1, false}, // type name refers to named type and vice versa
|
||||
{NewTypeName(0, nil, "t2", new(Interface)), true}, // type name refers to unnamed type
|
||||
{NewTypeName(0, pkg, "t3", n1), true}, // type name refers to named type with different type name
|
||||
{NewTypeName(0, nil, "t4", Typ[Int32]), true}, // type name refers to basic type with different name
|
||||
{NewTypeName(0, nil, "int32", Typ[Int32]), false}, // type name refers to basic type with same name
|
||||
{NewTypeName(0, pkg, "int32", Typ[Int32]), true}, // type name is declared in user-defined package (outside Universe)
|
||||
{NewTypeName(0, nil, "rune", Typ[Rune]), true}, // type name refers to basic type rune which is an alias already
|
||||
} {
|
||||
check(test.name, test.alias)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -14,13 +14,14 @@ import (
|
|||
"unicode"
|
||||
)
|
||||
|
||||
// A declInfo describes a package-level const, type, var, func, or alias declaration.
|
||||
// A declInfo describes a package-level const, type, var, or func declaration.
|
||||
type declInfo struct {
|
||||
file *Scope // scope of file containing this declaration
|
||||
lhs []*Var // lhs of n:1 variable declarations, or nil
|
||||
typ ast.Expr // type, or nil
|
||||
init ast.Expr // init/orig expression, or nil
|
||||
fdecl *ast.FuncDecl // func declaration, or nil
|
||||
alias bool // type alias declaration
|
||||
|
||||
// The deps field tracks initialization expression dependencies.
|
||||
// As a special (overloaded) case, it also tracks dependencies of
|
||||
|
@ -274,13 +275,6 @@ func (check *Checker) collectObjects() {
|
|||
check.declare(fileScope, nil, obj, token.NoPos)
|
||||
}
|
||||
|
||||
// Alias-related code. Keep for now.
|
||||
// case *ast.AliasSpec:
|
||||
// obj := NewAlias(s.Name.Pos(), pkg, s.Name.Name, nil)
|
||||
// obj.typ = nil // unresolved
|
||||
// obj.kind = d.Tok
|
||||
// check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, init: s.Orig})
|
||||
|
||||
case *ast.ValueSpec:
|
||||
switch d.Tok {
|
||||
case token.CONST:
|
||||
|
@ -347,7 +341,7 @@ func (check *Checker) collectObjects() {
|
|||
|
||||
case *ast.TypeSpec:
|
||||
obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
|
||||
check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, typ: s.Type})
|
||||
check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, typ: s.Type, alias: s.Assign.IsValid()})
|
||||
|
||||
default:
|
||||
check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
|
||||
|
|
150
src/go/types/testdata/decls4.src
vendored
Normal file
150
src/go/types/testdata/decls4.src
vendored
Normal file
|
@ -0,0 +1,150 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// type aliases
|
||||
|
||||
package decls4
|
||||
|
||||
type (
|
||||
T0 [10]int
|
||||
T1 []byte
|
||||
T2 struct {
|
||||
x int
|
||||
}
|
||||
T3 interface{
|
||||
m() T2
|
||||
}
|
||||
T4 func(int, T0) chan T2
|
||||
)
|
||||
|
||||
type (
|
||||
Ai = int
|
||||
A0 = T0
|
||||
A1 = T1
|
||||
A2 = T2
|
||||
A3 = T3
|
||||
A4 = T4
|
||||
|
||||
A10 = [10]int
|
||||
A11 = []byte
|
||||
A12 = struct {
|
||||
x int
|
||||
}
|
||||
A13 = interface{
|
||||
m() A2
|
||||
}
|
||||
A14 = func(int, A0) chan A2
|
||||
)
|
||||
|
||||
// check assignment compatibility due to equality of types
|
||||
var (
|
||||
xi_ int
|
||||
ai Ai = xi_
|
||||
|
||||
x0 T0
|
||||
a0 A0 = x0
|
||||
|
||||
x1 T1
|
||||
a1 A1 = x1
|
||||
|
||||
x2 T2
|
||||
a2 A2 = x2
|
||||
|
||||
x3 T3
|
||||
a3 A3 = x3
|
||||
|
||||
x4 T4
|
||||
a4 A4 = x4
|
||||
)
|
||||
|
||||
// alias receiver types
|
||||
func (Ai /* ERROR "invalid receiver" */) m1() {}
|
||||
func (T0) m1() {}
|
||||
func (A0) m1 /* ERROR already declared */ () {}
|
||||
func (A0) m2 () {}
|
||||
func (A3 /* ERROR invalid receiver */ ) m1 () {}
|
||||
func (A10 /* ERROR invalid receiver */ ) m1() {}
|
||||
|
||||
// x0 has methods m1, m2 declared via receiver type names T0 and A0
|
||||
var _ interface{ m1(); m2() } = x0
|
||||
|
||||
// cycles
|
||||
type (
|
||||
C2 /* ERROR illegal cycle */ = C2
|
||||
C3 /* ERROR illegal cycle */ = C4
|
||||
C4 = C3
|
||||
C5 struct {
|
||||
f *C6
|
||||
}
|
||||
C6 = C5
|
||||
C7 /* ERROR illegal cycle */ struct {
|
||||
f C8
|
||||
}
|
||||
C8 = C7
|
||||
)
|
||||
|
||||
// embedded fields
|
||||
var (
|
||||
s0 struct { T0 }
|
||||
s1 struct { A0 } = s0 /* ERROR cannot use */ // embedded field names are different
|
||||
)
|
||||
|
||||
// embedding and lookup of fields and methods
|
||||
func _(s struct{A0}) { s.A0 = x0 }
|
||||
|
||||
type eX struct{xf int}
|
||||
|
||||
func (eX) xm()
|
||||
|
||||
type eY = struct{eX} // field/method set of eY includes xf, xm
|
||||
|
||||
type eZ = *struct{eX} // field/method set of eZ includes xf, xm
|
||||
|
||||
type eA struct {
|
||||
eX // eX contributes xf, xm to eA
|
||||
}
|
||||
|
||||
type eA2 struct {
|
||||
*eX // *eX contributes xf, xm to eA
|
||||
}
|
||||
|
||||
type eB struct {
|
||||
eY // eY contributes xf, xm to eB
|
||||
}
|
||||
|
||||
type eB2 struct {
|
||||
*eY // *eY contributes xf, xm to eB
|
||||
}
|
||||
|
||||
type eC struct {
|
||||
eZ // eZ contributes xf, xm to eC
|
||||
}
|
||||
|
||||
var (
|
||||
_ = eA{}.xf
|
||||
_ = eA{}.xm
|
||||
_ = eA2{}.xf
|
||||
_ = eA2{}.xm
|
||||
_ = eB{}.xf
|
||||
_ = eB{}.xm
|
||||
_ = eB2{}.xf
|
||||
_ = eB2{}.xm
|
||||
_ = eC{}.xf
|
||||
_ = eC{}.xm
|
||||
)
|
||||
|
||||
// ambiguous selectors due to embedding via type aliases
|
||||
type eD struct {
|
||||
eY
|
||||
eZ
|
||||
}
|
||||
|
||||
var (
|
||||
_ = eD /* ERROR ambiguous selector */ {}.xf
|
||||
_ = eD /* ERROR ambiguous selector */ {}.xm
|
||||
)
|
||||
|
||||
var (
|
||||
_ interface{ xm() } = eD /* ERROR missing method xm */ {}
|
||||
)
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -3691,7 +3691,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
|
||||
|
@ -3769,7 +3769,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()
|
||||
|
@ -4135,50 +4137,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{})},
|
||||
|
@ -4186,81 +4196,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 {
|
||||
|
@ -4270,7 +4267,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 {
|
||||
|
@ -4348,7 +4345,7 @@ func TestStructOfGenericAlg(t *testing.T) {
|
|||
{Name: "S1", Type: st1},
|
||||
})
|
||||
|
||||
for _, table := range []struct {
|
||||
tests := []struct {
|
||||
rt Type
|
||||
idx []int
|
||||
}{
|
||||
|
@ -4429,7 +4426,9 @@ func TestStructOfGenericAlg(t *testing.T) {
|
|||
),
|
||||
idx: []int{2},
|
||||
},
|
||||
} {
|
||||
}
|
||||
|
||||
for _, table := range tests {
|
||||
v1 := New(table.rt).Elem()
|
||||
v2 := New(table.rt).Elem()
|
||||
|
||||
|
@ -4531,18 +4530,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
|
||||
|
@ -4550,7 +4552,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
|
||||
|
@ -4558,6 +4561,7 @@ func TestStructOfWithInterface(t *testing.T) {
|
|||
impl: true,
|
||||
},
|
||||
{
|
||||
name: "StructIPtr",
|
||||
typ: TypeOf(StructIPtr(want)),
|
||||
val: ValueOf(StructIPtr(want)),
|
||||
impl: false,
|
||||
|
@ -4567,13 +4571,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,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
@ -6019,6 +6026,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)
|
||||
|
@ -6044,3 +6052,38 @@ func TestUnaddressableField(t *testing.T) {
|
|||
lv.Set(rv)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
type Tint int
|
||||
|
||||
type Tint2 = Tint
|
||||
|
||||
type Talias1 struct {
|
||||
byte
|
||||
uint8
|
||||
int
|
||||
int32
|
||||
rune
|
||||
}
|
||||
|
||||
type Talias2 struct {
|
||||
Tint
|
||||
Tint2
|
||||
}
|
||||
|
||||
func TestAliasNames(t *testing.T) {
|
||||
t1 := Talias1{byte: 1, uint8: 2, int: 3, int32: 4, rune: 5}
|
||||
out := fmt.Sprintf("%#v", t1)
|
||||
want := "reflect_test.Talias1{byte:0x1, uint8:0x2, int:3, int32:4, rune:5}"
|
||||
if out != want {
|
||||
t.Errorf("Talias1 print:\nhave: %s\nwant: %s", out, want)
|
||||
}
|
||||
|
||||
t2 := Talias2{Tint: 1, Tint2: 2}
|
||||
out = fmt.Sprintf("%#v", t2)
|
||||
want = "reflect_test.Talias2{Tint:1, Tint2:2}"
|
||||
if out != want {
|
||||
t.Errorf("Talias2 print:\nhave: %s\nwant: %s", out, want)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
@ -2403,6 +2397,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")
|
||||
}
|
||||
|
@ -2415,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
|
||||
|
@ -2429,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:
|
||||
|
@ -2565,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
|
||||
|
@ -2761,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
|
||||
|
@ -2771,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
|
||||
}
|
||||
|
@ -2794,25 +2786,27 @@ func StructOf(fields []StructField) Type {
|
|||
}
|
||||
|
||||
func runtimeStructField(field StructField) structField {
|
||||
exported := field.PkgPath == ""
|
||||
if field.Name == "" {
|
||||
t := field.Type.(*rtype)
|
||||
if t.Kind() == Ptr {
|
||||
t = t.Elem().(*rtype)
|
||||
}
|
||||
exported = t.nameOff(t.str).isExported()
|
||||
} else if exported {
|
||||
b0 := field.Name[0]
|
||||
if ('a' <= b0 && b0 <= 'z') || b0 == '_' {
|
||||
panic("reflect.StructOf: field \"" + field.Name + "\" is unexported but has no PkgPath")
|
||||
}
|
||||
if field.PkgPath != "" {
|
||||
panic("reflect.StructOf: StructOf does not allow unexported fields")
|
||||
}
|
||||
|
||||
_ = 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")
|
||||
}
|
||||
|
||||
offsetAnon := uintptr(0)
|
||||
if field.Anonymous {
|
||||
offsetAnon |= 1
|
||||
}
|
||||
|
||||
resolveReflectType(field.Type.common()) // install in runtime
|
||||
return structField{
|
||||
name: newName(field.Name, string(field.Tag), field.PkgPath, exported),
|
||||
typ: field.Type.common(),
|
||||
offset: 0,
|
||||
name: newName(field.Name, string(field.Tag), "", true),
|
||||
typ: field.Type.common(),
|
||||
offsetAnon: offsetAnon,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2835,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())
|
||||
|
@ -3209,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -763,7 +763,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
|
||||
|
@ -774,7 +774,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}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
104
test/alias2.go
Normal file
104
test/alias2.go
Normal file
|
@ -0,0 +1,104 @@
|
|||
// errorcheck
|
||||
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Test basic restrictions on type aliases.
|
||||
|
||||
package p
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
. "reflect"
|
||||
)
|
||||
|
||||
type T0 struct{}
|
||||
|
||||
// Valid type alias declarations.
|
||||
|
||||
type _ = T0
|
||||
type _ = int
|
||||
type _ = struct{}
|
||||
type _ = reflect.Value
|
||||
type _ = Value
|
||||
|
||||
type (
|
||||
A0 = T0
|
||||
A1 = int
|
||||
A2 = struct{}
|
||||
A3 = reflect.Value
|
||||
A4 = Value
|
||||
A5 = Value
|
||||
|
||||
N0 A0
|
||||
)
|
||||
|
||||
// Methods can be declared on the original named type and the alias.
|
||||
func (T0) m1() {} // GCCGO_ERROR "previous"
|
||||
func (*T0) m1() {} // ERROR "method redeclared: T0\.m1|redefinition of .m1."
|
||||
func (A0) m1() {} // ERROR "T0\.m1 redeclared in this block|redefinition of .m1."
|
||||
func (A0) m1() {} // ERROR "T0\.m1 redeclared in this block|redefinition of .m1."
|
||||
func (A0) m2() {}
|
||||
|
||||
// Type aliases and the original type name can be used interchangeably.
|
||||
var _ A0 = T0{}
|
||||
var _ T0 = A0{}
|
||||
|
||||
// But aliases and original types cannot be used with new types based on them.
|
||||
var _ N0 = T0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment|incompatible type"
|
||||
var _ N0 = A0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment|incompatible type"
|
||||
|
||||
var _ A5 = Value{}
|
||||
|
||||
var _ interface {
|
||||
m1()
|
||||
m2()
|
||||
} = T0{}
|
||||
|
||||
var _ interface {
|
||||
m1()
|
||||
m2()
|
||||
} = A0{}
|
||||
|
||||
func _() {
|
||||
type _ = T0
|
||||
type _ = int
|
||||
type _ = struct{}
|
||||
type _ = reflect.Value
|
||||
type _ = Value
|
||||
|
||||
type (
|
||||
A0 = T0
|
||||
A1 = int
|
||||
A2 = struct{}
|
||||
A3 = reflect.Value
|
||||
A4 = Value
|
||||
A5 Value
|
||||
|
||||
N0 A0
|
||||
)
|
||||
|
||||
var _ A0 = T0{}
|
||||
var _ T0 = A0{}
|
||||
|
||||
var _ N0 = T0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment|incompatible type"
|
||||
var _ N0 = A0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment|incompatible type"
|
||||
|
||||
var _ A5 = Value{} // ERROR "cannot use reflect\.Value literal \(type reflect.Value\) as type A5 in assignment|incompatible type"
|
||||
}
|
||||
|
||||
// Invalid type alias declarations.
|
||||
|
||||
type _ = reflect.ValueOf // ERROR "reflect.ValueOf is not a type|expected type"
|
||||
|
||||
func (A1) m() {} // ERROR "cannot define new methods on non-local type int|may not define methods on non-local type"
|
||||
func (A2) m() {} // ERROR "invalid receiver type"
|
||||
func (A3) m() {} // ERROR "cannot define new methods on non-local type reflect.Value|may not define methods on non-local type"
|
||||
func (A4) m() {} // ERROR "reflect.Value.m redeclared in this block" "cannot define new methods on non-local type reflect.Value|may not define methods on non-local type"
|
||||
|
||||
type B1 = struct{}
|
||||
|
||||
func (B1) m() {} // ERROR "m redeclared in this block" "invalid receiver type"
|
||||
|
||||
// TODO(gri) expand
|
42
test/alias3.dir/a.go
Normal file
42
test/alias3.dir/a.go
Normal file
|
@ -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
|
26
test/alias3.dir/b.go
Normal file
26
test/alias3.dir/b.go
Normal file
|
@ -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{}
|
25
test/alias3.dir/c.go
Normal file
25
test/alias3.dir/c.go
Normal file
|
@ -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
|
||||
}
|
7
test/alias3.go
Normal file
7
test/alias3.go
Normal file
|
@ -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
|
26
test/fixedbugs/issue18640.go
Normal file
26
test/fixedbugs/issue18640.go
Normal file
|
@ -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
|
||||
)
|
22
test/fixedbugs/issue18655.go
Normal file
22
test/fixedbugs/issue18655.go
Normal file
|
@ -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"
|
Loading…
Reference in a new issue