libgo patch committed: Fix unexported embedded structs

Ian Lance Taylor iant@golang.org
Sat Nov 7 01:27:00 GMT 2015


There has been a long-standing discrepancy between the gc and the
gccgo Go compilers in their handling of the type descriptors for
unexported embedded structs.  gccgo correctly records a PkgPath for
the package where those embedded structs are defined.  gc does not.
The Go libraries are written expect the gc behaviour, which causes
them to both be slightly incorrect and to fail in some cases when used
with gccgo.  This was recently sorted out in the gc library.  I'm
bringing the patch over into gccgo now so that the compilers can
finally be consistent.

This patch adds three gc changes to libgo:
https://golang.org/cl/14010, https:/golang.org/cl/14011, and
https://golang.org/cl/14012.  The main Go bug for this is
https://golang.org/issue/7247 .  It has also been reported against
gccgo as https://gcc.gnu.org/PR66138 .

Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu.  Committed
to mainline and GCC 5 branch.

Ian
-------------- next part --------------
Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE	(revision 229880)
+++ gcc/go/gofrontend/MERGE	(working copy)
@@ -1,4 +1,4 @@
-10c1d6756ed1dcc814c49921c2a5e27f4677e0e6
+012ab5cb2ef1c26e8023ce90d3a2bba174da7b30
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
Index: libgo/go/encoding/json/decode_test.go
===================================================================
--- libgo/go/encoding/json/decode_test.go	(revision 229832)
+++ libgo/go/encoding/json/decode_test.go	(working copy)
@@ -118,6 +118,7 @@ type Top struct {
 	Loop
 	Embed0p // has Point with X, Y, used
 	Embed0q // has Point with Z, used
+	embed   // contains exported field
 }
 
 type Embed0 struct {
@@ -148,6 +149,10 @@ type Embed0q struct {
 	Point
 }
 
+type embed struct {
+	Q int
+}
+
 type Loop struct {
 	Loop1 int `json:",omitempty"`
 	Loop2 int `json:",omitempty"`
@@ -331,7 +336,8 @@ var unmarshalTests = []unmarshalTest{
 			"Loop2": 14,
 			"X": 15,
 			"Y": 16,
-			"Z": 17
+			"Z": 17,
+			"Q": 18
 		}`,
 		ptr: new(Top),
 		out: Top{
@@ -361,6 +367,9 @@ var unmarshalTests = []unmarshalTest{
 			Embed0q: Embed0q{
 				Point: Point{Z: 17},
 			},
+			embed: embed{
+				Q: 18,
+			},
 		},
 	},
 	{
@@ -507,12 +516,15 @@ func TestMarshalEmbeds(t *testing.T) {
 		Embed0q: Embed0q{
 			Point: Point{Z: 17},
 		},
+		embed: embed{
+			Q: 18,
+		},
 	}
 	b, err := Marshal(top)
 	if err != nil {
 		t.Fatal(err)
 	}
-	want := "{\"Level0\":1,\"Level1b\":2,\"Level1c\":3,\"Level1a\":5,\"LEVEL1B\":6,\"e\":{\"Level1a\":8,\"Level1b\":9,\"Level1c\":10,\"Level1d\":11,\"x\":12},\"Loop1\":13,\"Loop2\":14,\"X\":15,\"Y\":16,\"Z\":17}"
+	want := "{\"Level0\":1,\"Level1b\":2,\"Level1c\":3,\"Level1a\":5,\"LEVEL1B\":6,\"e\":{\"Level1a\":8,\"Level1b\":9,\"Level1c\":10,\"Level1d\":11,\"x\":12},\"Loop1\":13,\"Loop2\":14,\"X\":15,\"Y\":16,\"Z\":17,\"Q\":18}"
 	if string(b) != want {
 		t.Errorf("Wrong marshal result.\n got: %q\nwant: %q", b, want)
 	}
Index: libgo/go/encoding/json/encode.go
===================================================================
--- libgo/go/encoding/json/encode.go	(revision 229832)
+++ libgo/go/encoding/json/encode.go	(working copy)
@@ -1022,7 +1022,7 @@ func typeFields(t reflect.Type) []field
 			// Scan f.typ for fields to include.
 			for i := 0; i < f.typ.NumField(); i++ {
 				sf := f.typ.Field(i)
-				if sf.PkgPath != "" { // unexported
+				if sf.PkgPath != "" && !sf.Anonymous { // unexported
 					continue
 				}
 				tag := sf.Tag.Get("json")
Index: libgo/go/encoding/xml/marshal_test.go
===================================================================
--- libgo/go/encoding/xml/marshal_test.go	(revision 229832)
+++ libgo/go/encoding/xml/marshal_test.go	(working copy)
@@ -139,6 +139,7 @@ type EmbedA struct {
 	EmbedC
 	EmbedB EmbedB
 	FieldA string
+	embedD
 }
 
 type EmbedB struct {
@@ -153,6 +154,11 @@ type EmbedC struct {
 	FieldC  string
 }
 
+type embedD struct {
+	fieldD string
+	FieldE string // Promoted and visible when embedD is embedded.
+}
+
 type NameCasing struct {
 	XMLName struct{} `xml:"casing"`
 	Xy      string
@@ -711,6 +717,9 @@ var marshalTests = []struct {
 				},
 			},
 			FieldA: "A.A",
+			embedD: embedD{
+				FieldE: "A.D.E",
+			},
 		},
 		ExpectXML: `<EmbedA>` +
 			`<FieldB>A.C.B</FieldB>` +
@@ -724,6 +733,7 @@ var marshalTests = []struct {
 			`<FieldC>A.B.C.C</FieldC>` +
 			`</EmbedB>` +
 			`<FieldA>A.A</FieldA>` +
+			`<FieldE>A.D.E</FieldE>` +
 			`</EmbedA>`,
 	},
 
Index: libgo/go/encoding/xml/typeinfo.go
===================================================================
--- libgo/go/encoding/xml/typeinfo.go	(revision 229832)
+++ libgo/go/encoding/xml/typeinfo.go	(working copy)
@@ -60,7 +60,7 @@ func getTypeInfo(typ reflect.Type) (*typ
 		n := typ.NumField()
 		for i := 0; i < n; i++ {
 			f := typ.Field(i)
-			if f.PkgPath != "" || f.Tag.Get("xml") == "-" {
+			if (f.PkgPath != "" && !f.Anonymous) || f.Tag.Get("xml") == "-" {
 				continue // Private field
 			}
 
Index: libgo/go/reflect/export_test.go
===================================================================
--- libgo/go/reflect/export_test.go	(revision 229832)
+++ libgo/go/reflect/export_test.go	(working copy)
@@ -6,13 +6,13 @@ package reflect
 
 // MakeRO returns a copy of v with the read-only flag set.
 func MakeRO(v Value) Value {
-	v.flag |= flagRO
+	v.flag |= flagStickyRO
 	return v
 }
 
 // IsRO reports whether v's read-only flag is set.
 func IsRO(v Value) bool {
-	return v.flag&flagRO != 0
+	return v.flag&flagStickyRO != 0
 }
 
 var CallGC = &callGC
Index: libgo/go/reflect/type.go
===================================================================
--- libgo/go/reflect/type.go	(revision 229832)
+++ libgo/go/reflect/type.go	(working copy)
@@ -516,7 +516,7 @@ func (t *uncommonType) Method(i int) (m
 	fl := flag(Func)
 	if p.pkgPath != nil {
 		m.PkgPath = *p.pkgPath
-		fl |= flagRO
+		fl |= flagStickyRO
 	}
 	mt := p.typ
 	m.Type = toType(mt)
Index: libgo/go/reflect/value.go
===================================================================
--- libgo/go/reflect/value.go	(revision 229832)
+++ libgo/go/reflect/value.go	(working copy)
@@ -44,7 +44,8 @@ type Value struct {
 
 	// flag holds metadata about the value.
 	// The lowest bits are flag bits:
-	//	- flagRO: obtained via unexported field, so read-only
+	//	- flagStickyRO: obtained via unexported not embedded field, so read-only
+	//	- flagEmbedRO: obtained via unexported embedded field, so read-only
 	//	- flagIndir: val holds a pointer to the data
 	//	- flagAddr: v.CanAddr is true (implies flagIndir)
 	//	- flagMethod: v is a method value.
@@ -67,12 +68,14 @@ type flag uintptr
 const (
 	flagKindWidth        = 5 // there are 27 kinds
 	flagKindMask    flag = 1<<flagKindWidth - 1
-	flagRO          flag = 1 << 5
-	flagIndir       flag = 1 << 6
-	flagAddr        flag = 1 << 7
-	flagMethod      flag = 1 << 8
-	flagMethodFn    flag = 1 << 9 // gccgo: first fn parameter is always pointer
-	flagMethodShift      = 10
+	flagStickyRO    flag = 1 << 5
+	flagEmbedRO     flag = 1 << 6
+	flagIndir       flag = 1 << 7
+	flagAddr        flag = 1 << 8
+	flagMethod      flag = 1 << 9
+	flagMethodFn    flag = 1 << 10 // gccgo: first fn parameter is always pointer
+	flagMethodShift      = 11
+	flagRO          flag = flagStickyRO | flagEmbedRO
 )
 
 func (f flag) kind() Kind {
@@ -617,11 +620,15 @@ func (v Value) Field(i int) Value {
 	field := &tt.fields[i]
 	typ := field.typ
 
-	// Inherit permission bits from v.
-	fl := v.flag&(flagRO|flagIndir|flagAddr) | flag(typ.Kind())
+	// Inherit permission bits from v, but clear flagEmbedRO.
+	fl := v.flag&(flagStickyRO|flagIndir|flagAddr) | flag(typ.Kind())
 	// Using an unexported field forces flagRO.
 	if field.pkgPath != nil {
-		fl |= flagRO
+		if field.name == nil {
+			fl |= flagEmbedRO
+		} else {
+			fl |= flagStickyRO
+		}
 	}
 	// Either flagIndir is set and v.ptr points at struct,
 	// or flagIndir is not set and v.ptr is the actual struct data.
@@ -986,7 +993,7 @@ func (v Value) Method(i int) Value {
 	if v.typ.Kind() == Interface && v.IsNil() {
 		panic("reflect: Method on nil interface value")
 	}
-	fl := v.flag & (flagRO | flagIndir)
+	fl := v.flag & (flagStickyRO | flagIndir) // Clear flagEmbedRO
 	fl |= flag(Func)
 	fl |= flag(i)<<flagMethodShift | flagMethod
 	return Value{v.typ, v.ptr, fl}


More information about the Gcc-patches mailing list