mirror of
https://github.com/golang/go.git
synced 2026-01-30 23:52:05 +03:00
Compare commits
47 Commits
go1.8.2
...
dev.typeal
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a5ef1501d | ||
|
|
352996a381 | ||
|
|
bb5055d6f1 | ||
|
|
439c0c8be8 | ||
|
|
e396667ba3 | ||
|
|
daf6706f37 | ||
|
|
958c64bbab | ||
|
|
195e20a976 | ||
|
|
f55bc1c4eb | ||
|
|
51f508bb4a | ||
|
|
243dee1737 | ||
|
|
a43c0d2dc8 | ||
|
|
1054085dcf | ||
|
|
18a13d373a | ||
|
|
6efa2f22ac | ||
|
|
fb9770f09b | ||
|
|
b6a8fc8d8c | ||
|
|
1ba29926f3 | ||
|
|
a4c18f063b | ||
|
|
0954fdd51e | ||
|
|
3b4fc5d1c6 | ||
|
|
f8b4123613 | ||
|
|
9ecc3ee252 | ||
|
|
49b7af8a30 | ||
|
|
9bbb07ddec | ||
|
|
43c7094386 | ||
|
|
9657e0b077 | ||
|
|
de2e5459ae | ||
|
|
9259f3073a | ||
|
|
5d92916770 | ||
|
|
a7c884efc1 | ||
|
|
5802cfd900 | ||
|
|
d7cabd40dd | ||
|
|
cc2dcce3d7 | ||
|
|
5c160b28ba | ||
|
|
b2386dffa1 | ||
|
|
ac8421f9a5 | ||
|
|
f011e0c6c3 | ||
|
|
49de5f0351 | ||
|
|
5ceec42dc0 | ||
|
|
aa1f0681bc | ||
|
|
c80748e389 | ||
|
|
80d8b69e95 | ||
|
|
a917097b5e | ||
|
|
3e11940437 | ||
|
|
e0a05c274a | ||
|
|
2e5116bd99 |
@@ -22,8 +22,6 @@ using the go <code>tool</code> subcommand, such as <code>go tool vet</code>.
|
||||
This style of invocation allows, for instance, checking a single source file
|
||||
rather than an entire package: <code>go tool vet myprogram.go</code> as
|
||||
compared to <code>go vet mypackage</code>.
|
||||
Some of the commands, such as <code>yacc</code>, are accessible only through
|
||||
the go <code>tool</code> subcommand.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -95,12 +93,6 @@ gofmt</a> command with more general options.</td>
|
||||
calls whose arguments do not align with the format string.</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><a href="/cmd/yacc/">yacc</a></td>
|
||||
<td> </td>
|
||||
<td>Yacc is a version of yacc that generates parsers implemented in Go.</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -56,6 +56,13 @@ See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.8.2">Go
|
||||
1.8.2 milestone</a> on our issue tracker for details.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
go1.8.3 (released 2017/05/24) includes fixes to the compiler, runtime,
|
||||
documentation, and the <code>database/sql</code> package.
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.8.3">Go
|
||||
1.8.3 milestone</a> on our issue tracker for details.
|
||||
</p>
|
||||
|
||||
<h2 id="go1.7">go1.7 (released 2016/08/15)</h2>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!--{
|
||||
"Title": "The Go Programming Language Specification",
|
||||
"Subtitle": "Version of November 18, 2016",
|
||||
"Subtitle": "Version of January 31, 2017",
|
||||
"Path": "/ref/spec"
|
||||
}-->
|
||||
|
||||
@@ -738,7 +738,7 @@ The method set of any other type <code>T</code> consists of all
|
||||
The method set of the corresponding <a href="#Pointer_types">pointer type</a> <code>*T</code>
|
||||
is the set of all methods declared with receiver <code>*T</code> or <code>T</code>
|
||||
(that is, it also contains the method set of <code>T</code>).
|
||||
Further rules apply to structs containing anonymous fields, as described
|
||||
Further rules apply to structs containing embedded fields, as described
|
||||
in the section on <a href="#Struct_types">struct types</a>.
|
||||
Any other type has an empty method set.
|
||||
In a method set, each method must have a
|
||||
@@ -947,16 +947,16 @@ Moreover, the inner slices must be initialized individually.
|
||||
<p>
|
||||
A struct is a sequence of named elements, called fields, each of which has a
|
||||
name and a type. Field names may be specified explicitly (IdentifierList) or
|
||||
implicitly (AnonymousField).
|
||||
implicitly (EmbeddedField).
|
||||
Within a struct, non-<a href="#Blank_identifier">blank</a> field names must
|
||||
be <a href="#Uniqueness_of_identifiers">unique</a>.
|
||||
</p>
|
||||
|
||||
<pre class="ebnf">
|
||||
StructType = "struct" "{" { FieldDecl ";" } "}" .
|
||||
FieldDecl = (IdentifierList Type | AnonymousField) [ Tag ] .
|
||||
AnonymousField = [ "*" ] TypeName .
|
||||
Tag = string_lit .
|
||||
StructType = "struct" "{" { FieldDecl ";" } "}" .
|
||||
FieldDecl = (IdentifierList Type | EmbeddedField) [ Tag ] .
|
||||
EmbeddedField = [ "*" ] TypeName .
|
||||
Tag = string_lit .
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
@@ -974,16 +974,15 @@ struct {
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
A field declared with a type but no explicit field name is an <i>anonymous field</i>,
|
||||
also called an <i>embedded</i> field or an embedding of the type in the struct.
|
||||
An embedded type must be specified as
|
||||
A field declared with a type but no explicit field name is called an <i>embedded field</i>.
|
||||
An embedded field must be specified as
|
||||
a type name <code>T</code> or as a pointer to a non-interface type name <code>*T</code>,
|
||||
and <code>T</code> itself may not be
|
||||
a pointer type. The unqualified type name acts as the field name.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
// A struct with four anonymous fields of type T1, *T2, P.T3 and *P.T4
|
||||
// A struct with four embedded fields of types T1, *T2, P.T3 and *P.T4
|
||||
struct {
|
||||
T1 // field name is T1
|
||||
*T2 // field name is T2
|
||||
@@ -1000,15 +999,15 @@ in a struct type:
|
||||
|
||||
<pre>
|
||||
struct {
|
||||
T // conflicts with anonymous field *T and *P.T
|
||||
*T // conflicts with anonymous field T and *P.T
|
||||
*P.T // conflicts with anonymous field T and *T
|
||||
T // conflicts with embedded field *T and *P.T
|
||||
*T // conflicts with embedded field T and *P.T
|
||||
*P.T // conflicts with embedded field T and *T
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
A field or <a href="#Method_declarations">method</a> <code>f</code> of an
|
||||
anonymous field in a struct <code>x</code> is called <i>promoted</i> if
|
||||
embedded field in a struct <code>x</code> is called <i>promoted</i> if
|
||||
<code>x.f</code> is a legal <a href="#Selectors">selector</a> that denotes
|
||||
that field or method <code>f</code>.
|
||||
</p>
|
||||
@@ -1025,7 +1024,7 @@ promoted methods are included in the method set of the struct as follows:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
If <code>S</code> contains an anonymous field <code>T</code>,
|
||||
If <code>S</code> contains an embedded field <code>T</code>,
|
||||
the <a href="#Method_sets">method sets</a> of <code>S</code>
|
||||
and <code>*S</code> both include promoted methods with receiver
|
||||
<code>T</code>. The method set of <code>*S</code> also
|
||||
@@ -1033,7 +1032,7 @@ promoted methods are included in the method set of the struct as follows:
|
||||
</li>
|
||||
|
||||
<li>
|
||||
If <code>S</code> contains an anonymous field <code>*T</code>,
|
||||
If <code>S</code> contains an embedded field <code>*T</code>,
|
||||
the method sets of <code>S</code> and <code>*S</code> both
|
||||
include promoted methods with receiver <code>T</code> or
|
||||
<code>*T</code>.
|
||||
@@ -1434,8 +1433,8 @@ literal structure and corresponding components have identical types. In detail:
|
||||
<li>Two struct types are identical if they have the same sequence of fields,
|
||||
and if corresponding fields have the same names, and identical types,
|
||||
and identical tags.
|
||||
Two anonymous fields are considered to have the same name. Lower-case field
|
||||
names from different packages are always different.</li>
|
||||
<a href="#Exported_identifiers">Non-exported</a> field names from different
|
||||
packages are always different.</li>
|
||||
|
||||
<li>Two pointer types are identical if they have identical base types.</li>
|
||||
|
||||
@@ -1445,8 +1444,9 @@ literal structure and corresponding components have identical types. In detail:
|
||||
Parameter and result names are not required to match.</li>
|
||||
|
||||
<li>Two interface types are identical if they have the same set of methods
|
||||
with the same names and identical function types. Lower-case method names from
|
||||
different packages are always different. The order of the methods is irrelevant.</li>
|
||||
with the same names and identical function types.
|
||||
<a href="#Exported_identifiers">Non-exported</a> method names from different
|
||||
packages are always different. The order of the methods is irrelevant.</li>
|
||||
|
||||
<li>Two map types are identical if they have identical key and value types.</li>
|
||||
|
||||
@@ -1891,7 +1891,7 @@ type NewMutex Mutex
|
||||
type PtrMutex *Mutex
|
||||
|
||||
// The method set of *PrintableMutex contains the methods
|
||||
// Lock and Unlock bound to its anonymous field Mutex.
|
||||
// Lock and Unlock bound to its embedded field Mutex.
|
||||
type PrintableMutex struct {
|
||||
Mutex
|
||||
}
|
||||
@@ -2492,13 +2492,13 @@ If <code>x</code> is a package name, see the section on
|
||||
A selector <code>f</code> may denote a field or method <code>f</code> of
|
||||
a type <code>T</code>, or it may refer
|
||||
to a field or method <code>f</code> of a nested
|
||||
<a href="#Struct_types">anonymous field</a> of <code>T</code>.
|
||||
The number of anonymous fields traversed
|
||||
<a href="#Struct_types">embedded field</a> of <code>T</code>.
|
||||
The number of embedded fields traversed
|
||||
to reach <code>f</code> is called its <i>depth</i> in <code>T</code>.
|
||||
The depth of a field or method <code>f</code>
|
||||
declared in <code>T</code> is zero.
|
||||
The depth of a field or method <code>f</code> declared in
|
||||
an anonymous field <code>A</code> in <code>T</code> is the
|
||||
an embedded field <code>A</code> in <code>T</code> is the
|
||||
depth of <code>f</code> in <code>A</code> plus one.
|
||||
</p>
|
||||
|
||||
|
||||
@@ -27,4 +27,5 @@ func Init() {
|
||||
gc.Thearch.SSAMarkMoves = ssaMarkMoves
|
||||
gc.Thearch.SSAGenValue = ssaGenValue
|
||||
gc.Thearch.SSAGenBlock = ssaGenBlock
|
||||
gc.Thearch.ZeroAuto = zeroAuto
|
||||
}
|
||||
|
||||
@@ -166,6 +166,27 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64, ax *uint32, x0 *uin
|
||||
return p
|
||||
}
|
||||
|
||||
func zeroAuto(n *gc.Node, pp *obj.Prog) {
|
||||
// Note: this code must not clobber any registers.
|
||||
op := x86.AMOVQ
|
||||
if gc.Widthptr == 4 {
|
||||
op = x86.AMOVL
|
||||
}
|
||||
sym := gc.Linksym(n.Sym)
|
||||
size := n.Type.Size()
|
||||
for i := int64(0); i < size; i += int64(gc.Widthptr) {
|
||||
p := gc.AddAsmAfter(op, pp)
|
||||
pp = p
|
||||
p.From.Type = obj.TYPE_CONST
|
||||
p.From.Offset = 0
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Name = obj.NAME_AUTO
|
||||
p.To.Reg = x86.REG_SP
|
||||
p.To.Offset = n.Xoffset + i
|
||||
p.To.Sym = sym
|
||||
}
|
||||
}
|
||||
|
||||
func ginsnop() {
|
||||
// This is actually not the x86 NOP anymore,
|
||||
// but at the point where it gets used, AX is dead
|
||||
|
||||
@@ -21,4 +21,5 @@ func Init() {
|
||||
gc.Thearch.SSAMarkMoves = func(s *gc.SSAGenState, b *ssa.Block) {}
|
||||
gc.Thearch.SSAGenValue = ssaGenValue
|
||||
gc.Thearch.SSAGenBlock = ssaGenBlock
|
||||
gc.Thearch.ZeroAuto = zeroAuto
|
||||
}
|
||||
|
||||
@@ -92,6 +92,27 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64, r0 *uint32) *obj.Pr
|
||||
|
||||
return p
|
||||
}
|
||||
func zeroAuto(n *gc.Node, pp *obj.Prog) {
|
||||
// Note: this code must not clobber any registers.
|
||||
sym := gc.Linksym(n.Sym)
|
||||
size := n.Type.Size()
|
||||
p := gc.Prog(arm.AMOVW)
|
||||
p.From.Type = obj.TYPE_CONST
|
||||
p.From.Offset = 0
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = arm.REGTMP
|
||||
for i := int64(0); i < size; i += 4 {
|
||||
p := gc.AddAsmAfter(arm.AMOVW, pp)
|
||||
pp = p
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = arm.REGTMP
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Name = obj.NAME_AUTO
|
||||
p.To.Reg = arm.REGSP
|
||||
p.To.Offset = n.Xoffset + i
|
||||
p.To.Sym = sym
|
||||
}
|
||||
}
|
||||
|
||||
func ginsnop() {
|
||||
p := gc.Prog(arm.AAND)
|
||||
|
||||
@@ -21,4 +21,5 @@ func Init() {
|
||||
gc.Thearch.SSAMarkMoves = func(s *gc.SSAGenState, b *ssa.Block) {}
|
||||
gc.Thearch.SSAGenValue = ssaGenValue
|
||||
gc.Thearch.SSAGenBlock = ssaGenBlock
|
||||
gc.Thearch.ZeroAuto = zeroAuto
|
||||
}
|
||||
|
||||
@@ -103,6 +103,23 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog {
|
||||
return p
|
||||
}
|
||||
|
||||
func zeroAuto(n *gc.Node, pp *obj.Prog) {
|
||||
// Note: this code must not clobber any registers.
|
||||
sym := gc.Linksym(n.Sym)
|
||||
size := n.Type.Size()
|
||||
for i := int64(0); i < size; i += 8 {
|
||||
p := gc.AddAsmAfter(arm64.AMOVD, pp)
|
||||
pp = p
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = arm64.REGZERO
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Name = obj.NAME_AUTO
|
||||
p.To.Reg = arm64.REGSP
|
||||
p.To.Offset = n.Xoffset + i
|
||||
p.To.Sym = sym
|
||||
}
|
||||
}
|
||||
|
||||
func ginsnop() {
|
||||
p := gc.Prog(arm64.AHINT)
|
||||
p.From.Type = obj.TYPE_CONST
|
||||
|
||||
@@ -74,7 +74,13 @@ func widstruct(errtype *Type, t *Type, o int64, flag int) int64 {
|
||||
lastzero = o
|
||||
}
|
||||
o += w
|
||||
if o >= Thearch.MAXWIDTH {
|
||||
maxwidth := Thearch.MAXWIDTH
|
||||
// On 32-bit systems, reflect tables impose an additional constraint
|
||||
// that each field start offset must fit in 31 bits.
|
||||
if maxwidth < 1<<32 {
|
||||
maxwidth = 1<<31 - 1
|
||||
}
|
||||
if o >= maxwidth {
|
||||
yyerror("type %L too large", errtype)
|
||||
o = 8 // small but nonzero
|
||||
}
|
||||
|
||||
@@ -140,11 +140,12 @@ const debugFormat = false // default: false
|
||||
const forceObjFileStability = true
|
||||
|
||||
// Current export format version. Increase with each format change.
|
||||
// 3: added aliasTag and export of aliases
|
||||
// 2: removed unused bool in ODCL export
|
||||
// 4: type name objects support type aliases, uses aliasTag
|
||||
// 3: Go1.8 encoding (same as version 2, aliasTag defined but never used)
|
||||
// 2: removed unused bool in ODCL export (compiler only)
|
||||
// 1: header format change (more regular), export package for _ struct fields
|
||||
// 0: Go1.7 encoding
|
||||
const exportVersion = 3
|
||||
const exportVersion = 4
|
||||
|
||||
// exportInlined enables the export of inlined function bodies and related
|
||||
// dependencies. The compiler should work w/o any loss of functionality with
|
||||
@@ -351,8 +352,8 @@ func export(out *bufio.Writer, trace bool) int {
|
||||
p.tracef("\n")
|
||||
}
|
||||
|
||||
if sym.Flags&SymAlias != 0 {
|
||||
Fatalf("exporter: unexpected alias %v in inlined function body", sym)
|
||||
if sym.isAlias() {
|
||||
Fatalf("exporter: unexpected type alias %v in inlined function body", sym)
|
||||
}
|
||||
|
||||
p.obj(sym)
|
||||
@@ -446,30 +447,6 @@ func unidealType(typ *Type, val Val) *Type {
|
||||
}
|
||||
|
||||
func (p *exporter) obj(sym *Sym) {
|
||||
if sym.Flags&SymAlias != 0 {
|
||||
p.tag(aliasTag)
|
||||
p.pos(nil) // TODO(gri) fix position information
|
||||
// Aliases can only be exported from the package that
|
||||
// declares them (aliases to aliases are resolved to the
|
||||
// original object, and so are uses of aliases in inlined
|
||||
// exported function bodies). Thus, we only need the alias
|
||||
// name without package qualification.
|
||||
if sym.Pkg != localpkg {
|
||||
Fatalf("exporter: export of non-local alias: %v", sym)
|
||||
}
|
||||
p.string(sym.Name)
|
||||
orig := sym.Def.Sym
|
||||
if orig.Flags&SymAlias != 0 {
|
||||
Fatalf("exporter: original object %v marked as alias", sym)
|
||||
}
|
||||
p.qualifiedName(orig)
|
||||
return
|
||||
}
|
||||
|
||||
if sym != sym.Def.Sym {
|
||||
Fatalf("exporter: exported object %v is not original %v", sym, sym.Def.Sym)
|
||||
}
|
||||
|
||||
// Exported objects may be from different packages because they
|
||||
// may be re-exported via an exported alias or as dependencies in
|
||||
// exported inlined function bodies. Thus, exported object names
|
||||
@@ -509,7 +486,13 @@ func (p *exporter) obj(sym *Sym) {
|
||||
Fatalf("exporter: export of incomplete type %v", sym)
|
||||
}
|
||||
|
||||
p.tag(typeTag)
|
||||
if sym.isAlias() {
|
||||
p.tag(aliasTag)
|
||||
p.pos(n)
|
||||
p.qualifiedName(sym)
|
||||
} else {
|
||||
p.tag(typeTag)
|
||||
}
|
||||
p.typ(t)
|
||||
|
||||
case ONAME:
|
||||
@@ -868,19 +851,29 @@ func (p *exporter) methodList(t *Type) {
|
||||
|
||||
func (p *exporter) method(m *Field) {
|
||||
p.pos(m.Nname)
|
||||
p.fieldName(m)
|
||||
p.methodName(m.Sym)
|
||||
p.paramList(m.Type.Params(), false)
|
||||
p.paramList(m.Type.Results(), false)
|
||||
}
|
||||
|
||||
// fieldName is like qualifiedName but it doesn't record the package for exported names.
|
||||
func (p *exporter) fieldName(t *Field) {
|
||||
name := t.Sym.Name
|
||||
if t.Embedded != 0 {
|
||||
name = "" // anonymous field
|
||||
if bname := basetypeName(t.Type); bname != "" && !exportname(bname) {
|
||||
// anonymous field with unexported base type name
|
||||
name = "?" // unexported name to force export of package
|
||||
// anonymous field - we distinguish between 3 cases:
|
||||
// 1) field name matches base type name and is exported
|
||||
// 2) field name matches base type name and is not exported
|
||||
// 3) field name doesn't match base type name (alias name)
|
||||
bname := basetypeName(t.Type)
|
||||
if name == bname {
|
||||
if exportname(name) {
|
||||
name = "" // 1) we don't need to know the field name or package
|
||||
} else {
|
||||
name = "?" // 2) use unexported name "?" to force package export
|
||||
}
|
||||
} else {
|
||||
// 3) indicate alias and export name as is
|
||||
// (this requires an extra "@" but this is a rare case)
|
||||
p.string("@")
|
||||
}
|
||||
}
|
||||
p.string(name)
|
||||
@@ -889,16 +882,23 @@ func (p *exporter) fieldName(t *Field) {
|
||||
}
|
||||
}
|
||||
|
||||
// methodName is like qualifiedName but it doesn't record the package for exported names.
|
||||
func (p *exporter) methodName(sym *Sym) {
|
||||
p.string(sym.Name)
|
||||
if !exportname(sym.Name) {
|
||||
p.pkg(sym.Pkg)
|
||||
}
|
||||
}
|
||||
|
||||
func basetypeName(t *Type) string {
|
||||
s := t.Sym
|
||||
if s == nil && t.IsPtr() {
|
||||
s = t.Elem().Sym // deref
|
||||
}
|
||||
// s should exist, but be conservative
|
||||
if s != nil {
|
||||
return s.Name
|
||||
}
|
||||
return ""
|
||||
return "" // unnamed type
|
||||
}
|
||||
|
||||
func (p *exporter) paramList(params *Type, numbered bool) {
|
||||
@@ -1797,7 +1797,7 @@ const (
|
||||
nilTag
|
||||
unknownTag // not used by gc (only appears in packages with errors)
|
||||
|
||||
// Aliases
|
||||
// Type aliases
|
||||
aliasTag
|
||||
)
|
||||
|
||||
@@ -1835,7 +1835,7 @@ var tagString = [...]string{
|
||||
-nilTag: "nil",
|
||||
-unknownTag: "unknown",
|
||||
|
||||
// Aliases
|
||||
// Type aliases
|
||||
-aliasTag: "alias",
|
||||
}
|
||||
|
||||
@@ -1889,7 +1889,7 @@ func predeclared() []*Type {
|
||||
Types[TCOMPLEX128],
|
||||
Types[TSTRING],
|
||||
|
||||
// aliases
|
||||
// basic type aliases
|
||||
bytetype,
|
||||
runetype,
|
||||
|
||||
|
||||
@@ -86,10 +86,10 @@ func Import(in *bufio.Reader) {
|
||||
|
||||
// read version specific flags - extend as necessary
|
||||
switch p.version {
|
||||
// case 4:
|
||||
// case 5:
|
||||
// ...
|
||||
// fallthrough
|
||||
case 3, 2, 1:
|
||||
case 4, 3, 2, 1:
|
||||
p.debugFormat = p.rawStringln(p.rawByte()) == "debug"
|
||||
p.trackAllTypes = p.bool()
|
||||
p.posInfoFormat = p.bool()
|
||||
@@ -317,6 +317,12 @@ func (p *importer) obj(tag int) {
|
||||
val := p.value(typ)
|
||||
importconst(sym, idealType(typ), nodlit(val))
|
||||
|
||||
case aliasTag:
|
||||
p.pos()
|
||||
sym := p.qualifiedName()
|
||||
typ := p.typ()
|
||||
importalias(sym, typ)
|
||||
|
||||
case typeTag:
|
||||
p.typ()
|
||||
|
||||
@@ -356,17 +362,6 @@ func (p *importer) obj(tag int) {
|
||||
}
|
||||
}
|
||||
|
||||
case aliasTag:
|
||||
p.pos()
|
||||
alias := importpkg.Lookup(p.string())
|
||||
orig := p.qualifiedName()
|
||||
|
||||
// Although the protocol allows the alias to precede the original,
|
||||
// this never happens in files produced by gc.
|
||||
alias.Flags |= SymAlias
|
||||
alias.Def = orig.Def
|
||||
importsym(alias, orig.Def.Op)
|
||||
|
||||
default:
|
||||
formatErrorf("unexpected object (tag = %d)", tag)
|
||||
}
|
||||
@@ -473,14 +468,7 @@ func (p *importer) typ() *Type {
|
||||
result := p.paramList()
|
||||
nointerface := p.bool()
|
||||
|
||||
base := recv[0].Type
|
||||
star := false
|
||||
if base.IsPtr() {
|
||||
base = base.Elem()
|
||||
star = true
|
||||
}
|
||||
|
||||
n := methodname0(sym, star, base.Sym)
|
||||
n := newfuncname(methodname(sym, recv[0].Type))
|
||||
n.Type = functypefield(recv[0], params, result)
|
||||
checkwidth(n.Type)
|
||||
addmethod(sym, n.Type, false, nointerface)
|
||||
@@ -583,19 +571,22 @@ func (p *importer) fieldList() (fields []*Field) {
|
||||
|
||||
func (p *importer) field() *Field {
|
||||
p.pos()
|
||||
sym := p.fieldName()
|
||||
sym, alias := p.fieldName()
|
||||
typ := p.typ()
|
||||
note := p.string()
|
||||
|
||||
f := newField()
|
||||
if sym.Name == "" {
|
||||
// anonymous field - typ must be T or *T and T must be a type name
|
||||
// anonymous field: typ must be T or *T and T must be a type name
|
||||
s := typ.Sym
|
||||
if s == nil && typ.IsPtr() {
|
||||
s = typ.Elem().Sym // deref
|
||||
}
|
||||
sym = sym.Pkg.Lookup(s.Name)
|
||||
f.Embedded = 1
|
||||
} else if alias {
|
||||
// anonymous field: we have an explicit name because it's a type alias
|
||||
f.Embedded = 1
|
||||
}
|
||||
|
||||
f.Sym = sym
|
||||
@@ -618,7 +609,7 @@ func (p *importer) methodList() (methods []*Field) {
|
||||
|
||||
func (p *importer) method() *Field {
|
||||
p.pos()
|
||||
sym := p.fieldName()
|
||||
sym := p.methodName()
|
||||
params := p.paramList()
|
||||
result := p.paramList()
|
||||
|
||||
@@ -629,18 +620,44 @@ func (p *importer) method() *Field {
|
||||
return f
|
||||
}
|
||||
|
||||
func (p *importer) fieldName() *Sym {
|
||||
func (p *importer) fieldName() (*Sym, bool) {
|
||||
name := p.string()
|
||||
if p.version == 0 && name == "_" {
|
||||
// version 0 didn't export a package for _ fields
|
||||
// version 0 didn't export a package for _ field names
|
||||
// but used the builtin package instead
|
||||
return builtinpkg.Lookup(name), false
|
||||
}
|
||||
pkg := localpkg
|
||||
alias := false
|
||||
switch name {
|
||||
case "":
|
||||
// 1) field name matches base type name and is exported: nothing to do
|
||||
case "?":
|
||||
// 2) field name matches base type name and is not exported: need package
|
||||
name = ""
|
||||
pkg = p.pkg()
|
||||
case "@":
|
||||
// 3) field name doesn't match base type name (alias name): need name and possibly package
|
||||
name = p.string()
|
||||
alias = true
|
||||
fallthrough
|
||||
default:
|
||||
if !exportname(name) {
|
||||
pkg = p.pkg()
|
||||
}
|
||||
}
|
||||
return pkg.Lookup(name), alias
|
||||
}
|
||||
|
||||
func (p *importer) methodName() *Sym {
|
||||
name := p.string()
|
||||
if p.version == 0 && name == "_" {
|
||||
// version 0 didn't export a package for _ method names
|
||||
// but used the builtin package instead
|
||||
return builtinpkg.Lookup(name)
|
||||
}
|
||||
pkg := localpkg
|
||||
if name != "" && !exportname(name) {
|
||||
if name == "?" {
|
||||
name = ""
|
||||
}
|
||||
if !exportname(name) {
|
||||
pkg = p.pkg()
|
||||
}
|
||||
return pkg.Lookup(name)
|
||||
|
||||
@@ -519,10 +519,6 @@ func funchdr(n *Node) {
|
||||
Fatalf("funchdr: dclcontext = %d", dclcontext)
|
||||
}
|
||||
|
||||
if Ctxt.Flag_dynlink && importpkg == nil && n.Func.Nname != nil {
|
||||
makefuncsym(n.Func.Nname.Sym)
|
||||
}
|
||||
|
||||
dclcontext = PAUTO
|
||||
funcstart(n)
|
||||
|
||||
@@ -695,10 +691,20 @@ func typedcl0(s *Sym) *Node {
|
||||
|
||||
// node n, which was returned by typedcl0
|
||||
// is being declared to have uncompiled type t.
|
||||
// return the ODCLTYPE node to use.
|
||||
func typedcl1(n *Node, t *Node, local bool) *Node {
|
||||
n.Name.Param.Ntype = t
|
||||
n.Local = local
|
||||
// returns the ODCLTYPE node to use.
|
||||
func typedcl1(n *Node, t *Node, pragma Pragma, alias bool) *Node {
|
||||
if pragma != 0 && alias {
|
||||
yyerror("cannot specify directive with type alias")
|
||||
pragma = 0
|
||||
}
|
||||
|
||||
n.Local = true
|
||||
|
||||
p := n.Name.Param
|
||||
p.Ntype = t
|
||||
p.Pragma = pragma
|
||||
p.Alias = alias
|
||||
|
||||
return nod(ODCLTYPE, n, nil)
|
||||
}
|
||||
|
||||
@@ -1153,19 +1159,19 @@ bad:
|
||||
return nil
|
||||
}
|
||||
|
||||
func methodname(n *Node, t *Node) *Node {
|
||||
// methodname is a misnomer because this now returns a Sym, rather
|
||||
// than an ONAME.
|
||||
// TODO(mdempsky): Reconcile with methodsym.
|
||||
func methodname(s *Sym, recv *Type) *Sym {
|
||||
star := false
|
||||
if t.Op == OIND {
|
||||
if recv.IsPtr() {
|
||||
star = true
|
||||
t = t.Left
|
||||
recv = recv.Elem()
|
||||
}
|
||||
|
||||
return methodname0(n.Sym, star, t.Sym)
|
||||
}
|
||||
|
||||
func methodname0(s *Sym, star bool, tsym *Sym) *Node {
|
||||
tsym := recv.Sym
|
||||
if tsym == nil || isblanksym(s) {
|
||||
return newfuncname(s)
|
||||
return s
|
||||
}
|
||||
|
||||
var p string
|
||||
@@ -1181,14 +1187,13 @@ func methodname0(s *Sym, star bool, tsym *Sym) *Node {
|
||||
s = Pkglookup(p, tsym.Pkg)
|
||||
}
|
||||
|
||||
return newfuncname(s)
|
||||
return s
|
||||
}
|
||||
|
||||
// Add a method, declared as a function.
|
||||
// - msym is the method symbol
|
||||
// - t is function type (with receiver)
|
||||
func addmethod(msym *Sym, t *Type, local, nointerface bool) {
|
||||
// get field sym
|
||||
if msym == nil {
|
||||
Fatalf("no method symbol")
|
||||
}
|
||||
@@ -1309,7 +1314,7 @@ func funcsym(s *Sym) *Sym {
|
||||
s1 := Pkglookup(s.Name+"·f", s.Pkg)
|
||||
if !Ctxt.Flag_dynlink && s1.Def == nil {
|
||||
s1.Def = newfuncname(s1)
|
||||
s1.Def.Func.Shortname = newname(s)
|
||||
s1.Def.Func.Shortname = s
|
||||
funcsyms = append(funcsyms, s1.Def)
|
||||
}
|
||||
s.Fsym = s1
|
||||
@@ -1326,8 +1331,11 @@ func makefuncsym(s *Sym) {
|
||||
return
|
||||
}
|
||||
s1 := funcsym(s)
|
||||
if s1.Def != nil {
|
||||
return
|
||||
}
|
||||
s1.Def = newfuncname(s1)
|
||||
s1.Def.Func.Shortname = newname(s)
|
||||
s1.Def.Func.Shortname = s
|
||||
funcsyms = append(funcsyms, s1.Def)
|
||||
}
|
||||
|
||||
|
||||
@@ -45,8 +45,8 @@ func exportsym(n *Node) {
|
||||
fmt.Printf("export symbol %v\n", n.Sym)
|
||||
}
|
||||
|
||||
// Ensure original object is on exportlist before aliases.
|
||||
if n.Sym.Flags&SymAlias != 0 {
|
||||
// Ensure original types are on exportlist before type aliases.
|
||||
if n.Sym.isAlias() {
|
||||
exportlist = append(exportlist, n.Sym.Def)
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ func autoexport(n *Node, ctxt Class) {
|
||||
if (ctxt != PEXTERN && ctxt != PFUNC) || dclcontext != PEXTERN {
|
||||
return
|
||||
}
|
||||
if n.Name.Param != nil && n.Name.Param.Ntype != nil && n.Name.Param.Ntype.Op == OTFUNC && n.Name.Param.Ntype.Left != nil { // method
|
||||
if n.Type != nil && n.Type.IsKind(TFUNC) && n.Type.Recv() != nil { // method
|
||||
return
|
||||
}
|
||||
|
||||
@@ -348,6 +348,27 @@ func importvar(s *Sym, t *Type) {
|
||||
}
|
||||
}
|
||||
|
||||
// importalias declares symbol s as an imported type alias with type t.
|
||||
func importalias(s *Sym, t *Type) {
|
||||
importsym(s, OTYPE)
|
||||
if s.Def != nil && s.Def.Op == OTYPE {
|
||||
if eqtype(t, s.Def.Type) {
|
||||
return
|
||||
}
|
||||
yyerror("inconsistent definition for type alias %v during import\n\t%v (in %q)\n\t%v (in %q)", s, s.Def.Type, s.Importdef.Path, t, importpkg.Path)
|
||||
}
|
||||
|
||||
n := newname(s)
|
||||
n.Op = OTYPE
|
||||
s.Importdef = importpkg
|
||||
n.Type = t
|
||||
declare(n, PEXTERN)
|
||||
|
||||
if Debug['E'] != 0 {
|
||||
fmt.Printf("import type %v = %L\n", s, t)
|
||||
}
|
||||
}
|
||||
|
||||
func dumpasmhdr() {
|
||||
b, err := bio.Create(asmhdr)
|
||||
if err != nil {
|
||||
|
||||
@@ -1077,6 +1077,7 @@ var opprec = []int{
|
||||
OSEND: 3,
|
||||
OANDAND: 2,
|
||||
OOROR: 1,
|
||||
|
||||
// Statements handled by stmtfmt
|
||||
OAS: -1,
|
||||
OAS2: -1,
|
||||
@@ -1104,7 +1105,8 @@ var opprec = []int{
|
||||
OSWITCH: -1,
|
||||
OXCASE: -1,
|
||||
OXFALL: -1,
|
||||
OEND: 0,
|
||||
|
||||
OEND: 0,
|
||||
}
|
||||
|
||||
func (n *Node) exprfmt(s fmt.State, prec int) {
|
||||
|
||||
@@ -63,9 +63,12 @@ const (
|
||||
SymSiggen
|
||||
SymAsm
|
||||
SymAlgGen
|
||||
SymAlias // alias, original is Sym.Def.Sym
|
||||
)
|
||||
|
||||
func (sym *Sym) isAlias() bool {
|
||||
return sym.Def != nil && sym.Def.Sym != sym
|
||||
}
|
||||
|
||||
// The Class of a variable/function describes the "storage class"
|
||||
// of a variable or function. During parsing, storage classes are
|
||||
// called declaration contexts.
|
||||
@@ -87,7 +90,7 @@ const (
|
||||
// of the compilers arrays.
|
||||
//
|
||||
// typedef struct
|
||||
// { // must not move anything
|
||||
// { // must not move anything
|
||||
// uchar array[8]; // pointer to data
|
||||
// uchar nel[4]; // number of elements
|
||||
// uchar cap[4]; // allocated number of elements
|
||||
@@ -104,7 +107,7 @@ var sizeof_Array int // runtime sizeof(Array)
|
||||
// of the compilers strings.
|
||||
//
|
||||
// typedef struct
|
||||
// { // must not move anything
|
||||
// { // must not move anything
|
||||
// uchar array[8]; // pointer to data
|
||||
// uchar nel[4]; // number of elements
|
||||
// } String;
|
||||
@@ -361,6 +364,12 @@ type Arch struct {
|
||||
// SSAGenBlock emits end-of-block Progs. SSAGenValue should be called
|
||||
// for all values in the block before SSAGenBlock.
|
||||
SSAGenBlock func(s *SSAGenState, b, next *ssa.Block)
|
||||
|
||||
// ZeroAuto emits code to zero the given auto stack variable.
|
||||
// Code is added immediately after pp.
|
||||
// ZeroAuto must not use any non-temporary registers.
|
||||
// ZeroAuto will only be called for variables which contain a pointer.
|
||||
ZeroAuto func(n *Node, pp *obj.Prog)
|
||||
}
|
||||
|
||||
var pcloc int32
|
||||
|
||||
@@ -72,6 +72,15 @@ func Appendpp(p *obj.Prog, as obj.As, ftype obj.AddrType, freg int16, foffset in
|
||||
return q
|
||||
}
|
||||
|
||||
func AddAsmAfter(as obj.As, p *obj.Prog) *obj.Prog {
|
||||
q := Ctxt.NewProg()
|
||||
Clearp(q)
|
||||
q.As = as
|
||||
q.Link = p.Link
|
||||
p.Link = q
|
||||
return q
|
||||
}
|
||||
|
||||
func ggloblnod(nam *Node) {
|
||||
s := Linksym(nam.Sym)
|
||||
s.Gotype = Linksym(ngotype(nam))
|
||||
|
||||
@@ -340,13 +340,16 @@ func Main() {
|
||||
// Phase 1: const, type, and names and types of funcs.
|
||||
// This will gather all the information about types
|
||||
// and methods but doesn't depend on any of it.
|
||||
// We also defer type alias declarations until phase 2
|
||||
// to avoid cycles like #18640.
|
||||
defercheckwidth()
|
||||
|
||||
// Don't use range--typecheck can add closures to xtop.
|
||||
timings.Start("fe", "typecheck", "top1")
|
||||
for i := 0; i < len(xtop); i++ {
|
||||
if xtop[i].Op != ODCL && xtop[i].Op != OAS && xtop[i].Op != OAS2 {
|
||||
xtop[i] = typecheck(xtop[i], Etop)
|
||||
n := xtop[i]
|
||||
if op := n.Op; op != ODCL && op != OAS && op != OAS2 && (op != ODCLTYPE || !n.Left.Name.Param.Alias) {
|
||||
xtop[i] = typecheck(n, Etop)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,8 +359,9 @@ func Main() {
|
||||
// Don't use range--typecheck can add closures to xtop.
|
||||
timings.Start("fe", "typecheck", "top2")
|
||||
for i := 0; i < len(xtop); i++ {
|
||||
if xtop[i].Op == ODCL || xtop[i].Op == OAS || xtop[i].Op == OAS2 {
|
||||
xtop[i] = typecheck(xtop[i], Etop)
|
||||
n := xtop[i]
|
||||
if op := n.Op; op == ODCL || op == OAS || op == OAS2 || op == ODCLTYPE && n.Left.Name.Param.Alias {
|
||||
xtop[i] = typecheck(n, Etop)
|
||||
}
|
||||
}
|
||||
resumecheckwidth()
|
||||
@@ -367,8 +371,9 @@ func Main() {
|
||||
timings.Start("fe", "typecheck", "func")
|
||||
var fcount int64
|
||||
for i := 0; i < len(xtop); i++ {
|
||||
if xtop[i].Op == ODCLFUNC || xtop[i].Op == OCLOSURE {
|
||||
Curfn = xtop[i]
|
||||
n := xtop[i]
|
||||
if op := n.Op; op == ODCLFUNC || op == OCLOSURE {
|
||||
Curfn = n
|
||||
decldepth = 1
|
||||
saveerrors()
|
||||
typecheckslice(Curfn.Nbody.Slice(), Etop)
|
||||
@@ -460,8 +465,9 @@ func Main() {
|
||||
timings.Start("be", "compilefuncs")
|
||||
fcount = 0
|
||||
for i := 0; i < len(xtop); i++ {
|
||||
if xtop[i].Op == ODCLFUNC {
|
||||
funccompile(xtop[i])
|
||||
n := xtop[i]
|
||||
if n.Op == ODCLFUNC {
|
||||
funccompile(n)
|
||||
fcount++
|
||||
}
|
||||
}
|
||||
@@ -924,7 +930,7 @@ func mkpackage(pkgname string) {
|
||||
continue
|
||||
}
|
||||
|
||||
if s.Def.Sym != s && s.Flags&SymAlias == 0 {
|
||||
if s.isAlias() {
|
||||
// throw away top-level name left over
|
||||
// from previous import . "x"
|
||||
if s.Def.Name != nil && s.Def.Name.Pack != nil && !s.Def.Name.Pack.Used && nsyntaxerrors == 0 {
|
||||
|
||||
@@ -154,11 +154,7 @@ func (p *noder) importDecl(imp *syntax.ImportDecl) {
|
||||
|
||||
func (p *noder) varDecl(decl *syntax.VarDecl) []*Node {
|
||||
names := p.declNames(decl.NameList)
|
||||
|
||||
var typ *Node
|
||||
if decl.Type != nil {
|
||||
typ = p.typeExpr(decl.Type)
|
||||
}
|
||||
typ := p.typeExprOrNil(decl.Type)
|
||||
|
||||
var exprs []*Node
|
||||
if decl.Values != nil {
|
||||
@@ -171,11 +167,7 @@ func (p *noder) varDecl(decl *syntax.VarDecl) []*Node {
|
||||
|
||||
func (p *noder) constDecl(decl *syntax.ConstDecl) []*Node {
|
||||
names := p.declNames(decl.NameList)
|
||||
|
||||
var typ *Node
|
||||
if decl.Type != nil {
|
||||
typ = p.typeExpr(decl.Type)
|
||||
}
|
||||
typ := p.typeExprOrNil(decl.Type)
|
||||
|
||||
var exprs []*Node
|
||||
if decl.Values != nil {
|
||||
@@ -187,14 +179,11 @@ func (p *noder) constDecl(decl *syntax.ConstDecl) []*Node {
|
||||
|
||||
func (p *noder) typeDecl(decl *syntax.TypeDecl) *Node {
|
||||
name := typedcl0(p.name(decl.Name))
|
||||
name.Name.Param.Pragma = Pragma(decl.Pragma)
|
||||
|
||||
var typ *Node
|
||||
if decl.Type != nil {
|
||||
typ = p.typeExpr(decl.Type)
|
||||
}
|
||||
// decl.Type may be nil but in that case we got a syntax error during parsing
|
||||
typ := p.typeExprOrNil(decl.Type)
|
||||
|
||||
return typedcl1(name, typ, true)
|
||||
return typedcl1(name, typ, Pragma(decl.Pragma), decl.Alias)
|
||||
}
|
||||
|
||||
func (p *noder) declNames(names []*syntax.Name) []*Node {
|
||||
@@ -259,19 +248,19 @@ func (p *noder) funcHeader(fun *syntax.FuncDecl) *Node {
|
||||
yyerror("func main must have no arguments and no return values")
|
||||
}
|
||||
}
|
||||
|
||||
f.Func.Nname = newfuncname(name)
|
||||
} else {
|
||||
// Receiver MethodName Signature
|
||||
|
||||
f.Func.Shortname = newfuncname(name)
|
||||
f.Func.Nname = methodname(f.Func.Shortname, t.Left.Right)
|
||||
f.Func.Shortname = name
|
||||
name = nblank.Sym // filled in by typecheckfunc
|
||||
}
|
||||
|
||||
f.Func.Nname = newfuncname(name)
|
||||
f.Func.Nname.Name.Defn = f
|
||||
f.Func.Nname.Name.Param.Ntype = t // TODO: check if nname already has an ntype
|
||||
|
||||
declare(f.Func.Nname, PFUNC)
|
||||
if fun.Recv == nil {
|
||||
declare(f.Func.Nname, PFUNC)
|
||||
}
|
||||
|
||||
funchdr(f)
|
||||
return f
|
||||
}
|
||||
@@ -467,6 +456,13 @@ func (p *noder) typeExpr(typ syntax.Expr) *Node {
|
||||
return p.expr(typ)
|
||||
}
|
||||
|
||||
func (p *noder) typeExprOrNil(typ syntax.Expr) *Node {
|
||||
if typ != nil {
|
||||
return p.expr(typ)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *noder) chanDir(dir syntax.ChanDir) ChanDir {
|
||||
switch dir {
|
||||
case 0:
|
||||
|
||||
@@ -213,7 +213,7 @@ func dumpglobls() {
|
||||
}
|
||||
|
||||
for _, n := range funcsyms {
|
||||
dsymptr(n.Sym, 0, n.Sym.Def.Func.Shortname.Sym, 0)
|
||||
dsymptr(n.Sym, 0, n.Sym.Def.Func.Shortname, 0)
|
||||
ggloblsym(n.Sym, int32(Widthptr), obj.DUPOK|obj.RODATA)
|
||||
}
|
||||
|
||||
|
||||
@@ -120,7 +120,30 @@ func Gvarlive(n *Node) {
|
||||
}
|
||||
|
||||
func removevardef(firstp *obj.Prog) {
|
||||
// At VARKILLs, zero variable if it is ambiguously live.
|
||||
// After the VARKILL anything this variable references
|
||||
// might be collected. If it were to become live again later,
|
||||
// the GC will see references to already-collected objects.
|
||||
// See issue 20029.
|
||||
for p := firstp; p != nil; p = p.Link {
|
||||
if p.As != obj.AVARKILL {
|
||||
continue
|
||||
}
|
||||
n := p.To.Node.(*Node)
|
||||
if !n.Name.Needzero {
|
||||
continue
|
||||
}
|
||||
if n.Class != PAUTO {
|
||||
Fatalf("zero of variable which isn't PAUTO %v", n)
|
||||
}
|
||||
if n.Type.Size()%int64(Widthptr) != 0 {
|
||||
Fatalf("zero of variable not a multiple of ptr size %v", n)
|
||||
}
|
||||
Thearch.ZeroAuto(n, p)
|
||||
}
|
||||
|
||||
for p := firstp; p != nil; p = p.Link {
|
||||
|
||||
for p.Link != nil && (p.Link.As == obj.AVARDEF || p.Link.As == obj.AVARKILL || p.Link.As == obj.AVARLIVE) {
|
||||
p.Link = p.Link.Link
|
||||
}
|
||||
|
||||
@@ -511,7 +511,7 @@ func isExportedField(ft *Field) (bool, *Pkg) {
|
||||
// dnameField dumps a reflect.name for a struct field.
|
||||
func dnameField(s *Sym, ot int, spkg *Pkg, ft *Field) int {
|
||||
var name string
|
||||
if ft.Sym != nil && ft.Embedded == 0 {
|
||||
if ft.Sym != nil {
|
||||
name = ft.Sym.Name
|
||||
}
|
||||
isExported, fpkg := isExportedField(ft)
|
||||
@@ -1345,7 +1345,14 @@ ok:
|
||||
// ../../../../runtime/type.go:/structField
|
||||
ot = dnameField(s, ot, pkg, f)
|
||||
ot = dsymptr(s, ot, dtypesym(f.Type), 0)
|
||||
ot = duintptr(s, ot, uint64(f.Offset))
|
||||
offsetAnon := uint64(f.Offset) << 1
|
||||
if offsetAnon>>1 != uint64(f.Offset) {
|
||||
Fatalf("%v: bad field offset for %s", t, f.Sym.Name)
|
||||
}
|
||||
if f.Embedded != 0 {
|
||||
offsetAnon |= 1
|
||||
}
|
||||
ot = duintptr(s, ot, offsetAnon)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,9 @@ func TestArithmeticConst(t *testing.T) { runTest(t, "arithConst.go") }
|
||||
|
||||
func TestChan(t *testing.T) { runTest(t, "chan.go") }
|
||||
|
||||
// TestComparisonsConst tests results for comparison operations against constants.
|
||||
func TestComparisonsConst(t *testing.T) { runTest(t, "cmpConst.go") }
|
||||
|
||||
func TestCompound(t *testing.T) { runTest(t, "compound.go") }
|
||||
|
||||
func TestCtl(t *testing.T) { runTest(t, "ctl.go") }
|
||||
|
||||
@@ -27,7 +27,7 @@ type Node struct {
|
||||
// func
|
||||
Func *Func
|
||||
|
||||
// ONAME
|
||||
// ONAME, OTYPE, OPACK, OLABEL, some OLITERAL
|
||||
Name *Name
|
||||
|
||||
Sym *Sym // various
|
||||
@@ -59,8 +59,8 @@ type Node struct {
|
||||
Noescape bool // func arguments do not escape; TODO(rsc): move Noescape to Func struct (see CL 7360)
|
||||
Walkdef uint8 // tracks state during typecheckdef; 2 == loop detected
|
||||
Typecheck uint8 // tracks state during typechecking; 2 == loop detected
|
||||
Local bool
|
||||
IsStatic bool // whether this Node will be converted to purely static data
|
||||
Local bool // type created in this file (see also Type.Local); TODO(gri): move this into flags
|
||||
IsStatic bool // whether this Node will be converted to purely static data
|
||||
Initorder uint8
|
||||
Used bool // for variable/label declared and not used error
|
||||
Isddd bool // is the argument variadic
|
||||
@@ -180,14 +180,14 @@ func (n *Node) SetIota(x int64) {
|
||||
n.Xoffset = x
|
||||
}
|
||||
|
||||
// Name holds Node fields used only by named nodes (ONAME, OPACK, OLABEL, some OLITERAL).
|
||||
// Name holds Node fields used only by named nodes (ONAME, OTYPE, OPACK, OLABEL, some OLITERAL).
|
||||
type Name struct {
|
||||
Pack *Node // real package for import . names
|
||||
Pkg *Pkg // pkg for OPACK nodes
|
||||
Heapaddr *Node // temp holding heap address of param (could move to Param?)
|
||||
Defn *Node // initializing assignment
|
||||
Curfn *Node // function for local variables
|
||||
Param *Param // additional fields for ONAME
|
||||
Param *Param // additional fields for ONAME, OTYPE
|
||||
Decldepth int32 // declaration loop depth, increased for every loop or label
|
||||
Vargen int32 // unique name for ONAME within a function. Function outputs are numbered starting at one.
|
||||
Funcdepth int32
|
||||
@@ -280,15 +280,16 @@ type Param struct {
|
||||
Innermost *Node
|
||||
Outer *Node
|
||||
|
||||
// OTYPE pragmas
|
||||
// OTYPE
|
||||
//
|
||||
// TODO: Should Func pragmas also be stored on the Name?
|
||||
Pragma Pragma
|
||||
Alias bool // node is alias for Ntype (only used when type-checking ODCLTYPE)
|
||||
}
|
||||
|
||||
// Func holds Node fields used only with function-like nodes.
|
||||
type Func struct {
|
||||
Shortname *Node
|
||||
Shortname *Sym
|
||||
Enter Nodes // for example, allocate and initialize memory for escaping parameters
|
||||
Exit Nodes
|
||||
Cvars Nodes // closure params
|
||||
@@ -382,7 +383,7 @@ const (
|
||||
ODCLFUNC // func f() or func (r) f()
|
||||
ODCLFIELD // struct field, interface field, or func/method argument/return value.
|
||||
ODCLCONST // const pi = 3.14
|
||||
ODCLTYPE // type Int int
|
||||
ODCLTYPE // type Int int or type Int = int
|
||||
|
||||
ODELETE // delete(Left, Right)
|
||||
ODOT // Left.Sym (Left is of struct type)
|
||||
|
||||
2217
src/cmd/compile/internal/gc/testdata/cmpConst.go
vendored
Normal file
2217
src/cmd/compile/internal/gc/testdata/cmpConst.go
vendored
Normal file
File diff suppressed because it is too large
Load Diff
248
src/cmd/compile/internal/gc/testdata/gen/cmpConstGen.go
vendored
Normal file
248
src/cmd/compile/internal/gc/testdata/gen/cmpConstGen.go
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
// 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.
|
||||
|
||||
// This program generates a test to verify that the standard comparison
|
||||
// operators properly handle one const operand. The test file should be
|
||||
// generated with a known working version of go.
|
||||
// launch with `go run cmpConstGen.go` a file called cmpConst.go
|
||||
// will be written into the parent directory containing the tests
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/big"
|
||||
"sort"
|
||||
)
|
||||
|
||||
const (
|
||||
maxU64 = (1 << 64) - 1
|
||||
maxU32 = (1 << 32) - 1
|
||||
maxU16 = (1 << 16) - 1
|
||||
maxU8 = (1 << 8) - 1
|
||||
|
||||
maxI64 = (1 << 63) - 1
|
||||
maxI32 = (1 << 31) - 1
|
||||
maxI16 = (1 << 15) - 1
|
||||
maxI8 = (1 << 7) - 1
|
||||
|
||||
minI64 = -(1 << 63)
|
||||
minI32 = -(1 << 31)
|
||||
minI16 = -(1 << 15)
|
||||
minI8 = -(1 << 7)
|
||||
)
|
||||
|
||||
func cmp(left *big.Int, op string, right *big.Int) bool {
|
||||
switch left.Cmp(right) {
|
||||
case -1: // less than
|
||||
return op == "<" || op == "<=" || op == "!="
|
||||
case 0: // equal
|
||||
return op == "==" || op == "<=" || op == ">="
|
||||
case 1: // greater than
|
||||
return op == ">" || op == ">=" || op == "!="
|
||||
}
|
||||
panic("unexpected comparison value")
|
||||
}
|
||||
|
||||
func inRange(typ string, val *big.Int) bool {
|
||||
min, max := &big.Int{}, &big.Int{}
|
||||
switch typ {
|
||||
case "uint64":
|
||||
max = max.SetUint64(maxU64)
|
||||
case "uint32":
|
||||
max = max.SetUint64(maxU32)
|
||||
case "uint16":
|
||||
max = max.SetUint64(maxU16)
|
||||
case "uint8":
|
||||
max = max.SetUint64(maxU8)
|
||||
case "int64":
|
||||
min = min.SetInt64(minI64)
|
||||
max = max.SetInt64(maxI64)
|
||||
case "int32":
|
||||
min = min.SetInt64(minI32)
|
||||
max = max.SetInt64(maxI32)
|
||||
case "int16":
|
||||
min = min.SetInt64(minI16)
|
||||
max = max.SetInt64(maxI16)
|
||||
case "int8":
|
||||
min = min.SetInt64(minI8)
|
||||
max = max.SetInt64(maxI8)
|
||||
default:
|
||||
panic("unexpected type")
|
||||
}
|
||||
return cmp(min, "<=", val) && cmp(val, "<=", max)
|
||||
}
|
||||
|
||||
func getValues(typ string) []*big.Int {
|
||||
Uint := func(v uint64) *big.Int { return big.NewInt(0).SetUint64(v) }
|
||||
Int := func(v int64) *big.Int { return big.NewInt(0).SetInt64(v) }
|
||||
values := []*big.Int{
|
||||
// limits
|
||||
Uint(maxU64),
|
||||
Uint(maxU64 - 1),
|
||||
Uint(maxI64 + 1),
|
||||
Uint(maxI64),
|
||||
Uint(maxI64 - 1),
|
||||
Uint(maxU32 + 1),
|
||||
Uint(maxU32),
|
||||
Uint(maxU32 - 1),
|
||||
Uint(maxI32 + 1),
|
||||
Uint(maxI32),
|
||||
Uint(maxI32 - 1),
|
||||
Uint(maxU16 + 1),
|
||||
Uint(maxU16),
|
||||
Uint(maxU16 - 1),
|
||||
Uint(maxI16 + 1),
|
||||
Uint(maxI16),
|
||||
Uint(maxI16 - 1),
|
||||
Uint(maxU8 + 1),
|
||||
Uint(maxU8),
|
||||
Uint(maxU8 - 1),
|
||||
Uint(maxI8 + 1),
|
||||
Uint(maxI8),
|
||||
Uint(maxI8 - 1),
|
||||
Uint(0),
|
||||
Int(minI8 + 1),
|
||||
Int(minI8),
|
||||
Int(minI8 - 1),
|
||||
Int(minI16 + 1),
|
||||
Int(minI16),
|
||||
Int(minI16 - 1),
|
||||
Int(minI32 + 1),
|
||||
Int(minI32),
|
||||
Int(minI32 - 1),
|
||||
Int(minI64 + 1),
|
||||
Int(minI64),
|
||||
|
||||
// other possibly interesting values
|
||||
Uint(1),
|
||||
Int(-1),
|
||||
Uint(0xff << 56),
|
||||
Uint(0xff << 32),
|
||||
Uint(0xff << 24),
|
||||
}
|
||||
sort.Slice(values, func(i, j int) bool { return values[i].Cmp(values[j]) == -1 })
|
||||
var ret []*big.Int
|
||||
for _, val := range values {
|
||||
if !inRange(typ, val) {
|
||||
continue
|
||||
}
|
||||
ret = append(ret, val)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func sigString(v *big.Int) string {
|
||||
var t big.Int
|
||||
t.Abs(v)
|
||||
if v.Sign() == -1 {
|
||||
return "neg" + t.String()
|
||||
}
|
||||
return t.String()
|
||||
}
|
||||
|
||||
func main() {
|
||||
types := []string{
|
||||
"uint64", "uint32", "uint16", "uint8",
|
||||
"int64", "int32", "int16", "int8",
|
||||
}
|
||||
|
||||
w := new(bytes.Buffer)
|
||||
fmt.Fprintf(w, "// run\n")
|
||||
fmt.Fprintf(w, "// Code generated by gen/cmpConstGen.go. DO NOT EDIT.\n\n")
|
||||
fmt.Fprintf(w, "package main;\n")
|
||||
fmt.Fprintf(w, "import (\"fmt\"; \"reflect\"; \"runtime\";)\n")
|
||||
fmt.Fprintf(w, "// results show the expected result for the elements left of, equal to and right of the index.\n")
|
||||
fmt.Fprintf(w, "type result struct{l, e, r bool}\n")
|
||||
fmt.Fprintf(w, "var (\n")
|
||||
fmt.Fprintf(w, " eq = result{l: false, e: true, r: false}\n")
|
||||
fmt.Fprintf(w, " ne = result{l: true, e: false, r: true}\n")
|
||||
fmt.Fprintf(w, " lt = result{l: true, e: false, r: false}\n")
|
||||
fmt.Fprintf(w, " le = result{l: true, e: true, r: false}\n")
|
||||
fmt.Fprintf(w, " gt = result{l: false, e: false, r: true}\n")
|
||||
fmt.Fprintf(w, " ge = result{l: false, e: true, r: true}\n")
|
||||
fmt.Fprintf(w, ")\n")
|
||||
|
||||
operators := []struct{ op, name string }{
|
||||
{"<", "lt"},
|
||||
{"<=", "le"},
|
||||
{">", "gt"},
|
||||
{">=", "ge"},
|
||||
{"==", "eq"},
|
||||
{"!=", "ne"},
|
||||
}
|
||||
|
||||
for _, typ := range types {
|
||||
// generate a slice containing valid values for this type
|
||||
fmt.Fprintf(w, "\n// %v tests\n", typ)
|
||||
values := getValues(typ)
|
||||
fmt.Fprintf(w, "var %v_vals = []%v{\n", typ, typ)
|
||||
for _, val := range values {
|
||||
fmt.Fprintf(w, "%v,\n", val.String())
|
||||
}
|
||||
fmt.Fprintf(w, "}\n")
|
||||
|
||||
// generate test functions
|
||||
for _, r := range values {
|
||||
// TODO: could also test constant on lhs.
|
||||
sig := sigString(r)
|
||||
for _, op := range operators {
|
||||
// no need for go:noinline because the function is called indirectly
|
||||
fmt.Fprintf(w, "func %v_%v_%v(x %v) bool { return x %v %v; }\n", op.name, sig, typ, typ, op.op, r.String())
|
||||
}
|
||||
}
|
||||
|
||||
// generate a table of test cases
|
||||
fmt.Fprintf(w, "var %v_tests = []struct{\n", typ)
|
||||
fmt.Fprintf(w, " idx int // index of the constant used\n")
|
||||
fmt.Fprintf(w, " exp result // expected results\n")
|
||||
fmt.Fprintf(w, " fn func(%v) bool\n", typ)
|
||||
fmt.Fprintf(w, "}{\n")
|
||||
for i, r := range values {
|
||||
sig := sigString(r)
|
||||
for _, op := range operators {
|
||||
fmt.Fprintf(w, "{idx: %v,", i)
|
||||
fmt.Fprintf(w, "exp: %v,", op.name)
|
||||
fmt.Fprintf(w, "fn: %v_%v_%v},\n", op.name, sig, typ)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(w, "}\n")
|
||||
}
|
||||
|
||||
// emit the main function, looping over all test cases
|
||||
fmt.Fprintf(w, "func main() {\n")
|
||||
for _, typ := range types {
|
||||
fmt.Fprintf(w, "for i, test := range %v_tests {\n", typ)
|
||||
fmt.Fprintf(w, " for j, x := range %v_vals {\n", typ)
|
||||
fmt.Fprintf(w, " want := test.exp.l\n")
|
||||
fmt.Fprintf(w, " if j == test.idx {\nwant = test.exp.e\n}")
|
||||
fmt.Fprintf(w, " else if j > test.idx {\nwant = test.exp.r\n}\n")
|
||||
fmt.Fprintf(w, " if test.fn(x) != want {\n")
|
||||
fmt.Fprintf(w, " fn := runtime.FuncForPC(reflect.ValueOf(test.fn).Pointer()).Name()\n")
|
||||
fmt.Fprintf(w, " msg := fmt.Sprintf(\"test failed: %%v(%%v) != %%v [type=%v i=%%v j=%%v idx=%%v]\", fn, x, want, i, j, test.idx)\n", typ)
|
||||
fmt.Fprintf(w, " panic(msg)\n")
|
||||
fmt.Fprintf(w, " }\n")
|
||||
fmt.Fprintf(w, " }\n")
|
||||
fmt.Fprintf(w, "}\n")
|
||||
}
|
||||
fmt.Fprintf(w, "}\n")
|
||||
|
||||
// gofmt result
|
||||
b := w.Bytes()
|
||||
src, err := format.Source(b)
|
||||
if err != nil {
|
||||
fmt.Printf("%s\n", b)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// write to file
|
||||
err = ioutil.WriteFile("../cmpConst.go", src, 0666)
|
||||
if err != nil {
|
||||
log.Fatalf("can't write output: %v\n", err)
|
||||
}
|
||||
}
|
||||
@@ -96,16 +96,16 @@ func typekind(t *Type) string {
|
||||
return fmt.Sprintf("etype=%d", et)
|
||||
}
|
||||
|
||||
// sprint_depchain prints a dependency chain of nodes into fmt.
|
||||
// sprint_depchain prints a dependency chain of nodes into trace.
|
||||
// It is used by typecheck in the case of OLITERAL nodes
|
||||
// to print constant definition loops.
|
||||
func sprint_depchain(fmt_ *string, stack []*Node, cur *Node, first *Node) {
|
||||
func sprint_depchain(trace *string, stack []*Node, cur *Node, first *Node) {
|
||||
for i := len(stack) - 1; i >= 0; i-- {
|
||||
if n := stack[i]; n.Op == cur.Op {
|
||||
if n != first {
|
||||
sprint_depchain(fmt_, stack[:i], n, first)
|
||||
sprint_depchain(trace, stack[:i], n, first)
|
||||
}
|
||||
*fmt_ += fmt.Sprintf("\n\t%v: %v uses %v", n.Line(), n, cur)
|
||||
*trace += fmt.Sprintf("\n\t%v: %v uses %v", n.Line(), n, cur)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -152,7 +152,6 @@ func typecheck(n *Node, top int) *Node {
|
||||
if n.Typecheck == 2 {
|
||||
// Typechecking loop. Trying printing a meaningful message,
|
||||
// otherwise a stack trace of typechecking.
|
||||
var fmt_ string
|
||||
switch n.Op {
|
||||
// We can already diagnose variables used as types.
|
||||
case ONAME:
|
||||
@@ -160,22 +159,30 @@ func typecheck(n *Node, top int) *Node {
|
||||
yyerror("%v is not a type", n)
|
||||
}
|
||||
|
||||
case OTYPE:
|
||||
if top&Etype == Etype {
|
||||
var trace string
|
||||
sprint_depchain(&trace, typecheck_tcstack, n, n)
|
||||
yyerrorl(n.Lineno, "invalid recursive type alias %v%s", n, trace)
|
||||
}
|
||||
|
||||
case OLITERAL:
|
||||
if top&(Erv|Etype) == Etype {
|
||||
yyerror("%v is not a type", n)
|
||||
break
|
||||
}
|
||||
sprint_depchain(&fmt_, typecheck_tcstack, n, n)
|
||||
yyerrorl(n.Lineno, "constant definition loop%s", fmt_)
|
||||
var trace string
|
||||
sprint_depchain(&trace, typecheck_tcstack, n, n)
|
||||
yyerrorl(n.Lineno, "constant definition loop%s", trace)
|
||||
}
|
||||
|
||||
if nsavederrors+nerrors == 0 {
|
||||
fmt_ = ""
|
||||
var trace string
|
||||
for i := len(typecheck_tcstack) - 1; i >= 0; i-- {
|
||||
x := typecheck_tcstack[i]
|
||||
fmt_ += fmt.Sprintf("\n\t%v %v", x.Line(), x)
|
||||
trace += fmt.Sprintf("\n\t%v %v", x.Line(), x)
|
||||
}
|
||||
yyerror("typechecking loop involving %v%s", n, fmt_)
|
||||
yyerror("typechecking loop involving %v%s", n, trace)
|
||||
}
|
||||
|
||||
lineno = lno
|
||||
@@ -3428,7 +3435,14 @@ func typecheckfunc(n *Node) {
|
||||
t.SetNname(n.Func.Nname)
|
||||
rcvr := t.Recv()
|
||||
if rcvr != nil && n.Func.Shortname != nil {
|
||||
addmethod(n.Func.Shortname.Sym, t, true, n.Func.Pragma&Nointerface != 0)
|
||||
n.Func.Nname.Sym = methodname(n.Func.Shortname, rcvr.Type)
|
||||
declare(n.Func.Nname, PFUNC)
|
||||
|
||||
addmethod(n.Func.Shortname, t, true, n.Func.Pragma&Nointerface != 0)
|
||||
}
|
||||
|
||||
if Ctxt.Flag_dynlink && importpkg == nil && n.Func.Nname != nil {
|
||||
makefuncsym(n.Func.Nname.Sym)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3577,8 +3591,6 @@ func typecheckdeftype(n *Node) {
|
||||
|
||||
// copy new type and clear fields
|
||||
// that don't come along.
|
||||
// anything zeroed here must be zeroed in
|
||||
// typedcl2 too.
|
||||
copytype(n, t)
|
||||
|
||||
ret:
|
||||
@@ -3757,12 +3769,29 @@ func typecheckdef(n *Node) *Node {
|
||||
n.Name.Defn = typecheck(n.Name.Defn, Etop) // fills in n->type
|
||||
|
||||
case OTYPE:
|
||||
if p := n.Name.Param; p.Alias {
|
||||
// Type alias declaration: Simply use the rhs type - no need
|
||||
// to create a new type.
|
||||
// If we have a syntax error, p.Ntype may be nil.
|
||||
if p.Ntype != nil {
|
||||
p.Ntype = typecheck(p.Ntype, Etype)
|
||||
n.Type = p.Ntype.Type
|
||||
if n.Type == nil {
|
||||
n.Diag = true
|
||||
goto ret
|
||||
}
|
||||
n.Sym.Def = p.Ntype
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// regular type declaration
|
||||
if Curfn != nil {
|
||||
defercheckwidth()
|
||||
}
|
||||
n.Walkdef = 1
|
||||
n.Type = typ(TFORW)
|
||||
n.Type.Sym = n.Sym
|
||||
n.Type.Sym = n.Sym // TODO(gri) this also happens in typecheckdeftype(n) - where should it happen?
|
||||
nerrors0 := nerrors
|
||||
typecheckdeftype(n)
|
||||
if n.Type.Etype == TFORW && nerrors > nerrors0 {
|
||||
@@ -3770,7 +3799,6 @@ func typecheckdef(n *Node) *Node {
|
||||
// but it was reported. Silence future errors.
|
||||
n.Type.Broke = true
|
||||
}
|
||||
|
||||
if Curfn != nil {
|
||||
resumecheckwidth()
|
||||
}
|
||||
|
||||
@@ -398,6 +398,14 @@ func lexinit1() {
|
||||
// errortype.Orig = makeErrorInterface()
|
||||
s.Def = typenod(errortype)
|
||||
|
||||
// We create separate byte and rune types for better error messages
|
||||
// rather than just creating type alias *Sym's for the uint8 and
|
||||
// int32 types. Hence, (bytetype|runtype).Sym.isAlias() is false.
|
||||
// TODO(gri) Should we get rid of this special case (at the cost
|
||||
// of less informative error messages involving bytes and runes)?
|
||||
// (Alternatively, we could introduce an OTALIAS node representing
|
||||
// type aliases, albeit at the cost of having to deal with it everywhere).
|
||||
|
||||
// byte alias
|
||||
s = Pkglookup("byte", builtinpkg)
|
||||
bytetype = typ(TUINT8)
|
||||
|
||||
@@ -23,4 +23,5 @@ func Init() {
|
||||
gc.Thearch.SSAMarkMoves = func(s *gc.SSAGenState, b *ssa.Block) {}
|
||||
gc.Thearch.SSAGenValue = ssaGenValue
|
||||
gc.Thearch.SSAGenBlock = ssaGenBlock
|
||||
gc.Thearch.ZeroAuto = zeroAuto
|
||||
}
|
||||
|
||||
@@ -92,6 +92,23 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog {
|
||||
return p
|
||||
}
|
||||
|
||||
func zeroAuto(n *gc.Node, pp *obj.Prog) {
|
||||
// Note: this code must not clobber any registers.
|
||||
sym := gc.Linksym(n.Sym)
|
||||
size := n.Type.Size()
|
||||
for i := int64(0); i < size; i += 4 {
|
||||
p := gc.AddAsmAfter(mips.AMOVW, pp)
|
||||
pp = p
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = mips.REGZERO
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Name = obj.NAME_AUTO
|
||||
p.To.Reg = mips.REGSP
|
||||
p.To.Offset = n.Xoffset + i
|
||||
p.To.Sym = sym
|
||||
}
|
||||
}
|
||||
|
||||
func ginsnop() {
|
||||
p := gc.Prog(mips.ANOR)
|
||||
p.From.Type = obj.TYPE_REG
|
||||
|
||||
@@ -25,4 +25,5 @@ func Init() {
|
||||
gc.Thearch.SSAMarkMoves = func(s *gc.SSAGenState, b *ssa.Block) {}
|
||||
gc.Thearch.SSAGenValue = ssaGenValue
|
||||
gc.Thearch.SSAGenBlock = ssaGenBlock
|
||||
gc.Thearch.ZeroAuto = zeroAuto
|
||||
}
|
||||
|
||||
@@ -95,6 +95,23 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog {
|
||||
return p
|
||||
}
|
||||
|
||||
func zeroAuto(n *gc.Node, pp *obj.Prog) {
|
||||
// Note: this code must not clobber any registers.
|
||||
sym := gc.Linksym(n.Sym)
|
||||
size := n.Type.Size()
|
||||
for i := int64(0); i < size; i += 8 {
|
||||
p := gc.AddAsmAfter(mips.AMOVV, pp)
|
||||
pp = p
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = mips.REGZERO
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Name = obj.NAME_AUTO
|
||||
p.To.Reg = mips.REGSP
|
||||
p.To.Offset = n.Xoffset + i
|
||||
p.To.Sym = sym
|
||||
}
|
||||
}
|
||||
|
||||
func ginsnop() {
|
||||
p := gc.Prog(mips.ANOR)
|
||||
p.From.Type = obj.TYPE_REG
|
||||
|
||||
@@ -24,6 +24,7 @@ func Init() {
|
||||
gc.Thearch.SSAMarkMoves = ssaMarkMoves
|
||||
gc.Thearch.SSAGenValue = ssaGenValue
|
||||
gc.Thearch.SSAGenBlock = ssaGenBlock
|
||||
gc.Thearch.ZeroAuto = zeroAuto
|
||||
|
||||
initvariants()
|
||||
initproginfo()
|
||||
|
||||
@@ -90,6 +90,23 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog {
|
||||
return p
|
||||
}
|
||||
|
||||
func zeroAuto(n *gc.Node, pp *obj.Prog) {
|
||||
// Note: this code must not clobber any registers.
|
||||
sym := gc.Linksym(n.Sym)
|
||||
size := n.Type.Size()
|
||||
for i := int64(0); i < size; i += 8 {
|
||||
p := gc.AddAsmAfter(ppc64.AMOVD, pp)
|
||||
pp = p
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = ppc64.REGZERO
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Name = obj.NAME_AUTO
|
||||
p.To.Reg = ppc64.REGSP
|
||||
p.To.Offset = n.Xoffset + i
|
||||
p.To.Sym = sym
|
||||
}
|
||||
}
|
||||
|
||||
func ginsnop() {
|
||||
p := gc.Prog(ppc64.AOR)
|
||||
p.From.Type = obj.TYPE_REG
|
||||
|
||||
@@ -20,4 +20,5 @@ func Init() {
|
||||
gc.Thearch.SSAMarkMoves = ssaMarkMoves
|
||||
gc.Thearch.SSAGenValue = ssaGenValue
|
||||
gc.Thearch.SSAGenBlock = ssaGenBlock
|
||||
gc.Thearch.ZeroAuto = zeroAuto
|
||||
}
|
||||
|
||||
@@ -143,6 +143,19 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog {
|
||||
return p
|
||||
}
|
||||
|
||||
func zeroAuto(n *gc.Node, pp *obj.Prog) {
|
||||
// Note: this code must not clobber any registers.
|
||||
p := gc.AddAsmAfter(s390x.ACLEAR, pp)
|
||||
pp = p
|
||||
p.From.Type = obj.TYPE_CONST
|
||||
p.From.Offset = n.Type.Size()
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Name = obj.NAME_AUTO
|
||||
p.To.Reg = s390x.REGSP
|
||||
p.To.Offset = n.Xoffset
|
||||
p.To.Sym = gc.Linksym(n.Sym)
|
||||
}
|
||||
|
||||
func ginsnop() {
|
||||
p := gc.Prog(s390x.AOR)
|
||||
p.From.Type = obj.TYPE_REG
|
||||
|
||||
@@ -294,6 +294,39 @@ func checkFunc(f *Func) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that if a tuple has a memory type, it is second.
|
||||
for _, b := range f.Blocks {
|
||||
for _, v := range b.Values {
|
||||
if v.Type.IsTuple() && v.Type.FieldType(0).IsMemory() {
|
||||
f.Fatalf("memory is first in a tuple: %s\n", v.LongString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that only one memory is live at any point.
|
||||
// TODO: make this check examine interblock.
|
||||
if f.scheduled {
|
||||
for _, b := range f.Blocks {
|
||||
var mem *Value // the live memory
|
||||
for _, v := range b.Values {
|
||||
if v.Op != OpPhi {
|
||||
for _, a := range v.Args {
|
||||
if a.Type.IsMemory() || a.Type.IsTuple() && a.Type.FieldType(1).IsMemory() {
|
||||
if mem == nil {
|
||||
mem = a
|
||||
} else if mem != a {
|
||||
f.Fatalf("two live mems @ %s: %s and %s", v, mem, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if v.Type.IsMemory() || v.Type.IsTuple() && v.Type.FieldType(1).IsMemory() {
|
||||
mem = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// domCheck reports whether x dominates y (including x==y).
|
||||
|
||||
@@ -313,9 +313,13 @@ func cmpVal(v, w *Value, auxIDs auxmap) Cmp {
|
||||
// that generate memory.
|
||||
return lt2Cmp(v.ID < w.ID)
|
||||
}
|
||||
|
||||
if tc := v.Type.Compare(w.Type); tc != CMPeq {
|
||||
return tc
|
||||
// OpSelect is a pseudo-op. We need to be more agressive
|
||||
// regarding CSE to keep multiple OpSelect's of the same
|
||||
// argument from existing.
|
||||
if v.Op != OpSelect0 && v.Op != OpSelect1 {
|
||||
if tc := v.Type.Compare(w.Type); tc != CMPeq {
|
||||
return tc
|
||||
}
|
||||
}
|
||||
|
||||
if v.Aux != w.Aux {
|
||||
|
||||
@@ -35,8 +35,20 @@ type DummyFrontend struct {
|
||||
func (DummyFrontend) StringData(s string) interface{} {
|
||||
return nil
|
||||
}
|
||||
func (DummyFrontend) Auto(t Type) GCNode {
|
||||
return nil
|
||||
|
||||
type dummyGCNode struct {
|
||||
typ Type
|
||||
name string
|
||||
}
|
||||
|
||||
func (d *dummyGCNode) Typ() Type {
|
||||
return d.typ
|
||||
}
|
||||
func (d *dummyGCNode) String() string {
|
||||
return d.name
|
||||
}
|
||||
func (d DummyFrontend) Auto(t Type) GCNode {
|
||||
return &dummyGCNode{typ: t, name: "dummy"}
|
||||
}
|
||||
func (d DummyFrontend) SplitString(s LocalSlot) (LocalSlot, LocalSlot) {
|
||||
return LocalSlot{s.N, d.TypeBytePtr(), s.Off}, LocalSlot{s.N, d.TypeInt(), s.Off + 8}
|
||||
|
||||
@@ -537,8 +537,8 @@
|
||||
(CMP (MOVDconst [c]) x) && is32Bit(c) -> (InvertFlags (CMPconst x [c]))
|
||||
(CMPW x (MOVDconst [c])) -> (CMPWconst x [c])
|
||||
(CMPW (MOVDconst [c]) x) -> (InvertFlags (CMPWconst x [c]))
|
||||
(CMPU x (MOVDconst [c])) && is32Bit(c) -> (CMPUconst x [int64(uint32(c))])
|
||||
(CMPU (MOVDconst [c]) x) && is32Bit(c) -> (InvertFlags (CMPUconst x [int64(uint32(c))]))
|
||||
(CMPU x (MOVDconst [c])) && isU32Bit(c) -> (CMPUconst x [int64(uint32(c))])
|
||||
(CMPU (MOVDconst [c]) x) && isU32Bit(c) -> (InvertFlags (CMPUconst x [int64(uint32(c))]))
|
||||
(CMPWU x (MOVDconst [c])) -> (CMPWUconst x [int64(uint32(c))])
|
||||
(CMPWU (MOVDconst [c]) x) -> (InvertFlags (CMPWUconst x [int64(uint32(c))]))
|
||||
|
||||
|
||||
@@ -1699,6 +1699,24 @@ sinking:
|
||||
}
|
||||
p := d.Preds[0].b // block in loop exiting to d.
|
||||
|
||||
// Check that the spill value is still live at the start of d.
|
||||
// If it isn't, we can't move the spill here because some other value
|
||||
// may be using the same stack slot. See issue 20472.
|
||||
// The spill value can't be defined in d, so that makes our lives easier.
|
||||
for _, x := range stacklive[d.ID] {
|
||||
if x == vsp.ID {
|
||||
goto stillLive
|
||||
}
|
||||
}
|
||||
for _, v := range d.Values {
|
||||
if v.Op == OpLoadReg && v.Args[0] == vsp {
|
||||
goto stillLive
|
||||
}
|
||||
}
|
||||
// Spill is not live - abort sinking this spill.
|
||||
continue sinking
|
||||
stillLive:
|
||||
|
||||
endregs := s.endRegs[p.ID]
|
||||
for _, regrec := range endregs {
|
||||
if regrec.v == e && regrec.r != noRegister && regrec.c == e { // TODO: regrec.c != e implies different spill possible.
|
||||
|
||||
@@ -31,3 +31,68 @@ func TestLiveControlOps(t *testing.T) {
|
||||
regalloc(f.f)
|
||||
checkFunc(f.f)
|
||||
}
|
||||
|
||||
func TestSpillMove(t *testing.T) {
|
||||
// Test for issue 20472. We shouldn't move a spill out to exit blocks
|
||||
// if there is an exit block where the spill is dead but the pre-spill
|
||||
// value is live.
|
||||
c := testConfig(t)
|
||||
ptrType := &TypeImpl{Size_: 8, Ptr: true, Name: "testptr"} // dummy for testing
|
||||
arg1Aux := c.fe.Auto(TypeInt64)
|
||||
arg2Aux := c.fe.Auto(ptrType)
|
||||
f := Fun(c, "entry",
|
||||
Bloc("entry",
|
||||
Valu("mem", OpInitMem, TypeMem, 0, nil),
|
||||
Valu("x", OpArg, TypeInt64, 0, arg1Aux),
|
||||
Valu("p", OpArg, ptrType, 0, arg2Aux),
|
||||
Valu("a", OpAMD64TESTQ, TypeFlags, 0, nil, "x", "x"),
|
||||
Goto("loop1"),
|
||||
),
|
||||
Bloc("loop1",
|
||||
Valu("y", OpAMD64MULQ, TypeInt64, 0, nil, "x", "x"),
|
||||
Eq("a", "loop2", "exit1"),
|
||||
),
|
||||
Bloc("loop2",
|
||||
Eq("a", "loop1", "exit2"),
|
||||
),
|
||||
Bloc("exit1",
|
||||
// store before call, y is available in a register
|
||||
Valu("mem2", OpAMD64MOVQstore, TypeMem, 0, nil, "p", "y", "mem"),
|
||||
Valu("mem3", OpAMD64CALLstatic, TypeMem, 0, nil, "mem2"),
|
||||
Exit("mem3"),
|
||||
),
|
||||
Bloc("exit2",
|
||||
// store after call, y must be loaded from a spill location
|
||||
Valu("mem4", OpAMD64CALLstatic, TypeMem, 0, nil, "mem"),
|
||||
Valu("mem5", OpAMD64MOVQstore, TypeMem, 0, nil, "p", "y", "mem4"),
|
||||
Exit("mem5"),
|
||||
),
|
||||
)
|
||||
flagalloc(f.f)
|
||||
regalloc(f.f)
|
||||
checkFunc(f.f)
|
||||
// There should still be a spill in Loop1, and nowhere else.
|
||||
if numSpills(f.blocks["loop1"]) != 1 {
|
||||
t.Errorf("spill missing from loop1")
|
||||
}
|
||||
if numSpills(f.blocks["loop2"]) != 0 {
|
||||
t.Errorf("spill present in loop2")
|
||||
}
|
||||
if numSpills(f.blocks["exit1"]) != 0 {
|
||||
t.Errorf("spill present in exit1")
|
||||
}
|
||||
if numSpills(f.blocks["exit2"]) != 0 {
|
||||
t.Errorf("spill present in exit2")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func numSpills(b *Block) int {
|
||||
n := 0
|
||||
for _, v := range b.Values {
|
||||
if v.Op == OpStoreReg {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -6784,7 +6784,7 @@ func rewriteValueS390X_OpS390XCMPU(v *Value, config *Config) bool {
|
||||
b := v.Block
|
||||
_ = b
|
||||
// match: (CMPU x (MOVDconst [c]))
|
||||
// cond: is32Bit(c)
|
||||
// cond: isU32Bit(c)
|
||||
// result: (CMPUconst x [int64(uint32(c))])
|
||||
for {
|
||||
x := v.Args[0]
|
||||
@@ -6793,7 +6793,7 @@ func rewriteValueS390X_OpS390XCMPU(v *Value, config *Config) bool {
|
||||
break
|
||||
}
|
||||
c := v_1.AuxInt
|
||||
if !(is32Bit(c)) {
|
||||
if !(isU32Bit(c)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpS390XCMPUconst)
|
||||
@@ -6802,7 +6802,7 @@ func rewriteValueS390X_OpS390XCMPU(v *Value, config *Config) bool {
|
||||
return true
|
||||
}
|
||||
// match: (CMPU (MOVDconst [c]) x)
|
||||
// cond: is32Bit(c)
|
||||
// cond: isU32Bit(c)
|
||||
// result: (InvertFlags (CMPUconst x [int64(uint32(c))]))
|
||||
for {
|
||||
v_0 := v.Args[0]
|
||||
@@ -6811,7 +6811,7 @@ func rewriteValueS390X_OpS390XCMPU(v *Value, config *Config) bool {
|
||||
}
|
||||
c := v_0.AuxInt
|
||||
x := v.Args[1]
|
||||
if !(is32Bit(c)) {
|
||||
if !(isU32Bit(c)) {
|
||||
break
|
||||
}
|
||||
v.reset(OpS390XInvertFlags)
|
||||
|
||||
@@ -148,19 +148,20 @@ func schedule(f *Func) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: make this logic permanent in types.IsMemory?
|
||||
isMem := func(v *Value) bool {
|
||||
return v.Type.IsMemory() || v.Type.IsTuple() && v.Type.FieldType(1).IsMemory()
|
||||
}
|
||||
|
||||
for _, b := range f.Blocks {
|
||||
// Find store chain for block.
|
||||
// Store chains for different blocks overwrite each other, so
|
||||
// the calculated store chain is good only for this block.
|
||||
for _, v := range b.Values {
|
||||
if v.Op != OpPhi && v.Type.IsMemory() {
|
||||
mem := v
|
||||
if v.Op == OpSelect1 {
|
||||
v = v.Args[0]
|
||||
}
|
||||
if v.Op != OpPhi && isMem(v) {
|
||||
for _, w := range v.Args {
|
||||
if w.Type.IsMemory() {
|
||||
nextMem[w.ID] = mem
|
||||
if isMem(w) {
|
||||
nextMem[w.ID] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -179,15 +180,15 @@ func schedule(f *Func) {
|
||||
uses[w.ID]++
|
||||
}
|
||||
// Any load must come before the following store.
|
||||
if v.Type.IsMemory() || !w.Type.IsMemory() {
|
||||
continue // not a load
|
||||
if !isMem(v) && isMem(w) {
|
||||
// v is a load.
|
||||
s := nextMem[w.ID]
|
||||
if s == nil || s.Block != b {
|
||||
continue
|
||||
}
|
||||
additionalArgs[s.ID] = append(additionalArgs[s.ID], v)
|
||||
uses[v.ID]++
|
||||
}
|
||||
s := nextMem[w.ID]
|
||||
if s == nil || s.Block != b {
|
||||
continue
|
||||
}
|
||||
additionalArgs[s.ID] = append(additionalArgs[s.ID], v)
|
||||
uses[v.ID]++
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@ type (
|
||||
// Name Type
|
||||
TypeDecl struct {
|
||||
Name *Name
|
||||
Alias bool
|
||||
Type Expr
|
||||
Group *Group // nil means not part of a group
|
||||
Pragma Pragma
|
||||
|
||||
@@ -325,7 +325,7 @@ func (p *parser) constDecl(group *Group) Decl {
|
||||
return d
|
||||
}
|
||||
|
||||
// TypeSpec = identifier Type .
|
||||
// TypeSpec = identifier [ "=" ] Type .
|
||||
func (p *parser) typeDecl(group *Group) Decl {
|
||||
if trace {
|
||||
defer p.trace("typeDecl")()
|
||||
@@ -335,6 +335,7 @@ func (p *parser) typeDecl(group *Group) Decl {
|
||||
d.init(p)
|
||||
|
||||
d.Name = p.name()
|
||||
d.Alias = p.got(_Assign)
|
||||
d.Type = p.tryType()
|
||||
if d.Type == nil {
|
||||
p.syntax_error("in type declaration")
|
||||
|
||||
@@ -619,7 +619,11 @@ func (p *printer) printRawNode(n Node) {
|
||||
if n.Group == nil {
|
||||
p.print(_Type, blank)
|
||||
}
|
||||
p.print(n.Name, blank, n.Type)
|
||||
p.print(n.Name, blank)
|
||||
if n.Alias {
|
||||
p.print(_Assign, blank)
|
||||
}
|
||||
p.print(n.Type)
|
||||
|
||||
case *VarDecl:
|
||||
if n.Group == nil {
|
||||
|
||||
@@ -22,3 +22,20 @@ func TestPrint(t *testing.T) {
|
||||
Fprint(os.Stdout, ast, true)
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func TestPrintString(t *testing.T) {
|
||||
for _, want := range []string{
|
||||
"package p",
|
||||
"package p; type _ = int; type T1 = struct{}; type ( _ = *struct{}; T2 = float32 )",
|
||||
// TODO(gri) expand
|
||||
} {
|
||||
ast, err := ParseBytes([]byte(want), nil, nil, 0)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
if got := String(ast); got != want {
|
||||
t.Errorf("%q: got %q", want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,4 +31,5 @@ func Init() {
|
||||
gc.Thearch.SSAMarkMoves = ssaMarkMoves
|
||||
gc.Thearch.SSAGenValue = ssaGenValue
|
||||
gc.Thearch.SSAGenBlock = ssaGenBlock
|
||||
gc.Thearch.ZeroAuto = zeroAuto
|
||||
}
|
||||
|
||||
@@ -84,6 +84,23 @@ func zerorange(p *obj.Prog, frame int64, lo int64, hi int64, ax *uint32) *obj.Pr
|
||||
return p
|
||||
}
|
||||
|
||||
func zeroAuto(n *gc.Node, pp *obj.Prog) {
|
||||
// Note: this code must not clobber any registers.
|
||||
sym := gc.Linksym(n.Sym)
|
||||
size := n.Type.Size()
|
||||
for i := int64(0); i < size; i += 4 {
|
||||
p := gc.AddAsmAfter(x86.AMOVL, pp)
|
||||
pp = p
|
||||
p.From.Type = obj.TYPE_CONST
|
||||
p.From.Offset = 0
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Name = obj.NAME_AUTO
|
||||
p.To.Reg = x86.REG_SP
|
||||
p.To.Offset = n.Xoffset + i
|
||||
p.To.Sym = sym
|
||||
}
|
||||
}
|
||||
|
||||
func ginsnop() {
|
||||
p := gc.Prog(x86.AXCHGL)
|
||||
p.From.Type = obj.TYPE_REG
|
||||
|
||||
1
src/cmd/dist/test.go
vendored
1
src/cmd/dist/test.go
vendored
@@ -600,6 +600,7 @@ func (t *tester) registerTests() {
|
||||
})
|
||||
}
|
||||
}
|
||||
return // skip API check on go1.8.typealias branch
|
||||
if t.goos != "nacl" && t.goos != "android" && !t.iOS() {
|
||||
t.tests = append(t.tests, distTest{
|
||||
name: "api",
|
||||
|
||||
@@ -71,6 +71,7 @@ var tests = []test{
|
||||
`const MultiLineConst = ...`, // Multi line constant.
|
||||
`var MultiLineVar = map\[struct{ ... }\]struct{ ... }{ ... }`, // Multi line variable.
|
||||
`func MultiLineFunc\(x interface{ ... }\) \(r struct{ ... }\)`, // Multi line function.
|
||||
`type T1 = T2`, // Type alias
|
||||
},
|
||||
[]string{
|
||||
`const internalConstant = 2`, // No internal constants.
|
||||
@@ -89,6 +90,7 @@ var tests = []test{
|
||||
`unexportedTypedConstant`, // No unexported typed constant.
|
||||
`Field`, // No fields.
|
||||
`Method`, // No methods.
|
||||
`type T1 T2`, // Type alias does not display as type declaration.
|
||||
},
|
||||
},
|
||||
// Package dump -u
|
||||
@@ -265,6 +267,18 @@ var tests = []test{
|
||||
`error`, // No embedded error.
|
||||
},
|
||||
},
|
||||
// Type T1 dump (alias).
|
||||
{
|
||||
"type T1",
|
||||
[]string{p+".T1"},
|
||||
[]string{
|
||||
`type T1 = T2`,
|
||||
},
|
||||
[]string{
|
||||
`type T1 T2`,
|
||||
`type ExportedType`,
|
||||
},
|
||||
},
|
||||
// Type -u with unexported fields.
|
||||
{
|
||||
"type with unexported fields and -u",
|
||||
|
||||
@@ -258,7 +258,11 @@ func (pkg *Package) oneLineNodeDepth(node ast.Node, depth int) string {
|
||||
return fmt.Sprintf("func %s%s%s", recv, name, fnc)
|
||||
|
||||
case *ast.TypeSpec:
|
||||
return fmt.Sprintf("type %s %s", n.Name.Name, pkg.oneLineNodeDepth(n.Type, depth))
|
||||
sep := " "
|
||||
if n.Assign.IsValid() {
|
||||
sep = " = "
|
||||
}
|
||||
return fmt.Sprintf("type %s%s%s", n.Name.Name, sep, pkg.oneLineNodeDepth(n.Type, depth))
|
||||
|
||||
case *ast.FuncType:
|
||||
var params []string
|
||||
|
||||
4
src/cmd/doc/testdata/pkg.go
vendored
4
src/cmd/doc/testdata/pkg.go
vendored
@@ -172,3 +172,7 @@ const (
|
||||
)
|
||||
|
||||
const ConstGroup4 ExportedType = ExportedType{}
|
||||
|
||||
type T2 int
|
||||
|
||||
type T1 = T2
|
||||
|
||||
@@ -3073,6 +3073,26 @@ func (b *builder) ccompile(p *Package, outfile string, flags []string, file stri
|
||||
desc := p.ImportPath
|
||||
output, err := b.runOut(p.Dir, desc, nil, compiler, flags, "-o", outfile, "-c", file)
|
||||
if len(output) > 0 {
|
||||
// On FreeBSD 11, when we pass -g to clang 3.8 it
|
||||
// invokes its internal assembler with -dwarf-version=2.
|
||||
// When it sees .section .note.GNU-stack, it warns
|
||||
// "DWARF2 only supports one section per compilation unit".
|
||||
// This warning makes no sense, since the section is empty,
|
||||
// but it confuses people.
|
||||
// We work around the problem by detecting the warning
|
||||
// and dropping -g and trying again.
|
||||
if bytes.Contains(output, []byte("DWARF2 only supports one section per compilation unit")) {
|
||||
newFlags := make([]string, 0, len(flags))
|
||||
for _, f := range flags {
|
||||
if !strings.HasPrefix(f, "-g") {
|
||||
newFlags = append(newFlags, f)
|
||||
}
|
||||
}
|
||||
if len(newFlags) < len(flags) {
|
||||
return b.ccompile(p, outfile, newFlags, file, compiler)
|
||||
}
|
||||
}
|
||||
|
||||
b.showOutput(p.Dir, desc, b.processOutput(output))
|
||||
if err != nil {
|
||||
err = errPrintedOutput
|
||||
|
||||
24
src/cmd/gofmt/testdata/typealias.golden
vendored
Normal file
24
src/cmd/gofmt/testdata/typealias.golden
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
package q
|
||||
|
||||
import "p"
|
||||
|
||||
type _ = int
|
||||
type a = struct{ x int }
|
||||
type b = p.B
|
||||
|
||||
type (
|
||||
_ = chan<- int
|
||||
aa = interface{}
|
||||
bb = p.BB
|
||||
)
|
||||
|
||||
// TODO(gri) We may want to put the '=' into a separate column if
|
||||
// we have mixed (regular and alias) type declarations in a group.
|
||||
type (
|
||||
_ chan<- int
|
||||
_ = chan<- int
|
||||
aa0 interface{}
|
||||
aaa = interface{}
|
||||
bb0 p.BB
|
||||
bbb = p.BB
|
||||
)
|
||||
24
src/cmd/gofmt/testdata/typealias.input
vendored
Normal file
24
src/cmd/gofmt/testdata/typealias.input
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
package q
|
||||
|
||||
import "p"
|
||||
|
||||
type _ = int
|
||||
type a = struct{ x int }
|
||||
type b = p.B
|
||||
|
||||
type (
|
||||
_ = chan<- int
|
||||
aa = interface{}
|
||||
bb = p.BB
|
||||
)
|
||||
|
||||
// TODO(gri) We may want to put the '=' into a separate column if
|
||||
// we have mixed (regular and alias) type declarations in a group.
|
||||
type (
|
||||
_ chan<- int
|
||||
_ = chan<- int
|
||||
aa0 interface{}
|
||||
aaa = interface{}
|
||||
bb0 p.BB
|
||||
bbb = p.BB
|
||||
)
|
||||
@@ -255,7 +255,7 @@ func decodetypeStructFieldType(s *Symbol, i int) *Symbol {
|
||||
|
||||
func decodetypeStructFieldOffs(arch *sys.Arch, s *Symbol, i int) int64 {
|
||||
off := decodetypeStructFieldArrayOff(s, i)
|
||||
return int64(decodeInuxi(arch, s.P[off+2*SysArch.PtrSize:], SysArch.IntSize))
|
||||
return int64(decodeInuxi(arch, s.P[off+2*SysArch.PtrSize:], SysArch.IntSize) >> 1)
|
||||
}
|
||||
|
||||
// InterfaceType.methods.length
|
||||
|
||||
@@ -1955,12 +1955,12 @@ func (s *Stmt) QueryContext(ctx context.Context, args ...interface{}) (*Rows, er
|
||||
rowsi: rowsi,
|
||||
// releaseConn set below
|
||||
}
|
||||
rows.initContextClose(ctx)
|
||||
s.db.addDep(s, rows)
|
||||
rows.releaseConn = func(err error) {
|
||||
releaseConn(err)
|
||||
s.db.removeDep(s, rows)
|
||||
}
|
||||
rows.initContextClose(ctx)
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -322,7 +322,7 @@ func TestQueryContext(t *testing.T) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
if err := ctx.Err(); err != context.Canceled {
|
||||
t.Fatalf("context err = %v; want context.Canceled")
|
||||
t.Fatalf("context err = %v; want context.Canceled", ctx.Err())
|
||||
}
|
||||
default:
|
||||
t.Fatalf("context err = nil; want context.Canceled")
|
||||
@@ -413,7 +413,8 @@ func TestTxContextWait(t *testing.T) {
|
||||
db := newTestDB(t, "people")
|
||||
defer closeDB(t, db)
|
||||
|
||||
ctx, _ := context.WithTimeout(context.Background(), time.Millisecond*15)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*15)
|
||||
defer cancel()
|
||||
|
||||
tx, err := db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
|
||||
@@ -848,6 +848,7 @@ type (
|
||||
TypeSpec struct {
|
||||
Doc *CommentGroup // associated documentation; or nil
|
||||
Name *Ident // type name
|
||||
Assign token.Pos // position of '=', if any
|
||||
Type Expr // *Ident, *ParenExpr, *SelectorExpr, *StarExpr, or any of the *XxxTypes
|
||||
Comment *CommentGroup // line comments; or nil
|
||||
}
|
||||
|
||||
@@ -290,7 +290,8 @@ func defaultContext() Context {
|
||||
// in all releases >= Go 1.x. Code that requires Go 1.x or later should
|
||||
// say "+build go1.x", and code that should only be built before Go 1.x
|
||||
// (perhaps it is the stub to use in that case) should say "+build !go1.x".
|
||||
c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3", "go1.4", "go1.5", "go1.6", "go1.7", "go1.8"}
|
||||
// NOTE: If you add to this list, also update the doc comment in doc.go.
|
||||
c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3", "go1.4", "go1.5", "go1.6", "go1.7", "go1.8", "go1.8.typealias"}
|
||||
|
||||
env := os.Getenv("CGO_ENABLED")
|
||||
if env == "" {
|
||||
|
||||
@@ -105,6 +105,7 @@
|
||||
// - "go1.6", from Go version 1.6 onward
|
||||
// - "go1.7", from Go version 1.7 onward
|
||||
// - "go1.8", from Go version 1.8 onward
|
||||
// - "go1.8.typealias", for Go version 1.8 with aliases
|
||||
// - any additional words listed in ctxt.BuildTags
|
||||
//
|
||||
// If a file's name, after stripping the extension and a possible _test suffix,
|
||||
|
||||
@@ -101,6 +101,7 @@ var importerTests = [...]importerTest{
|
||||
{pkgpath: "unicode", name: "IsUpper", want: "func IsUpper(r rune) bool"},
|
||||
{pkgpath: "unicode", name: "MaxRune", want: "const MaxRune untyped rune", wantval: "1114111"},
|
||||
{pkgpath: "imports", wantinits: []string{"imports..import", "fmt..import", "math..import"}},
|
||||
{pkgpath: "alias", name: "IntAlias2", want: "type IntAlias2 = Int"},
|
||||
}
|
||||
|
||||
func TestGoxImporter(t *testing.T) {
|
||||
|
||||
@@ -370,27 +370,41 @@ func (p *parser) parseConst(pkg *types.Package) *types.Const {
|
||||
return types.NewConst(token.NoPos, pkg, name, typ, val)
|
||||
}
|
||||
|
||||
// TypeName = ExportedName .
|
||||
func (p *parser) parseTypeName() *types.TypeName {
|
||||
pkg, name := p.parseExportedName()
|
||||
scope := pkg.Scope()
|
||||
if obj := scope.Lookup(name); obj != nil {
|
||||
return obj.(*types.TypeName)
|
||||
}
|
||||
obj := types.NewTypeName(token.NoPos, pkg, name, nil)
|
||||
// a named type may be referred to before the underlying type
|
||||
// is known - set it up
|
||||
types.NewNamed(obj, nil, nil)
|
||||
scope.Insert(obj)
|
||||
return obj
|
||||
}
|
||||
|
||||
// NamedType = TypeName Type { Method } .
|
||||
// NamedType = TypeName [ "=" ] Type { Method } .
|
||||
// TypeName = ExportedName .
|
||||
// Method = "func" "(" Param ")" Name ParamList ResultList ";" .
|
||||
func (p *parser) parseNamedType(n int) types.Type {
|
||||
obj := p.parseTypeName()
|
||||
pkg, name := p.parseExportedName()
|
||||
scope := pkg.Scope()
|
||||
|
||||
if p.tok == '=' {
|
||||
// type alias
|
||||
p.next()
|
||||
typ := p.parseType(pkg)
|
||||
if obj := scope.Lookup(name); obj != nil {
|
||||
typ = obj.Type() // use previously imported type
|
||||
if typ == nil {
|
||||
p.errorf("%v (type alias) used in cycle", obj)
|
||||
}
|
||||
} else {
|
||||
obj = types.NewTypeName(token.NoPos, pkg, name, typ)
|
||||
scope.Insert(obj)
|
||||
}
|
||||
p.typeMap[n] = typ
|
||||
return typ
|
||||
}
|
||||
|
||||
// named type
|
||||
obj := scope.Lookup(name)
|
||||
if obj == nil {
|
||||
// a named type may be referred to before the underlying type
|
||||
// is known - set it up
|
||||
tname := types.NewTypeName(token.NoPos, pkg, name, nil)
|
||||
types.NewNamed(tname, nil, nil)
|
||||
scope.Insert(tname)
|
||||
obj = tname
|
||||
}
|
||||
|
||||
pkg := obj.Pkg()
|
||||
typ := obj.Type()
|
||||
p.typeMap[n] = typ
|
||||
|
||||
@@ -409,8 +423,8 @@ func (p *parser) parseNamedType(n int) types.Type {
|
||||
nt.SetUnderlying(underlying.Underlying())
|
||||
}
|
||||
|
||||
// collect associated methods
|
||||
for p.tok == scanner.Ident {
|
||||
// collect associated methods
|
||||
p.expectKeyword("func")
|
||||
p.expect('(')
|
||||
receiver, _ := p.parseParam(pkg)
|
||||
|
||||
4
src/go/internal/gccgoimporter/testdata/alias.gox
vendored
Normal file
4
src/go/internal/gccgoimporter/testdata/alias.gox
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
v1;
|
||||
package alias;
|
||||
pkgpath alias;
|
||||
type <type 115 "I1" <type 116 interface { M1 (? <type 117 "IntAlias2" = <type 118 "IntAlias" = <type 119 "Int" <type -11>>>>) < type 114>; M2 () <type 1>; }>>;
|
||||
@@ -98,10 +98,10 @@ func BImportData(fset *token.FileSet, imports map[string]*types.Package, data []
|
||||
|
||||
// read version specific flags - extend as necessary
|
||||
switch p.version {
|
||||
// case 4:
|
||||
// case 5:
|
||||
// ...
|
||||
// fallthrough
|
||||
case 3, 2, 1:
|
||||
case 4, 3, 2, 1:
|
||||
p.debugFormat = p.rawStringln(p.rawByte()) == "debug"
|
||||
p.trackAllTypes = p.int() != 0
|
||||
p.posInfoFormat = p.int() != 0
|
||||
@@ -208,7 +208,6 @@ func (p *importer) pkg() *types.Package {
|
||||
}
|
||||
|
||||
// objTag returns the tag value for each object kind.
|
||||
// obj must not be a *types.Alias.
|
||||
func objTag(obj types.Object) int {
|
||||
switch obj.(type) {
|
||||
case *types.Const:
|
||||
@@ -219,7 +218,6 @@ func objTag(obj types.Object) int {
|
||||
return varTag
|
||||
case *types.Func:
|
||||
return funcTag
|
||||
// Aliases are not exported multiple times, thus we should not see them here.
|
||||
default:
|
||||
errorf("unexpected object: %v (%T)", obj, obj) // panics
|
||||
panic("unreachable")
|
||||
@@ -237,14 +235,14 @@ func (p *importer) declare(obj types.Object) {
|
||||
pkg := obj.Pkg()
|
||||
if alt := pkg.Scope().Insert(obj); alt != nil {
|
||||
// This can only trigger if we import a (non-type) object a second time.
|
||||
// Excluding aliases, this cannot happen because 1) we only import a package
|
||||
// Excluding type aliases, this cannot happen because 1) we only import a package
|
||||
// once; and b) we ignore compiler-specific export data which may contain
|
||||
// functions whose inlined function bodies refer to other functions that
|
||||
// were already imported.
|
||||
// However, aliases require reexporting the original object, so we need
|
||||
// However, type aliases require reexporting the original type, so we need
|
||||
// to allow it (see also the comment in cmd/compile/internal/gc/bimport.go,
|
||||
// method importer.obj, switch case importing functions).
|
||||
// Note that the original itself cannot be an alias.
|
||||
// TODO(gri) review/update this comment once the gc compiler handles type aliases.
|
||||
if !sameObj(obj, alt) {
|
||||
errorf("inconsistent import:\n\t%v\npreviously imported as:\n\t%v\n", obj, alt)
|
||||
}
|
||||
@@ -260,6 +258,13 @@ func (p *importer) obj(tag int) {
|
||||
val := p.value()
|
||||
p.declare(types.NewConst(pos, pkg, name, typ, val))
|
||||
|
||||
case aliasTag:
|
||||
// TODO(gri) verify type alias hookup is correct
|
||||
pos := p.pos()
|
||||
pkg, name := p.qualifiedName()
|
||||
typ := p.typ(nil)
|
||||
p.declare(types.NewTypeName(pos, pkg, name, typ))
|
||||
|
||||
case typeTag:
|
||||
p.typ(nil)
|
||||
|
||||
@@ -277,19 +282,6 @@ func (p *importer) obj(tag int) {
|
||||
sig := types.NewSignature(nil, params, result, isddd)
|
||||
p.declare(types.NewFunc(pos, pkg, name, sig))
|
||||
|
||||
case aliasTag:
|
||||
pos := p.pos()
|
||||
name := p.string()
|
||||
var orig types.Object
|
||||
if pkg, name := p.qualifiedName(); pkg != nil {
|
||||
orig = pkg.Scope().Lookup(name)
|
||||
}
|
||||
// Alias-related code. Keep for now.
|
||||
_ = pos
|
||||
_ = name
|
||||
_ = orig
|
||||
// p.declare(types.NewAlias(pos, p.pkgList[0], name, orig))
|
||||
|
||||
default:
|
||||
errorf("unexpected object tag %d", tag)
|
||||
}
|
||||
@@ -349,9 +341,7 @@ var (
|
||||
|
||||
func (p *importer) qualifiedName() (pkg *types.Package, name string) {
|
||||
name = p.string()
|
||||
if name != "" {
|
||||
pkg = p.pkg()
|
||||
}
|
||||
pkg = p.pkg()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -556,17 +546,17 @@ func (p *importer) fieldList(parent *types.Package) (fields []*types.Var, tags [
|
||||
fields = make([]*types.Var, n)
|
||||
tags = make([]string, n)
|
||||
for i := range fields {
|
||||
fields[i] = p.field(parent)
|
||||
tags[i] = p.string()
|
||||
fields[i], tags[i] = p.field(parent)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *importer) field(parent *types.Package) *types.Var {
|
||||
func (p *importer) field(parent *types.Package) (*types.Var, string) {
|
||||
pos := p.pos()
|
||||
pkg, name := p.fieldName(parent)
|
||||
pkg, name, alias := p.fieldName(parent)
|
||||
typ := p.typ(parent)
|
||||
tag := p.string()
|
||||
|
||||
anonymous := false
|
||||
if name == "" {
|
||||
@@ -578,12 +568,15 @@ func (p *importer) field(parent *types.Package) *types.Var {
|
||||
case *types.Named:
|
||||
name = typ.Obj().Name()
|
||||
default:
|
||||
errorf("anonymous field expected")
|
||||
errorf("named base type expected")
|
||||
}
|
||||
anonymous = true
|
||||
} else if alias {
|
||||
// anonymous field: we have an explicit name because it's an alias
|
||||
anonymous = true
|
||||
}
|
||||
|
||||
return types.NewField(pos, pkg, name, typ, anonymous)
|
||||
return types.NewField(pos, pkg, name, typ, anonymous), tag
|
||||
}
|
||||
|
||||
func (p *importer) methodList(parent *types.Package) (methods []*types.Func) {
|
||||
@@ -598,31 +591,42 @@ func (p *importer) methodList(parent *types.Package) (methods []*types.Func) {
|
||||
|
||||
func (p *importer) method(parent *types.Package) *types.Func {
|
||||
pos := p.pos()
|
||||
pkg, name := p.fieldName(parent)
|
||||
pkg, name, _ := p.fieldName(parent)
|
||||
params, isddd := p.paramList()
|
||||
result, _ := p.paramList()
|
||||
sig := types.NewSignature(nil, params, result, isddd)
|
||||
return types.NewFunc(pos, pkg, name, sig)
|
||||
}
|
||||
|
||||
func (p *importer) fieldName(parent *types.Package) (*types.Package, string) {
|
||||
name := p.string()
|
||||
pkg := parent
|
||||
func (p *importer) fieldName(parent *types.Package) (pkg *types.Package, name string, alias bool) {
|
||||
name = p.string()
|
||||
pkg = parent
|
||||
if pkg == nil {
|
||||
// use the imported package instead
|
||||
pkg = p.pkgList[0]
|
||||
}
|
||||
if p.version == 0 && name == "_" {
|
||||
// version 0 didn't export a package for _ fields
|
||||
return pkg, name
|
||||
return
|
||||
}
|
||||
if name != "" && !exported(name) {
|
||||
if name == "?" {
|
||||
name = ""
|
||||
}
|
||||
switch name {
|
||||
case "":
|
||||
// 1) field name matches base type name and is exported: nothing to do
|
||||
case "?":
|
||||
// 2) field name matches base type name and is not exported: need package
|
||||
name = ""
|
||||
pkg = p.pkg()
|
||||
case "@":
|
||||
// 3) field name doesn't match type name (alias)
|
||||
name = p.string()
|
||||
alias = true
|
||||
fallthrough
|
||||
default:
|
||||
if !exported(name) {
|
||||
pkg = p.pkg()
|
||||
}
|
||||
}
|
||||
return pkg, name
|
||||
return
|
||||
}
|
||||
|
||||
func (p *importer) paramList() (*types.Tuple, bool) {
|
||||
@@ -893,7 +897,7 @@ const (
|
||||
nilTag // only used by gc (appears in exported inlined function bodies)
|
||||
unknownTag // not used by gc (only appears in packages with errors)
|
||||
|
||||
// Aliases
|
||||
// Type aliases
|
||||
aliasTag
|
||||
)
|
||||
|
||||
@@ -917,7 +921,7 @@ var predeclared = []types.Type{
|
||||
types.Typ[types.Complex128],
|
||||
types.Typ[types.String],
|
||||
|
||||
// aliases
|
||||
// basic type aliases
|
||||
types.Universe.Lookup("byte").Type(),
|
||||
types.Universe.Lookup("rune").Type(),
|
||||
|
||||
|
||||
@@ -2327,7 +2327,10 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Token, _ int) ast.
|
||||
// (Global identifiers are resolved in a separate phase after parsing.)
|
||||
spec := &ast.TypeSpec{Doc: doc, Name: ident}
|
||||
p.declare(spec, nil, p.topScope, ast.Typ, ident)
|
||||
|
||||
if p.tok == token.ASSIGN {
|
||||
spec.Assign = p.pos
|
||||
p.next()
|
||||
}
|
||||
spec.Type = p.parseType()
|
||||
p.expectSemi() // call before accessing p.linecomment
|
||||
spec.Comment = p.lineComment
|
||||
|
||||
@@ -46,6 +46,8 @@ var valids = []string{
|
||||
`package p; const (x = 0; y; z)`, // issue 9639
|
||||
`package p; var _ = map[P]int{P{}:0, {}:1}`,
|
||||
`package p; var _ = map[*P]int{&P{}:0, {}:1}`,
|
||||
`package p; type T = int`,
|
||||
`package p; type (T = p.T; _ = struct{}; x = *T)`,
|
||||
}
|
||||
|
||||
func TestValid(t *testing.T) {
|
||||
|
||||
@@ -1447,6 +1447,9 @@ func (p *printer) spec(spec ast.Spec, n int, doIndent bool) {
|
||||
} else {
|
||||
p.print(vtab)
|
||||
}
|
||||
if s.Assign.IsValid() {
|
||||
p.print(token.ASSIGN, blank)
|
||||
}
|
||||
p.expr(s.Type)
|
||||
p.setComment(s.Comment)
|
||||
|
||||
|
||||
15
src/go/printer/testdata/declarations.golden
vendored
15
src/go/printer/testdata/declarations.golden
vendored
@@ -985,3 +985,18 @@ func _(struct {
|
||||
x int
|
||||
y int
|
||||
}) // no extra comma between } and )
|
||||
|
||||
// alias declarations
|
||||
|
||||
type c0 struct{}
|
||||
type c1 = C
|
||||
type c2 = struct{ x int }
|
||||
type c3 = p.C
|
||||
type (
|
||||
s struct{}
|
||||
a = A
|
||||
b = A
|
||||
c = foo
|
||||
d = interface{}
|
||||
ddd = p.Foo
|
||||
)
|
||||
|
||||
15
src/go/printer/testdata/declarations.input
vendored
15
src/go/printer/testdata/declarations.input
vendored
@@ -999,3 +999,18 @@ func _(struct {
|
||||
x int
|
||||
y int
|
||||
}) // no extra comma between } and )
|
||||
|
||||
// alias declarations
|
||||
|
||||
type c0 struct{}
|
||||
type c1 = C
|
||||
type c2 = struct{ x int}
|
||||
type c3 = p.C
|
||||
type (
|
||||
s struct{}
|
||||
a = A
|
||||
b = A
|
||||
c = foo
|
||||
d = interface{}
|
||||
ddd = p.Foo
|
||||
)
|
||||
@@ -1295,155 +1295,3 @@ func f(x int) { y := x; print(y) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Alias-related code. Keep for now.
|
||||
/*
|
||||
func TestAliases(t *testing.T) {
|
||||
testenv.MustHaveGoBuild(t)
|
||||
|
||||
const src = `
|
||||
package b
|
||||
|
||||
import (
|
||||
"./testdata/alias"
|
||||
a "./testdata/alias"
|
||||
"math"
|
||||
)
|
||||
|
||||
const (
|
||||
c1 = alias.Pi1
|
||||
c2 => a.Pi1
|
||||
c3 => a.Pi2
|
||||
c4 => math.Pi
|
||||
)
|
||||
|
||||
var (
|
||||
v1 => alias.Default
|
||||
v2 => a.Default
|
||||
v3 = f1
|
||||
)
|
||||
|
||||
type (
|
||||
t1 => alias.Context
|
||||
t2 => a.Context
|
||||
)
|
||||
|
||||
func f1 => alias.Sin
|
||||
func f2 => a.Sin
|
||||
|
||||
func _() {
|
||||
assert(c1 == alias.Pi1 && c2 == a.Pi1 && c3 == a.Pi2 && c4 == math.Pi)
|
||||
assert(c2 == c2 && c2 == c3 && c3 == c4)
|
||||
v1 = v2 // must be assignable
|
||||
var _ *t1 = new(t2) // must be assignable
|
||||
var _ t2 = alias.Default
|
||||
f1(1) // must be callable
|
||||
f2(1)
|
||||
_ = alias.Sin(1)
|
||||
_ = a.Sin(1)
|
||||
}
|
||||
`
|
||||
|
||||
if out := compile(t, "testdata", "alias.go"); out != "" {
|
||||
defer os.Remove(out)
|
||||
}
|
||||
|
||||
DefPredeclaredTestFuncs() // declare assert built-in for testing
|
||||
mustTypecheck(t, "Aliases", src, nil)
|
||||
}
|
||||
|
||||
func compile(t *testing.T, dirname, filename string) string {
|
||||
cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", filename)
|
||||
cmd.Dir = dirname
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Logf("%s", out)
|
||||
t.Fatalf("go tool compile %s failed: %s", filename, err)
|
||||
}
|
||||
// filename should end with ".go"
|
||||
return filepath.Join(dirname, filename[:len(filename)-2]+"o")
|
||||
}
|
||||
|
||||
func TestAliasDefUses(t *testing.T) {
|
||||
testenv.MustHaveGoBuild(t)
|
||||
|
||||
const src = `
|
||||
package p
|
||||
|
||||
import(
|
||||
"go/build"
|
||||
"go/types"
|
||||
)
|
||||
|
||||
// Defs
|
||||
const Invalid => types.Invalid
|
||||
type Struct => types.Struct
|
||||
var Default => build.Default
|
||||
func Implements => types.Implements
|
||||
|
||||
// Uses
|
||||
const _ = Invalid
|
||||
var _ types.Struct = Struct{} // types must be identical
|
||||
var _ build.Context = Default
|
||||
var _ = Implements(nil, nil)
|
||||
`
|
||||
|
||||
info := Info{
|
||||
Defs: make(map[*ast.Ident]Object),
|
||||
Uses: make(map[*ast.Ident]Object),
|
||||
}
|
||||
mustTypecheck(t, "TestAliasDefUses", src, &info)
|
||||
|
||||
// verify Defs
|
||||
defs := map[string]string{
|
||||
"Invalid": "types.Invalid",
|
||||
"Struct": "types.Struct",
|
||||
"Default": "build.Default",
|
||||
"Implements": "types.Implements",
|
||||
}
|
||||
|
||||
for ident, obj := range info.Defs {
|
||||
if alias, ok := obj.(*Alias); ok {
|
||||
if want := defs[ident.Name]; want != "" {
|
||||
orig := alias.Orig()
|
||||
if got := orig.Pkg().Name() + "." + orig.Name(); got != want {
|
||||
t.Errorf("%v: got %v, want %v", ident, got, want)
|
||||
}
|
||||
delete(defs, ident.Name) // mark as found
|
||||
} else {
|
||||
t.Errorf("unexpected alias def of %v", ident)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(defs) != 0 {
|
||||
t.Errorf("missing aliases: %v", defs)
|
||||
}
|
||||
|
||||
// verify Uses
|
||||
uses := map[string]string{
|
||||
"Invalid": "types.Invalid",
|
||||
"Struct": "types.Struct",
|
||||
"Default": "build.Default",
|
||||
"Implements": "types.Implements",
|
||||
}
|
||||
|
||||
for ident, obj := range info.Uses {
|
||||
if alias, ok := obj.(*Alias); ok {
|
||||
if want := uses[ident.Name]; want != "" {
|
||||
orig := alias.Orig()
|
||||
if got := orig.Pkg().Name() + "." + orig.Name(); got != want {
|
||||
t.Errorf("%v: got %v, want %v", ident, got, want)
|
||||
}
|
||||
delete(uses, ident.Name) // mark as found
|
||||
} else {
|
||||
t.Errorf("unexpected alias use of %v", ident)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(uses) != 0 {
|
||||
t.Errorf("missing aliases: %v", defs)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -275,8 +275,6 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
|
||||
// so we don't need a "package" mode for operands: package names
|
||||
// can only appear in qualified identifiers which are mapped to
|
||||
// selector expressions.
|
||||
// (see also decl.go: checker.aliasDecl)
|
||||
// TODO(gri) factor this code out and share with checker.aliasDecl
|
||||
if ident, ok := e.X.(*ast.Ident); ok {
|
||||
_, obj := check.scope.LookupParent(ident.Name, check.pos)
|
||||
if pname, _ := obj.(*PkgName); pname != nil {
|
||||
@@ -296,12 +294,6 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
|
||||
// ok to continue
|
||||
}
|
||||
check.recordUse(e.Sel, exp)
|
||||
exp = original(exp)
|
||||
|
||||
// avoid further errors if the imported object is an alias that's broken
|
||||
if exp == nil {
|
||||
goto Error
|
||||
}
|
||||
|
||||
// Simplified version of the code for *ast.Idents:
|
||||
// - imported objects are always fully initialized
|
||||
|
||||
@@ -68,11 +68,11 @@ var tests = [][]string{
|
||||
{"testdata/decls1.src"},
|
||||
{"testdata/decls2a.src", "testdata/decls2b.src"},
|
||||
{"testdata/decls3.src"},
|
||||
{"testdata/decls4.src"},
|
||||
{"testdata/const0.src"},
|
||||
{"testdata/const1.src"},
|
||||
{"testdata/constdecl.src"},
|
||||
{"testdata/vardecl.src"},
|
||||
//{"testdata/aliasdecl.src"},
|
||||
{"testdata/expr0.src"},
|
||||
{"testdata/expr1.src"},
|
||||
{"testdata/expr2.src"},
|
||||
|
||||
@@ -81,14 +81,10 @@ func (check *Checker) objDecl(obj Object, def *Named, path []*TypeName) {
|
||||
check.varDecl(obj, d.lhs, d.typ, d.init)
|
||||
case *TypeName:
|
||||
// invalid recursive types are detected via path
|
||||
check.typeDecl(obj, d.typ, def, path)
|
||||
check.typeDecl(obj, d.typ, def, path, d.alias)
|
||||
case *Func:
|
||||
// functions may be recursive - no need to track dependencies
|
||||
check.funcDecl(obj, d)
|
||||
// Alias-related code. Keep for now.
|
||||
// case *Alias:
|
||||
// // aliases cannot be recursive - no need to track dependencies
|
||||
// check.aliasDecl(obj, d)
|
||||
default:
|
||||
unreachable()
|
||||
}
|
||||
@@ -219,33 +215,42 @@ func (n *Named) setUnderlying(typ Type) {
|
||||
}
|
||||
}
|
||||
|
||||
func (check *Checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, path []*TypeName) {
|
||||
func (check *Checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, path []*TypeName, alias bool) {
|
||||
assert(obj.typ == nil)
|
||||
|
||||
// type declarations cannot use iota
|
||||
assert(check.iota == nil)
|
||||
|
||||
named := &Named{obj: obj}
|
||||
def.setUnderlying(named)
|
||||
obj.typ = named // make sure recursive type declarations terminate
|
||||
if alias {
|
||||
|
||||
// determine underlying type of named
|
||||
check.typExpr(typ, named, append(path, obj))
|
||||
obj.typ = Typ[Invalid]
|
||||
obj.typ = check.typExpr(typ, nil, append(path, obj))
|
||||
|
||||
// The underlying type of named may be itself a named type that is
|
||||
// incomplete:
|
||||
//
|
||||
// type (
|
||||
// A B
|
||||
// B *C
|
||||
// C A
|
||||
// )
|
||||
//
|
||||
// The type of C is the (named) type of A which is incomplete,
|
||||
// and which has as its underlying type the named type B.
|
||||
// Determine the (final, unnamed) underlying type by resolving
|
||||
// any forward chain (they always end in an unnamed type).
|
||||
named.underlying = underlying(named.underlying)
|
||||
} else {
|
||||
|
||||
named := &Named{obj: obj}
|
||||
def.setUnderlying(named)
|
||||
obj.typ = named // make sure recursive type declarations terminate
|
||||
|
||||
// determine underlying type of named
|
||||
check.typExpr(typ, named, append(path, obj))
|
||||
|
||||
// The underlying type of named may be itself a named type that is
|
||||
// incomplete:
|
||||
//
|
||||
// type (
|
||||
// A B
|
||||
// B *C
|
||||
// C A
|
||||
// )
|
||||
//
|
||||
// The type of C is the (named) type of A which is incomplete,
|
||||
// and which has as its underlying type the named type B.
|
||||
// Determine the (final, unnamed) underlying type by resolving
|
||||
// any forward chain (they always end in an unnamed type).
|
||||
named.underlying = underlying(named.underlying)
|
||||
|
||||
}
|
||||
|
||||
// check and add associated methods
|
||||
// TODO(gri) It's easy to create pathological cases where the
|
||||
@@ -268,21 +273,23 @@ func (check *Checker) addMethodDecls(obj *TypeName) {
|
||||
|
||||
// spec: "If the base type is a struct type, the non-blank method
|
||||
// and field names must be distinct."
|
||||
base := obj.typ.(*Named)
|
||||
if t, _ := base.underlying.(*Struct); t != nil {
|
||||
for _, fld := range t.fields {
|
||||
if fld.name != "_" {
|
||||
assert(mset.insert(fld) == nil)
|
||||
base, _ := obj.typ.(*Named) // nil if receiver base type is type alias
|
||||
if base != nil {
|
||||
if t, _ := base.underlying.(*Struct); t != nil {
|
||||
for _, fld := range t.fields {
|
||||
if fld.name != "_" {
|
||||
assert(mset.insert(fld) == nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Checker.Files may be called multiple times; additional package files
|
||||
// may add methods to already type-checked types. Add pre-existing methods
|
||||
// so that we can detect redeclarations.
|
||||
for _, m := range base.methods {
|
||||
assert(m.name != "_")
|
||||
assert(mset.insert(m) == nil)
|
||||
// Checker.Files may be called multiple times; additional package files
|
||||
// may add methods to already type-checked types. Add pre-existing methods
|
||||
// so that we can detect redeclarations.
|
||||
for _, m := range base.methods {
|
||||
assert(m.name != "_")
|
||||
assert(mset.insert(m) == nil)
|
||||
}
|
||||
}
|
||||
|
||||
// type-check methods
|
||||
@@ -295,7 +302,7 @@ func (check *Checker) addMethodDecls(obj *TypeName) {
|
||||
case *Var:
|
||||
check.errorf(m.pos, "field and method with the same name %s", m.name)
|
||||
case *Func:
|
||||
check.errorf(m.pos, "method %s already declared for %s", m.name, base)
|
||||
check.errorf(m.pos, "method %s already declared for %s", m.name, obj)
|
||||
default:
|
||||
unreachable()
|
||||
}
|
||||
@@ -303,9 +310,12 @@ func (check *Checker) addMethodDecls(obj *TypeName) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// type-check
|
||||
check.objDecl(m, nil, nil)
|
||||
|
||||
// methods with blank _ names cannot be found - don't keep them
|
||||
if m.name != "_" {
|
||||
if base != nil && m.name != "_" {
|
||||
base.methods = append(base.methods, m)
|
||||
}
|
||||
}
|
||||
@@ -333,106 +343,6 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
// original returns the original Object if obj is an Alias;
|
||||
// otherwise it returns obj. The result is never an Alias,
|
||||
// but it may be nil.
|
||||
func original(obj Object) Object {
|
||||
// an alias stands for the original object; use that one instead
|
||||
if alias, _ := obj.(*disabledAlias); alias != nil {
|
||||
obj = alias.orig
|
||||
// aliases always refer to non-alias originals
|
||||
if _, ok := obj.(*disabledAlias); ok {
|
||||
panic("original is an alias")
|
||||
}
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
func (check *Checker) aliasDecl(obj *disabledAlias, decl *declInfo) {
|
||||
assert(obj.typ == nil)
|
||||
|
||||
// alias declarations cannot use iota
|
||||
assert(check.iota == nil)
|
||||
|
||||
// assume alias is invalid to start with
|
||||
obj.typ = Typ[Invalid]
|
||||
|
||||
// rhs must be package-qualified identifer pkg.sel (see also call.go: checker.selector)
|
||||
// TODO(gri) factor this code out and share with checker.selector
|
||||
rhs := decl.init
|
||||
var pkg *Package
|
||||
var sel *ast.Ident
|
||||
if sexpr, ok := rhs.(*ast.SelectorExpr); ok {
|
||||
if ident, ok := sexpr.X.(*ast.Ident); ok {
|
||||
_, obj := check.scope.LookupParent(ident.Name, check.pos)
|
||||
if pname, _ := obj.(*PkgName); pname != nil {
|
||||
assert(pname.pkg == check.pkg)
|
||||
check.recordUse(ident, pname)
|
||||
pname.used = true
|
||||
pkg = pname.imported
|
||||
sel = sexpr.Sel
|
||||
}
|
||||
}
|
||||
}
|
||||
if pkg == nil {
|
||||
check.errorf(rhs.Pos(), "invalid alias: %v is not a package-qualified identifier", rhs)
|
||||
return
|
||||
}
|
||||
|
||||
// qualified identifier must denote an exported object
|
||||
orig := pkg.scope.Lookup(sel.Name)
|
||||
if orig == nil || !orig.Exported() {
|
||||
if !pkg.fake {
|
||||
check.errorf(rhs.Pos(), "%s is not exported by package %s", sel.Name, pkg.name)
|
||||
}
|
||||
return
|
||||
}
|
||||
check.recordUse(sel, orig)
|
||||
orig = original(orig)
|
||||
|
||||
// avoid further errors if the imported object is an alias that's broken
|
||||
if orig == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// An alias declaration must not refer to package unsafe.
|
||||
if orig.Pkg() == Unsafe {
|
||||
check.errorf(rhs.Pos(), "invalid alias: %s refers to package unsafe (%v)", obj.Name(), orig)
|
||||
return
|
||||
}
|
||||
|
||||
// The original must be of the same kind as the alias declaration.
|
||||
var why string
|
||||
switch obj.kind {
|
||||
case token.CONST:
|
||||
if _, ok := orig.(*Const); !ok {
|
||||
why = "constant"
|
||||
}
|
||||
case token.TYPE:
|
||||
if _, ok := orig.(*TypeName); !ok {
|
||||
why = "type"
|
||||
}
|
||||
case token.VAR:
|
||||
if _, ok := orig.(*Var); !ok {
|
||||
why = "variable"
|
||||
}
|
||||
case token.FUNC:
|
||||
if _, ok := orig.(*Func); !ok {
|
||||
why = "function"
|
||||
}
|
||||
default:
|
||||
unreachable()
|
||||
}
|
||||
if why != "" {
|
||||
check.errorf(rhs.Pos(), "invalid alias: %v is not a %s", orig, why)
|
||||
return
|
||||
}
|
||||
|
||||
// alias is valid
|
||||
obj.typ = orig.Type()
|
||||
obj.orig = orig
|
||||
}
|
||||
|
||||
func (check *Checker) declStmt(decl ast.Decl) {
|
||||
pkg := check.pkg
|
||||
|
||||
@@ -540,7 +450,7 @@ func (check *Checker) declStmt(decl ast.Decl) {
|
||||
// the innermost containing block."
|
||||
scopePos := s.Name.Pos()
|
||||
check.declare(check.scope, s.Name, obj, scopePos)
|
||||
check.typeDecl(obj, s.Type, nil, nil)
|
||||
check.typeDecl(obj, s.Type, nil, nil, s.Assign.IsValid())
|
||||
|
||||
default:
|
||||
check.invalidAST(s.Pos(), "const, type, or var declaration expected")
|
||||
|
||||
@@ -239,10 +239,10 @@ func fib(x int) int {
|
||||
// type S string:
|
||||
// defined at fib.go:4:6
|
||||
// used at 6:23
|
||||
// type int int:
|
||||
// type int:
|
||||
// defined at -
|
||||
// used at 8:12, 8:17
|
||||
// type string string:
|
||||
// type string:
|
||||
// defined at -
|
||||
// used at 4:8
|
||||
// var b S:
|
||||
|
||||
@@ -67,24 +67,22 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
|
||||
}
|
||||
|
||||
typ, isPtr := deref(T)
|
||||
named, _ := typ.(*Named)
|
||||
|
||||
// *typ where typ is an interface has no methods.
|
||||
if isPtr {
|
||||
utyp := typ
|
||||
if named != nil {
|
||||
utyp = named.underlying
|
||||
}
|
||||
if _, ok := utyp.(*Interface); ok {
|
||||
return
|
||||
}
|
||||
if isPtr && IsInterface(typ) {
|
||||
return
|
||||
}
|
||||
|
||||
// Start with typ as single entry at shallowest depth.
|
||||
// If typ is not a named type, insert a nil type instead.
|
||||
current := []embeddedType{{named, nil, isPtr, false}}
|
||||
current := []embeddedType{{typ, nil, isPtr, false}}
|
||||
|
||||
// named types that we have seen already, allocated lazily
|
||||
// Named types that we have seen already, allocated lazily.
|
||||
// Used to avoid endless searches in case of recursive types.
|
||||
// Since only Named types can be used for recursive types, we
|
||||
// only need to track those.
|
||||
// (If we ever allow type aliases to construct recursive types,
|
||||
// we must use type identity rather than pointer equality for
|
||||
// the map key comparison, as we do in consolidateMultiples.)
|
||||
var seen map[*Named]bool
|
||||
|
||||
// search current depth
|
||||
@@ -93,11 +91,12 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
|
||||
|
||||
// look for (pkg, name) in all types at current depth
|
||||
for _, e := range current {
|
||||
// The very first time only, e.typ may be nil.
|
||||
// In this case, we don't have a named type and
|
||||
// we simply continue with the underlying type.
|
||||
if e.typ != nil {
|
||||
if seen[e.typ] {
|
||||
typ := e.typ
|
||||
|
||||
// If we have a named type, we may have associated methods.
|
||||
// Look for those first.
|
||||
if named, _ := typ.(*Named); named != nil {
|
||||
if seen[named] {
|
||||
// We have seen this type before, at a more shallow depth
|
||||
// (note that multiples of this type at the current depth
|
||||
// were consolidated before). The type at that depth shadows
|
||||
@@ -108,10 +107,10 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
|
||||
if seen == nil {
|
||||
seen = make(map[*Named]bool)
|
||||
}
|
||||
seen[e.typ] = true
|
||||
seen[named] = true
|
||||
|
||||
// look for a matching attached method
|
||||
if i, m := lookupMethod(e.typ.methods, pkg, name); m != nil {
|
||||
if i, m := lookupMethod(named.methods, pkg, name); m != nil {
|
||||
// potential match
|
||||
assert(m.typ != nil)
|
||||
index = concat(e.index, i)
|
||||
@@ -124,7 +123,7 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
|
||||
}
|
||||
|
||||
// continue with underlying type
|
||||
typ = e.typ.underlying
|
||||
typ = named.underlying
|
||||
}
|
||||
|
||||
switch t := typ.(type) {
|
||||
@@ -147,16 +146,15 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
|
||||
// we have a name collision on the same depth; in either
|
||||
// case we don't need to look further).
|
||||
// Embedded fields are always of the form T or *T where
|
||||
// T is a named type. If e.typ appeared multiple times at
|
||||
// T is a type name. If e.typ appeared multiple times at
|
||||
// this depth, f.typ appears multiple times at the next
|
||||
// depth.
|
||||
if obj == nil && f.anonymous {
|
||||
// Ignore embedded basic types - only user-defined
|
||||
// named types can have methods or struct fields.
|
||||
typ, isPtr := deref(f.typ)
|
||||
if t, _ := typ.(*Named); t != nil {
|
||||
next = append(next, embeddedType{t, concat(e.index, i), e.indirect || isPtr, e.multiples})
|
||||
}
|
||||
// TODO(gri) optimization: ignore types that can't
|
||||
// have fields or methods (only Named, Struct, and
|
||||
// Interface types need to be considered).
|
||||
next = append(next, embeddedType{typ, concat(e.index, i), e.indirect || isPtr, e.multiples})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,12 +191,12 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
|
||||
return nil, nil, false // not found
|
||||
}
|
||||
|
||||
// embeddedType represents an embedded named type
|
||||
// embeddedType represents an embedded type
|
||||
type embeddedType struct {
|
||||
typ *Named // nil means use the outer typ variable instead
|
||||
index []int // embedded field indices, starting with index at depth 0
|
||||
indirect bool // if set, there was a pointer indirection on the path to this field
|
||||
multiples bool // if set, typ appears multiple times at this depth
|
||||
typ Type
|
||||
index []int // embedded field indices, starting with index at depth 0
|
||||
indirect bool // if set, there was a pointer indirection on the path to this field
|
||||
multiples bool // if set, typ appears multiple times at this depth
|
||||
}
|
||||
|
||||
// consolidateMultiples collects multiple list entries with the same type
|
||||
@@ -209,10 +207,10 @@ func consolidateMultiples(list []embeddedType) []embeddedType {
|
||||
return list // at most one entry - nothing to do
|
||||
}
|
||||
|
||||
n := 0 // number of entries w/ unique type
|
||||
prev := make(map[*Named]int) // index at which type was previously seen
|
||||
n := 0 // number of entries w/ unique type
|
||||
prev := make(map[Type]int) // index at which type was previously seen
|
||||
for _, e := range list {
|
||||
if i, found := prev[e.typ]; found {
|
||||
if i, found := lookupType(prev, e.typ); found {
|
||||
list[i].multiples = true
|
||||
// ignore this entry
|
||||
} else {
|
||||
@@ -224,6 +222,21 @@ func consolidateMultiples(list []embeddedType) []embeddedType {
|
||||
return list[:n]
|
||||
}
|
||||
|
||||
func lookupType(m map[Type]int, typ Type) (int, bool) {
|
||||
// fast path: maybe the types are equal
|
||||
if i, found := m[typ]; found {
|
||||
return i, true
|
||||
}
|
||||
|
||||
for t, i := range m {
|
||||
if Identical(t, typ) {
|
||||
return i, true
|
||||
}
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// MissingMethod returns (nil, false) if V implements T, otherwise it
|
||||
// returns a missing method required by T and whether it is missing or
|
||||
// just has the wrong type.
|
||||
|
||||
@@ -72,24 +72,22 @@ func NewMethodSet(T Type) *MethodSet {
|
||||
var base methodSet
|
||||
|
||||
typ, isPtr := deref(T)
|
||||
named, _ := typ.(*Named)
|
||||
|
||||
// *typ where typ is an interface has no methods.
|
||||
if isPtr {
|
||||
utyp := typ
|
||||
if named != nil {
|
||||
utyp = named.underlying
|
||||
}
|
||||
if _, ok := utyp.(*Interface); ok {
|
||||
return &emptyMethodSet
|
||||
}
|
||||
if isPtr && IsInterface(typ) {
|
||||
return &emptyMethodSet
|
||||
}
|
||||
|
||||
// Start with typ as single entry at shallowest depth.
|
||||
// If typ is not a named type, insert a nil type instead.
|
||||
current := []embeddedType{{named, nil, isPtr, false}}
|
||||
current := []embeddedType{{typ, nil, isPtr, false}}
|
||||
|
||||
// named types that we have seen already, allocated lazily
|
||||
// Named types that we have seen already, allocated lazily.
|
||||
// Used to avoid endless searches in case of recursive types.
|
||||
// Since only Named types can be used for recursive types, we
|
||||
// only need to track those.
|
||||
// (If we ever allow type aliases to construct recursive types,
|
||||
// we must use type identity rather than pointer equality for
|
||||
// the map key comparison, as we do in consolidateMultiples.)
|
||||
var seen map[*Named]bool
|
||||
|
||||
// collect methods at current depth
|
||||
@@ -101,11 +99,12 @@ func NewMethodSet(T Type) *MethodSet {
|
||||
var mset methodSet
|
||||
|
||||
for _, e := range current {
|
||||
// The very first time only, e.typ may be nil.
|
||||
// In this case, we don't have a named type and
|
||||
// we simply continue with the underlying type.
|
||||
if e.typ != nil {
|
||||
if seen[e.typ] {
|
||||
typ := e.typ
|
||||
|
||||
// If we have a named type, we may have associated methods.
|
||||
// Look for those first.
|
||||
if named, _ := typ.(*Named); named != nil {
|
||||
if seen[named] {
|
||||
// We have seen this type before, at a more shallow depth
|
||||
// (note that multiples of this type at the current depth
|
||||
// were consolidated before). The type at that depth shadows
|
||||
@@ -116,12 +115,12 @@ func NewMethodSet(T Type) *MethodSet {
|
||||
if seen == nil {
|
||||
seen = make(map[*Named]bool)
|
||||
}
|
||||
seen[e.typ] = true
|
||||
seen[named] = true
|
||||
|
||||
mset = mset.add(e.typ.methods, e.index, e.indirect, e.multiples)
|
||||
mset = mset.add(named.methods, e.index, e.indirect, e.multiples)
|
||||
|
||||
// continue with underlying type
|
||||
typ = e.typ.underlying
|
||||
typ = named.underlying
|
||||
}
|
||||
|
||||
switch t := typ.(type) {
|
||||
@@ -130,16 +129,15 @@ func NewMethodSet(T Type) *MethodSet {
|
||||
fset = fset.add(f, e.multiples)
|
||||
|
||||
// Embedded fields are always of the form T or *T where
|
||||
// T is a named type. If typ appeared multiple times at
|
||||
// T is a type name. If typ appeared multiple times at
|
||||
// this depth, f.Type appears multiple times at the next
|
||||
// depth.
|
||||
if f.anonymous {
|
||||
// Ignore embedded basic types - only user-defined
|
||||
// named types can have methods or struct fields.
|
||||
typ, isPtr := deref(f.typ)
|
||||
if t, _ := typ.(*Named); t != nil {
|
||||
next = append(next, embeddedType{t, concat(e.index, i), e.indirect || isPtr, e.multiples})
|
||||
}
|
||||
// TODO(gri) optimization: ignore types that can't
|
||||
// have fields or methods (only Named, Struct, and
|
||||
// Interface types need to be considered).
|
||||
next = append(next, embeddedType{typ, concat(e.index, i), e.indirect || isPtr, e.multiples})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ type Object interface {
|
||||
Name() string // package local object name
|
||||
Type() Type // object type
|
||||
Exported() bool // reports whether the name starts with a capital letter
|
||||
Id() string // object id (see Id below)
|
||||
Id() string // object name if exported, qualified name if not exported (see func Id)
|
||||
|
||||
// String returns a human-readable string of the object.
|
||||
String() string
|
||||
@@ -64,15 +64,10 @@ func Id(pkg *Package, name string) string {
|
||||
// inside a package and outside a package - which breaks some
|
||||
// tests)
|
||||
path := "_"
|
||||
// TODO(gri): shouldn't !ast.IsExported(name) => pkg != nil be an precondition?
|
||||
// if pkg == nil {
|
||||
// panic("nil package in lookup of unexported name")
|
||||
// }
|
||||
if pkg != nil {
|
||||
// pkg is nil for objects in Universe scope and possibly types
|
||||
// introduced via Eval (see also comment in object.sameId)
|
||||
if pkg != nil && pkg.path != "" {
|
||||
path = pkg.path
|
||||
if path == "" {
|
||||
path = "_"
|
||||
}
|
||||
}
|
||||
return path + "." + name
|
||||
}
|
||||
@@ -154,7 +149,7 @@ func NewConst(pos token.Pos, pkg *Package, name string, typ Type, val constant.V
|
||||
func (obj *Const) Val() constant.Value { return obj.val }
|
||||
func (*Const) isDependency() {} // a constant may be a dependency of an initialization expression
|
||||
|
||||
// A TypeName represents a declared type.
|
||||
// A TypeName represents a name for a (named or alias) type.
|
||||
type TypeName struct {
|
||||
object
|
||||
}
|
||||
@@ -163,6 +158,26 @@ func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName {
|
||||
return &TypeName{object{nil, pos, pkg, name, typ, 0, token.NoPos}}
|
||||
}
|
||||
|
||||
// IsAlias reports whether obj is an alias name for a type.
|
||||
func (obj *TypeName) IsAlias() bool {
|
||||
switch t := obj.typ.(type) {
|
||||
case nil:
|
||||
return false
|
||||
case *Basic:
|
||||
// Any user-defined type name for a basic type is an alias for a
|
||||
// basic type (because basic types are pre-declared in the Universe
|
||||
// scope, outside any package scope), and so is any type name with
|
||||
// a different name than the name of the basic type it refers to.
|
||||
// Additionaly, we need to look for "byte" and "rune" because they
|
||||
// are aliases but have the same names (for better error messages).
|
||||
return obj.pkg != nil || t.name != obj.name || t == universeByte || t == universeRune
|
||||
case *Named:
|
||||
return obj != t.obj
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// A Variable represents a declared variable (including function parameters and results, and struct fields).
|
||||
type Var struct {
|
||||
object
|
||||
@@ -215,28 +230,6 @@ func (obj *Func) FullName() string {
|
||||
func (obj *Func) Scope() *Scope { return obj.typ.(*Signature).scope }
|
||||
func (*Func) isDependency() {} // a function may be a dependency of an initialization expression
|
||||
|
||||
// An Alias represents a declared alias.
|
||||
type disabledAlias struct {
|
||||
object
|
||||
orig Object // aliased constant, type, variable, or function; never an alias
|
||||
kind token.Token // token.CONST, token.TYPE, token.VAR, or token.FUNC (only needed during resolve phase)
|
||||
}
|
||||
|
||||
func disabledNewAlias(pos token.Pos, pkg *Package, name string, orig Object) *disabledAlias {
|
||||
var typ Type = Typ[Invalid]
|
||||
if orig != nil {
|
||||
typ = orig.Type()
|
||||
}
|
||||
// No need to set a valid Alias.kind - that field is only used during identifier
|
||||
// resolution (1st type-checker pass). We could store the field outside but it's
|
||||
// easier to keep it here.
|
||||
return &disabledAlias{object{nil, pos, pkg, name, typ, 0, token.NoPos}, orig, token.ILLEGAL}
|
||||
}
|
||||
|
||||
// Orig returns the aliased object, or nil if there was an error.
|
||||
// The returned object is never an Alias.
|
||||
func (obj *disabledAlias) disabledOrig() Object { return obj.orig }
|
||||
|
||||
// A Label represents a declared label.
|
||||
type Label struct {
|
||||
object
|
||||
@@ -264,7 +257,9 @@ type Nil struct {
|
||||
}
|
||||
|
||||
func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
|
||||
var tname *TypeName
|
||||
typ := obj.Type()
|
||||
|
||||
switch obj := obj.(type) {
|
||||
case *PkgName:
|
||||
fmt.Fprintf(buf, "package %s", obj.Name())
|
||||
@@ -277,8 +272,8 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
|
||||
buf.WriteString("const")
|
||||
|
||||
case *TypeName:
|
||||
tname = obj
|
||||
buf.WriteString("type")
|
||||
typ = typ.Underlying()
|
||||
|
||||
case *Var:
|
||||
if obj.isField {
|
||||
@@ -295,10 +290,6 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
|
||||
}
|
||||
return
|
||||
|
||||
// Alias-related code. Keep for now.
|
||||
// case *Alias:
|
||||
// buf.WriteString("alias")
|
||||
|
||||
case *Label:
|
||||
buf.WriteString("label")
|
||||
typ = nil
|
||||
@@ -322,10 +313,27 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
|
||||
writePackage(buf, obj.Pkg(), qf)
|
||||
}
|
||||
buf.WriteString(obj.Name())
|
||||
if typ != nil {
|
||||
buf.WriteByte(' ')
|
||||
WriteType(buf, typ, qf)
|
||||
|
||||
if typ == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if tname != nil {
|
||||
// We have a type object: Don't print anything more for
|
||||
// basic types since there's no more information (names
|
||||
// are the same; see also comment in TypeName.IsAlias).
|
||||
if _, ok := typ.(*Basic); ok {
|
||||
return
|
||||
}
|
||||
if tname.IsAlias() {
|
||||
buf.WriteString(" =")
|
||||
} else {
|
||||
typ = typ.Underlying()
|
||||
}
|
||||
}
|
||||
|
||||
buf.WriteByte(' ')
|
||||
WriteType(buf, typ, qf)
|
||||
}
|
||||
|
||||
func writePackage(buf *bytes.Buffer, pkg *Package, qf Qualifier) {
|
||||
@@ -353,15 +361,14 @@ func ObjectString(obj Object, qf Qualifier) string {
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (obj *PkgName) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *Const) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *TypeName) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *Var) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *Func) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *disabledAlias) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *Label) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *Builtin) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *Nil) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *PkgName) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *Const) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *TypeName) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *Var) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *Func) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *Label) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *Builtin) String() string { return ObjectString(obj, nil) }
|
||||
func (obj *Nil) String() string { return ObjectString(obj, nil) }
|
||||
|
||||
func writeFuncName(buf *bytes.Buffer, f *Func, qf Qualifier) {
|
||||
if f.typ != nil {
|
||||
|
||||
43
src/go/types/object_test.go
Normal file
43
src/go/types/object_test.go
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package types
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestIsAlias(t *testing.T) {
|
||||
check := func(obj *TypeName, want bool) {
|
||||
if got := obj.IsAlias(); got != want {
|
||||
t.Errorf("%v: got IsAlias = %v; want %v", obj, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// predeclared types
|
||||
for _, name := range Universe.Names() {
|
||||
if obj, _ := Universe.Lookup(name).(*TypeName); obj != nil {
|
||||
check(obj, name == "byte" || name == "rune")
|
||||
}
|
||||
}
|
||||
|
||||
// various other types
|
||||
pkg := NewPackage("p", "p")
|
||||
t1 := NewTypeName(0, pkg, "t1", nil)
|
||||
n1 := NewNamed(t1, new(Struct), nil)
|
||||
for _, test := range []struct {
|
||||
name *TypeName
|
||||
alias bool
|
||||
}{
|
||||
{NewTypeName(0, nil, "t0", nil), false}, // no type yet
|
||||
{NewTypeName(0, pkg, "t0", nil), false}, // no type yet
|
||||
{t1, false}, // type name refers to named type and vice versa
|
||||
{NewTypeName(0, nil, "t2", new(Interface)), true}, // type name refers to unnamed type
|
||||
{NewTypeName(0, pkg, "t3", n1), true}, // type name refers to named type with different type name
|
||||
{NewTypeName(0, nil, "t4", Typ[Int32]), true}, // type name refers to basic type with different name
|
||||
{NewTypeName(0, nil, "int32", Typ[Int32]), false}, // type name refers to basic type with same name
|
||||
{NewTypeName(0, pkg, "int32", Typ[Int32]), true}, // type name is declared in user-defined package (outside Universe)
|
||||
{NewTypeName(0, nil, "rune", Typ[Rune]), true}, // type name refers to basic type rune which is an alias already
|
||||
} {
|
||||
check(test.name, test.alias)
|
||||
}
|
||||
}
|
||||
@@ -139,7 +139,7 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
|
||||
case *Basic:
|
||||
// Basic types are singletons except for the rune and byte
|
||||
// aliases, thus we cannot solely rely on the x == y check
|
||||
// above.
|
||||
// above. See also comment in TypeName.IsAlias.
|
||||
if y, ok := y.(*Basic); ok {
|
||||
return x.kind == y.kind
|
||||
}
|
||||
|
||||
@@ -14,13 +14,14 @@ import (
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// A declInfo describes a package-level const, type, var, func, or alias declaration.
|
||||
// A declInfo describes a package-level const, type, var, or func declaration.
|
||||
type declInfo struct {
|
||||
file *Scope // scope of file containing this declaration
|
||||
lhs []*Var // lhs of n:1 variable declarations, or nil
|
||||
typ ast.Expr // type, or nil
|
||||
init ast.Expr // init/orig expression, or nil
|
||||
fdecl *ast.FuncDecl // func declaration, or nil
|
||||
alias bool // type alias declaration
|
||||
|
||||
// The deps field tracks initialization expression dependencies.
|
||||
// As a special (overloaded) case, it also tracks dependencies of
|
||||
@@ -274,13 +275,6 @@ func (check *Checker) collectObjects() {
|
||||
check.declare(fileScope, nil, obj, token.NoPos)
|
||||
}
|
||||
|
||||
// Alias-related code. Keep for now.
|
||||
// case *ast.AliasSpec:
|
||||
// obj := NewAlias(s.Name.Pos(), pkg, s.Name.Name, nil)
|
||||
// obj.typ = nil // unresolved
|
||||
// obj.kind = d.Tok
|
||||
// check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, init: s.Orig})
|
||||
|
||||
case *ast.ValueSpec:
|
||||
switch d.Tok {
|
||||
case token.CONST:
|
||||
@@ -347,7 +341,7 @@ func (check *Checker) collectObjects() {
|
||||
|
||||
case *ast.TypeSpec:
|
||||
obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
|
||||
check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, typ: s.Type})
|
||||
check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, typ: s.Type, alias: s.Assign.IsValid()})
|
||||
|
||||
default:
|
||||
check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
|
||||
|
||||
150
src/go/types/testdata/decls4.src
vendored
Normal file
150
src/go/types/testdata/decls4.src
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// type aliases
|
||||
|
||||
package decls4
|
||||
|
||||
type (
|
||||
T0 [10]int
|
||||
T1 []byte
|
||||
T2 struct {
|
||||
x int
|
||||
}
|
||||
T3 interface{
|
||||
m() T2
|
||||
}
|
||||
T4 func(int, T0) chan T2
|
||||
)
|
||||
|
||||
type (
|
||||
Ai = int
|
||||
A0 = T0
|
||||
A1 = T1
|
||||
A2 = T2
|
||||
A3 = T3
|
||||
A4 = T4
|
||||
|
||||
A10 = [10]int
|
||||
A11 = []byte
|
||||
A12 = struct {
|
||||
x int
|
||||
}
|
||||
A13 = interface{
|
||||
m() A2
|
||||
}
|
||||
A14 = func(int, A0) chan A2
|
||||
)
|
||||
|
||||
// check assignment compatibility due to equality of types
|
||||
var (
|
||||
xi_ int
|
||||
ai Ai = xi_
|
||||
|
||||
x0 T0
|
||||
a0 A0 = x0
|
||||
|
||||
x1 T1
|
||||
a1 A1 = x1
|
||||
|
||||
x2 T2
|
||||
a2 A2 = x2
|
||||
|
||||
x3 T3
|
||||
a3 A3 = x3
|
||||
|
||||
x4 T4
|
||||
a4 A4 = x4
|
||||
)
|
||||
|
||||
// alias receiver types
|
||||
func (Ai /* ERROR "invalid receiver" */) m1() {}
|
||||
func (T0) m1() {}
|
||||
func (A0) m1 /* ERROR already declared */ () {}
|
||||
func (A0) m2 () {}
|
||||
func (A3 /* ERROR invalid receiver */ ) m1 () {}
|
||||
func (A10 /* ERROR invalid receiver */ ) m1() {}
|
||||
|
||||
// x0 has methods m1, m2 declared via receiver type names T0 and A0
|
||||
var _ interface{ m1(); m2() } = x0
|
||||
|
||||
// cycles
|
||||
type (
|
||||
C2 /* ERROR illegal cycle */ = C2
|
||||
C3 /* ERROR illegal cycle */ = C4
|
||||
C4 = C3
|
||||
C5 struct {
|
||||
f *C6
|
||||
}
|
||||
C6 = C5
|
||||
C7 /* ERROR illegal cycle */ struct {
|
||||
f C8
|
||||
}
|
||||
C8 = C7
|
||||
)
|
||||
|
||||
// embedded fields
|
||||
var (
|
||||
s0 struct { T0 }
|
||||
s1 struct { A0 } = s0 /* ERROR cannot use */ // embedded field names are different
|
||||
)
|
||||
|
||||
// embedding and lookup of fields and methods
|
||||
func _(s struct{A0}) { s.A0 = x0 }
|
||||
|
||||
type eX struct{xf int}
|
||||
|
||||
func (eX) xm()
|
||||
|
||||
type eY = struct{eX} // field/method set of eY includes xf, xm
|
||||
|
||||
type eZ = *struct{eX} // field/method set of eZ includes xf, xm
|
||||
|
||||
type eA struct {
|
||||
eX // eX contributes xf, xm to eA
|
||||
}
|
||||
|
||||
type eA2 struct {
|
||||
*eX // *eX contributes xf, xm to eA
|
||||
}
|
||||
|
||||
type eB struct {
|
||||
eY // eY contributes xf, xm to eB
|
||||
}
|
||||
|
||||
type eB2 struct {
|
||||
*eY // *eY contributes xf, xm to eB
|
||||
}
|
||||
|
||||
type eC struct {
|
||||
eZ // eZ contributes xf, xm to eC
|
||||
}
|
||||
|
||||
var (
|
||||
_ = eA{}.xf
|
||||
_ = eA{}.xm
|
||||
_ = eA2{}.xf
|
||||
_ = eA2{}.xm
|
||||
_ = eB{}.xf
|
||||
_ = eB{}.xm
|
||||
_ = eB2{}.xf
|
||||
_ = eB2{}.xm
|
||||
_ = eC{}.xf
|
||||
_ = eC{}.xm
|
||||
)
|
||||
|
||||
// ambiguous selectors due to embedding via type aliases
|
||||
type eD struct {
|
||||
eY
|
||||
eZ
|
||||
}
|
||||
|
||||
var (
|
||||
_ = eD /* ERROR ambiguous selector */ {}.xf
|
||||
_ = eD /* ERROR ambiguous selector */ {}.xm
|
||||
)
|
||||
|
||||
var (
|
||||
_ interface{ xm() } = eD /* ERROR missing method xm */ {}
|
||||
)
|
||||
@@ -56,6 +56,7 @@ func RelativeTo(pkg *Package) Qualifier {
|
||||
// This flag is exported in the x/tools/go/types package. We don't
|
||||
// need it at the moment in the std repo and so we don't export it
|
||||
// anymore. We should eventually try to remove it altogether.
|
||||
// TODO(gri) remove this
|
||||
var gcCompatibilityMode bool
|
||||
|
||||
// TypeString returns the string representation of typ.
|
||||
|
||||
@@ -45,17 +45,6 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, path []*TypeNa
|
||||
delete(check.unusedDotImports[scope], pkg)
|
||||
}
|
||||
|
||||
// Alias-related code. Keep for now.
|
||||
// An alias stands for the original object; use that one instead.
|
||||
// TODO(gri) We should be able to factor out the Typ[Invalid] test.
|
||||
// if alias, _ := obj.(*Alias); alias != nil {
|
||||
// obj = original(obj)
|
||||
// if obj == nil || typ == Typ[Invalid] {
|
||||
// return
|
||||
// }
|
||||
// assert(typ == obj.Type())
|
||||
// }
|
||||
|
||||
switch obj := obj.(type) {
|
||||
case *PkgName:
|
||||
check.errorf(e.Pos(), "use of package %s not in selector", obj.name)
|
||||
@@ -661,47 +650,41 @@ func (check *Checker) structType(styp *Struct, e *ast.StructType, path []*TypeNa
|
||||
}
|
||||
} else {
|
||||
// anonymous field
|
||||
name := anonymousFieldIdent(f.Type)
|
||||
// spec: "An embedded type must be specified as a type name T or as a pointer
|
||||
// to a non-interface type name *T, and T itself may not be a pointer type."
|
||||
pos := f.Type.Pos()
|
||||
name := anonymousFieldIdent(f.Type)
|
||||
if name == nil {
|
||||
check.invalidAST(pos, "anonymous field type %s has no name", f.Type)
|
||||
continue
|
||||
}
|
||||
t, isPtr := deref(typ)
|
||||
switch t := t.(type) {
|
||||
// Because we have a name, typ must be of the form T or *T, where T is the name
|
||||
// of a (named or alias) type, and t (= deref(typ)) must be the type of T.
|
||||
switch t := t.Underlying().(type) {
|
||||
case *Basic:
|
||||
if t == Typ[Invalid] {
|
||||
// error was reported before
|
||||
continue
|
||||
}
|
||||
|
||||
// unsafe.Pointer is treated like a regular pointer
|
||||
if t.kind == UnsafePointer {
|
||||
check.errorf(pos, "anonymous field type cannot be unsafe.Pointer")
|
||||
continue
|
||||
}
|
||||
add(f, name, true, pos)
|
||||
|
||||
case *Named:
|
||||
// spec: "An embedded type must be specified as a type name
|
||||
// T or as a pointer to a non-interface type name *T, and T
|
||||
// itself may not be a pointer type."
|
||||
switch u := t.underlying.(type) {
|
||||
case *Basic:
|
||||
// unsafe.Pointer is treated like a regular pointer
|
||||
if u.kind == UnsafePointer {
|
||||
check.errorf(pos, "anonymous field type cannot be unsafe.Pointer")
|
||||
continue
|
||||
}
|
||||
case *Pointer:
|
||||
check.errorf(pos, "anonymous field type cannot be a pointer")
|
||||
case *Pointer:
|
||||
check.errorf(pos, "anonymous field type cannot be a pointer")
|
||||
continue
|
||||
|
||||
case *Interface:
|
||||
if isPtr {
|
||||
check.errorf(pos, "anonymous field type cannot be a pointer to an interface")
|
||||
continue
|
||||
case *Interface:
|
||||
if isPtr {
|
||||
check.errorf(pos, "anonymous field type cannot be a pointer to an interface")
|
||||
continue
|
||||
}
|
||||
}
|
||||
add(f, name, true, pos)
|
||||
|
||||
default:
|
||||
check.invalidAST(pos, "anonymous field type %s must be named", typ)
|
||||
}
|
||||
add(f, name, true, pos)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -714,7 +697,10 @@ func anonymousFieldIdent(e ast.Expr) *ast.Ident {
|
||||
case *ast.Ident:
|
||||
return e
|
||||
case *ast.StarExpr:
|
||||
return anonymousFieldIdent(e.X)
|
||||
// *T is valid, but **T is not
|
||||
if _, ok := e.X.(*ast.StarExpr); !ok {
|
||||
return anonymousFieldIdent(e.X)
|
||||
}
|
||||
case *ast.SelectorExpr:
|
||||
return e.Sel
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by golang.org/x/tools/cmd/bundle.
|
||||
// Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT.
|
||||
//go:generate bundle -o h2_bundle.go -prefix http2 -underscore golang.org/x/net/http2
|
||||
|
||||
// Package http2 implements the HTTP/2 protocol.
|
||||
@@ -3536,9 +3536,13 @@ func (sc *http2serverConn) serve() {
|
||||
sc.idleTimerCh = sc.idleTimer.C
|
||||
}
|
||||
|
||||
var gracefulShutdownCh <-chan struct{}
|
||||
var gracefulShutdownCh chan struct{}
|
||||
if sc.hs != nil {
|
||||
gracefulShutdownCh = http2h1ServerShutdownChan(sc.hs)
|
||||
ch := http2h1ServerShutdownChan(sc.hs)
|
||||
if ch != nil {
|
||||
gracefulShutdownCh = make(chan struct{})
|
||||
go sc.awaitGracefulShutdown(ch, gracefulShutdownCh)
|
||||
}
|
||||
}
|
||||
|
||||
go sc.readFrames()
|
||||
@@ -3587,6 +3591,14 @@ func (sc *http2serverConn) serve() {
|
||||
}
|
||||
}
|
||||
|
||||
func (sc *http2serverConn) awaitGracefulShutdown(sharedCh <-chan struct{}, privateCh chan struct{}) {
|
||||
select {
|
||||
case <-sc.doneServing:
|
||||
case <-sharedCh:
|
||||
close(privateCh)
|
||||
}
|
||||
}
|
||||
|
||||
// readPreface reads the ClientPreface greeting from the peer
|
||||
// or returns an error on timeout or an invalid greeting.
|
||||
func (sc *http2serverConn) readPreface() error {
|
||||
@@ -6003,7 +6015,6 @@ func http2commaSeparatedTrailers(req *Request) (string, error) {
|
||||
}
|
||||
if len(keys) > 0 {
|
||||
sort.Strings(keys)
|
||||
|
||||
return strings.Join(keys, ","), nil
|
||||
}
|
||||
return "", nil
|
||||
|
||||
@@ -3731,7 +3731,7 @@ func checkSameType(t *testing.T, x, y interface{}) {
|
||||
|
||||
func TestArrayOf(t *testing.T) {
|
||||
// check construction and use of type not in binary
|
||||
for _, table := range []struct {
|
||||
tests := []struct {
|
||||
n int
|
||||
value func(i int) interface{}
|
||||
comparable bool
|
||||
@@ -3809,7 +3809,9 @@ func TestArrayOf(t *testing.T) {
|
||||
comparable: true,
|
||||
want: "[{0 0} {1 1} {2 2} {3 3} {4 4} {5 5} {6 6} {7 7} {8 8} {9 9}]",
|
||||
},
|
||||
} {
|
||||
}
|
||||
|
||||
for _, table := range tests {
|
||||
at := ArrayOf(table.n, TypeOf(table.value(0)))
|
||||
v := New(at).Elem()
|
||||
vok := New(at).Elem()
|
||||
@@ -4175,50 +4177,58 @@ func TestStructOfExportRules(t *testing.T) {
|
||||
f()
|
||||
}
|
||||
|
||||
for i, test := range []struct {
|
||||
tests := []struct {
|
||||
field StructField
|
||||
mustPanic bool
|
||||
exported bool
|
||||
}{
|
||||
{
|
||||
field: StructField{Name: "", Type: TypeOf(S1{})},
|
||||
mustPanic: false,
|
||||
exported: true,
|
||||
field: StructField{Name: "S1", Anonymous: true, Type: TypeOf(S1{})},
|
||||
exported: true,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "", Type: TypeOf((*S1)(nil))},
|
||||
mustPanic: false,
|
||||
exported: true,
|
||||
field: StructField{Name: "S1", Anonymous: true, Type: TypeOf((*S1)(nil))},
|
||||
exported: true,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "", Type: TypeOf(s2{})},
|
||||
mustPanic: false,
|
||||
exported: false,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "", Type: TypeOf((*s2)(nil))},
|
||||
mustPanic: false,
|
||||
exported: false,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "", Type: TypeOf(S1{}), PkgPath: "other/pkg"},
|
||||
field: StructField{Name: "s2", Anonymous: true, Type: TypeOf(s2{})},
|
||||
mustPanic: true,
|
||||
exported: true,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "", Type: TypeOf((*S1)(nil)), PkgPath: "other/pkg"},
|
||||
field: StructField{Name: "s2", Anonymous: true, Type: TypeOf((*s2)(nil))},
|
||||
mustPanic: true,
|
||||
exported: true,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "", Type: TypeOf(s2{}), PkgPath: "other/pkg"},
|
||||
field: StructField{Name: "Name", Type: nil, PkgPath: ""},
|
||||
mustPanic: true,
|
||||
exported: false,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "", Type: TypeOf((*s2)(nil)), PkgPath: "other/pkg"},
|
||||
field: StructField{Name: "", Type: TypeOf(S1{}), PkgPath: ""},
|
||||
mustPanic: true,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "S1", Anonymous: true, Type: TypeOf(S1{}), PkgPath: "other/pkg"},
|
||||
mustPanic: true,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "S1", Anonymous: true, Type: TypeOf((*S1)(nil)), PkgPath: "other/pkg"},
|
||||
mustPanic: true,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "s2", Anonymous: true, Type: TypeOf(s2{}), PkgPath: "other/pkg"},
|
||||
mustPanic: true,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "s2", Anonymous: true, Type: TypeOf((*s2)(nil)), PkgPath: "other/pkg"},
|
||||
mustPanic: true,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "s2", Type: TypeOf(int(0)), PkgPath: "other/pkg"},
|
||||
mustPanic: true,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "s2", Type: TypeOf(int(0)), PkgPath: "other/pkg"},
|
||||
mustPanic: true,
|
||||
exported: false,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "S", Type: TypeOf(S1{})},
|
||||
@@ -4226,81 +4236,68 @@ func TestStructOfExportRules(t *testing.T) {
|
||||
exported: true,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "S", Type: TypeOf((*S1)(nil))},
|
||||
mustPanic: false,
|
||||
exported: true,
|
||||
field: StructField{Name: "S", Type: TypeOf((*S1)(nil))},
|
||||
exported: true,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "S", Type: TypeOf(s2{})},
|
||||
mustPanic: false,
|
||||
exported: true,
|
||||
field: StructField{Name: "S", Type: TypeOf(s2{})},
|
||||
exported: true,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "S", Type: TypeOf((*s2)(nil))},
|
||||
mustPanic: false,
|
||||
exported: true,
|
||||
field: StructField{Name: "S", Type: TypeOf((*s2)(nil))},
|
||||
exported: true,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "s", Type: TypeOf(S1{})},
|
||||
mustPanic: true,
|
||||
exported: false,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "s", Type: TypeOf((*S1)(nil))},
|
||||
mustPanic: true,
|
||||
exported: false,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "s", Type: TypeOf(s2{})},
|
||||
mustPanic: true,
|
||||
exported: false,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "s", Type: TypeOf((*s2)(nil))},
|
||||
mustPanic: true,
|
||||
exported: false,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "s", Type: TypeOf(S1{}), PkgPath: "other/pkg"},
|
||||
mustPanic: true, // TODO(sbinet): creating a name with a package path
|
||||
exported: false,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "s", Type: TypeOf((*S1)(nil)), PkgPath: "other/pkg"},
|
||||
mustPanic: true, // TODO(sbinet): creating a name with a package path
|
||||
exported: false,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "s", Type: TypeOf(s2{}), PkgPath: "other/pkg"},
|
||||
mustPanic: true, // TODO(sbinet): creating a name with a package path
|
||||
exported: false,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "s", Type: TypeOf((*s2)(nil)), PkgPath: "other/pkg"},
|
||||
mustPanic: true, // TODO(sbinet): creating a name with a package path
|
||||
exported: false,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "", Type: TypeOf(ΦType{})},
|
||||
mustPanic: false,
|
||||
exported: true,
|
||||
mustPanic: true,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "", Type: TypeOf(φType{})},
|
||||
mustPanic: false,
|
||||
exported: false,
|
||||
mustPanic: true,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "Φ", Type: TypeOf(0)},
|
||||
mustPanic: false,
|
||||
exported: true,
|
||||
field: StructField{Name: "Φ", Type: TypeOf(0)},
|
||||
exported: true,
|
||||
},
|
||||
{
|
||||
field: StructField{Name: "φ", Type: TypeOf(0)},
|
||||
mustPanic: false,
|
||||
exported: false,
|
||||
field: StructField{Name: "φ", Type: TypeOf(0)},
|
||||
exported: false,
|
||||
},
|
||||
} {
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
testPanic(i, test.mustPanic, func() {
|
||||
typ := StructOf([]StructField{test.field})
|
||||
if typ == nil {
|
||||
@@ -4310,7 +4307,7 @@ func TestStructOfExportRules(t *testing.T) {
|
||||
field := typ.Field(0)
|
||||
n := field.Name
|
||||
if n == "" {
|
||||
n = field.Type.Name()
|
||||
panic("field.Name must not be empty")
|
||||
}
|
||||
exported := isExported(n)
|
||||
if exported != test.exported {
|
||||
@@ -4388,7 +4385,7 @@ func TestStructOfGenericAlg(t *testing.T) {
|
||||
{Name: "S1", Type: st1},
|
||||
})
|
||||
|
||||
for _, table := range []struct {
|
||||
tests := []struct {
|
||||
rt Type
|
||||
idx []int
|
||||
}{
|
||||
@@ -4469,7 +4466,9 @@ func TestStructOfGenericAlg(t *testing.T) {
|
||||
),
|
||||
idx: []int{2},
|
||||
},
|
||||
} {
|
||||
}
|
||||
|
||||
for _, table := range tests {
|
||||
v1 := New(table.rt).Elem()
|
||||
v2 := New(table.rt).Elem()
|
||||
|
||||
@@ -4571,18 +4570,21 @@ func TestStructOfWithInterface(t *testing.T) {
|
||||
type Iface interface {
|
||||
Get() int
|
||||
}
|
||||
for i, table := range []struct {
|
||||
tests := []struct {
|
||||
name string
|
||||
typ Type
|
||||
val Value
|
||||
impl bool
|
||||
}{
|
||||
{
|
||||
name: "StructI",
|
||||
typ: TypeOf(StructI(want)),
|
||||
val: ValueOf(StructI(want)),
|
||||
impl: true,
|
||||
},
|
||||
{
|
||||
typ: PtrTo(TypeOf(StructI(want))),
|
||||
name: "StructI",
|
||||
typ: PtrTo(TypeOf(StructI(want))),
|
||||
val: ValueOf(func() interface{} {
|
||||
v := StructI(want)
|
||||
return &v
|
||||
@@ -4590,7 +4592,8 @@ func TestStructOfWithInterface(t *testing.T) {
|
||||
impl: true,
|
||||
},
|
||||
{
|
||||
typ: PtrTo(TypeOf(StructIPtr(want))),
|
||||
name: "StructIPtr",
|
||||
typ: PtrTo(TypeOf(StructIPtr(want))),
|
||||
val: ValueOf(func() interface{} {
|
||||
v := StructIPtr(want)
|
||||
return &v
|
||||
@@ -4598,6 +4601,7 @@ func TestStructOfWithInterface(t *testing.T) {
|
||||
impl: true,
|
||||
},
|
||||
{
|
||||
name: "StructIPtr",
|
||||
typ: TypeOf(StructIPtr(want)),
|
||||
val: ValueOf(StructIPtr(want)),
|
||||
impl: false,
|
||||
@@ -4607,13 +4611,16 @@ func TestStructOfWithInterface(t *testing.T) {
|
||||
// val: ValueOf(StructI(want)),
|
||||
// impl: true,
|
||||
// },
|
||||
} {
|
||||
}
|
||||
|
||||
for i, table := range tests {
|
||||
rt := StructOf(
|
||||
[]StructField{
|
||||
{
|
||||
Name: "",
|
||||
PkgPath: "",
|
||||
Type: table.typ,
|
||||
Name: table.name,
|
||||
Anonymous: true,
|
||||
PkgPath: "",
|
||||
Type: table.typ,
|
||||
},
|
||||
},
|
||||
)
|
||||
@@ -6059,6 +6066,7 @@ func TestSwapper(t *testing.T) {
|
||||
want: []pairPtr{{5, 6, &c}, {3, 4, &b}, {1, 2, &a}},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
inStr := fmt.Sprint(tt.in)
|
||||
Swapper(tt.in)(tt.i, tt.j)
|
||||
@@ -6084,3 +6092,36 @@ func TestUnaddressableField(t *testing.T) {
|
||||
lv.Set(rv)
|
||||
})
|
||||
}
|
||||
|
||||
type Tint int
|
||||
|
||||
type Tint2 = Tint
|
||||
|
||||
type Talias1 struct {
|
||||
byte
|
||||
uint8
|
||||
int
|
||||
int32
|
||||
rune
|
||||
}
|
||||
|
||||
type Talias2 struct {
|
||||
Tint
|
||||
Tint2
|
||||
}
|
||||
|
||||
func TestAliasNames(t *testing.T) {
|
||||
t1 := Talias1{byte: 1, uint8: 2, int: 3, int32: 4, rune: 5}
|
||||
out := fmt.Sprintf("%#v", t1)
|
||||
want := "reflect_test.Talias1{byte:0x1, uint8:0x2, int:3, int32:4, rune:5}"
|
||||
if out != want {
|
||||
t.Errorf("Talias1 print:\nhave: %s\nwant: %s", out, want)
|
||||
}
|
||||
|
||||
t2 := Talias2{Tint: 1, Tint2: 2}
|
||||
out = fmt.Sprintf("%#v", t2)
|
||||
want = "reflect_test.Talias2{Tint:1, Tint2:2}"
|
||||
if out != want {
|
||||
t.Errorf("Talias2 print:\nhave: %s\nwant: %s", out, want)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,9 +417,17 @@ type sliceType struct {
|
||||
|
||||
// Struct field
|
||||
type structField struct {
|
||||
name name // name is empty for embedded fields
|
||||
typ *rtype // type of field
|
||||
offset uintptr // byte offset of field within struct
|
||||
name name // name is always non-empty
|
||||
typ *rtype // type of field
|
||||
offsetAnon uintptr // byte offset of field<<1 | isAnonymous
|
||||
}
|
||||
|
||||
func (f *structField) offset() uintptr {
|
||||
return f.offsetAnon >> 1
|
||||
}
|
||||
|
||||
func (f *structField) anon() bool {
|
||||
return f.offsetAnon&1 != 0
|
||||
}
|
||||
|
||||
// structType represents a struct type.
|
||||
@@ -1215,16 +1223,8 @@ func (t *structType) Field(i int) (f StructField) {
|
||||
}
|
||||
p := &t.fields[i]
|
||||
f.Type = toType(p.typ)
|
||||
if name := p.name.name(); name != "" {
|
||||
f.Name = name
|
||||
} else {
|
||||
t := f.Type
|
||||
if t.Kind() == Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
f.Name = t.Name()
|
||||
f.Anonymous = true
|
||||
}
|
||||
f.Name = p.name.name()
|
||||
f.Anonymous = p.anon()
|
||||
if !p.name.isExported() {
|
||||
f.PkgPath = p.name.pkgPath()
|
||||
if f.PkgPath == "" {
|
||||
@@ -1234,7 +1234,7 @@ func (t *structType) Field(i int) (f StructField) {
|
||||
if tag := p.name.tag(); tag != "" {
|
||||
f.Tag = StructTag(tag)
|
||||
}
|
||||
f.Offset = p.offset
|
||||
f.Offset = p.offset()
|
||||
|
||||
// NOTE(rsc): This is the only allocation in the interface
|
||||
// presented by a reflect.Type. It would be nice to avoid,
|
||||
@@ -1321,19 +1321,15 @@ func (t *structType) FieldByNameFunc(match func(string) bool) (result StructFiel
|
||||
visited[t] = true
|
||||
for i := range t.fields {
|
||||
f := &t.fields[i]
|
||||
// Find name and type for field f.
|
||||
var fname string
|
||||
// Find name and (for anonymous field) type for field f.
|
||||
fname := f.name.name()
|
||||
var ntyp *rtype
|
||||
if name := f.name.name(); name != "" {
|
||||
fname = name
|
||||
} else {
|
||||
if f.anon() {
|
||||
// Anonymous field of type T or *T.
|
||||
// Name taken from type.
|
||||
ntyp = f.typ
|
||||
if ntyp.Kind() == Ptr {
|
||||
ntyp = ntyp.Elem().common()
|
||||
}
|
||||
fname = ntyp.Name()
|
||||
}
|
||||
|
||||
// Does it match?
|
||||
@@ -1390,14 +1386,12 @@ func (t *structType) FieldByName(name string) (f StructField, present bool) {
|
||||
if name != "" {
|
||||
for i := range t.fields {
|
||||
tf := &t.fields[i]
|
||||
tfname := tf.name.name()
|
||||
if tfname == "" {
|
||||
hasAnon = true
|
||||
continue
|
||||
}
|
||||
if tfname == name {
|
||||
if tf.name.name() == name {
|
||||
return t.Field(i), true
|
||||
}
|
||||
if tf.anon() {
|
||||
hasAnon = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if !hasAnon {
|
||||
@@ -1695,7 +1689,7 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool {
|
||||
if cmpTags && tf.name.tag() != vf.name.tag() {
|
||||
return false
|
||||
}
|
||||
if tf.offset != vf.offset {
|
||||
if tf.offsetAnon != vf.offsetAnon {
|
||||
return false
|
||||
}
|
||||
if !tf.name.isExported() {
|
||||
@@ -2404,6 +2398,9 @@ func StructOf(fields []StructField) Type {
|
||||
lastzero := uintptr(0)
|
||||
repr = append(repr, "struct {"...)
|
||||
for i, field := range fields {
|
||||
if field.Name == "" {
|
||||
panic("reflect.StructOf: field " + strconv.Itoa(i) + " has no name")
|
||||
}
|
||||
if field.Type == nil {
|
||||
panic("reflect.StructOf: field " + strconv.Itoa(i) + " has no type")
|
||||
}
|
||||
@@ -2416,13 +2413,11 @@ func StructOf(fields []StructField) Type {
|
||||
hasPtr = true
|
||||
}
|
||||
|
||||
name := ""
|
||||
// Update string and hash
|
||||
if f.name.nameLen() > 0 {
|
||||
hash = fnv1(hash, []byte(f.name.name())...)
|
||||
repr = append(repr, (" " + f.name.name())...)
|
||||
name = f.name.name()
|
||||
} else {
|
||||
name := f.name.name()
|
||||
hash = fnv1(hash, []byte(name)...)
|
||||
repr = append(repr, (" " + name)...)
|
||||
if f.anon() {
|
||||
// Embedded field
|
||||
if f.typ.Kind() == Ptr {
|
||||
// Embedded ** and *interface{} are illegal
|
||||
@@ -2430,11 +2425,7 @@ func StructOf(fields []StructField) Type {
|
||||
if k := elem.Kind(); k == Ptr || k == Interface {
|
||||
panic("reflect.StructOf: illegal anonymous field type " + ft.String())
|
||||
}
|
||||
name = elem.String()
|
||||
} else {
|
||||
name = ft.String()
|
||||
}
|
||||
// TODO(sbinet) check for syntactically impossible type names?
|
||||
|
||||
switch f.typ.Kind() {
|
||||
case Interface:
|
||||
@@ -2566,11 +2557,12 @@ func StructOf(fields []StructField) Type {
|
||||
comparable = comparable && (ft.alg.equal != nil)
|
||||
hashable = hashable && (ft.alg.hash != nil)
|
||||
|
||||
f.offset = align(size, uintptr(ft.align))
|
||||
offset := align(size, uintptr(ft.align))
|
||||
if ft.align > typalign {
|
||||
typalign = ft.align
|
||||
}
|
||||
size = f.offset + ft.size
|
||||
size = offset + ft.size
|
||||
f.offsetAnon |= offset << 1
|
||||
|
||||
if ft.size == 0 {
|
||||
lastzero = size
|
||||
@@ -2762,7 +2754,7 @@ func StructOf(fields []StructField) Type {
|
||||
typ.alg.hash = func(p unsafe.Pointer, seed uintptr) uintptr {
|
||||
o := seed
|
||||
for _, ft := range typ.fields {
|
||||
pi := unsafe.Pointer(uintptr(p) + ft.offset)
|
||||
pi := unsafe.Pointer(uintptr(p) + ft.offset())
|
||||
o = ft.typ.alg.hash(pi, o)
|
||||
}
|
||||
return o
|
||||
@@ -2772,8 +2764,8 @@ func StructOf(fields []StructField) Type {
|
||||
if comparable {
|
||||
typ.alg.equal = func(p, q unsafe.Pointer) bool {
|
||||
for _, ft := range typ.fields {
|
||||
pi := unsafe.Pointer(uintptr(p) + ft.offset)
|
||||
qi := unsafe.Pointer(uintptr(q) + ft.offset)
|
||||
pi := unsafe.Pointer(uintptr(p) + ft.offset())
|
||||
qi := unsafe.Pointer(uintptr(q) + ft.offset())
|
||||
if !ft.typ.alg.equal(pi, qi) {
|
||||
return false
|
||||
}
|
||||
@@ -2795,25 +2787,27 @@ func StructOf(fields []StructField) Type {
|
||||
}
|
||||
|
||||
func runtimeStructField(field StructField) structField {
|
||||
exported := field.PkgPath == ""
|
||||
if field.Name == "" {
|
||||
t := field.Type.(*rtype)
|
||||
if t.Kind() == Ptr {
|
||||
t = t.Elem().(*rtype)
|
||||
}
|
||||
exported = t.nameOff(t.str).isExported()
|
||||
} else if exported {
|
||||
b0 := field.Name[0]
|
||||
if ('a' <= b0 && b0 <= 'z') || b0 == '_' {
|
||||
panic("reflect.StructOf: field \"" + field.Name + "\" is unexported but has no PkgPath")
|
||||
}
|
||||
if field.PkgPath != "" {
|
||||
panic("reflect.StructOf: StructOf does not allow unexported fields")
|
||||
}
|
||||
|
||||
_ = resolveReflectType(field.Type.common())
|
||||
// Best-effort check for misuse.
|
||||
// Since PkgPath is empty, not much harm done if Unicode lowercase slips through.
|
||||
c := field.Name[0]
|
||||
if 'a' <= c && c <= 'z' || c == '_' {
|
||||
panic("reflect.StructOf: field \"" + field.Name + "\" is unexported but missing PkgPath")
|
||||
}
|
||||
|
||||
offsetAnon := uintptr(0)
|
||||
if field.Anonymous {
|
||||
offsetAnon |= 1
|
||||
}
|
||||
|
||||
resolveReflectType(field.Type.common()) // install in runtime
|
||||
return structField{
|
||||
name: newName(field.Name, string(field.Tag), field.PkgPath, exported),
|
||||
typ: field.Type.common(),
|
||||
offset: 0,
|
||||
name: newName(field.Name, string(field.Tag), "", true),
|
||||
typ: field.Type.common(),
|
||||
offsetAnon: offsetAnon,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2836,7 +2830,7 @@ func typeptrdata(t *rtype) uintptr {
|
||||
}
|
||||
}
|
||||
f := st.fields[field]
|
||||
return f.offset + f.typ.ptrdata
|
||||
return f.offset() + f.typ.ptrdata
|
||||
|
||||
default:
|
||||
panic("reflect.typeptrdata: unexpected type, " + t.String())
|
||||
@@ -3210,7 +3204,7 @@ func addTypeBits(bv *bitVector, offset uintptr, t *rtype) {
|
||||
tt := (*structType)(unsafe.Pointer(t))
|
||||
for i := range tt.fields {
|
||||
f := &tt.fields[i]
|
||||
addTypeBits(bv, offset+f.offset, f.typ)
|
||||
addTypeBits(bv, offset+f.offset(), f.typ)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -769,7 +769,7 @@ func (v Value) Field(i int) Value {
|
||||
fl := v.flag&(flagStickyRO|flagIndir|flagAddr) | flag(typ.Kind())
|
||||
// Using an unexported field forces flagRO.
|
||||
if !field.name.isExported() {
|
||||
if field.name.name() == "" {
|
||||
if field.anon() {
|
||||
fl |= flagEmbedRO
|
||||
} else {
|
||||
fl |= flagStickyRO
|
||||
@@ -780,7 +780,7 @@ func (v Value) Field(i int) Value {
|
||||
// In the former case, we want v.ptr + offset.
|
||||
// In the latter case, we must have field.offset = 0,
|
||||
// so v.ptr + field.offset is still okay.
|
||||
ptr := unsafe.Pointer(uintptr(v.ptr) + field.offset)
|
||||
ptr := unsafe.Pointer(uintptr(v.ptr) + field.offset())
|
||||
return Value{typ, ptr, fl}
|
||||
}
|
||||
|
||||
|
||||
@@ -531,7 +531,7 @@ func cgoCheckArg(t *_type, p unsafe.Pointer, indir, top bool, msg string) {
|
||||
return
|
||||
}
|
||||
for _, f := range st.fields {
|
||||
cgoCheckArg(f.typ, add(p, f.offset), true, top, msg)
|
||||
cgoCheckArg(f.typ, add(p, f.offset()), true, top, msg)
|
||||
}
|
||||
case kindPtr, kindUnsafePointer:
|
||||
if indir {
|
||||
|
||||
@@ -400,10 +400,12 @@ func (h *mheap) sysAlloc(n uintptr) unsafe.Pointer {
|
||||
if p == 0 {
|
||||
return nil
|
||||
}
|
||||
// p can be just about anywhere in the address
|
||||
// space, including before arena_end.
|
||||
if p == h.arena_end {
|
||||
h.arena_end = new_end
|
||||
h.arena_reserved = reserved
|
||||
} else if h.arena_start <= p && p+p_size-h.arena_start-1 <= _MaxArena32 {
|
||||
} else if h.arena_end < p && p+p_size-h.arena_start-1 <= _MaxArena32 {
|
||||
// Keep everything page-aligned.
|
||||
// Our pages are bigger than hardware pages.
|
||||
h.arena_end = p + p_size
|
||||
@@ -413,6 +415,16 @@ func (h *mheap) sysAlloc(n uintptr) unsafe.Pointer {
|
||||
h.arena_used = used
|
||||
h.arena_reserved = reserved
|
||||
} else {
|
||||
// We got a mapping, but it's not
|
||||
// linear with our current arena, so
|
||||
// we can't use it.
|
||||
//
|
||||
// TODO: Make it possible to allocate
|
||||
// from this. We can't decrease
|
||||
// arena_used, but we could introduce
|
||||
// a new variable for the current
|
||||
// allocation position.
|
||||
|
||||
// We haven't added this allocation to
|
||||
// the stats, so subtract it from a
|
||||
// fake stat (but avoid underflow).
|
||||
|
||||
@@ -374,6 +374,7 @@ func heapBitsForAddr(addr uintptr) heapBits {
|
||||
// heapBitsForSpan returns the heapBits for the span base address base.
|
||||
func heapBitsForSpan(base uintptr) (hbits heapBits) {
|
||||
if base < mheap_.arena_start || base >= mheap_.arena_used {
|
||||
print("runtime: base ", hex(base), " not in range [", hex(mheap_.arena_start), ",", hex(mheap_.arena_used), ")\n")
|
||||
throw("heapBitsForSpan: base out of range")
|
||||
}
|
||||
return heapBitsForAddr(base)
|
||||
|
||||
@@ -1918,7 +1918,7 @@ func gchelper() {
|
||||
traceGCScanDone()
|
||||
}
|
||||
|
||||
nproc := work.nproc // work.nproc can change right after we increment work.ndone
|
||||
nproc := atomic.Load(&work.nproc) // work.nproc can change right after we increment work.ndone
|
||||
if atomic.Xadd(&work.ndone, +1) == nproc-1 {
|
||||
notewakeup(&work.alldone)
|
||||
}
|
||||
|
||||
@@ -98,15 +98,18 @@ TEXT runtime·usleep(SB),NOSPLIT,$8
|
||||
MOVL $1000000, CX
|
||||
DIVL CX
|
||||
MOVL AX, 0(SP)
|
||||
MOVL $1000, AX // usec to nsec
|
||||
MULL DX
|
||||
MOVL DX, 4(SP)
|
||||
|
||||
// select(0, 0, 0, 0, &tv)
|
||||
MOVL $142, AX
|
||||
// pselect6(0, 0, 0, 0, &ts, 0)
|
||||
MOVL $308, AX
|
||||
MOVL $0, BX
|
||||
MOVL $0, CX
|
||||
MOVL $0, DX
|
||||
MOVL $0, SI
|
||||
LEAL 0(SP), DI
|
||||
MOVL $0, BP
|
||||
INVOKE_SYSCALL
|
||||
RET
|
||||
|
||||
|
||||
@@ -82,15 +82,18 @@ TEXT runtime·usleep(SB),NOSPLIT,$16
|
||||
MOVL $1000000, CX
|
||||
DIVL CX
|
||||
MOVQ AX, 0(SP)
|
||||
MOVQ DX, 8(SP)
|
||||
MOVL $1000, AX // usec to nsec
|
||||
MULL DX
|
||||
MOVQ AX, 8(SP)
|
||||
|
||||
// select(0, 0, 0, 0, &tv)
|
||||
// pselect6(0, 0, 0, 0, &ts, 0)
|
||||
MOVL $0, DI
|
||||
MOVL $0, SI
|
||||
MOVL $0, DX
|
||||
MOVL $0, R10
|
||||
MOVQ SP, R8
|
||||
MOVL $23, AX
|
||||
MOVL $0, R9
|
||||
MOVL $270, AX
|
||||
SYSCALL
|
||||
RET
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
#define SYS_gettid (SYS_BASE + 224)
|
||||
#define SYS_tkill (SYS_BASE + 238)
|
||||
#define SYS_sched_yield (SYS_BASE + 158)
|
||||
#define SYS_select (SYS_BASE + 142) // newselect
|
||||
#define SYS_pselect6 (SYS_BASE + 335)
|
||||
#define SYS_ugetrlimit (SYS_BASE + 191)
|
||||
#define SYS_sched_getaffinity (SYS_BASE + 242)
|
||||
#define SYS_clock_gettime (SYS_BASE + 263)
|
||||
@@ -387,13 +387,16 @@ TEXT runtime·usleep(SB),NOSPLIT,$12
|
||||
MOVW usec+0(FP), R0
|
||||
CALL runtime·usplitR0(SB)
|
||||
MOVW R0, 4(R13)
|
||||
MOVW $1000, R0 // usec to nsec
|
||||
MUL R0, R1
|
||||
MOVW R1, 8(R13)
|
||||
MOVW $0, R0
|
||||
MOVW $0, R1
|
||||
MOVW $0, R2
|
||||
MOVW $0, R3
|
||||
MOVW $4(R13), R4
|
||||
MOVW $SYS_select, R7
|
||||
MOVW $0, R5
|
||||
MOVW $SYS_pselect6, R7
|
||||
SWI $0
|
||||
RET
|
||||
|
||||
|
||||
@@ -390,9 +390,13 @@ type ptrtype struct {
|
||||
}
|
||||
|
||||
type structfield struct {
|
||||
name name
|
||||
typ *_type
|
||||
offset uintptr
|
||||
name name
|
||||
typ *_type
|
||||
offsetAnon uintptr
|
||||
}
|
||||
|
||||
func (f *structfield) offset() uintptr {
|
||||
return f.offsetAnon >> 1
|
||||
}
|
||||
|
||||
type structtype struct {
|
||||
@@ -650,7 +654,7 @@ func typesEqual(t, v *_type) bool {
|
||||
if tf.name.tag() != vf.name.tag() {
|
||||
return false
|
||||
}
|
||||
if tf.offset != vf.offset {
|
||||
if tf.offsetAnon != vf.offsetAnon {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
104
test/alias2.go
Normal file
104
test/alias2.go
Normal file
@@ -0,0 +1,104 @@
|
||||
// errorcheck
|
||||
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Test basic restrictions on type aliases.
|
||||
|
||||
package p
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
. "reflect"
|
||||
)
|
||||
|
||||
type T0 struct{}
|
||||
|
||||
// Valid type alias declarations.
|
||||
|
||||
type _ = T0
|
||||
type _ = int
|
||||
type _ = struct{}
|
||||
type _ = reflect.Value
|
||||
type _ = Value
|
||||
|
||||
type (
|
||||
A0 = T0
|
||||
A1 = int
|
||||
A2 = struct{}
|
||||
A3 = reflect.Value
|
||||
A4 = Value
|
||||
A5 = Value
|
||||
|
||||
N0 A0
|
||||
)
|
||||
|
||||
// Methods can be declared on the original named type and the alias.
|
||||
func (T0) m1() {} // GCCGO_ERROR "previous"
|
||||
func (*T0) m1() {} // ERROR "method redeclared: T0\.m1|redefinition of .m1."
|
||||
func (A0) m1() {} // ERROR "T0\.m1 redeclared in this block|redefinition of .m1."
|
||||
func (A0) m1() {} // ERROR "T0\.m1 redeclared in this block|redefinition of .m1."
|
||||
func (A0) m2() {}
|
||||
|
||||
// Type aliases and the original type name can be used interchangeably.
|
||||
var _ A0 = T0{}
|
||||
var _ T0 = A0{}
|
||||
|
||||
// But aliases and original types cannot be used with new types based on them.
|
||||
var _ N0 = T0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment|incompatible type"
|
||||
var _ N0 = A0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment|incompatible type"
|
||||
|
||||
var _ A5 = Value{}
|
||||
|
||||
var _ interface {
|
||||
m1()
|
||||
m2()
|
||||
} = T0{}
|
||||
|
||||
var _ interface {
|
||||
m1()
|
||||
m2()
|
||||
} = A0{}
|
||||
|
||||
func _() {
|
||||
type _ = T0
|
||||
type _ = int
|
||||
type _ = struct{}
|
||||
type _ = reflect.Value
|
||||
type _ = Value
|
||||
|
||||
type (
|
||||
A0 = T0
|
||||
A1 = int
|
||||
A2 = struct{}
|
||||
A3 = reflect.Value
|
||||
A4 = Value
|
||||
A5 Value
|
||||
|
||||
N0 A0
|
||||
)
|
||||
|
||||
var _ A0 = T0{}
|
||||
var _ T0 = A0{}
|
||||
|
||||
var _ N0 = T0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment|incompatible type"
|
||||
var _ N0 = A0{} // ERROR "cannot use T0 literal \(type T0\) as type N0 in assignment|incompatible type"
|
||||
|
||||
var _ A5 = Value{} // ERROR "cannot use reflect\.Value literal \(type reflect.Value\) as type A5 in assignment|incompatible type"
|
||||
}
|
||||
|
||||
// Invalid type alias declarations.
|
||||
|
||||
type _ = reflect.ValueOf // ERROR "reflect.ValueOf is not a type|expected type"
|
||||
|
||||
func (A1) m() {} // ERROR "cannot define new methods on non-local type int|may not define methods on non-local type"
|
||||
func (A2) m() {} // ERROR "invalid receiver type"
|
||||
func (A3) m() {} // ERROR "cannot define new methods on non-local type reflect.Value|may not define methods on non-local type"
|
||||
func (A4) m() {} // ERROR "reflect.Value.m redeclared in this block" "cannot define new methods on non-local type reflect.Value|may not define methods on non-local type"
|
||||
|
||||
type B1 = struct{}
|
||||
|
||||
func (B1) m() {} // ERROR "m redeclared in this block" "invalid receiver type"
|
||||
|
||||
// TODO(gri) expand
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user