Commit e48a2958 authored by Matthew Dempsky's avatar Matthew Dempsky

cmd/compile: treat empty and absent struct field tags as identical

Fixes #15439.

Change-Id: I5a32384c46e20f8db6968e5a9e854c45ab262fe4
Reviewed-on: https://go-review.googlesource.com/22429Reviewed-by: 's avatarJosh Bleecher Snyder <josharian@gmail.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 6f3f02f8
...@@ -720,18 +720,7 @@ func (p *exporter) field(f *Field) { ...@@ -720,18 +720,7 @@ func (p *exporter) field(f *Field) {
p.pos(f.Sym.Def) p.pos(f.Sym.Def)
p.fieldName(f.Sym, f) p.fieldName(f.Sym, f)
p.typ(f.Type) p.typ(f.Type)
// TODO(gri) Do we care that a non-present tag cannot be distinguished p.string(f.Note)
// from a present but empty ta string? (reflect doesn't seem to make
// a difference). Investigate.
p.note(f.Note)
}
func (p *exporter) note(n *string) {
var s string
if n != nil {
s = *n
}
p.string(s)
} }
func (p *exporter) methodList(t *Type) { func (p *exporter) methodList(t *Type) {
...@@ -847,7 +836,7 @@ func (p *exporter) param(q *Field, n int, numbered bool) { ...@@ -847,7 +836,7 @@ func (p *exporter) param(q *Field, n int, numbered bool) {
// TODO(gri) This is compiler-specific (escape info). // TODO(gri) This is compiler-specific (escape info).
// Move into compiler-specific section eventually? // Move into compiler-specific section eventually?
// (Not having escape info causes tests to fail, e.g. runtime GCInfoTest) // (Not having escape info causes tests to fail, e.g. runtime GCInfoTest)
p.note(q.Note) p.string(q.Note)
} }
func parName(f *Field, numbered bool) string { func parName(f *Field, numbered bool) string {
......
...@@ -457,7 +457,7 @@ func (p *importer) field() *Node { ...@@ -457,7 +457,7 @@ func (p *importer) field() *Node {
p.pos() p.pos()
sym := p.fieldName() sym := p.fieldName()
typ := p.typ() typ := p.typ()
note := p.note() note := p.string()
var n *Node var n *Node
if sym.Name != "" { if sym.Name != "" {
...@@ -475,18 +475,11 @@ func (p *importer) field() *Node { ...@@ -475,18 +475,11 @@ func (p *importer) field() *Node {
n = embedded(s, pkg) n = embedded(s, pkg)
n.Right = typenod(typ) n.Right = typenod(typ)
} }
n.SetVal(note) n.SetVal(Val{U: note})
return n return n
} }
func (p *importer) note() (v Val) {
if s := p.string(); s != "" {
v.U = s
}
return
}
// parser.go:hidden_interfacedcl_list // parser.go:hidden_interfacedcl_list
func (p *importer) methodList() (methods []*Node) { func (p *importer) methodList() (methods []*Node) {
if n := p.int(); n > 0 { if n := p.int(); n > 0 {
...@@ -572,7 +565,7 @@ func (p *importer) param(named bool) *Node { ...@@ -572,7 +565,7 @@ func (p *importer) param(named bool) *Node {
// TODO(gri) This is compiler-specific (escape info). // TODO(gri) This is compiler-specific (escape info).
// Move into compiler-specific section eventually? // Move into compiler-specific section eventually?
n.SetVal(p.note()) n.SetVal(Val{U: p.string()})
return n return n
} }
......
...@@ -757,7 +757,7 @@ func structfield(n *Node) *Field { ...@@ -757,7 +757,7 @@ func structfield(n *Node) *Field {
switch u := n.Val().U.(type) { switch u := n.Val().U.(type) {
case string: case string:
f.Note = &u f.Note = u
default: default:
Yyerror("field annotation must be string") Yyerror("field annotation must be string")
case nil: case nil:
......
...@@ -1181,7 +1181,7 @@ func escassign(e *EscState, dst, src *Node, step *EscStep) { ...@@ -1181,7 +1181,7 @@ func escassign(e *EscState, dst, src *Node, step *EscStep) {
var tags [1 << (bitsPerOutputInTag + EscReturnBits)]string var tags [1 << (bitsPerOutputInTag + EscReturnBits)]string
// mktag returns the string representation for an escape analysis tag. // mktag returns the string representation for an escape analysis tag.
func mktag(mask int) *string { func mktag(mask int) string {
switch mask & EscMask { switch mask & EscMask {
case EscNone, EscReturn: case EscNone, EscReturn:
break break
...@@ -1191,22 +1191,22 @@ func mktag(mask int) *string { ...@@ -1191,22 +1191,22 @@ func mktag(mask int) *string {
} }
if mask < len(tags) && tags[mask] != "" { if mask < len(tags) && tags[mask] != "" {
return &tags[mask] return tags[mask]
} }
s := fmt.Sprintf("esc:0x%x", mask) s := fmt.Sprintf("esc:0x%x", mask)
if mask < len(tags) { if mask < len(tags) {
tags[mask] = s tags[mask] = s
} }
return &s return s
} }
// parsetag decodes an escape analysis tag and returns the esc value. // parsetag decodes an escape analysis tag and returns the esc value.
func parsetag(note *string) uint16 { func parsetag(note string) uint16 {
if note == nil || !strings.HasPrefix(*note, "esc:") { if !strings.HasPrefix(note, "esc:") {
return EscUnknown return EscUnknown
} }
n, _ := strconv.ParseInt((*note)[4:], 0, 0) n, _ := strconv.ParseInt(note[4:], 0, 0)
em := uint16(n) em := uint16(n)
if em == 0 { if em == 0 {
return EscNone return EscNone
...@@ -1268,7 +1268,7 @@ func describeEscape(em uint16) string { ...@@ -1268,7 +1268,7 @@ func describeEscape(em uint16) string {
// escassignfromtag models the input-to-output assignment flow of one of a function // escassignfromtag models the input-to-output assignment flow of one of a function
// calls arguments, where the flow is encoded in "note". // calls arguments, where the flow is encoded in "note".
func escassignfromtag(e *EscState, note *string, dsts Nodes, src *Node) uint16 { func escassignfromtag(e *EscState, note string, dsts Nodes, src *Node) uint16 {
em := parsetag(note) em := parsetag(note)
if src.Op == OLITERAL { if src.Op == OLITERAL {
return em return em
...@@ -1997,7 +1997,7 @@ func esctag(e *EscState, func_ *Node) { ...@@ -1997,7 +1997,7 @@ func esctag(e *EscState, func_ *Node) {
} }
Warnl(func_.Lineno, "%v assuming %v is unsafe uintptr", funcSym(func_), name) Warnl(func_.Lineno, "%v assuming %v is unsafe uintptr", funcSym(func_), name)
} }
t.Note = &unsafeUintptrTag t.Note = unsafeUintptrTag
} }
} }
......
...@@ -1700,8 +1700,8 @@ func Fldconv(f *Field, flag FmtFlag) string { ...@@ -1700,8 +1700,8 @@ func Fldconv(f *Field, flag FmtFlag) string {
// (The escape analysis tags do not apply to func vars.) // (The escape analysis tags do not apply to func vars.)
// But it must not suppress struct field tags. // But it must not suppress struct field tags.
// See golang.org/issue/13777 and golang.org/issue/14331. // See golang.org/issue/13777 and golang.org/issue/14331.
if flag&FmtShort == 0 && (!fmtbody || !f.Funarg) && f.Note != nil { if flag&FmtShort == 0 && (!fmtbody || !f.Funarg) && f.Note != "" {
str += " " + strconv.Quote(*f.Note) str += " " + strconv.Quote(f.Note)
} }
if fmtmode == FTypeId && (sf&FmtUnsigned != 0) { if fmtmode == FTypeId && (sf&FmtUnsigned != 0) {
......
...@@ -373,7 +373,7 @@ func ordercall(n *Node, order *Order) { ...@@ -373,7 +373,7 @@ func ordercall(n *Node, order *Order) {
if t == nil { if t == nil {
break break
} }
if t.Note != nil && *t.Note == unsafeUintptrTag { if t.Note == unsafeUintptrTag {
xp := n.List.Addr(i) xp := n.List.Addr(i)
for (*xp).Op == OCONVNOP && !(*xp).Type.IsPtr() { for (*xp).Op == OCONVNOP && !(*xp).Type.IsPtr() {
xp = &(*xp).Left xp = &(*xp).Left
......
...@@ -501,14 +501,11 @@ func isExportedField(ft *Field) bool { ...@@ -501,14 +501,11 @@ func isExportedField(ft *Field) bool {
// dnameField dumps a reflect.name for a struct field. // dnameField dumps a reflect.name for a struct field.
func dnameField(s *Sym, ot int, ft *Field) int { func dnameField(s *Sym, ot int, ft *Field) int {
var name, tag string var name string
if ft.Sym != nil && ft.Embedded == 0 { if ft.Sym != nil && ft.Embedded == 0 {
name = ft.Sym.Name name = ft.Sym.Name
} }
if ft.Note != nil { nsym := dname(name, ft.Note, nil, isExportedField(ft))
tag = *ft.Note
}
nsym := dname(name, tag, nil, isExportedField(ft))
return dsymptrLSym(Linksym(s), ot, nsym, 0) return dsymptrLSym(Linksym(s), ot, nsym, 0)
} }
......
...@@ -619,10 +619,6 @@ func cplxsubtype(et EType) EType { ...@@ -619,10 +619,6 @@ func cplxsubtype(et EType) EType {
return 0 return 0
} }
func eqnote(a, b *string) bool {
return a == b || a != nil && b != nil && *a == *b
}
// Eqtype reports whether t1 and t2 are identical, following the spec rules. // Eqtype reports whether t1 and t2 are identical, following the spec rules.
// //
// Any cyclic type must go through a named type, and if one is // Any cyclic type must go through a named type, and if one is
...@@ -670,7 +666,7 @@ func eqtype1(t1, t2 *Type, assumedEqual map[typePair]struct{}) bool { ...@@ -670,7 +666,7 @@ func eqtype1(t1, t2 *Type, assumedEqual map[typePair]struct{}) bool {
t1, i1 := IterFields(t1) t1, i1 := IterFields(t1)
t2, i2 := IterFields(t2) t2, i2 := IterFields(t2)
for ; t1 != nil && t2 != nil; t1, t2 = i1.Next(), i2.Next() { for ; t1 != nil && t2 != nil; t1, t2 = i1.Next(), i2.Next() {
if t1.Sym != t2.Sym || t1.Embedded != t2.Embedded || !eqtype1(t1.Type, t2.Type, assumedEqual) || !eqnote(t1.Note, t2.Note) { if t1.Sym != t2.Sym || t1.Embedded != t2.Embedded || !eqtype1(t1.Type, t2.Type, assumedEqual) || t1.Note != t2.Note {
return false return false
} }
} }
......
...@@ -300,7 +300,7 @@ type Field struct { ...@@ -300,7 +300,7 @@ type Field struct {
// or interface Type. // or interface Type.
Offset int64 Offset int64
Note *string // literal string annotation Note string // literal string annotation
} }
// End returns the offset of the first byte immediately after this field. // End returns the offset of the first byte immediately after this field.
...@@ -1003,15 +1003,7 @@ func (t *Type) cmp(x *Type) ssa.Cmp { ...@@ -1003,15 +1003,7 @@ func (t *Type) cmp(x *Type) ssa.Cmp {
return cmpForNe(t1.Embedded < x1.Embedded) return cmpForNe(t1.Embedded < x1.Embedded)
} }
if t1.Note != x1.Note { if t1.Note != x1.Note {
if t1.Note == nil { return cmpForNe(t1.Note < x1.Note)
return ssa.CMPlt
}
if x1.Note == nil {
return ssa.CMPgt
}
if *t1.Note != *x1.Note {
return cmpForNe(*t1.Note < *x1.Note)
}
} }
if c := t1.Sym.cmpsym(x1.Sym); c != ssa.CMPeq { if c := t1.Sym.cmpsym(x1.Sym); c != ssa.CMPeq {
return c return c
......
...@@ -3807,7 +3807,7 @@ func usefield(n *Node) { ...@@ -3807,7 +3807,7 @@ func usefield(n *Node) {
if field == nil { if field == nil {
Fatalf("usefield %v %v without paramfld", n.Left.Type, n.Sym) Fatalf("usefield %v %v without paramfld", n.Left.Type, n.Sym)
} }
if field.Note == nil || !strings.Contains(*field.Note, "go:\"track\"") { if !strings.Contains(field.Note, "go:\"track\"") {
return return
} }
......
// run
// 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 main
import "reflect"
func main() {
a := &struct{ x int }{}
b := &struct{ x int "" }{}
ta := reflect.TypeOf(a)
tb := reflect.TypeOf(b)
// Ensure cmd/compile treats absent and empty tags as equivalent.
a = b
// Ensure package reflect treats absent and empty tags as equivalent.
if !tb.AssignableTo(ta) {
panic("fail")
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment