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) {
p.pos(f.Sym.Def)
p.fieldName(f.Sym, f)
p.typ(f.Type)
// TODO(gri) Do we care that a non-present tag cannot be distinguished
// 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)
p.string(f.Note)
}
func (p *exporter) methodList(t *Type) {
......@@ -847,7 +836,7 @@ func (p *exporter) param(q *Field, n int, numbered bool) {
// TODO(gri) This is compiler-specific (escape info).
// Move into compiler-specific section eventually?
// (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 {
......
......@@ -457,7 +457,7 @@ func (p *importer) field() *Node {
p.pos()
sym := p.fieldName()
typ := p.typ()
note := p.note()
note := p.string()
var n *Node
if sym.Name != "" {
......@@ -475,18 +475,11 @@ func (p *importer) field() *Node {
n = embedded(s, pkg)
n.Right = typenod(typ)
}
n.SetVal(note)
n.SetVal(Val{U: note})
return n
}
func (p *importer) note() (v Val) {
if s := p.string(); s != "" {
v.U = s
}
return
}
// parser.go:hidden_interfacedcl_list
func (p *importer) methodList() (methods []*Node) {
if n := p.int(); n > 0 {
......@@ -572,7 +565,7 @@ func (p *importer) param(named bool) *Node {
// TODO(gri) This is compiler-specific (escape info).
// Move into compiler-specific section eventually?
n.SetVal(p.note())
n.SetVal(Val{U: p.string()})
return n
}
......
......@@ -757,7 +757,7 @@ func structfield(n *Node) *Field {
switch u := n.Val().U.(type) {
case string:
f.Note = &u
f.Note = u
default:
Yyerror("field annotation must be string")
case nil:
......
......@@ -1181,7 +1181,7 @@ func escassign(e *EscState, dst, src *Node, step *EscStep) {
var tags [1 << (bitsPerOutputInTag + EscReturnBits)]string
// mktag returns the string representation for an escape analysis tag.
func mktag(mask int) *string {
func mktag(mask int) string {
switch mask & EscMask {
case EscNone, EscReturn:
break
......@@ -1191,22 +1191,22 @@ func mktag(mask int) *string {
}
if mask < len(tags) && tags[mask] != "" {
return &tags[mask]
return tags[mask]
}
s := fmt.Sprintf("esc:0x%x", mask)
if mask < len(tags) {
tags[mask] = s
}
return &s
return s
}
// parsetag decodes an escape analysis tag and returns the esc value.
func parsetag(note *string) uint16 {
if note == nil || !strings.HasPrefix(*note, "esc:") {
func parsetag(note string) uint16 {
if !strings.HasPrefix(note, "esc:") {
return EscUnknown
}
n, _ := strconv.ParseInt((*note)[4:], 0, 0)
n, _ := strconv.ParseInt(note[4:], 0, 0)
em := uint16(n)
if em == 0 {
return EscNone
......@@ -1268,7 +1268,7 @@ func describeEscape(em uint16) string {
// escassignfromtag models the input-to-output assignment flow of one of a function
// 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)
if src.Op == OLITERAL {
return em
......@@ -1997,7 +1997,7 @@ func esctag(e *EscState, func_ *Node) {
}
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 {
// (The escape analysis tags do not apply to func vars.)
// But it must not suppress struct field tags.
// See golang.org/issue/13777 and golang.org/issue/14331.
if flag&FmtShort == 0 && (!fmtbody || !f.Funarg) && f.Note != nil {
str += " " + strconv.Quote(*f.Note)
if flag&FmtShort == 0 && (!fmtbody || !f.Funarg) && f.Note != "" {
str += " " + strconv.Quote(f.Note)
}
if fmtmode == FTypeId && (sf&FmtUnsigned != 0) {
......
......@@ -373,7 +373,7 @@ func ordercall(n *Node, order *Order) {
if t == nil {
break
}
if t.Note != nil && *t.Note == unsafeUintptrTag {
if t.Note == unsafeUintptrTag {
xp := n.List.Addr(i)
for (*xp).Op == OCONVNOP && !(*xp).Type.IsPtr() {
xp = &(*xp).Left
......
......@@ -501,14 +501,11 @@ func isExportedField(ft *Field) bool {
// dnameField dumps a reflect.name for a struct field.
func dnameField(s *Sym, ot int, ft *Field) int {
var name, tag string
var name string
if ft.Sym != nil && ft.Embedded == 0 {
name = ft.Sym.Name
}
if ft.Note != nil {
tag = *ft.Note
}
nsym := dname(name, tag, nil, isExportedField(ft))
nsym := dname(name, ft.Note, nil, isExportedField(ft))
return dsymptrLSym(Linksym(s), ot, nsym, 0)
}
......
......@@ -619,10 +619,6 @@ func cplxsubtype(et EType) EType {
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.
//
// 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 {
t1, i1 := IterFields(t1)
t2, i2 := IterFields(t2)
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
}
}
......
......@@ -300,7 +300,7 @@ type Field struct {
// or interface Type.
Offset int64
Note *string // literal string annotation
Note string // literal string annotation
}
// End returns the offset of the first byte immediately after this field.
......@@ -1003,15 +1003,7 @@ func (t *Type) cmp(x *Type) ssa.Cmp {
return cmpForNe(t1.Embedded < x1.Embedded)
}
if t1.Note != x1.Note {
if t1.Note == nil {
return ssa.CMPlt
}
if x1.Note == nil {
return ssa.CMPgt
}
if *t1.Note != *x1.Note {
return cmpForNe(*t1.Note < *x1.Note)
}
return cmpForNe(t1.Note < x1.Note)
}
if c := t1.Sym.cmpsym(x1.Sym); c != ssa.CMPeq {
return c
......
......@@ -3807,7 +3807,7 @@ func usefield(n *Node) {
if field == nil {
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
}
......
// 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