mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +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
63 changed files with 1335 additions and 840 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
<!--{
|
<!--{
|
||||||
"Title": "The Go Programming Language Specification",
|
"Title": "The Go Programming Language Specification",
|
||||||
"Subtitle": "Version of November 18, 2016",
|
"Subtitle": "Version of January 31, 2017",
|
||||||
"Path": "/ref/spec"
|
"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>
|
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>
|
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>).
|
(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>.
|
in the section on <a href="#Struct_types">struct types</a>.
|
||||||
Any other type has an empty method set.
|
Any other type has an empty method set.
|
||||||
In a method set, each method must have a
|
In a method set, each method must have a
|
||||||
|
|
@ -947,16 +947,16 @@ Moreover, the inner slices must be initialized individually.
|
||||||
<p>
|
<p>
|
||||||
A struct is a sequence of named elements, called fields, each of which has a
|
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
|
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
|
Within a struct, non-<a href="#Blank_identifier">blank</a> field names must
|
||||||
be <a href="#Uniqueness_of_identifiers">unique</a>.
|
be <a href="#Uniqueness_of_identifiers">unique</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<pre class="ebnf">
|
<pre class="ebnf">
|
||||||
StructType = "struct" "{" { FieldDecl ";" } "}" .
|
StructType = "struct" "{" { FieldDecl ";" } "}" .
|
||||||
FieldDecl = (IdentifierList Type | AnonymousField) [ Tag ] .
|
FieldDecl = (IdentifierList Type | EmbeddedField) [ Tag ] .
|
||||||
AnonymousField = [ "*" ] TypeName .
|
EmbeddedField = [ "*" ] TypeName .
|
||||||
Tag = string_lit .
|
Tag = string_lit .
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
|
|
@ -974,16 +974,15 @@ struct {
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
A field declared with a type but no explicit field name is an <i>anonymous field</i>,
|
A field declared with a type but no explicit field name is called an <i>embedded field</i>.
|
||||||
also called an <i>embedded</i> field or an embedding of the type in the struct.
|
An embedded field must be specified as
|
||||||
An embedded type must be specified as
|
|
||||||
a type name <code>T</code> or as a pointer to a non-interface type name <code>*T</code>,
|
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
|
and <code>T</code> itself may not be
|
||||||
a pointer type. The unqualified type name acts as the field name.
|
a pointer type. The unqualified type name acts as the field name.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<pre>
|
<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 {
|
struct {
|
||||||
T1 // field name is T1
|
T1 // field name is T1
|
||||||
*T2 // field name is T2
|
*T2 // field name is T2
|
||||||
|
|
@ -1000,15 +999,15 @@ in a struct type:
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
struct {
|
struct {
|
||||||
T // conflicts with anonymous field *T and *P.T
|
T // conflicts with embedded field *T and *P.T
|
||||||
*T // conflicts with anonymous field T and *P.T
|
*T // conflicts with embedded field T and *P.T
|
||||||
*P.T // conflicts with anonymous field T and *T
|
*P.T // conflicts with embedded field T and *T
|
||||||
}
|
}
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
A field or <a href="#Method_declarations">method</a> <code>f</code> of an
|
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
|
<code>x.f</code> is a legal <a href="#Selectors">selector</a> that denotes
|
||||||
that field or method <code>f</code>.
|
that field or method <code>f</code>.
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -1025,7 +1024,7 @@ promoted methods are included in the method set of the struct as follows:
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<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 <a href="#Method_sets">method sets</a> of <code>S</code>
|
the <a href="#Method_sets">method sets</a> of <code>S</code>
|
||||||
and <code>*S</code> both include promoted methods with receiver
|
and <code>*S</code> both include promoted methods with receiver
|
||||||
<code>T</code>. The method set of <code>*S</code> also
|
<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>
|
||||||
|
|
||||||
<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
|
the method sets of <code>S</code> and <code>*S</code> both
|
||||||
include promoted methods with receiver <code>T</code> or
|
include promoted methods with receiver <code>T</code> or
|
||||||
<code>*T</code>.
|
<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,
|
<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 if corresponding fields have the same names, and identical types,
|
||||||
and identical tags.
|
and identical tags.
|
||||||
Two anonymous fields are considered to have the same name. Lower-case field
|
<a href="#Exported_identifiers">Non-exported</a> field names from different
|
||||||
names from different packages are always different.</li>
|
packages are always different.</li>
|
||||||
|
|
||||||
<li>Two pointer types are identical if they have identical base types.</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>
|
Parameter and result names are not required to match.</li>
|
||||||
|
|
||||||
<li>Two interface types are identical if they have the same set of methods
|
<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
|
with the same names and identical function types.
|
||||||
different packages are always different. The order of the methods is irrelevant.</li>
|
<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>
|
<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
|
type PtrMutex *Mutex
|
||||||
|
|
||||||
// The method set of *PrintableMutex contains the methods
|
// 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 {
|
type PrintableMutex struct {
|
||||||
Mutex
|
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 selector <code>f</code> may denote a field or method <code>f</code> of
|
||||||
a type <code>T</code>, or it may refer
|
a type <code>T</code>, or it may refer
|
||||||
to a field or method <code>f</code> of a nested
|
to a field or method <code>f</code> of a nested
|
||||||
<a href="#Struct_types">anonymous field</a> of <code>T</code>.
|
<a href="#Struct_types">embedded field</a> of <code>T</code>.
|
||||||
The number of anonymous fields traversed
|
The number of embedded fields traversed
|
||||||
to reach <code>f</code> is called its <i>depth</i> in <code>T</code>.
|
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>
|
The depth of a field or method <code>f</code>
|
||||||
declared in <code>T</code> is zero.
|
declared in <code>T</code> is zero.
|
||||||
The depth of a field or method <code>f</code> declared in
|
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.
|
depth of <code>f</code> in <code>A</code> plus one.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,13 @@ func widstruct(errtype *Type, t *Type, o int64, flag int) int64 {
|
||||||
lastzero = o
|
lastzero = o
|
||||||
}
|
}
|
||||||
o += w
|
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)
|
yyerror("type %L too large", errtype)
|
||||||
o = 8 // small but nonzero
|
o = 8 // small but nonzero
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -140,11 +140,12 @@ const debugFormat = false // default: false
|
||||||
const forceObjFileStability = true
|
const forceObjFileStability = true
|
||||||
|
|
||||||
// Current export format version. Increase with each format change.
|
// Current export format version. Increase with each format change.
|
||||||
// 3: added aliasTag and export of aliases
|
// 4: type name objects support type aliases, uses aliasTag
|
||||||
// 2: removed unused bool in ODCL export
|
// 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
|
// 1: header format change (more regular), export package for _ struct fields
|
||||||
// 0: Go1.7 encoding
|
// 0: Go1.7 encoding
|
||||||
const exportVersion = 3
|
const exportVersion = 4
|
||||||
|
|
||||||
// exportInlined enables the export of inlined function bodies and related
|
// exportInlined enables the export of inlined function bodies and related
|
||||||
// dependencies. The compiler should work w/o any loss of functionality with
|
// 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")
|
p.tracef("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
if sym.Flags&SymAlias != 0 {
|
if sym.isAlias() {
|
||||||
Fatalf("exporter: unexpected alias %v in inlined function body", sym)
|
Fatalf("exporter: unexpected type alias %v in inlined function body", sym)
|
||||||
}
|
}
|
||||||
|
|
||||||
p.obj(sym)
|
p.obj(sym)
|
||||||
|
|
@ -446,30 +447,6 @@ func unidealType(typ *Type, val Val) *Type {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *exporter) obj(sym *Sym) {
|
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
|
// Exported objects may be from different packages because they
|
||||||
// may be re-exported via an exported alias or as dependencies in
|
// may be re-exported via an exported alias or as dependencies in
|
||||||
// exported inlined function bodies. Thus, exported object names
|
// 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)
|
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)
|
p.typ(t)
|
||||||
|
|
||||||
case ONAME:
|
case ONAME:
|
||||||
|
|
@ -868,19 +851,29 @@ func (p *exporter) methodList(t *Type) {
|
||||||
|
|
||||||
func (p *exporter) method(m *Field) {
|
func (p *exporter) method(m *Field) {
|
||||||
p.pos(m.Nname)
|
p.pos(m.Nname)
|
||||||
p.fieldName(m)
|
p.methodName(m.Sym)
|
||||||
p.paramList(m.Type.Params(), false)
|
p.paramList(m.Type.Params(), false)
|
||||||
p.paramList(m.Type.Results(), 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) {
|
func (p *exporter) fieldName(t *Field) {
|
||||||
name := t.Sym.Name
|
name := t.Sym.Name
|
||||||
if t.Embedded != 0 {
|
if t.Embedded != 0 {
|
||||||
name = "" // anonymous field
|
// anonymous field - we distinguish between 3 cases:
|
||||||
if bname := basetypeName(t.Type); bname != "" && !exportname(bname) {
|
// 1) field name matches base type name and is exported
|
||||||
// anonymous field with unexported base type name
|
// 2) field name matches base type name and is not exported
|
||||||
name = "?" // unexported name to force export of package
|
// 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)
|
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 {
|
func basetypeName(t *Type) string {
|
||||||
s := t.Sym
|
s := t.Sym
|
||||||
if s == nil && t.IsPtr() {
|
if s == nil && t.IsPtr() {
|
||||||
s = t.Elem().Sym // deref
|
s = t.Elem().Sym // deref
|
||||||
}
|
}
|
||||||
// s should exist, but be conservative
|
|
||||||
if s != nil {
|
if s != nil {
|
||||||
return s.Name
|
return s.Name
|
||||||
}
|
}
|
||||||
return ""
|
return "" // unnamed type
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *exporter) paramList(params *Type, numbered bool) {
|
func (p *exporter) paramList(params *Type, numbered bool) {
|
||||||
|
|
@ -1797,7 +1797,7 @@ const (
|
||||||
nilTag
|
nilTag
|
||||||
unknownTag // not used by gc (only appears in packages with errors)
|
unknownTag // not used by gc (only appears in packages with errors)
|
||||||
|
|
||||||
// Aliases
|
// Type aliases
|
||||||
aliasTag
|
aliasTag
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -1835,7 +1835,7 @@ var tagString = [...]string{
|
||||||
-nilTag: "nil",
|
-nilTag: "nil",
|
||||||
-unknownTag: "unknown",
|
-unknownTag: "unknown",
|
||||||
|
|
||||||
// Aliases
|
// Type aliases
|
||||||
-aliasTag: "alias",
|
-aliasTag: "alias",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1889,7 +1889,7 @@ func predeclared() []*Type {
|
||||||
Types[TCOMPLEX128],
|
Types[TCOMPLEX128],
|
||||||
Types[TSTRING],
|
Types[TSTRING],
|
||||||
|
|
||||||
// aliases
|
// basic type aliases
|
||||||
bytetype,
|
bytetype,
|
||||||
runetype,
|
runetype,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -86,10 +86,10 @@ func Import(in *bufio.Reader) {
|
||||||
|
|
||||||
// read version specific flags - extend as necessary
|
// read version specific flags - extend as necessary
|
||||||
switch p.version {
|
switch p.version {
|
||||||
// case 4:
|
// case 5:
|
||||||
// ...
|
// ...
|
||||||
// fallthrough
|
// fallthrough
|
||||||
case 3, 2, 1:
|
case 4, 3, 2, 1:
|
||||||
p.debugFormat = p.rawStringln(p.rawByte()) == "debug"
|
p.debugFormat = p.rawStringln(p.rawByte()) == "debug"
|
||||||
p.trackAllTypes = p.bool()
|
p.trackAllTypes = p.bool()
|
||||||
p.posInfoFormat = p.bool()
|
p.posInfoFormat = p.bool()
|
||||||
|
|
@ -317,6 +317,12 @@ func (p *importer) obj(tag int) {
|
||||||
val := p.value(typ)
|
val := p.value(typ)
|
||||||
importconst(sym, idealType(typ), nodlit(val))
|
importconst(sym, idealType(typ), nodlit(val))
|
||||||
|
|
||||||
|
case aliasTag:
|
||||||
|
p.pos()
|
||||||
|
sym := p.qualifiedName()
|
||||||
|
typ := p.typ()
|
||||||
|
importalias(sym, typ)
|
||||||
|
|
||||||
case typeTag:
|
case typeTag:
|
||||||
p.typ()
|
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:
|
default:
|
||||||
formatErrorf("unexpected object (tag = %d)", tag)
|
formatErrorf("unexpected object (tag = %d)", tag)
|
||||||
}
|
}
|
||||||
|
|
@ -473,14 +468,7 @@ func (p *importer) typ() *Type {
|
||||||
result := p.paramList()
|
result := p.paramList()
|
||||||
nointerface := p.bool()
|
nointerface := p.bool()
|
||||||
|
|
||||||
base := recv[0].Type
|
n := newfuncname(methodname(sym, recv[0].Type))
|
||||||
star := false
|
|
||||||
if base.IsPtr() {
|
|
||||||
base = base.Elem()
|
|
||||||
star = true
|
|
||||||
}
|
|
||||||
|
|
||||||
n := methodname0(sym, star, base.Sym)
|
|
||||||
n.Type = functypefield(recv[0], params, result)
|
n.Type = functypefield(recv[0], params, result)
|
||||||
checkwidth(n.Type)
|
checkwidth(n.Type)
|
||||||
addmethod(sym, n.Type, false, nointerface)
|
addmethod(sym, n.Type, false, nointerface)
|
||||||
|
|
@ -583,19 +571,22 @@ func (p *importer) fieldList() (fields []*Field) {
|
||||||
|
|
||||||
func (p *importer) field() *Field {
|
func (p *importer) field() *Field {
|
||||||
p.pos()
|
p.pos()
|
||||||
sym := p.fieldName()
|
sym, alias := p.fieldName()
|
||||||
typ := p.typ()
|
typ := p.typ()
|
||||||
note := p.string()
|
note := p.string()
|
||||||
|
|
||||||
f := newField()
|
f := newField()
|
||||||
if sym.Name == "" {
|
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
|
s := typ.Sym
|
||||||
if s == nil && typ.IsPtr() {
|
if s == nil && typ.IsPtr() {
|
||||||
s = typ.Elem().Sym // deref
|
s = typ.Elem().Sym // deref
|
||||||
}
|
}
|
||||||
sym = sym.Pkg.Lookup(s.Name)
|
sym = sym.Pkg.Lookup(s.Name)
|
||||||
f.Embedded = 1
|
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
|
f.Sym = sym
|
||||||
|
|
@ -618,7 +609,7 @@ func (p *importer) methodList() (methods []*Field) {
|
||||||
|
|
||||||
func (p *importer) method() *Field {
|
func (p *importer) method() *Field {
|
||||||
p.pos()
|
p.pos()
|
||||||
sym := p.fieldName()
|
sym := p.methodName()
|
||||||
params := p.paramList()
|
params := p.paramList()
|
||||||
result := p.paramList()
|
result := p.paramList()
|
||||||
|
|
||||||
|
|
@ -629,18 +620,44 @@ func (p *importer) method() *Field {
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *importer) fieldName() *Sym {
|
func (p *importer) fieldName() (*Sym, bool) {
|
||||||
name := p.string()
|
name := p.string()
|
||||||
if p.version == 0 && name == "_" {
|
if p.version == 0 && name == "_" {
|
||||||
// version 0 didn't export a package for _ 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
|
// but used the builtin package instead
|
||||||
return builtinpkg.Lookup(name)
|
return builtinpkg.Lookup(name)
|
||||||
}
|
}
|
||||||
pkg := localpkg
|
pkg := localpkg
|
||||||
if name != "" && !exportname(name) {
|
if !exportname(name) {
|
||||||
if name == "?" {
|
|
||||||
name = ""
|
|
||||||
}
|
|
||||||
pkg = p.pkg()
|
pkg = p.pkg()
|
||||||
}
|
}
|
||||||
return pkg.Lookup(name)
|
return pkg.Lookup(name)
|
||||||
|
|
|
||||||
|
|
@ -519,10 +519,6 @@ func funchdr(n *Node) {
|
||||||
Fatalf("funchdr: dclcontext = %d", dclcontext)
|
Fatalf("funchdr: dclcontext = %d", dclcontext)
|
||||||
}
|
}
|
||||||
|
|
||||||
if Ctxt.Flag_dynlink && importpkg == nil && n.Func.Nname != nil {
|
|
||||||
makefuncsym(n.Func.Nname.Sym)
|
|
||||||
}
|
|
||||||
|
|
||||||
dclcontext = PAUTO
|
dclcontext = PAUTO
|
||||||
funcstart(n)
|
funcstart(n)
|
||||||
|
|
||||||
|
|
@ -695,10 +691,20 @@ func typedcl0(s *Sym) *Node {
|
||||||
|
|
||||||
// node n, which was returned by typedcl0
|
// node n, which was returned by typedcl0
|
||||||
// is being declared to have uncompiled type t.
|
// is being declared to have uncompiled type t.
|
||||||
// return the ODCLTYPE node to use.
|
// returns the ODCLTYPE node to use.
|
||||||
func typedcl1(n *Node, t *Node, local bool) *Node {
|
func typedcl1(n *Node, t *Node, pragma Pragma, alias bool) *Node {
|
||||||
n.Name.Param.Ntype = t
|
if pragma != 0 && alias {
|
||||||
n.Local = local
|
yyerror("cannot specify directive with type alias")
|
||||||
|
pragma = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
n.Local = true
|
||||||
|
|
||||||
|
p := n.Name.Param
|
||||||
|
p.Ntype = t
|
||||||
|
p.Pragma = pragma
|
||||||
|
p.Alias = alias
|
||||||
|
|
||||||
return nod(ODCLTYPE, n, nil)
|
return nod(ODCLTYPE, n, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1153,19 +1159,19 @@ bad:
|
||||||
return nil
|
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
|
star := false
|
||||||
if t.Op == OIND {
|
if recv.IsPtr() {
|
||||||
star = true
|
star = true
|
||||||
t = t.Left
|
recv = recv.Elem()
|
||||||
}
|
}
|
||||||
|
|
||||||
return methodname0(n.Sym, star, t.Sym)
|
tsym := recv.Sym
|
||||||
}
|
|
||||||
|
|
||||||
func methodname0(s *Sym, star bool, tsym *Sym) *Node {
|
|
||||||
if tsym == nil || isblanksym(s) {
|
if tsym == nil || isblanksym(s) {
|
||||||
return newfuncname(s)
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
var p string
|
var p string
|
||||||
|
|
@ -1181,14 +1187,13 @@ func methodname0(s *Sym, star bool, tsym *Sym) *Node {
|
||||||
s = Pkglookup(p, tsym.Pkg)
|
s = Pkglookup(p, tsym.Pkg)
|
||||||
}
|
}
|
||||||
|
|
||||||
return newfuncname(s)
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a method, declared as a function.
|
// Add a method, declared as a function.
|
||||||
// - msym is the method symbol
|
// - msym is the method symbol
|
||||||
// - t is function type (with receiver)
|
// - t is function type (with receiver)
|
||||||
func addmethod(msym *Sym, t *Type, local, nointerface bool) {
|
func addmethod(msym *Sym, t *Type, local, nointerface bool) {
|
||||||
// get field sym
|
|
||||||
if msym == nil {
|
if msym == nil {
|
||||||
Fatalf("no method symbol")
|
Fatalf("no method symbol")
|
||||||
}
|
}
|
||||||
|
|
@ -1309,7 +1314,7 @@ func funcsym(s *Sym) *Sym {
|
||||||
s1 := Pkglookup(s.Name+"·f", s.Pkg)
|
s1 := Pkglookup(s.Name+"·f", s.Pkg)
|
||||||
if !Ctxt.Flag_dynlink && s1.Def == nil {
|
if !Ctxt.Flag_dynlink && s1.Def == nil {
|
||||||
s1.Def = newfuncname(s1)
|
s1.Def = newfuncname(s1)
|
||||||
s1.Def.Func.Shortname = newname(s)
|
s1.Def.Func.Shortname = s
|
||||||
funcsyms = append(funcsyms, s1.Def)
|
funcsyms = append(funcsyms, s1.Def)
|
||||||
}
|
}
|
||||||
s.Fsym = s1
|
s.Fsym = s1
|
||||||
|
|
@ -1326,8 +1331,11 @@ func makefuncsym(s *Sym) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s1 := funcsym(s)
|
s1 := funcsym(s)
|
||||||
|
if s1.Def != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
s1.Def = newfuncname(s1)
|
s1.Def = newfuncname(s1)
|
||||||
s1.Def.Func.Shortname = newname(s)
|
s1.Def.Func.Shortname = s
|
||||||
funcsyms = append(funcsyms, s1.Def)
|
funcsyms = append(funcsyms, s1.Def)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,8 @@ func exportsym(n *Node) {
|
||||||
fmt.Printf("export symbol %v\n", n.Sym)
|
fmt.Printf("export symbol %v\n", n.Sym)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure original object is on exportlist before aliases.
|
// Ensure original types are on exportlist before type aliases.
|
||||||
if n.Sym.Flags&SymAlias != 0 {
|
if n.Sym.isAlias() {
|
||||||
exportlist = append(exportlist, n.Sym.Def)
|
exportlist = append(exportlist, n.Sym.Def)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -83,7 +83,7 @@ func autoexport(n *Node, ctxt Class) {
|
||||||
if (ctxt != PEXTERN && ctxt != PFUNC) || dclcontext != PEXTERN {
|
if (ctxt != PEXTERN && ctxt != PFUNC) || dclcontext != PEXTERN {
|
||||||
return
|
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
|
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() {
|
func dumpasmhdr() {
|
||||||
b, err := bio.Create(asmhdr)
|
b, err := bio.Create(asmhdr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -1077,6 +1077,7 @@ var opprec = []int{
|
||||||
OSEND: 3,
|
OSEND: 3,
|
||||||
OANDAND: 2,
|
OANDAND: 2,
|
||||||
OOROR: 1,
|
OOROR: 1,
|
||||||
|
|
||||||
// Statements handled by stmtfmt
|
// Statements handled by stmtfmt
|
||||||
OAS: -1,
|
OAS: -1,
|
||||||
OAS2: -1,
|
OAS2: -1,
|
||||||
|
|
@ -1104,7 +1105,8 @@ var opprec = []int{
|
||||||
OSWITCH: -1,
|
OSWITCH: -1,
|
||||||
OXCASE: -1,
|
OXCASE: -1,
|
||||||
OXFALL: -1,
|
OXFALL: -1,
|
||||||
OEND: 0,
|
|
||||||
|
OEND: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) exprfmt(s fmt.State, prec int) {
|
func (n *Node) exprfmt(s fmt.State, prec int) {
|
||||||
|
|
|
||||||
|
|
@ -63,9 +63,12 @@ const (
|
||||||
SymSiggen
|
SymSiggen
|
||||||
SymAsm
|
SymAsm
|
||||||
SymAlgGen
|
SymAlgGen
|
||||||
SymAlias // alias, original is Sym.Def.Sym
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (sym *Sym) isAlias() bool {
|
||||||
|
return sym.Def != nil && sym.Def.Sym != sym
|
||||||
|
}
|
||||||
|
|
||||||
// The Class of a variable/function describes the "storage class"
|
// The Class of a variable/function describes the "storage class"
|
||||||
// of a variable or function. During parsing, storage classes are
|
// of a variable or function. During parsing, storage classes are
|
||||||
// called declaration contexts.
|
// called declaration contexts.
|
||||||
|
|
@ -87,7 +90,7 @@ const (
|
||||||
// of the compilers arrays.
|
// of the compilers arrays.
|
||||||
//
|
//
|
||||||
// typedef struct
|
// typedef struct
|
||||||
// { // must not move anything
|
// { // must not move anything
|
||||||
// uchar array[8]; // pointer to data
|
// uchar array[8]; // pointer to data
|
||||||
// uchar nel[4]; // number of elements
|
// uchar nel[4]; // number of elements
|
||||||
// uchar cap[4]; // allocated number of elements
|
// uchar cap[4]; // allocated number of elements
|
||||||
|
|
@ -104,7 +107,7 @@ var sizeof_Array int // runtime sizeof(Array)
|
||||||
// of the compilers strings.
|
// of the compilers strings.
|
||||||
//
|
//
|
||||||
// typedef struct
|
// typedef struct
|
||||||
// { // must not move anything
|
// { // must not move anything
|
||||||
// uchar array[8]; // pointer to data
|
// uchar array[8]; // pointer to data
|
||||||
// uchar nel[4]; // number of elements
|
// uchar nel[4]; // number of elements
|
||||||
// } String;
|
// } String;
|
||||||
|
|
|
||||||
|
|
@ -340,13 +340,16 @@ func Main() {
|
||||||
// Phase 1: const, type, and names and types of funcs.
|
// Phase 1: const, type, and names and types of funcs.
|
||||||
// This will gather all the information about types
|
// This will gather all the information about types
|
||||||
// and methods but doesn't depend on any of it.
|
// 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()
|
defercheckwidth()
|
||||||
|
|
||||||
// Don't use range--typecheck can add closures to xtop.
|
// Don't use range--typecheck can add closures to xtop.
|
||||||
timings.Start("fe", "typecheck", "top1")
|
timings.Start("fe", "typecheck", "top1")
|
||||||
for i := 0; i < len(xtop); i++ {
|
for i := 0; i < len(xtop); i++ {
|
||||||
if xtop[i].Op != ODCL && xtop[i].Op != OAS && xtop[i].Op != OAS2 {
|
n := xtop[i]
|
||||||
xtop[i] = typecheck(xtop[i], Etop)
|
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.
|
// Don't use range--typecheck can add closures to xtop.
|
||||||
timings.Start("fe", "typecheck", "top2")
|
timings.Start("fe", "typecheck", "top2")
|
||||||
for i := 0; i < len(xtop); i++ {
|
for i := 0; i < len(xtop); i++ {
|
||||||
if xtop[i].Op == ODCL || xtop[i].Op == OAS || xtop[i].Op == OAS2 {
|
n := xtop[i]
|
||||||
xtop[i] = typecheck(xtop[i], Etop)
|
if op := n.Op; op == ODCL || op == OAS || op == OAS2 || op == ODCLTYPE && n.Left.Name.Param.Alias {
|
||||||
|
xtop[i] = typecheck(n, Etop)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resumecheckwidth()
|
resumecheckwidth()
|
||||||
|
|
@ -367,8 +371,9 @@ func Main() {
|
||||||
timings.Start("fe", "typecheck", "func")
|
timings.Start("fe", "typecheck", "func")
|
||||||
var fcount int64
|
var fcount int64
|
||||||
for i := 0; i < len(xtop); i++ {
|
for i := 0; i < len(xtop); i++ {
|
||||||
if xtop[i].Op == ODCLFUNC || xtop[i].Op == OCLOSURE {
|
n := xtop[i]
|
||||||
Curfn = xtop[i]
|
if op := n.Op; op == ODCLFUNC || op == OCLOSURE {
|
||||||
|
Curfn = n
|
||||||
decldepth = 1
|
decldepth = 1
|
||||||
saveerrors()
|
saveerrors()
|
||||||
typecheckslice(Curfn.Nbody.Slice(), Etop)
|
typecheckslice(Curfn.Nbody.Slice(), Etop)
|
||||||
|
|
@ -460,8 +465,9 @@ func Main() {
|
||||||
timings.Start("be", "compilefuncs")
|
timings.Start("be", "compilefuncs")
|
||||||
fcount = 0
|
fcount = 0
|
||||||
for i := 0; i < len(xtop); i++ {
|
for i := 0; i < len(xtop); i++ {
|
||||||
if xtop[i].Op == ODCLFUNC {
|
n := xtop[i]
|
||||||
funccompile(xtop[i])
|
if n.Op == ODCLFUNC {
|
||||||
|
funccompile(n)
|
||||||
fcount++
|
fcount++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -924,7 +930,7 @@ func mkpackage(pkgname string) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.Def.Sym != s && s.Flags&SymAlias == 0 {
|
if s.isAlias() {
|
||||||
// throw away top-level name left over
|
// throw away top-level name left over
|
||||||
// from previous import . "x"
|
// from previous import . "x"
|
||||||
if s.Def.Name != nil && s.Def.Name.Pack != nil && !s.Def.Name.Pack.Used && nsyntaxerrors == 0 {
|
if s.Def.Name != nil && s.Def.Name.Pack != nil && !s.Def.Name.Pack.Used && nsyntaxerrors == 0 {
|
||||||
|
|
|
||||||
|
|
@ -154,11 +154,7 @@ func (p *noder) importDecl(imp *syntax.ImportDecl) {
|
||||||
|
|
||||||
func (p *noder) varDecl(decl *syntax.VarDecl) []*Node {
|
func (p *noder) varDecl(decl *syntax.VarDecl) []*Node {
|
||||||
names := p.declNames(decl.NameList)
|
names := p.declNames(decl.NameList)
|
||||||
|
typ := p.typeExprOrNil(decl.Type)
|
||||||
var typ *Node
|
|
||||||
if decl.Type != nil {
|
|
||||||
typ = p.typeExpr(decl.Type)
|
|
||||||
}
|
|
||||||
|
|
||||||
var exprs []*Node
|
var exprs []*Node
|
||||||
if decl.Values != nil {
|
if decl.Values != nil {
|
||||||
|
|
@ -171,11 +167,7 @@ func (p *noder) varDecl(decl *syntax.VarDecl) []*Node {
|
||||||
|
|
||||||
func (p *noder) constDecl(decl *syntax.ConstDecl) []*Node {
|
func (p *noder) constDecl(decl *syntax.ConstDecl) []*Node {
|
||||||
names := p.declNames(decl.NameList)
|
names := p.declNames(decl.NameList)
|
||||||
|
typ := p.typeExprOrNil(decl.Type)
|
||||||
var typ *Node
|
|
||||||
if decl.Type != nil {
|
|
||||||
typ = p.typeExpr(decl.Type)
|
|
||||||
}
|
|
||||||
|
|
||||||
var exprs []*Node
|
var exprs []*Node
|
||||||
if decl.Values != nil {
|
if decl.Values != nil {
|
||||||
|
|
@ -187,14 +179,11 @@ func (p *noder) constDecl(decl *syntax.ConstDecl) []*Node {
|
||||||
|
|
||||||
func (p *noder) typeDecl(decl *syntax.TypeDecl) *Node {
|
func (p *noder) typeDecl(decl *syntax.TypeDecl) *Node {
|
||||||
name := typedcl0(p.name(decl.Name))
|
name := typedcl0(p.name(decl.Name))
|
||||||
name.Name.Param.Pragma = Pragma(decl.Pragma)
|
|
||||||
|
|
||||||
var typ *Node
|
// decl.Type may be nil but in that case we got a syntax error during parsing
|
||||||
if decl.Type != nil {
|
typ := p.typeExprOrNil(decl.Type)
|
||||||
typ = p.typeExpr(decl.Type)
|
|
||||||
}
|
|
||||||
|
|
||||||
return typedcl1(name, typ, true)
|
return typedcl1(name, typ, Pragma(decl.Pragma), decl.Alias)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *noder) declNames(names []*syntax.Name) []*Node {
|
func (p *noder) declNames(names []*syntax.Name) []*Node {
|
||||||
|
|
@ -259,19 +248,19 @@ func (p *noder) funcHeader(fun *syntax.FuncDecl) *Node {
|
||||||
yyerror("func main must have no arguments and no return values")
|
yyerror("func main must have no arguments and no return values")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
f.Func.Nname = newfuncname(name)
|
|
||||||
} else {
|
} else {
|
||||||
// Receiver MethodName Signature
|
f.Func.Shortname = name
|
||||||
|
name = nblank.Sym // filled in by typecheckfunc
|
||||||
f.Func.Shortname = newfuncname(name)
|
|
||||||
f.Func.Nname = methodname(f.Func.Shortname, t.Left.Right)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f.Func.Nname = newfuncname(name)
|
||||||
f.Func.Nname.Name.Defn = f
|
f.Func.Nname.Name.Defn = f
|
||||||
f.Func.Nname.Name.Param.Ntype = t // TODO: check if nname already has an ntype
|
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)
|
funchdr(f)
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
@ -467,6 +456,13 @@ func (p *noder) typeExpr(typ syntax.Expr) *Node {
|
||||||
return p.expr(typ)
|
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 {
|
func (p *noder) chanDir(dir syntax.ChanDir) ChanDir {
|
||||||
switch dir {
|
switch dir {
|
||||||
case 0:
|
case 0:
|
||||||
|
|
|
||||||
|
|
@ -213,7 +213,7 @@ func dumpglobls() {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, n := range funcsyms {
|
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)
|
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.
|
// dnameField dumps a reflect.name for a struct field.
|
||||||
func dnameField(s *Sym, ot int, spkg *Pkg, ft *Field) int {
|
func dnameField(s *Sym, ot int, spkg *Pkg, ft *Field) int {
|
||||||
var name string
|
var name string
|
||||||
if ft.Sym != nil && ft.Embedded == 0 {
|
if ft.Sym != nil {
|
||||||
name = ft.Sym.Name
|
name = ft.Sym.Name
|
||||||
}
|
}
|
||||||
isExported, fpkg := isExportedField(ft)
|
isExported, fpkg := isExportedField(ft)
|
||||||
|
|
@ -1345,7 +1345,14 @@ ok:
|
||||||
// ../../../../runtime/type.go:/structField
|
// ../../../../runtime/type.go:/structField
|
||||||
ot = dnameField(s, ot, pkg, f)
|
ot = dnameField(s, ot, pkg, f)
|
||||||
ot = dsymptr(s, ot, dtypesym(f.Type), 0)
|
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 *Func
|
Func *Func
|
||||||
|
|
||||||
// ONAME
|
// ONAME, OTYPE, OPACK, OLABEL, some OLITERAL
|
||||||
Name *Name
|
Name *Name
|
||||||
|
|
||||||
Sym *Sym // various
|
Sym *Sym // various
|
||||||
|
|
@ -59,8 +59,8 @@ type Node struct {
|
||||||
Noescape bool // func arguments do not escape; TODO(rsc): move Noescape to Func struct (see CL 7360)
|
Noescape bool // func arguments do not escape; TODO(rsc): move Noescape to Func struct (see CL 7360)
|
||||||
Walkdef uint8 // tracks state during typecheckdef; 2 == loop detected
|
Walkdef uint8 // tracks state during typecheckdef; 2 == loop detected
|
||||||
Typecheck uint8 // tracks state during typechecking; 2 == loop detected
|
Typecheck uint8 // tracks state during typechecking; 2 == loop detected
|
||||||
Local bool
|
Local bool // type created in this file (see also Type.Local); TODO(gri): move this into flags
|
||||||
IsStatic bool // whether this Node will be converted to purely static data
|
IsStatic bool // whether this Node will be converted to purely static data
|
||||||
Initorder uint8
|
Initorder uint8
|
||||||
Used bool // for variable/label declared and not used error
|
Used bool // for variable/label declared and not used error
|
||||||
Isddd bool // is the argument variadic
|
Isddd bool // is the argument variadic
|
||||||
|
|
@ -180,14 +180,14 @@ func (n *Node) SetIota(x int64) {
|
||||||
n.Xoffset = x
|
n.Xoffset = x
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name holds Node fields used only by named nodes (ONAME, OPACK, OLABEL, some OLITERAL).
|
// Name holds Node fields used only by named nodes (ONAME, OTYPE, OPACK, OLABEL, some OLITERAL).
|
||||||
type Name struct {
|
type Name struct {
|
||||||
Pack *Node // real package for import . names
|
Pack *Node // real package for import . names
|
||||||
Pkg *Pkg // pkg for OPACK nodes
|
Pkg *Pkg // pkg for OPACK nodes
|
||||||
Heapaddr *Node // temp holding heap address of param (could move to Param?)
|
Heapaddr *Node // temp holding heap address of param (could move to Param?)
|
||||||
Defn *Node // initializing assignment
|
Defn *Node // initializing assignment
|
||||||
Curfn *Node // function for local variables
|
Curfn *Node // function for local variables
|
||||||
Param *Param // additional fields for ONAME
|
Param *Param // additional fields for ONAME, OTYPE
|
||||||
Decldepth int32 // declaration loop depth, increased for every loop or label
|
Decldepth int32 // declaration loop depth, increased for every loop or label
|
||||||
Vargen int32 // unique name for ONAME within a function. Function outputs are numbered starting at one.
|
Vargen int32 // unique name for ONAME within a function. Function outputs are numbered starting at one.
|
||||||
Funcdepth int32
|
Funcdepth int32
|
||||||
|
|
@ -280,15 +280,16 @@ type Param struct {
|
||||||
Innermost *Node
|
Innermost *Node
|
||||||
Outer *Node
|
Outer *Node
|
||||||
|
|
||||||
// OTYPE pragmas
|
// OTYPE
|
||||||
//
|
//
|
||||||
// TODO: Should Func pragmas also be stored on the Name?
|
// TODO: Should Func pragmas also be stored on the Name?
|
||||||
Pragma Pragma
|
Pragma Pragma
|
||||||
|
Alias bool // node is alias for Ntype (only used when type-checking ODCLTYPE)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Func holds Node fields used only with function-like nodes.
|
// Func holds Node fields used only with function-like nodes.
|
||||||
type Func struct {
|
type Func struct {
|
||||||
Shortname *Node
|
Shortname *Sym
|
||||||
Enter Nodes // for example, allocate and initialize memory for escaping parameters
|
Enter Nodes // for example, allocate and initialize memory for escaping parameters
|
||||||
Exit Nodes
|
Exit Nodes
|
||||||
Cvars Nodes // closure params
|
Cvars Nodes // closure params
|
||||||
|
|
@ -382,7 +383,7 @@ const (
|
||||||
ODCLFUNC // func f() or func (r) f()
|
ODCLFUNC // func f() or func (r) f()
|
||||||
ODCLFIELD // struct field, interface field, or func/method argument/return value.
|
ODCLFIELD // struct field, interface field, or func/method argument/return value.
|
||||||
ODCLCONST // const pi = 3.14
|
ODCLCONST // const pi = 3.14
|
||||||
ODCLTYPE // type Int int
|
ODCLTYPE // type Int int or type Int = int
|
||||||
|
|
||||||
ODELETE // delete(Left, Right)
|
ODELETE // delete(Left, Right)
|
||||||
ODOT // Left.Sym (Left is of struct type)
|
ODOT // Left.Sym (Left is of struct type)
|
||||||
|
|
|
||||||
|
|
@ -96,16 +96,16 @@ func typekind(t *Type) string {
|
||||||
return fmt.Sprintf("etype=%d", et)
|
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
|
// It is used by typecheck in the case of OLITERAL nodes
|
||||||
// to print constant definition loops.
|
// 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-- {
|
for i := len(stack) - 1; i >= 0; i-- {
|
||||||
if n := stack[i]; n.Op == cur.Op {
|
if n := stack[i]; n.Op == cur.Op {
|
||||||
if n != first {
|
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
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -152,7 +152,6 @@ func typecheck(n *Node, top int) *Node {
|
||||||
if n.Typecheck == 2 {
|
if n.Typecheck == 2 {
|
||||||
// Typechecking loop. Trying printing a meaningful message,
|
// Typechecking loop. Trying printing a meaningful message,
|
||||||
// otherwise a stack trace of typechecking.
|
// otherwise a stack trace of typechecking.
|
||||||
var fmt_ string
|
|
||||||
switch n.Op {
|
switch n.Op {
|
||||||
// We can already diagnose variables used as types.
|
// We can already diagnose variables used as types.
|
||||||
case ONAME:
|
case ONAME:
|
||||||
|
|
@ -160,22 +159,30 @@ func typecheck(n *Node, top int) *Node {
|
||||||
yyerror("%v is not a type", n)
|
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:
|
case OLITERAL:
|
||||||
if top&(Erv|Etype) == Etype {
|
if top&(Erv|Etype) == Etype {
|
||||||
yyerror("%v is not a type", n)
|
yyerror("%v is not a type", n)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
sprint_depchain(&fmt_, typecheck_tcstack, n, n)
|
var trace string
|
||||||
yyerrorl(n.Lineno, "constant definition loop%s", fmt_)
|
sprint_depchain(&trace, typecheck_tcstack, n, n)
|
||||||
|
yyerrorl(n.Lineno, "constant definition loop%s", trace)
|
||||||
}
|
}
|
||||||
|
|
||||||
if nsavederrors+nerrors == 0 {
|
if nsavederrors+nerrors == 0 {
|
||||||
fmt_ = ""
|
var trace string
|
||||||
for i := len(typecheck_tcstack) - 1; i >= 0; i-- {
|
for i := len(typecheck_tcstack) - 1; i >= 0; i-- {
|
||||||
x := typecheck_tcstack[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
|
lineno = lno
|
||||||
|
|
@ -3429,7 +3436,14 @@ func typecheckfunc(n *Node) {
|
||||||
t.SetNname(n.Func.Nname)
|
t.SetNname(n.Func.Nname)
|
||||||
rcvr := t.Recv()
|
rcvr := t.Recv()
|
||||||
if rcvr != nil && n.Func.Shortname != nil {
|
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
|
// copy new type and clear fields
|
||||||
// that don't come along.
|
// that don't come along.
|
||||||
// anything zeroed here must be zeroed in
|
|
||||||
// typedcl2 too.
|
|
||||||
copytype(n, t)
|
copytype(n, t)
|
||||||
|
|
||||||
ret:
|
ret:
|
||||||
|
|
@ -3758,12 +3770,29 @@ func typecheckdef(n *Node) *Node {
|
||||||
n.Name.Defn = typecheck(n.Name.Defn, Etop) // fills in n->type
|
n.Name.Defn = typecheck(n.Name.Defn, Etop) // fills in n->type
|
||||||
|
|
||||||
case OTYPE:
|
case OTYPE:
|
||||||
|
if p := n.Name.Param; p.Alias {
|
||||||
|
// Type alias declaration: Simply use the rhs type - no need
|
||||||
|
// to create a new type.
|
||||||
|
// If we have a syntax error, p.Ntype may be nil.
|
||||||
|
if p.Ntype != nil {
|
||||||
|
p.Ntype = typecheck(p.Ntype, Etype)
|
||||||
|
n.Type = p.Ntype.Type
|
||||||
|
if n.Type == nil {
|
||||||
|
n.Diag = true
|
||||||
|
goto ret
|
||||||
|
}
|
||||||
|
n.Sym.Def = p.Ntype
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// regular type declaration
|
||||||
if Curfn != nil {
|
if Curfn != nil {
|
||||||
defercheckwidth()
|
defercheckwidth()
|
||||||
}
|
}
|
||||||
n.Walkdef = 1
|
n.Walkdef = 1
|
||||||
n.Type = typ(TFORW)
|
n.Type = typ(TFORW)
|
||||||
n.Type.Sym = n.Sym
|
n.Type.Sym = n.Sym // TODO(gri) this also happens in typecheckdeftype(n) - where should it happen?
|
||||||
nerrors0 := nerrors
|
nerrors0 := nerrors
|
||||||
typecheckdeftype(n)
|
typecheckdeftype(n)
|
||||||
if n.Type.Etype == TFORW && nerrors > nerrors0 {
|
if n.Type.Etype == TFORW && nerrors > nerrors0 {
|
||||||
|
|
@ -3771,7 +3800,6 @@ func typecheckdef(n *Node) *Node {
|
||||||
// but it was reported. Silence future errors.
|
// but it was reported. Silence future errors.
|
||||||
n.Type.Broke = true
|
n.Type.Broke = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if Curfn != nil {
|
if Curfn != nil {
|
||||||
resumecheckwidth()
|
resumecheckwidth()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -398,6 +398,14 @@ func lexinit1() {
|
||||||
// errortype.Orig = makeErrorInterface()
|
// errortype.Orig = makeErrorInterface()
|
||||||
s.Def = typenod(errortype)
|
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
|
// byte alias
|
||||||
s = Pkglookup("byte", builtinpkg)
|
s = Pkglookup("byte", builtinpkg)
|
||||||
bytetype = typ(TUINT8)
|
bytetype = typ(TUINT8)
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ type (
|
||||||
// Name Type
|
// Name Type
|
||||||
TypeDecl struct {
|
TypeDecl struct {
|
||||||
Name *Name
|
Name *Name
|
||||||
|
Alias bool
|
||||||
Type Expr
|
Type Expr
|
||||||
Group *Group // nil means not part of a group
|
Group *Group // nil means not part of a group
|
||||||
Pragma Pragma
|
Pragma Pragma
|
||||||
|
|
|
||||||
|
|
@ -325,7 +325,7 @@ func (p *parser) constDecl(group *Group) Decl {
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
// TypeSpec = identifier Type .
|
// TypeSpec = identifier [ "=" ] Type .
|
||||||
func (p *parser) typeDecl(group *Group) Decl {
|
func (p *parser) typeDecl(group *Group) Decl {
|
||||||
if trace {
|
if trace {
|
||||||
defer p.trace("typeDecl")()
|
defer p.trace("typeDecl")()
|
||||||
|
|
@ -335,6 +335,7 @@ func (p *parser) typeDecl(group *Group) Decl {
|
||||||
d.init(p)
|
d.init(p)
|
||||||
|
|
||||||
d.Name = p.name()
|
d.Name = p.name()
|
||||||
|
d.Alias = p.got(_Assign)
|
||||||
d.Type = p.tryType()
|
d.Type = p.tryType()
|
||||||
if d.Type == nil {
|
if d.Type == nil {
|
||||||
p.syntax_error("in type declaration")
|
p.syntax_error("in type declaration")
|
||||||
|
|
|
||||||
|
|
@ -619,7 +619,11 @@ func (p *printer) printRawNode(n Node) {
|
||||||
if n.Group == nil {
|
if n.Group == nil {
|
||||||
p.print(_Type, blank)
|
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:
|
case *VarDecl:
|
||||||
if n.Group == nil {
|
if n.Group == nil {
|
||||||
|
|
|
||||||
|
|
@ -22,3 +22,20 @@ func TestPrint(t *testing.T) {
|
||||||
Fprint(os.Stdout, ast, true)
|
Fprint(os.Stdout, ast, true)
|
||||||
fmt.Println()
|
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.
|
`const MultiLineConst = ...`, // Multi line constant.
|
||||||
`var MultiLineVar = map\[struct{ ... }\]struct{ ... }{ ... }`, // Multi line variable.
|
`var MultiLineVar = map\[struct{ ... }\]struct{ ... }{ ... }`, // Multi line variable.
|
||||||
`func MultiLineFunc\(x interface{ ... }\) \(r struct{ ... }\)`, // Multi line function.
|
`func MultiLineFunc\(x interface{ ... }\) \(r struct{ ... }\)`, // Multi line function.
|
||||||
|
`type T1 = T2`, // Type alias
|
||||||
},
|
},
|
||||||
[]string{
|
[]string{
|
||||||
`const internalConstant = 2`, // No internal constants.
|
`const internalConstant = 2`, // No internal constants.
|
||||||
|
|
@ -89,6 +90,7 @@ var tests = []test{
|
||||||
`unexportedTypedConstant`, // No unexported typed constant.
|
`unexportedTypedConstant`, // No unexported typed constant.
|
||||||
`Field`, // No fields.
|
`Field`, // No fields.
|
||||||
`Method`, // No methods.
|
`Method`, // No methods.
|
||||||
|
`type T1 T2`, // Type alias does not display as type declaration.
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// Package dump -u
|
// Package dump -u
|
||||||
|
|
@ -265,6 +267,18 @@ var tests = []test{
|
||||||
`error`, // No embedded error.
|
`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 -u with unexported fields.
|
||||||
{
|
{
|
||||||
"type with unexported fields and -u",
|
"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)
|
return fmt.Sprintf("func %s%s%s", recv, name, fnc)
|
||||||
|
|
||||||
case *ast.TypeSpec:
|
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:
|
case *ast.FuncType:
|
||||||
var params []string
|
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{}
|
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 {
|
func decodetypeStructFieldOffs(arch *sys.Arch, s *Symbol, i int) int64 {
|
||||||
off := decodetypeStructFieldArrayOff(s, i)
|
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
|
// InterfaceType.methods.length
|
||||||
|
|
|
||||||
|
|
@ -848,6 +848,7 @@ type (
|
||||||
TypeSpec struct {
|
TypeSpec struct {
|
||||||
Doc *CommentGroup // associated documentation; or nil
|
Doc *CommentGroup // associated documentation; or nil
|
||||||
Name *Ident // type name
|
Name *Ident // type name
|
||||||
|
Assign token.Pos // position of '=', if any
|
||||||
Type Expr // *Ident, *ParenExpr, *SelectorExpr, *StarExpr, or any of the *XxxTypes
|
Type Expr // *Ident, *ParenExpr, *SelectorExpr, *StarExpr, or any of the *XxxTypes
|
||||||
Comment *CommentGroup // line comments; or nil
|
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
|
// 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
|
// 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".
|
// (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")
|
env := os.Getenv("CGO_ENABLED")
|
||||||
if env == "" {
|
if env == "" {
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,7 @@
|
||||||
// - "go1.6", from Go version 1.6 onward
|
// - "go1.6", from Go version 1.6 onward
|
||||||
// - "go1.7", from Go version 1.7 onward
|
// - "go1.7", from Go version 1.7 onward
|
||||||
// - "go1.8", from Go version 1.8 onward
|
// - "go1.8", from Go version 1.8 onward
|
||||||
|
// - "go1.9", from Go version 1.9 onward
|
||||||
// - any additional words listed in ctxt.BuildTags
|
// - any additional words listed in ctxt.BuildTags
|
||||||
//
|
//
|
||||||
// If a file's name, after stripping the extension and a possible _test suffix,
|
// 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: "IsUpper", want: "func IsUpper(r rune) bool"},
|
||||||
{pkgpath: "unicode", name: "MaxRune", want: "const MaxRune untyped rune", wantval: "1114111"},
|
{pkgpath: "unicode", name: "MaxRune", want: "const MaxRune untyped rune", wantval: "1114111"},
|
||||||
{pkgpath: "imports", wantinits: []string{"imports..import", "fmt..import", "math..import"}},
|
{pkgpath: "imports", wantinits: []string{"imports..import", "fmt..import", "math..import"}},
|
||||||
|
{pkgpath: "alias", name: "IntAlias2", want: "type IntAlias2 = Int"},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGoxImporter(t *testing.T) {
|
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)
|
return types.NewConst(token.NoPos, pkg, name, typ, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TypeName = ExportedName .
|
// NamedType = TypeName [ "=" ] Type { Method } .
|
||||||
func (p *parser) parseTypeName() *types.TypeName {
|
// TypeName = ExportedName .
|
||||||
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 } .
|
|
||||||
// Method = "func" "(" Param ")" Name ParamList ResultList ";" .
|
// Method = "func" "(" Param ")" Name ParamList ResultList ";" .
|
||||||
func (p *parser) parseNamedType(n int) types.Type {
|
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()
|
typ := obj.Type()
|
||||||
p.typeMap[n] = typ
|
p.typeMap[n] = typ
|
||||||
|
|
||||||
|
|
@ -409,8 +423,8 @@ func (p *parser) parseNamedType(n int) types.Type {
|
||||||
nt.SetUnderlying(underlying.Underlying())
|
nt.SetUnderlying(underlying.Underlying())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// collect associated methods
|
||||||
for p.tok == scanner.Ident {
|
for p.tok == scanner.Ident {
|
||||||
// collect associated methods
|
|
||||||
p.expectKeyword("func")
|
p.expectKeyword("func")
|
||||||
p.expect('(')
|
p.expect('(')
|
||||||
receiver, _ := p.parseParam(pkg)
|
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
|
// read version specific flags - extend as necessary
|
||||||
switch p.version {
|
switch p.version {
|
||||||
// case 4:
|
// case 5:
|
||||||
// ...
|
// ...
|
||||||
// fallthrough
|
// fallthrough
|
||||||
case 3, 2, 1:
|
case 4, 3, 2, 1:
|
||||||
p.debugFormat = p.rawStringln(p.rawByte()) == "debug"
|
p.debugFormat = p.rawStringln(p.rawByte()) == "debug"
|
||||||
p.trackAllTypes = p.int() != 0
|
p.trackAllTypes = p.int() != 0
|
||||||
p.posInfoFormat = 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.
|
// objTag returns the tag value for each object kind.
|
||||||
// obj must not be a *types.Alias.
|
|
||||||
func objTag(obj types.Object) int {
|
func objTag(obj types.Object) int {
|
||||||
switch obj.(type) {
|
switch obj.(type) {
|
||||||
case *types.Const:
|
case *types.Const:
|
||||||
|
|
@ -219,7 +218,6 @@ func objTag(obj types.Object) int {
|
||||||
return varTag
|
return varTag
|
||||||
case *types.Func:
|
case *types.Func:
|
||||||
return funcTag
|
return funcTag
|
||||||
// Aliases are not exported multiple times, thus we should not see them here.
|
|
||||||
default:
|
default:
|
||||||
errorf("unexpected object: %v (%T)", obj, obj) // panics
|
errorf("unexpected object: %v (%T)", obj, obj) // panics
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
|
|
@ -237,14 +235,14 @@ func (p *importer) declare(obj types.Object) {
|
||||||
pkg := obj.Pkg()
|
pkg := obj.Pkg()
|
||||||
if alt := pkg.Scope().Insert(obj); alt != nil {
|
if alt := pkg.Scope().Insert(obj); alt != nil {
|
||||||
// This can only trigger if we import a (non-type) object a second time.
|
// 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
|
// once; and b) we ignore compiler-specific export data which may contain
|
||||||
// functions whose inlined function bodies refer to other functions that
|
// functions whose inlined function bodies refer to other functions that
|
||||||
// were already imported.
|
// 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,
|
// to allow it (see also the comment in cmd/compile/internal/gc/bimport.go,
|
||||||
// method importer.obj, switch case importing functions).
|
// 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) {
|
if !sameObj(obj, alt) {
|
||||||
errorf("inconsistent import:\n\t%v\npreviously imported as:\n\t%v\n", 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()
|
val := p.value()
|
||||||
p.declare(types.NewConst(pos, pkg, name, typ, val))
|
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:
|
case typeTag:
|
||||||
p.typ(nil)
|
p.typ(nil)
|
||||||
|
|
||||||
|
|
@ -277,19 +282,6 @@ func (p *importer) obj(tag int) {
|
||||||
sig := types.NewSignature(nil, params, result, isddd)
|
sig := types.NewSignature(nil, params, result, isddd)
|
||||||
p.declare(types.NewFunc(pos, pkg, name, sig))
|
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:
|
default:
|
||||||
errorf("unexpected object tag %d", tag)
|
errorf("unexpected object tag %d", tag)
|
||||||
}
|
}
|
||||||
|
|
@ -349,9 +341,7 @@ var (
|
||||||
|
|
||||||
func (p *importer) qualifiedName() (pkg *types.Package, name string) {
|
func (p *importer) qualifiedName() (pkg *types.Package, name string) {
|
||||||
name = p.string()
|
name = p.string()
|
||||||
if name != "" {
|
pkg = p.pkg()
|
||||||
pkg = p.pkg()
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -556,17 +546,17 @@ func (p *importer) fieldList(parent *types.Package) (fields []*types.Var, tags [
|
||||||
fields = make([]*types.Var, n)
|
fields = make([]*types.Var, n)
|
||||||
tags = make([]string, n)
|
tags = make([]string, n)
|
||||||
for i := range fields {
|
for i := range fields {
|
||||||
fields[i] = p.field(parent)
|
fields[i], tags[i] = p.field(parent)
|
||||||
tags[i] = p.string()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *importer) field(parent *types.Package) *types.Var {
|
func (p *importer) field(parent *types.Package) (*types.Var, string) {
|
||||||
pos := p.pos()
|
pos := p.pos()
|
||||||
pkg, name := p.fieldName(parent)
|
pkg, name, alias := p.fieldName(parent)
|
||||||
typ := p.typ(parent)
|
typ := p.typ(parent)
|
||||||
|
tag := p.string()
|
||||||
|
|
||||||
anonymous := false
|
anonymous := false
|
||||||
if name == "" {
|
if name == "" {
|
||||||
|
|
@ -578,12 +568,15 @@ func (p *importer) field(parent *types.Package) *types.Var {
|
||||||
case *types.Named:
|
case *types.Named:
|
||||||
name = typ.Obj().Name()
|
name = typ.Obj().Name()
|
||||||
default:
|
default:
|
||||||
errorf("anonymous field expected")
|
errorf("named base type expected")
|
||||||
}
|
}
|
||||||
anonymous = true
|
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) {
|
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 {
|
func (p *importer) method(parent *types.Package) *types.Func {
|
||||||
pos := p.pos()
|
pos := p.pos()
|
||||||
pkg, name := p.fieldName(parent)
|
pkg, name, _ := p.fieldName(parent)
|
||||||
params, isddd := p.paramList()
|
params, isddd := p.paramList()
|
||||||
result, _ := p.paramList()
|
result, _ := p.paramList()
|
||||||
sig := types.NewSignature(nil, params, result, isddd)
|
sig := types.NewSignature(nil, params, result, isddd)
|
||||||
return types.NewFunc(pos, pkg, name, sig)
|
return types.NewFunc(pos, pkg, name, sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *importer) fieldName(parent *types.Package) (*types.Package, string) {
|
func (p *importer) fieldName(parent *types.Package) (pkg *types.Package, name string, alias bool) {
|
||||||
name := p.string()
|
name = p.string()
|
||||||
pkg := parent
|
pkg = parent
|
||||||
if pkg == nil {
|
if pkg == nil {
|
||||||
// use the imported package instead
|
// use the imported package instead
|
||||||
pkg = p.pkgList[0]
|
pkg = p.pkgList[0]
|
||||||
}
|
}
|
||||||
if p.version == 0 && name == "_" {
|
if p.version == 0 && name == "_" {
|
||||||
// version 0 didn't export a package for _ fields
|
// version 0 didn't export a package for _ fields
|
||||||
return pkg, name
|
return
|
||||||
}
|
}
|
||||||
if name != "" && !exported(name) {
|
switch name {
|
||||||
if name == "?" {
|
case "":
|
||||||
name = ""
|
// 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()
|
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) {
|
func (p *importer) paramList() (*types.Tuple, bool) {
|
||||||
|
|
@ -893,7 +897,7 @@ const (
|
||||||
nilTag // only used by gc (appears in exported inlined function bodies)
|
nilTag // only used by gc (appears in exported inlined function bodies)
|
||||||
unknownTag // not used by gc (only appears in packages with errors)
|
unknownTag // not used by gc (only appears in packages with errors)
|
||||||
|
|
||||||
// Aliases
|
// Type aliases
|
||||||
aliasTag
|
aliasTag
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -917,7 +921,7 @@ var predeclared = []types.Type{
|
||||||
types.Typ[types.Complex128],
|
types.Typ[types.Complex128],
|
||||||
types.Typ[types.String],
|
types.Typ[types.String],
|
||||||
|
|
||||||
// aliases
|
// basic type aliases
|
||||||
types.Universe.Lookup("byte").Type(),
|
types.Universe.Lookup("byte").Type(),
|
||||||
types.Universe.Lookup("rune").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.)
|
// (Global identifiers are resolved in a separate phase after parsing.)
|
||||||
spec := &ast.TypeSpec{Doc: doc, Name: ident}
|
spec := &ast.TypeSpec{Doc: doc, Name: ident}
|
||||||
p.declare(spec, nil, p.topScope, ast.Typ, 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()
|
spec.Type = p.parseType()
|
||||||
p.expectSemi() // call before accessing p.linecomment
|
p.expectSemi() // call before accessing p.linecomment
|
||||||
spec.Comment = p.lineComment
|
spec.Comment = p.lineComment
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,8 @@ var valids = []string{
|
||||||
`package p; const (x = 0; y; z)`, // issue 9639
|
`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; 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) {
|
func TestValid(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -1447,6 +1447,9 @@ func (p *printer) spec(spec ast.Spec, n int, doIndent bool) {
|
||||||
} else {
|
} else {
|
||||||
p.print(vtab)
|
p.print(vtab)
|
||||||
}
|
}
|
||||||
|
if s.Assign.IsValid() {
|
||||||
|
p.print(token.ASSIGN, blank)
|
||||||
|
}
|
||||||
p.expr(s.Type)
|
p.expr(s.Type)
|
||||||
p.setComment(s.Comment)
|
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
|
x int
|
||||||
y int
|
y int
|
||||||
}) // no extra comma between } and )
|
}) // 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
|
x int
|
||||||
y int
|
y int
|
||||||
}) // no extra comma between } and )
|
}) // 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
|
// so we don't need a "package" mode for operands: package names
|
||||||
// can only appear in qualified identifiers which are mapped to
|
// can only appear in qualified identifiers which are mapped to
|
||||||
// selector expressions.
|
// 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 {
|
if ident, ok := e.X.(*ast.Ident); ok {
|
||||||
_, obj := check.scope.LookupParent(ident.Name, check.pos)
|
_, obj := check.scope.LookupParent(ident.Name, check.pos)
|
||||||
if pname, _ := obj.(*PkgName); pname != nil {
|
if pname, _ := obj.(*PkgName); pname != nil {
|
||||||
|
|
@ -296,12 +294,6 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
|
||||||
// ok to continue
|
// ok to continue
|
||||||
}
|
}
|
||||||
check.recordUse(e.Sel, exp)
|
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:
|
// Simplified version of the code for *ast.Idents:
|
||||||
// - imported objects are always fully initialized
|
// - imported objects are always fully initialized
|
||||||
|
|
|
||||||
|
|
@ -68,11 +68,11 @@ var tests = [][]string{
|
||||||
{"testdata/decls1.src"},
|
{"testdata/decls1.src"},
|
||||||
{"testdata/decls2a.src", "testdata/decls2b.src"},
|
{"testdata/decls2a.src", "testdata/decls2b.src"},
|
||||||
{"testdata/decls3.src"},
|
{"testdata/decls3.src"},
|
||||||
|
{"testdata/decls4.src"},
|
||||||
{"testdata/const0.src"},
|
{"testdata/const0.src"},
|
||||||
{"testdata/const1.src"},
|
{"testdata/const1.src"},
|
||||||
{"testdata/constdecl.src"},
|
{"testdata/constdecl.src"},
|
||||||
{"testdata/vardecl.src"},
|
{"testdata/vardecl.src"},
|
||||||
//{"testdata/aliasdecl.src"},
|
|
||||||
{"testdata/expr0.src"},
|
{"testdata/expr0.src"},
|
||||||
{"testdata/expr1.src"},
|
{"testdata/expr1.src"},
|
||||||
{"testdata/expr2.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)
|
check.varDecl(obj, d.lhs, d.typ, d.init)
|
||||||
case *TypeName:
|
case *TypeName:
|
||||||
// invalid recursive types are detected via path
|
// 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:
|
case *Func:
|
||||||
// functions may be recursive - no need to track dependencies
|
// functions may be recursive - no need to track dependencies
|
||||||
check.funcDecl(obj, d)
|
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:
|
default:
|
||||||
unreachable()
|
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)
|
assert(obj.typ == nil)
|
||||||
|
|
||||||
// type declarations cannot use iota
|
// type declarations cannot use iota
|
||||||
assert(check.iota == nil)
|
assert(check.iota == nil)
|
||||||
|
|
||||||
named := &Named{obj: obj}
|
if alias {
|
||||||
def.setUnderlying(named)
|
|
||||||
obj.typ = named // make sure recursive type declarations terminate
|
|
||||||
|
|
||||||
// determine underlying type of named
|
obj.typ = Typ[Invalid]
|
||||||
check.typExpr(typ, named, append(path, obj))
|
obj.typ = check.typExpr(typ, nil, append(path, obj))
|
||||||
|
|
||||||
// The underlying type of named may be itself a named type that is
|
} else {
|
||||||
// incomplete:
|
|
||||||
//
|
named := &Named{obj: obj}
|
||||||
// type (
|
def.setUnderlying(named)
|
||||||
// A B
|
obj.typ = named // make sure recursive type declarations terminate
|
||||||
// B *C
|
|
||||||
// C A
|
// determine underlying type of named
|
||||||
// )
|
check.typExpr(typ, named, append(path, obj))
|
||||||
//
|
|
||||||
// The type of C is the (named) type of A which is incomplete,
|
// The underlying type of named may be itself a named type that is
|
||||||
// and which has as its underlying type the named type B.
|
// incomplete:
|
||||||
// Determine the (final, unnamed) underlying type by resolving
|
//
|
||||||
// any forward chain (they always end in an unnamed type).
|
// type (
|
||||||
named.underlying = underlying(named.underlying)
|
// 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
|
// check and add associated methods
|
||||||
// TODO(gri) It's easy to create pathological cases where the
|
// 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
|
// spec: "If the base type is a struct type, the non-blank method
|
||||||
// and field names must be distinct."
|
// and field names must be distinct."
|
||||||
base := obj.typ.(*Named)
|
base, _ := obj.typ.(*Named) // nil if receiver base type is type alias
|
||||||
if t, _ := base.underlying.(*Struct); t != nil {
|
if base != nil {
|
||||||
for _, fld := range t.fields {
|
if t, _ := base.underlying.(*Struct); t != nil {
|
||||||
if fld.name != "_" {
|
for _, fld := range t.fields {
|
||||||
assert(mset.insert(fld) == nil)
|
if fld.name != "_" {
|
||||||
|
assert(mset.insert(fld) == nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Checker.Files may be called multiple times; additional package files
|
// Checker.Files may be called multiple times; additional package files
|
||||||
// may add methods to already type-checked types. Add pre-existing methods
|
// may add methods to already type-checked types. Add pre-existing methods
|
||||||
// so that we can detect redeclarations.
|
// so that we can detect redeclarations.
|
||||||
for _, m := range base.methods {
|
for _, m := range base.methods {
|
||||||
assert(m.name != "_")
|
assert(m.name != "_")
|
||||||
assert(mset.insert(m) == nil)
|
assert(mset.insert(m) == nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// type-check methods
|
// type-check methods
|
||||||
|
|
@ -295,7 +302,7 @@ func (check *Checker) addMethodDecls(obj *TypeName) {
|
||||||
case *Var:
|
case *Var:
|
||||||
check.errorf(m.pos, "field and method with the same name %s", m.name)
|
check.errorf(m.pos, "field and method with the same name %s", m.name)
|
||||||
case *Func:
|
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:
|
default:
|
||||||
unreachable()
|
unreachable()
|
||||||
}
|
}
|
||||||
|
|
@ -303,9 +310,12 @@ func (check *Checker) addMethodDecls(obj *TypeName) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// type-check
|
||||||
check.objDecl(m, nil, nil)
|
check.objDecl(m, nil, nil)
|
||||||
|
|
||||||
// methods with blank _ names cannot be found - don't keep them
|
// 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)
|
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) {
|
func (check *Checker) declStmt(decl ast.Decl) {
|
||||||
pkg := check.pkg
|
pkg := check.pkg
|
||||||
|
|
||||||
|
|
@ -540,7 +450,7 @@ func (check *Checker) declStmt(decl ast.Decl) {
|
||||||
// the innermost containing block."
|
// the innermost containing block."
|
||||||
scopePos := s.Name.Pos()
|
scopePos := s.Name.Pos()
|
||||||
check.declare(check.scope, s.Name, obj, scopePos)
|
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:
|
default:
|
||||||
check.invalidAST(s.Pos(), "const, type, or var declaration expected")
|
check.invalidAST(s.Pos(), "const, type, or var declaration expected")
|
||||||
|
|
|
||||||
|
|
@ -239,10 +239,10 @@ func fib(x int) int {
|
||||||
// type S string:
|
// type S string:
|
||||||
// defined at fib.go:4:6
|
// defined at fib.go:4:6
|
||||||
// used at 6:23
|
// used at 6:23
|
||||||
// type int int:
|
// type int:
|
||||||
// defined at -
|
// defined at -
|
||||||
// used at 8:12, 8:17
|
// used at 8:12, 8:17
|
||||||
// type string string:
|
// type string:
|
||||||
// defined at -
|
// defined at -
|
||||||
// used at 4:8
|
// used at 4:8
|
||||||
// var b S:
|
// var b S:
|
||||||
|
|
|
||||||
|
|
@ -67,24 +67,22 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
|
||||||
}
|
}
|
||||||
|
|
||||||
typ, isPtr := deref(T)
|
typ, isPtr := deref(T)
|
||||||
named, _ := typ.(*Named)
|
|
||||||
|
|
||||||
// *typ where typ is an interface has no methods.
|
// *typ where typ is an interface has no methods.
|
||||||
if isPtr {
|
if isPtr && IsInterface(typ) {
|
||||||
utyp := typ
|
return
|
||||||
if named != nil {
|
|
||||||
utyp = named.underlying
|
|
||||||
}
|
|
||||||
if _, ok := utyp.(*Interface); ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start with typ as single entry at shallowest depth.
|
// Start with typ as single entry at shallowest depth.
|
||||||
// If typ is not a named type, insert a nil type instead.
|
current := []embeddedType{{typ, nil, isPtr, false}}
|
||||||
current := []embeddedType{{named, 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
|
var seen map[*Named]bool
|
||||||
|
|
||||||
// search current depth
|
// 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
|
// look for (pkg, name) in all types at current depth
|
||||||
for _, e := range current {
|
for _, e := range current {
|
||||||
// The very first time only, e.typ may be nil.
|
typ := e.typ
|
||||||
// In this case, we don't have a named type and
|
|
||||||
// we simply continue with the underlying type.
|
// If we have a named type, we may have associated methods.
|
||||||
if e.typ != nil {
|
// Look for those first.
|
||||||
if seen[e.typ] {
|
if named, _ := typ.(*Named); named != nil {
|
||||||
|
if seen[named] {
|
||||||
// We have seen this type before, at a more shallow depth
|
// We have seen this type before, at a more shallow depth
|
||||||
// (note that multiples of this type at the current depth
|
// (note that multiples of this type at the current depth
|
||||||
// were consolidated before). The type at that depth shadows
|
// 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 {
|
if seen == nil {
|
||||||
seen = make(map[*Named]bool)
|
seen = make(map[*Named]bool)
|
||||||
}
|
}
|
||||||
seen[e.typ] = true
|
seen[named] = true
|
||||||
|
|
||||||
// look for a matching attached method
|
// 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
|
// potential match
|
||||||
assert(m.typ != nil)
|
assert(m.typ != nil)
|
||||||
index = concat(e.index, i)
|
index = concat(e.index, i)
|
||||||
|
|
@ -124,7 +123,7 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
|
||||||
}
|
}
|
||||||
|
|
||||||
// continue with underlying type
|
// continue with underlying type
|
||||||
typ = e.typ.underlying
|
typ = named.underlying
|
||||||
}
|
}
|
||||||
|
|
||||||
switch t := typ.(type) {
|
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
|
// we have a name collision on the same depth; in either
|
||||||
// case we don't need to look further).
|
// case we don't need to look further).
|
||||||
// Embedded fields are always of the form T or *T where
|
// 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
|
// this depth, f.typ appears multiple times at the next
|
||||||
// depth.
|
// depth.
|
||||||
if obj == nil && f.anonymous {
|
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)
|
typ, isPtr := deref(f.typ)
|
||||||
if t, _ := typ.(*Named); t != nil {
|
// TODO(gri) optimization: ignore types that can't
|
||||||
next = append(next, embeddedType{t, concat(e.index, i), e.indirect || isPtr, e.multiples})
|
// 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
|
return nil, nil, false // not found
|
||||||
}
|
}
|
||||||
|
|
||||||
// embeddedType represents an embedded named type
|
// embeddedType represents an embedded type
|
||||||
type embeddedType struct {
|
type embeddedType struct {
|
||||||
typ *Named // nil means use the outer typ variable instead
|
typ Type
|
||||||
index []int // embedded field indices, starting with index at depth 0
|
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
|
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
|
multiples bool // if set, typ appears multiple times at this depth
|
||||||
}
|
}
|
||||||
|
|
||||||
// consolidateMultiples collects multiple list entries with the same type
|
// 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
|
return list // at most one entry - nothing to do
|
||||||
}
|
}
|
||||||
|
|
||||||
n := 0 // number of entries w/ unique type
|
n := 0 // number of entries w/ unique type
|
||||||
prev := make(map[*Named]int) // index at which type was previously seen
|
prev := make(map[Type]int) // index at which type was previously seen
|
||||||
for _, e := range list {
|
for _, e := range list {
|
||||||
if i, found := prev[e.typ]; found {
|
if i, found := lookupType(prev, e.typ); found {
|
||||||
list[i].multiples = true
|
list[i].multiples = true
|
||||||
// ignore this entry
|
// ignore this entry
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -224,6 +222,21 @@ func consolidateMultiples(list []embeddedType) []embeddedType {
|
||||||
return list[:n]
|
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
|
// MissingMethod returns (nil, false) if V implements T, otherwise it
|
||||||
// returns a missing method required by T and whether it is missing or
|
// returns a missing method required by T and whether it is missing or
|
||||||
// just has the wrong type.
|
// just has the wrong type.
|
||||||
|
|
|
||||||
|
|
@ -72,24 +72,22 @@ func NewMethodSet(T Type) *MethodSet {
|
||||||
var base methodSet
|
var base methodSet
|
||||||
|
|
||||||
typ, isPtr := deref(T)
|
typ, isPtr := deref(T)
|
||||||
named, _ := typ.(*Named)
|
|
||||||
|
|
||||||
// *typ where typ is an interface has no methods.
|
// *typ where typ is an interface has no methods.
|
||||||
if isPtr {
|
if isPtr && IsInterface(typ) {
|
||||||
utyp := typ
|
return &emptyMethodSet
|
||||||
if named != nil {
|
|
||||||
utyp = named.underlying
|
|
||||||
}
|
|
||||||
if _, ok := utyp.(*Interface); ok {
|
|
||||||
return &emptyMethodSet
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start with typ as single entry at shallowest depth.
|
// Start with typ as single entry at shallowest depth.
|
||||||
// If typ is not a named type, insert a nil type instead.
|
current := []embeddedType{{typ, nil, isPtr, false}}
|
||||||
current := []embeddedType{{named, 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
|
var seen map[*Named]bool
|
||||||
|
|
||||||
// collect methods at current depth
|
// collect methods at current depth
|
||||||
|
|
@ -101,11 +99,12 @@ func NewMethodSet(T Type) *MethodSet {
|
||||||
var mset methodSet
|
var mset methodSet
|
||||||
|
|
||||||
for _, e := range current {
|
for _, e := range current {
|
||||||
// The very first time only, e.typ may be nil.
|
typ := e.typ
|
||||||
// In this case, we don't have a named type and
|
|
||||||
// we simply continue with the underlying type.
|
// If we have a named type, we may have associated methods.
|
||||||
if e.typ != nil {
|
// Look for those first.
|
||||||
if seen[e.typ] {
|
if named, _ := typ.(*Named); named != nil {
|
||||||
|
if seen[named] {
|
||||||
// We have seen this type before, at a more shallow depth
|
// We have seen this type before, at a more shallow depth
|
||||||
// (note that multiples of this type at the current depth
|
// (note that multiples of this type at the current depth
|
||||||
// were consolidated before). The type at that depth shadows
|
// were consolidated before). The type at that depth shadows
|
||||||
|
|
@ -116,12 +115,12 @@ func NewMethodSet(T Type) *MethodSet {
|
||||||
if seen == nil {
|
if seen == nil {
|
||||||
seen = make(map[*Named]bool)
|
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
|
// continue with underlying type
|
||||||
typ = e.typ.underlying
|
typ = named.underlying
|
||||||
}
|
}
|
||||||
|
|
||||||
switch t := typ.(type) {
|
switch t := typ.(type) {
|
||||||
|
|
@ -130,16 +129,15 @@ func NewMethodSet(T Type) *MethodSet {
|
||||||
fset = fset.add(f, e.multiples)
|
fset = fset.add(f, e.multiples)
|
||||||
|
|
||||||
// Embedded fields are always of the form T or *T where
|
// 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
|
// this depth, f.Type appears multiple times at the next
|
||||||
// depth.
|
// depth.
|
||||||
if f.anonymous {
|
if f.anonymous {
|
||||||
// Ignore embedded basic types - only user-defined
|
|
||||||
// named types can have methods or struct fields.
|
|
||||||
typ, isPtr := deref(f.typ)
|
typ, isPtr := deref(f.typ)
|
||||||
if t, _ := typ.(*Named); t != nil {
|
// TODO(gri) optimization: ignore types that can't
|
||||||
next = append(next, embeddedType{t, concat(e.index, i), e.indirect || isPtr, e.multiples})
|
// 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
|
Name() string // package local object name
|
||||||
Type() Type // object type
|
Type() Type // object type
|
||||||
Exported() bool // reports whether the name starts with a capital letter
|
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 returns a human-readable string of the object.
|
||||||
String() string
|
String() string
|
||||||
|
|
@ -64,15 +64,10 @@ func Id(pkg *Package, name string) string {
|
||||||
// inside a package and outside a package - which breaks some
|
// inside a package and outside a package - which breaks some
|
||||||
// tests)
|
// tests)
|
||||||
path := "_"
|
path := "_"
|
||||||
// TODO(gri): shouldn't !ast.IsExported(name) => pkg != nil be an precondition?
|
// pkg is nil for objects in Universe scope and possibly types
|
||||||
// if pkg == nil {
|
// introduced via Eval (see also comment in object.sameId)
|
||||||
// panic("nil package in lookup of unexported name")
|
if pkg != nil && pkg.path != "" {
|
||||||
// }
|
|
||||||
if pkg != nil {
|
|
||||||
path = pkg.path
|
path = pkg.path
|
||||||
if path == "" {
|
|
||||||
path = "_"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return path + "." + name
|
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 (obj *Const) Val() constant.Value { return obj.val }
|
||||||
func (*Const) isDependency() {} // a constant may be a dependency of an initialization expression
|
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 {
|
type TypeName struct {
|
||||||
object
|
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}}
|
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).
|
// A Variable represents a declared variable (including function parameters and results, and struct fields).
|
||||||
type Var struct {
|
type Var struct {
|
||||||
object
|
object
|
||||||
|
|
@ -215,28 +230,6 @@ func (obj *Func) FullName() string {
|
||||||
func (obj *Func) Scope() *Scope { return obj.typ.(*Signature).scope }
|
func (obj *Func) Scope() *Scope { return obj.typ.(*Signature).scope }
|
||||||
func (*Func) isDependency() {} // a function may be a dependency of an initialization expression
|
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.
|
// A Label represents a declared label.
|
||||||
type Label struct {
|
type Label struct {
|
||||||
object
|
object
|
||||||
|
|
@ -264,7 +257,9 @@ type Nil struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
|
func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
|
||||||
|
var tname *TypeName
|
||||||
typ := obj.Type()
|
typ := obj.Type()
|
||||||
|
|
||||||
switch obj := obj.(type) {
|
switch obj := obj.(type) {
|
||||||
case *PkgName:
|
case *PkgName:
|
||||||
fmt.Fprintf(buf, "package %s", obj.Name())
|
fmt.Fprintf(buf, "package %s", obj.Name())
|
||||||
|
|
@ -277,8 +272,8 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
|
||||||
buf.WriteString("const")
|
buf.WriteString("const")
|
||||||
|
|
||||||
case *TypeName:
|
case *TypeName:
|
||||||
|
tname = obj
|
||||||
buf.WriteString("type")
|
buf.WriteString("type")
|
||||||
typ = typ.Underlying()
|
|
||||||
|
|
||||||
case *Var:
|
case *Var:
|
||||||
if obj.isField {
|
if obj.isField {
|
||||||
|
|
@ -295,10 +290,6 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
||||||
// Alias-related code. Keep for now.
|
|
||||||
// case *Alias:
|
|
||||||
// buf.WriteString("alias")
|
|
||||||
|
|
||||||
case *Label:
|
case *Label:
|
||||||
buf.WriteString("label")
|
buf.WriteString("label")
|
||||||
typ = nil
|
typ = nil
|
||||||
|
|
@ -322,10 +313,27 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
|
||||||
writePackage(buf, obj.Pkg(), qf)
|
writePackage(buf, obj.Pkg(), qf)
|
||||||
}
|
}
|
||||||
buf.WriteString(obj.Name())
|
buf.WriteString(obj.Name())
|
||||||
if typ != nil {
|
|
||||||
buf.WriteByte(' ')
|
if typ == nil {
|
||||||
WriteType(buf, typ, qf)
|
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) {
|
func writePackage(buf *bytes.Buffer, pkg *Package, qf Qualifier) {
|
||||||
|
|
@ -353,15 +361,14 @@ func ObjectString(obj Object, qf Qualifier) string {
|
||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (obj *PkgName) 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 *Const) String() string { return ObjectString(obj, nil) }
|
||||||
func (obj *TypeName) 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 *Var) String() string { return ObjectString(obj, nil) }
|
||||||
func (obj *Func) 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 *Label) String() string { return ObjectString(obj, nil) }
|
func (obj *Builtin) 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 *Nil) String() string { return ObjectString(obj, nil) }
|
|
||||||
|
|
||||||
func writeFuncName(buf *bytes.Buffer, f *Func, qf Qualifier) {
|
func writeFuncName(buf *bytes.Buffer, f *Func, qf Qualifier) {
|
||||||
if f.typ != nil {
|
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:
|
case *Basic:
|
||||||
// Basic types are singletons except for the rune and byte
|
// Basic types are singletons except for the rune and byte
|
||||||
// aliases, thus we cannot solely rely on the x == y check
|
// 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 {
|
if y, ok := y.(*Basic); ok {
|
||||||
return x.kind == y.kind
|
return x.kind == y.kind
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,14 @@ import (
|
||||||
"unicode"
|
"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 {
|
type declInfo struct {
|
||||||
file *Scope // scope of file containing this declaration
|
file *Scope // scope of file containing this declaration
|
||||||
lhs []*Var // lhs of n:1 variable declarations, or nil
|
lhs []*Var // lhs of n:1 variable declarations, or nil
|
||||||
typ ast.Expr // type, or nil
|
typ ast.Expr // type, or nil
|
||||||
init ast.Expr // init/orig expression, or nil
|
init ast.Expr // init/orig expression, or nil
|
||||||
fdecl *ast.FuncDecl // func declaration, or nil
|
fdecl *ast.FuncDecl // func declaration, or nil
|
||||||
|
alias bool // type alias declaration
|
||||||
|
|
||||||
// The deps field tracks initialization expression dependencies.
|
// The deps field tracks initialization expression dependencies.
|
||||||
// As a special (overloaded) case, it also tracks dependencies of
|
// 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)
|
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:
|
case *ast.ValueSpec:
|
||||||
switch d.Tok {
|
switch d.Tok {
|
||||||
case token.CONST:
|
case token.CONST:
|
||||||
|
|
@ -347,7 +341,7 @@ func (check *Checker) collectObjects() {
|
||||||
|
|
||||||
case *ast.TypeSpec:
|
case *ast.TypeSpec:
|
||||||
obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
|
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:
|
default:
|
||||||
check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
|
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
|
// 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
|
// 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.
|
// anymore. We should eventually try to remove it altogether.
|
||||||
|
// TODO(gri) remove this
|
||||||
var gcCompatibilityMode bool
|
var gcCompatibilityMode bool
|
||||||
|
|
||||||
// TypeString returns the string representation of typ.
|
// 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)
|
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) {
|
switch obj := obj.(type) {
|
||||||
case *PkgName:
|
case *PkgName:
|
||||||
check.errorf(e.Pos(), "use of package %s not in selector", obj.name)
|
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 {
|
} else {
|
||||||
// anonymous field
|
// 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()
|
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)
|
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:
|
case *Basic:
|
||||||
if t == Typ[Invalid] {
|
if t == Typ[Invalid] {
|
||||||
// error was reported before
|
// error was reported before
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// unsafe.Pointer is treated like a regular pointer
|
// unsafe.Pointer is treated like a regular pointer
|
||||||
if t.kind == UnsafePointer {
|
if t.kind == UnsafePointer {
|
||||||
check.errorf(pos, "anonymous field type cannot be unsafe.Pointer")
|
check.errorf(pos, "anonymous field type cannot be unsafe.Pointer")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
add(f, name, true, pos)
|
|
||||||
|
|
||||||
case *Named:
|
case *Pointer:
|
||||||
// spec: "An embedded type must be specified as a type name
|
check.errorf(pos, "anonymous field type cannot be a pointer")
|
||||||
// T or as a pointer to a non-interface type name *T, and T
|
continue
|
||||||
// itself may not be a pointer type."
|
|
||||||
switch u := t.underlying.(type) {
|
case *Interface:
|
||||||
case *Basic:
|
if isPtr {
|
||||||
// unsafe.Pointer is treated like a regular pointer
|
check.errorf(pos, "anonymous field type cannot be a pointer to an interface")
|
||||||
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")
|
|
||||||
continue
|
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:
|
case *ast.Ident:
|
||||||
return e
|
return e
|
||||||
case *ast.StarExpr:
|
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:
|
case *ast.SelectorExpr:
|
||||||
return e.Sel
|
return e.Sel
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3691,7 +3691,7 @@ func checkSameType(t *testing.T, x, y interface{}) {
|
||||||
|
|
||||||
func TestArrayOf(t *testing.T) {
|
func TestArrayOf(t *testing.T) {
|
||||||
// check construction and use of type not in binary
|
// check construction and use of type not in binary
|
||||||
for _, table := range []struct {
|
tests := []struct {
|
||||||
n int
|
n int
|
||||||
value func(i int) interface{}
|
value func(i int) interface{}
|
||||||
comparable bool
|
comparable bool
|
||||||
|
|
@ -3769,7 +3769,9 @@ func TestArrayOf(t *testing.T) {
|
||||||
comparable: true,
|
comparable: true,
|
||||||
want: "[{0 0} {1 1} {2 2} {3 3} {4 4} {5 5} {6 6} {7 7} {8 8} {9 9}]",
|
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)))
|
at := ArrayOf(table.n, TypeOf(table.value(0)))
|
||||||
v := New(at).Elem()
|
v := New(at).Elem()
|
||||||
vok := New(at).Elem()
|
vok := New(at).Elem()
|
||||||
|
|
@ -4135,50 +4137,58 @@ func TestStructOfExportRules(t *testing.T) {
|
||||||
f()
|
f()
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, test := range []struct {
|
tests := []struct {
|
||||||
field StructField
|
field StructField
|
||||||
mustPanic bool
|
mustPanic bool
|
||||||
exported bool
|
exported bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
field: StructField{Name: "", Type: TypeOf(S1{})},
|
field: StructField{Name: "S1", Anonymous: true, Type: TypeOf(S1{})},
|
||||||
mustPanic: false,
|
exported: true,
|
||||||
exported: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: StructField{Name: "", Type: TypeOf((*S1)(nil))},
|
field: StructField{Name: "S1", Anonymous: true, Type: TypeOf((*S1)(nil))},
|
||||||
mustPanic: false,
|
exported: true,
|
||||||
exported: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: StructField{Name: "", Type: TypeOf(s2{})},
|
field: StructField{Name: "s2", Anonymous: true, 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"},
|
|
||||||
mustPanic: true,
|
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,
|
mustPanic: true,
|
||||||
exported: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: StructField{Name: "", Type: TypeOf(s2{}), PkgPath: "other/pkg"},
|
field: StructField{Name: "Name", Type: nil, PkgPath: ""},
|
||||||
mustPanic: true,
|
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,
|
mustPanic: true,
|
||||||
exported: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: StructField{Name: "S", Type: TypeOf(S1{})},
|
field: StructField{Name: "S", Type: TypeOf(S1{})},
|
||||||
|
|
@ -4186,81 +4196,68 @@ func TestStructOfExportRules(t *testing.T) {
|
||||||
exported: true,
|
exported: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: StructField{Name: "S", Type: TypeOf((*S1)(nil))},
|
field: StructField{Name: "S", Type: TypeOf((*S1)(nil))},
|
||||||
mustPanic: false,
|
exported: true,
|
||||||
exported: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: StructField{Name: "S", Type: TypeOf(s2{})},
|
field: StructField{Name: "S", Type: TypeOf(s2{})},
|
||||||
mustPanic: false,
|
exported: true,
|
||||||
exported: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: StructField{Name: "S", Type: TypeOf((*s2)(nil))},
|
field: StructField{Name: "S", Type: TypeOf((*s2)(nil))},
|
||||||
mustPanic: false,
|
exported: true,
|
||||||
exported: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: StructField{Name: "s", Type: TypeOf(S1{})},
|
field: StructField{Name: "s", Type: TypeOf(S1{})},
|
||||||
mustPanic: true,
|
mustPanic: true,
|
||||||
exported: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: StructField{Name: "s", Type: TypeOf((*S1)(nil))},
|
field: StructField{Name: "s", Type: TypeOf((*S1)(nil))},
|
||||||
mustPanic: true,
|
mustPanic: true,
|
||||||
exported: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: StructField{Name: "s", Type: TypeOf(s2{})},
|
field: StructField{Name: "s", Type: TypeOf(s2{})},
|
||||||
mustPanic: true,
|
mustPanic: true,
|
||||||
exported: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: StructField{Name: "s", Type: TypeOf((*s2)(nil))},
|
field: StructField{Name: "s", Type: TypeOf((*s2)(nil))},
|
||||||
mustPanic: true,
|
mustPanic: true,
|
||||||
exported: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: StructField{Name: "s", Type: TypeOf(S1{}), PkgPath: "other/pkg"},
|
field: StructField{Name: "s", Type: TypeOf(S1{}), PkgPath: "other/pkg"},
|
||||||
mustPanic: true, // TODO(sbinet): creating a name with a package path
|
mustPanic: true, // TODO(sbinet): creating a name with a package path
|
||||||
exported: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: StructField{Name: "s", Type: TypeOf((*S1)(nil)), PkgPath: "other/pkg"},
|
field: StructField{Name: "s", Type: TypeOf((*S1)(nil)), PkgPath: "other/pkg"},
|
||||||
mustPanic: true, // TODO(sbinet): creating a name with a package path
|
mustPanic: true, // TODO(sbinet): creating a name with a package path
|
||||||
exported: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: StructField{Name: "s", Type: TypeOf(s2{}), PkgPath: "other/pkg"},
|
field: StructField{Name: "s", Type: TypeOf(s2{}), PkgPath: "other/pkg"},
|
||||||
mustPanic: true, // TODO(sbinet): creating a name with a package path
|
mustPanic: true, // TODO(sbinet): creating a name with a package path
|
||||||
exported: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: StructField{Name: "s", Type: TypeOf((*s2)(nil)), PkgPath: "other/pkg"},
|
field: StructField{Name: "s", Type: TypeOf((*s2)(nil)), PkgPath: "other/pkg"},
|
||||||
mustPanic: true, // TODO(sbinet): creating a name with a package path
|
mustPanic: true, // TODO(sbinet): creating a name with a package path
|
||||||
exported: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: StructField{Name: "", Type: TypeOf(ΦType{})},
|
field: StructField{Name: "", Type: TypeOf(ΦType{})},
|
||||||
mustPanic: false,
|
mustPanic: true,
|
||||||
exported: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: StructField{Name: "", Type: TypeOf(φType{})},
|
field: StructField{Name: "", Type: TypeOf(φType{})},
|
||||||
mustPanic: false,
|
mustPanic: true,
|
||||||
exported: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: StructField{Name: "Φ", Type: TypeOf(0)},
|
field: StructField{Name: "Φ", Type: TypeOf(0)},
|
||||||
mustPanic: false,
|
exported: true,
|
||||||
exported: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: StructField{Name: "φ", Type: TypeOf(0)},
|
field: StructField{Name: "φ", Type: TypeOf(0)},
|
||||||
mustPanic: false,
|
exported: false,
|
||||||
exported: false,
|
|
||||||
},
|
},
|
||||||
} {
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
testPanic(i, test.mustPanic, func() {
|
testPanic(i, test.mustPanic, func() {
|
||||||
typ := StructOf([]StructField{test.field})
|
typ := StructOf([]StructField{test.field})
|
||||||
if typ == nil {
|
if typ == nil {
|
||||||
|
|
@ -4270,7 +4267,7 @@ func TestStructOfExportRules(t *testing.T) {
|
||||||
field := typ.Field(0)
|
field := typ.Field(0)
|
||||||
n := field.Name
|
n := field.Name
|
||||||
if n == "" {
|
if n == "" {
|
||||||
n = field.Type.Name()
|
panic("field.Name must not be empty")
|
||||||
}
|
}
|
||||||
exported := isExported(n)
|
exported := isExported(n)
|
||||||
if exported != test.exported {
|
if exported != test.exported {
|
||||||
|
|
@ -4348,7 +4345,7 @@ func TestStructOfGenericAlg(t *testing.T) {
|
||||||
{Name: "S1", Type: st1},
|
{Name: "S1", Type: st1},
|
||||||
})
|
})
|
||||||
|
|
||||||
for _, table := range []struct {
|
tests := []struct {
|
||||||
rt Type
|
rt Type
|
||||||
idx []int
|
idx []int
|
||||||
}{
|
}{
|
||||||
|
|
@ -4429,7 +4426,9 @@ func TestStructOfGenericAlg(t *testing.T) {
|
||||||
),
|
),
|
||||||
idx: []int{2},
|
idx: []int{2},
|
||||||
},
|
},
|
||||||
} {
|
}
|
||||||
|
|
||||||
|
for _, table := range tests {
|
||||||
v1 := New(table.rt).Elem()
|
v1 := New(table.rt).Elem()
|
||||||
v2 := New(table.rt).Elem()
|
v2 := New(table.rt).Elem()
|
||||||
|
|
||||||
|
|
@ -4531,18 +4530,21 @@ func TestStructOfWithInterface(t *testing.T) {
|
||||||
type Iface interface {
|
type Iface interface {
|
||||||
Get() int
|
Get() int
|
||||||
}
|
}
|
||||||
for i, table := range []struct {
|
tests := []struct {
|
||||||
|
name string
|
||||||
typ Type
|
typ Type
|
||||||
val Value
|
val Value
|
||||||
impl bool
|
impl bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
name: "StructI",
|
||||||
typ: TypeOf(StructI(want)),
|
typ: TypeOf(StructI(want)),
|
||||||
val: ValueOf(StructI(want)),
|
val: ValueOf(StructI(want)),
|
||||||
impl: true,
|
impl: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
typ: PtrTo(TypeOf(StructI(want))),
|
name: "StructI",
|
||||||
|
typ: PtrTo(TypeOf(StructI(want))),
|
||||||
val: ValueOf(func() interface{} {
|
val: ValueOf(func() interface{} {
|
||||||
v := StructI(want)
|
v := StructI(want)
|
||||||
return &v
|
return &v
|
||||||
|
|
@ -4550,7 +4552,8 @@ func TestStructOfWithInterface(t *testing.T) {
|
||||||
impl: true,
|
impl: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
typ: PtrTo(TypeOf(StructIPtr(want))),
|
name: "StructIPtr",
|
||||||
|
typ: PtrTo(TypeOf(StructIPtr(want))),
|
||||||
val: ValueOf(func() interface{} {
|
val: ValueOf(func() interface{} {
|
||||||
v := StructIPtr(want)
|
v := StructIPtr(want)
|
||||||
return &v
|
return &v
|
||||||
|
|
@ -4558,6 +4561,7 @@ func TestStructOfWithInterface(t *testing.T) {
|
||||||
impl: true,
|
impl: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
name: "StructIPtr",
|
||||||
typ: TypeOf(StructIPtr(want)),
|
typ: TypeOf(StructIPtr(want)),
|
||||||
val: ValueOf(StructIPtr(want)),
|
val: ValueOf(StructIPtr(want)),
|
||||||
impl: false,
|
impl: false,
|
||||||
|
|
@ -4567,13 +4571,16 @@ func TestStructOfWithInterface(t *testing.T) {
|
||||||
// val: ValueOf(StructI(want)),
|
// val: ValueOf(StructI(want)),
|
||||||
// impl: true,
|
// impl: true,
|
||||||
// },
|
// },
|
||||||
} {
|
}
|
||||||
|
|
||||||
|
for i, table := range tests {
|
||||||
rt := StructOf(
|
rt := StructOf(
|
||||||
[]StructField{
|
[]StructField{
|
||||||
{
|
{
|
||||||
Name: "",
|
Name: table.name,
|
||||||
PkgPath: "",
|
Anonymous: true,
|
||||||
Type: table.typ,
|
PkgPath: "",
|
||||||
|
Type: table.typ,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -6019,6 +6026,7 @@ func TestSwapper(t *testing.T) {
|
||||||
want: []pairPtr{{5, 6, &c}, {3, 4, &b}, {1, 2, &a}},
|
want: []pairPtr{{5, 6, &c}, {3, 4, &b}, {1, 2, &a}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
inStr := fmt.Sprint(tt.in)
|
inStr := fmt.Sprint(tt.in)
|
||||||
Swapper(tt.in)(tt.i, tt.j)
|
Swapper(tt.in)(tt.i, tt.j)
|
||||||
|
|
@ -6044,3 +6052,38 @@ func TestUnaddressableField(t *testing.T) {
|
||||||
lv.Set(rv)
|
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
|
// Struct field
|
||||||
type structField struct {
|
type structField struct {
|
||||||
name name // name is empty for embedded fields
|
name name // name is always non-empty
|
||||||
typ *rtype // type of field
|
typ *rtype // type of field
|
||||||
offset uintptr // byte offset of field within struct
|
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.
|
// structType represents a struct type.
|
||||||
|
|
@ -1215,16 +1223,8 @@ func (t *structType) Field(i int) (f StructField) {
|
||||||
}
|
}
|
||||||
p := &t.fields[i]
|
p := &t.fields[i]
|
||||||
f.Type = toType(p.typ)
|
f.Type = toType(p.typ)
|
||||||
if name := p.name.name(); name != "" {
|
f.Name = p.name.name()
|
||||||
f.Name = name
|
f.Anonymous = p.anon()
|
||||||
} else {
|
|
||||||
t := f.Type
|
|
||||||
if t.Kind() == Ptr {
|
|
||||||
t = t.Elem()
|
|
||||||
}
|
|
||||||
f.Name = t.Name()
|
|
||||||
f.Anonymous = true
|
|
||||||
}
|
|
||||||
if !p.name.isExported() {
|
if !p.name.isExported() {
|
||||||
f.PkgPath = p.name.pkgPath()
|
f.PkgPath = p.name.pkgPath()
|
||||||
if f.PkgPath == "" {
|
if f.PkgPath == "" {
|
||||||
|
|
@ -1234,7 +1234,7 @@ func (t *structType) Field(i int) (f StructField) {
|
||||||
if tag := p.name.tag(); tag != "" {
|
if tag := p.name.tag(); tag != "" {
|
||||||
f.Tag = StructTag(tag)
|
f.Tag = StructTag(tag)
|
||||||
}
|
}
|
||||||
f.Offset = p.offset
|
f.Offset = p.offset()
|
||||||
|
|
||||||
// NOTE(rsc): This is the only allocation in the interface
|
// NOTE(rsc): This is the only allocation in the interface
|
||||||
// presented by a reflect.Type. It would be nice to avoid,
|
// 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
|
visited[t] = true
|
||||||
for i := range t.fields {
|
for i := range t.fields {
|
||||||
f := &t.fields[i]
|
f := &t.fields[i]
|
||||||
// Find name and type for field f.
|
// Find name and (for anonymous field) type for field f.
|
||||||
var fname string
|
fname := f.name.name()
|
||||||
var ntyp *rtype
|
var ntyp *rtype
|
||||||
if name := f.name.name(); name != "" {
|
if f.anon() {
|
||||||
fname = name
|
|
||||||
} else {
|
|
||||||
// Anonymous field of type T or *T.
|
// Anonymous field of type T or *T.
|
||||||
// Name taken from type.
|
|
||||||
ntyp = f.typ
|
ntyp = f.typ
|
||||||
if ntyp.Kind() == Ptr {
|
if ntyp.Kind() == Ptr {
|
||||||
ntyp = ntyp.Elem().common()
|
ntyp = ntyp.Elem().common()
|
||||||
}
|
}
|
||||||
fname = ntyp.Name()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Does it match?
|
// Does it match?
|
||||||
|
|
@ -1390,14 +1386,12 @@ func (t *structType) FieldByName(name string) (f StructField, present bool) {
|
||||||
if name != "" {
|
if name != "" {
|
||||||
for i := range t.fields {
|
for i := range t.fields {
|
||||||
tf := &t.fields[i]
|
tf := &t.fields[i]
|
||||||
tfname := tf.name.name()
|
if tf.name.name() == name {
|
||||||
if tfname == "" {
|
|
||||||
hasAnon = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if tfname == name {
|
|
||||||
return t.Field(i), true
|
return t.Field(i), true
|
||||||
}
|
}
|
||||||
|
if tf.anon() {
|
||||||
|
hasAnon = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !hasAnon {
|
if !hasAnon {
|
||||||
|
|
@ -1694,7 +1688,7 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool {
|
||||||
if cmpTags && tf.name.tag() != vf.name.tag() {
|
if cmpTags && tf.name.tag() != vf.name.tag() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if tf.offset != vf.offset {
|
if tf.offsetAnon != vf.offsetAnon {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if !tf.name.isExported() {
|
if !tf.name.isExported() {
|
||||||
|
|
@ -2403,6 +2397,9 @@ func StructOf(fields []StructField) Type {
|
||||||
lastzero := uintptr(0)
|
lastzero := uintptr(0)
|
||||||
repr = append(repr, "struct {"...)
|
repr = append(repr, "struct {"...)
|
||||||
for i, field := range fields {
|
for i, field := range fields {
|
||||||
|
if field.Name == "" {
|
||||||
|
panic("reflect.StructOf: field " + strconv.Itoa(i) + " has no name")
|
||||||
|
}
|
||||||
if field.Type == nil {
|
if field.Type == nil {
|
||||||
panic("reflect.StructOf: field " + strconv.Itoa(i) + " has no type")
|
panic("reflect.StructOf: field " + strconv.Itoa(i) + " has no type")
|
||||||
}
|
}
|
||||||
|
|
@ -2415,13 +2412,11 @@ func StructOf(fields []StructField) Type {
|
||||||
hasPtr = true
|
hasPtr = true
|
||||||
}
|
}
|
||||||
|
|
||||||
name := ""
|
|
||||||
// Update string and hash
|
// Update string and hash
|
||||||
if f.name.nameLen() > 0 {
|
name := f.name.name()
|
||||||
hash = fnv1(hash, []byte(f.name.name())...)
|
hash = fnv1(hash, []byte(name)...)
|
||||||
repr = append(repr, (" " + f.name.name())...)
|
repr = append(repr, (" " + name)...)
|
||||||
name = f.name.name()
|
if f.anon() {
|
||||||
} else {
|
|
||||||
// Embedded field
|
// Embedded field
|
||||||
if f.typ.Kind() == Ptr {
|
if f.typ.Kind() == Ptr {
|
||||||
// Embedded ** and *interface{} are illegal
|
// Embedded ** and *interface{} are illegal
|
||||||
|
|
@ -2429,11 +2424,7 @@ func StructOf(fields []StructField) Type {
|
||||||
if k := elem.Kind(); k == Ptr || k == Interface {
|
if k := elem.Kind(); k == Ptr || k == Interface {
|
||||||
panic("reflect.StructOf: illegal anonymous field type " + ft.String())
|
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() {
|
switch f.typ.Kind() {
|
||||||
case Interface:
|
case Interface:
|
||||||
|
|
@ -2565,11 +2556,12 @@ func StructOf(fields []StructField) Type {
|
||||||
comparable = comparable && (ft.alg.equal != nil)
|
comparable = comparable && (ft.alg.equal != nil)
|
||||||
hashable = hashable && (ft.alg.hash != nil)
|
hashable = hashable && (ft.alg.hash != nil)
|
||||||
|
|
||||||
f.offset = align(size, uintptr(ft.align))
|
offset := align(size, uintptr(ft.align))
|
||||||
if ft.align > typalign {
|
if ft.align > typalign {
|
||||||
typalign = ft.align
|
typalign = ft.align
|
||||||
}
|
}
|
||||||
size = f.offset + ft.size
|
size = offset + ft.size
|
||||||
|
f.offsetAnon |= offset << 1
|
||||||
|
|
||||||
if ft.size == 0 {
|
if ft.size == 0 {
|
||||||
lastzero = size
|
lastzero = size
|
||||||
|
|
@ -2761,7 +2753,7 @@ func StructOf(fields []StructField) Type {
|
||||||
typ.alg.hash = func(p unsafe.Pointer, seed uintptr) uintptr {
|
typ.alg.hash = func(p unsafe.Pointer, seed uintptr) uintptr {
|
||||||
o := seed
|
o := seed
|
||||||
for _, ft := range typ.fields {
|
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)
|
o = ft.typ.alg.hash(pi, o)
|
||||||
}
|
}
|
||||||
return o
|
return o
|
||||||
|
|
@ -2771,8 +2763,8 @@ func StructOf(fields []StructField) Type {
|
||||||
if comparable {
|
if comparable {
|
||||||
typ.alg.equal = func(p, q unsafe.Pointer) bool {
|
typ.alg.equal = func(p, q unsafe.Pointer) bool {
|
||||||
for _, ft := range typ.fields {
|
for _, ft := range typ.fields {
|
||||||
pi := unsafe.Pointer(uintptr(p) + ft.offset)
|
pi := unsafe.Pointer(uintptr(p) + ft.offset())
|
||||||
qi := unsafe.Pointer(uintptr(q) + ft.offset)
|
qi := unsafe.Pointer(uintptr(q) + ft.offset())
|
||||||
if !ft.typ.alg.equal(pi, qi) {
|
if !ft.typ.alg.equal(pi, qi) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -2794,25 +2786,27 @@ func StructOf(fields []StructField) Type {
|
||||||
}
|
}
|
||||||
|
|
||||||
func runtimeStructField(field StructField) structField {
|
func runtimeStructField(field StructField) structField {
|
||||||
exported := field.PkgPath == ""
|
if field.PkgPath != "" {
|
||||||
if field.Name == "" {
|
panic("reflect.StructOf: StructOf does not allow unexported fields")
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = 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{
|
return structField{
|
||||||
name: newName(field.Name, string(field.Tag), field.PkgPath, exported),
|
name: newName(field.Name, string(field.Tag), "", true),
|
||||||
typ: field.Type.common(),
|
typ: field.Type.common(),
|
||||||
offset: 0,
|
offsetAnon: offsetAnon,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2835,7 +2829,7 @@ func typeptrdata(t *rtype) uintptr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f := st.fields[field]
|
f := st.fields[field]
|
||||||
return f.offset + f.typ.ptrdata
|
return f.offset() + f.typ.ptrdata
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic("reflect.typeptrdata: unexpected type, " + t.String())
|
panic("reflect.typeptrdata: unexpected type, " + t.String())
|
||||||
|
|
@ -3209,7 +3203,7 @@ func addTypeBits(bv *bitVector, offset uintptr, t *rtype) {
|
||||||
tt := (*structType)(unsafe.Pointer(t))
|
tt := (*structType)(unsafe.Pointer(t))
|
||||||
for i := range tt.fields {
|
for i := range tt.fields {
|
||||||
f := &tt.fields[i]
|
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())
|
fl := v.flag&(flagStickyRO|flagIndir|flagAddr) | flag(typ.Kind())
|
||||||
// Using an unexported field forces flagRO.
|
// Using an unexported field forces flagRO.
|
||||||
if !field.name.isExported() {
|
if !field.name.isExported() {
|
||||||
if field.name.name() == "" {
|
if field.anon() {
|
||||||
fl |= flagEmbedRO
|
fl |= flagEmbedRO
|
||||||
} else {
|
} else {
|
||||||
fl |= flagStickyRO
|
fl |= flagStickyRO
|
||||||
|
|
@ -774,7 +774,7 @@ func (v Value) Field(i int) Value {
|
||||||
// In the former case, we want v.ptr + offset.
|
// In the former case, we want v.ptr + offset.
|
||||||
// In the latter case, we must have field.offset = 0,
|
// In the latter case, we must have field.offset = 0,
|
||||||
// so v.ptr + field.offset is still okay.
|
// 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}
|
return Value{typ, ptr, fl}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -531,7 +531,7 @@ func cgoCheckArg(t *_type, p unsafe.Pointer, indir, top bool, msg string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, f := range st.fields {
|
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:
|
case kindPtr, kindUnsafePointer:
|
||||||
if indir {
|
if indir {
|
||||||
|
|
|
||||||
|
|
@ -390,9 +390,13 @@ type ptrtype struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type structfield struct {
|
type structfield struct {
|
||||||
name name
|
name name
|
||||||
typ *_type
|
typ *_type
|
||||||
offset uintptr
|
offsetAnon uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *structfield) offset() uintptr {
|
||||||
|
return f.offsetAnon >> 1
|
||||||
}
|
}
|
||||||
|
|
||||||
type structtype struct {
|
type structtype struct {
|
||||||
|
|
@ -650,7 +654,7 @@ func typesEqual(t, v *_type) bool {
|
||||||
if tf.name.tag() != vf.name.tag() {
|
if tf.name.tag() != vf.name.tag() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if tf.offset != vf.offset {
|
if tf.offsetAnon != vf.offsetAnon {
|
||||||
return false
|
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…
Add table
Add a link
Reference in a new issue