From a7c5b619f56467413bf15d0ac8ba30d04e3424e8 Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 14 Sep 2017 03:48:51 +0000 Subject: [PATCH] compiler, reflect: fix struct field names for embedded aliases This adds much of https://golang.org/cl/35731 and https://golang.org/cl/35732 to the gofrontend code. This is a step toward updating libgo to the 1.9 release. The gofrontend already supports type aliases, and this is required for correct support of type aliases when used as embedded fields. The change to expressions.cc is to handle the << 1, used for the newly renamed offsetAnon field, in the constant context used for type descriptor initialization. Reviewed-on: https://go-review.googlesource.com/62710 git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@252746 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/expressions.cc | 63 +++++++++++++++----------- gcc/go/gofrontend/types.cc | 24 +++++----- libgo/go/reflect/all_test.go | 95 +++++++++++++++++++--------------------- libgo/go/reflect/type.go | 83 ++++++++++++++++++----------------- libgo/go/reflect/value.go | 4 +- 6 files changed, 140 insertions(+), 131 deletions(-) diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index c4600de4cc8..a905f583859 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -8c6d9ff6f60b737d1e96c0dab0b4e67402bf3316 +b0a46c2cdb915ddc4a4e401af9ef6eb2bcd4d4ea The first line of this file holds the git revision number of the last merge done from the gofrontend repository. diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index 6f9c1c96c3f..1e4d90647b8 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -6044,35 +6044,46 @@ Binary_expression::do_get_backend(Translate_context* context) { go_assert(left_type->integer_type() != NULL); - mpz_t bitsval; int bits = left_type->integer_type()->bits(); - mpz_init_set_ui(bitsval, bits); - Bexpression* bits_expr = - gogo->backend()->integer_constant_expression(right_btype, bitsval); - Bexpression* compare = - gogo->backend()->binary_expression(OPERATOR_LT, - right, bits_expr, loc); - - Bexpression* zero_expr = - gogo->backend()->integer_constant_expression(left_btype, zero); - overflow = zero_expr; - Bfunction* bfn = context->function()->func_value()->get_decl(); - if (this->op_ == OPERATOR_RSHIFT - && !left_type->integer_type()->is_unsigned()) + + Numeric_constant nc; + unsigned long ul; + if (!this->right_->numeric_constant_value(&nc) + || nc.to_unsigned_long(&ul) != Numeric_constant::NC_UL_VALID + || ul >= static_cast(bits)) { - Bexpression* neg_expr = - gogo->backend()->binary_expression(OPERATOR_LT, left, - zero_expr, loc); - Bexpression* neg_one_expr = - gogo->backend()->integer_constant_expression(left_btype, neg_one); - overflow = gogo->backend()->conditional_expression(bfn, - btype, neg_expr, - neg_one_expr, - zero_expr, loc); + mpz_t bitsval; + mpz_init_set_ui(bitsval, bits); + Bexpression* bits_expr = + gogo->backend()->integer_constant_expression(right_btype, bitsval); + Bexpression* compare = + gogo->backend()->binary_expression(OPERATOR_LT, + right, bits_expr, loc); + + Bexpression* zero_expr = + gogo->backend()->integer_constant_expression(left_btype, zero); + overflow = zero_expr; + Bfunction* bfn = context->function()->func_value()->get_decl(); + if (this->op_ == OPERATOR_RSHIFT + && !left_type->integer_type()->is_unsigned()) + { + Bexpression* neg_expr = + gogo->backend()->binary_expression(OPERATOR_LT, left, + zero_expr, loc); + Bexpression* neg_one_expr = + gogo->backend()->integer_constant_expression(left_btype, + neg_one); + overflow = gogo->backend()->conditional_expression(bfn, + btype, + neg_expr, + neg_one_expr, + zero_expr, + loc); + } + ret = gogo->backend()->conditional_expression(bfn, btype, compare, + ret, overflow, loc); + mpz_clear(bitsval); } - ret = gogo->backend()->conditional_expression(bfn, btype, compare, ret, - overflow, loc); - mpz_clear(bitsval); } // Add checks for division by zero and division overflow as needed. diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc index cdf1f402834..e91922c1cac 100644 --- a/gcc/go/gofrontend/types.cc +++ b/gcc/go/gofrontend/types.cc @@ -6372,7 +6372,7 @@ Struct_type::make_struct_type_descriptor_type() "pkgPath", pointer_string_type, "typ", ptdt, "tag", pointer_string_type, - "offset", uintptr_type); + "offsetAnon", uintptr_type); Type* nsf = Type::make_builtin_named_type("structField", sf); Type* slice_type = Type::make_array_type(nsf, NULL); @@ -6429,14 +6429,9 @@ Struct_type::do_type_descriptor(Gogo* gogo, Named_type* name) Struct_field_list::const_iterator q = f->begin(); go_assert(q->is_field_name("name")); - if (pf->is_anonymous()) - fvals->push_back(Expression::make_nil(bloc)); - else - { - std::string n = Gogo::unpack_hidden_name(pf->field_name()); - Expression* s = Expression::make_string(n, bloc); - fvals->push_back(Expression::make_unary(OPERATOR_AND, s, bloc)); - } + std::string n = Gogo::unpack_hidden_name(pf->field_name()); + Expression* s = Expression::make_string(n, bloc); + fvals->push_back(Expression::make_unary(OPERATOR_AND, s, bloc)); ++q; go_assert(q->is_field_name("pkgPath")); @@ -6469,8 +6464,15 @@ Struct_type::do_type_descriptor(Gogo* gogo, Named_type* name) } ++q; - go_assert(q->is_field_name("offset")); - fvals->push_back(Expression::make_struct_field_offset(this, &*pf)); + go_assert(q->is_field_name("offsetAnon")); + Type* uintptr_type = Type::lookup_integer_type("uintptr"); + Expression* o = Expression::make_struct_field_offset(this, &*pf); + Expression* one = Expression::make_integer_ul(1, uintptr_type, bloc); + o = Expression::make_binary(OPERATOR_LSHIFT, o, one, bloc); + int av = pf->is_anonymous() ? 1 : 0; + Expression* anon = Expression::make_integer_ul(av, uintptr_type, bloc); + o = Expression::make_binary(OPERATOR_OR, o, anon, bloc); + fvals->push_back(o); Expression* v = Expression::make_struct_composite_literal(element_type, fvals, bloc); diff --git a/libgo/go/reflect/all_test.go b/libgo/go/reflect/all_test.go index 3686167ed42..6ac33526774 100644 --- a/libgo/go/reflect/all_test.go +++ b/libgo/go/reflect/all_test.go @@ -4184,50 +4184,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: "s2", Anonymous: true, Type: TypeOf(s2{})}, + mustPanic: true, }, { - field: StructField{Name: "", Type: TypeOf((*s2)(nil))}, - mustPanic: false, - exported: false, + field: StructField{Name: "s2", Anonymous: true, Type: TypeOf((*s2)(nil))}, + mustPanic: true, }, { - field: StructField{Name: "", Type: TypeOf(S1{}), PkgPath: "other/pkg"}, + field: StructField{Name: "Name", Type: nil, PkgPath: ""}, mustPanic: true, - exported: true, }, { - field: StructField{Name: "", Type: TypeOf((*S1)(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, - exported: true, }, { - field: StructField{Name: "", Type: TypeOf(s2{}), PkgPath: "other/pkg"}, + field: StructField{Name: "s2", Anonymous: true, Type: TypeOf((*s2)(nil)), PkgPath: "other/pkg"}, mustPanic: true, - exported: false, }, { - field: StructField{Name: "", Type: TypeOf((*s2)(nil)), PkgPath: "other/pkg"}, + 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{})}, @@ -4235,81 +4243,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 { @@ -4319,7 +4314,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 { diff --git a/libgo/go/reflect/type.go b/libgo/go/reflect/type.go index 3ae0f182b3a..97b986a7bba 100644 --- a/libgo/go/reflect/type.go +++ b/libgo/go/reflect/type.go @@ -370,11 +370,19 @@ type sliceType struct { // Struct field type structField struct { - name *string // nil for embedded fields - pkgPath *string // nil for exported Names; otherwise import path - typ *rtype // type of field - tag *string // nil if no tag - offset uintptr // byte offset of field within struct + name *string // name is always non-empty + pkgPath *string // nil for exported Names; otherwise import path + typ *rtype // type of field + tag *string // nil if no tag + 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. @@ -880,23 +888,15 @@ func (t *structType) Field(i int) (f StructField) { } p := &t.fields[i] f.Type = toType(p.typ) - if p.name != nil { - f.Name = *p.name - } else { - t := f.Type - if t.Kind() == Ptr { - t = t.Elem() - } - f.Name = t.Name() - f.Anonymous = true - } + f.Name = *p.name + f.Anonymous = p.anon() if p.pkgPath != nil { f.PkgPath = *p.pkgPath } if p.tag != nil { f.Tag = StructTag(*p.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, @@ -984,18 +984,15 @@ func (t *structType) FieldByNameFunc(match func(string) bool) (result StructFiel for i := range t.fields { f := &t.fields[i] // Find name and type for field f. - var fname string + fname := *f.name var ntyp *rtype - if f.name != nil { - fname = *f.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? @@ -1053,13 +1050,12 @@ func (t *structType) FieldByName(name string) (f StructField, present bool) { if name != "" { for i := range t.fields { tf := &t.fields[i] - if tf.name == nil { - hasAnon = true - continue - } if *tf.name == name { return t.Field(i), true } + if tf.anon() { + hasAnon = true + } } } if !hasAnon { @@ -1390,7 +1386,7 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool { if cmpTags && tf.tag != vf.tag && (tf.tag == nil || vf.tag == nil || *tf.tag != *vf.tag) { return false } - if tf.offset != vf.offset { + if tf.offsetAnon != vf.offsetAnon { return false } } @@ -1946,11 +1942,10 @@ func StructOf(fields []StructField) Type { hasPtr = true } - name := "" // Update string and hash + name := *f.name hash = (hash << 1) + ft.hash - if f.name != nil { - name = *f.name + if !f.anon() { repr = append(repr, (" " + name)...) } else { // Embedded field @@ -2009,11 +2004,12 @@ func StructOf(fields []StructField) Type { comparable = comparable && (ft.equalfn != nil) hashable = hashable && (ft.hashfn != nil) - f.offset = align(size, uintptr(ft.fieldAlign)) + offset := align(size, uintptr(ft.fieldAlign)) if int8(ft.fieldAlign) > typalign { typalign = int8(ft.fieldAlign) } - size = f.offset + ft.size + size = offset + ft.size + f.offsetAnon |= offset << 1 if ft.size == 0 { lastzero = size @@ -2148,7 +2144,7 @@ func StructOf(fields []StructField) Type { typ.hashfn = 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.hashfn(pi, o) } return o @@ -2160,8 +2156,8 @@ func StructOf(fields []StructField) Type { if comparable { typ.equalfn = 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.equalfn(pi, qi) { return false } @@ -2196,6 +2192,11 @@ func runtimeStructField(field StructField) structField { } } + offsetAnon := uintptr(0) + if field.Anonymous { + offsetAnon |= 1 + } + var pkgPath *string if field.PkgPath != "" { s := field.PkgPath @@ -2212,11 +2213,11 @@ func runtimeStructField(field StructField) structField { } return structField{ - name: name, - pkgPath: pkgPath, - typ: field.Type.common(), - tag: tag, - offset: 0, + name: name, + pkgPath: pkgPath, + typ: field.Type.common(), + tag: tag, + offsetAnon: offsetAnon, } } @@ -2239,7 +2240,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()) @@ -2513,7 +2514,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) } } } diff --git a/libgo/go/reflect/value.go b/libgo/go/reflect/value.go index 1b4c5407e57..208bb2f4c01 100644 --- a/libgo/go/reflect/value.go +++ b/libgo/go/reflect/value.go @@ -625,7 +625,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.pkgPath != nil { - if field.name == nil { + if field.anon() { fl |= flagEmbedRO } else { fl |= flagStickyRO @@ -636,7 +636,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} } -- 2.11.4.GIT