mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/compile: treat empty and absent struct field tags as identical
Fixes #15439. Change-Id: I5a32384c46e20f8db6968e5a9e854c45ab262fe4 Reviewed-on: https://go-review.googlesource.com/22429 Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com> Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
6f3f02f80d
commit
e48a2958d1
11 changed files with 48 additions and 56 deletions
|
|
@ -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) {
|
||||||
// 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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
|
|
||||||
// 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 {
|
||||||
|
|
||||||
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) {
|
||||||
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 {
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
|
|
||||||
// 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) {
|
||||||
}
|
}
|
||||||
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 {
|
||||||
// (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) {
|
||||||
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 {
|
||||||
|
|
||||||
// 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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
// 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 {
|
||||||
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) {
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
25
test/fixedbugs/issue15439.go
Normal file
25
test/fixedbugs/issue15439.go
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
// 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue