summaryrefslogtreecommitdiff
path: root/libgo/go/reflect
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2020-12-23 09:57:37 -0800
committerIan Lance Taylor <iant@golang.org>2020-12-30 15:13:24 -0800
commitcfcbb4227fb20191e04eb8d7766ae6202f526afd (patch)
treee2effea96f6f204451779f044415c2385e45042b /libgo/go/reflect
parent0696141107d61483f38482b941549959a0d7f613 (diff)
downloadgcc-cfcbb4227fb20191e04eb8d7766ae6202f526afd.tar.gz
libgo: update to Go1.16beta1 release
This does not yet include support for the //go:embed directive added in this release. * Makefile.am (check-runtime): Don't create check-runtime-dir. (mostlyclean-local): Don't remove check-runtime-dir. (check-go-tool, check-vet): Copy in go.mod and modules.txt. (check-cgo-test, check-carchive-test): Add go.mod file. * Makefile.in: Regenerate. Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/280172
Diffstat (limited to 'libgo/go/reflect')
-rw-r--r--libgo/go/reflect/all_test.go249
-rw-r--r--libgo/go/reflect/deepequal.go18
-rw-r--r--libgo/go/reflect/type.go65
-rw-r--r--libgo/go/reflect/value.go37
4 files changed, 318 insertions, 51 deletions
diff --git a/libgo/go/reflect/all_test.go b/libgo/go/reflect/all_test.go
index 68efab6e145..11193022911 100644
--- a/libgo/go/reflect/all_test.go
+++ b/libgo/go/reflect/all_test.go
@@ -74,6 +74,10 @@ var typeTests = []pair{
{struct{ x ([]int8) }{}, "[]int8"},
{struct{ x (map[string]int32) }{}, "map[string]int32"},
{struct{ x (chan<- string) }{}, "chan<- string"},
+ {struct{ x (chan<- chan string) }{}, "chan<- chan string"},
+ {struct{ x (chan<- <-chan string) }{}, "chan<- <-chan string"},
+ {struct{ x (<-chan <-chan string) }{}, "<-chan <-chan string"},
+ {struct{ x (chan (<-chan string)) }{}, "chan (<-chan string)"},
{struct {
x struct {
c chan *int32
@@ -1721,6 +1725,14 @@ func TestSelectMaxCases(t *testing.T) {
_, _, _ = Select(sCases)
}
+func TestSelectNop(t *testing.T) {
+ // "select { default: }" should always return the default case.
+ chosen, _, _ := Select([]SelectCase{{Dir: SelectDefault}})
+ if chosen != 0 {
+ t.Fatalf("expected Select to return 0, but got %#v", chosen)
+ }
+}
+
func BenchmarkSelect(b *testing.B) {
channel := make(chan int)
close(channel)
@@ -4005,9 +4017,12 @@ var convertTests = []struct {
{V(int16(-3)), V(string("\uFFFD"))},
{V(int32(-4)), V(string("\uFFFD"))},
{V(int64(-5)), V(string("\uFFFD"))},
+ {V(int64(-1 << 32)), V(string("\uFFFD"))},
+ {V(int64(1 << 32)), V(string("\uFFFD"))},
{V(uint(0x110001)), V(string("\uFFFD"))},
{V(uint32(0x110002)), V(string("\uFFFD"))},
{V(uint64(0x110003)), V(string("\uFFFD"))},
+ {V(uint64(1 << 32)), V(string("\uFFFD"))},
{V(uintptr(0x110004)), V(string("\uFFFD"))},
// named string
@@ -4263,24 +4278,6 @@ var gFloat32 float32
func TestConvertNaNs(t *testing.T) {
const snan uint32 = 0x7f800001
-
- // Test to see if a store followed by a load of a signaling NaN
- // maintains the signaling bit. The only platform known to fail
- // this test is 386,GO386=387. The real test below will always fail
- // if the platform can't even store+load a float without mucking
- // with the bits.
- gFloat32 = math.Float32frombits(snan)
- runtime.Gosched() // make sure we don't optimize the store/load away
- r := math.Float32bits(gFloat32)
- if r != snan {
- // This should only happen on 386,GO386=387. We have no way to
- // test for 387, so we just make sure we're at least on 386.
- if runtime.GOARCH != "386" {
- t.Errorf("store/load of sNaN not faithful")
- }
- t.Skip("skipping test, float store+load not faithful")
- }
-
type myFloat32 float32
x := V(myFloat32(math.Float32frombits(snan)))
y := x.Convert(TypeOf(float32(0)))
@@ -5519,6 +5516,18 @@ func TestChanOf(t *testing.T) {
// check that type already in binary is found
type T1 int
checkSameType(t, ChanOf(BothDir, TypeOf(T1(1))), (chan T1)(nil))
+
+ // Check arrow token association in undefined chan types.
+ var left chan<- chan T
+ var right chan (<-chan T)
+ tLeft := ChanOf(SendDir, ChanOf(BothDir, TypeOf(T(""))))
+ tRight := ChanOf(BothDir, ChanOf(RecvDir, TypeOf(T(""))))
+ if tLeft != TypeOf(left) {
+ t.Errorf("chan<-chan: have %s, want %T", tLeft, left)
+ }
+ if tRight != TypeOf(right) {
+ t.Errorf("chan<-chan: have %s, want %T", tRight, right)
+ }
}
func TestChanOfDir(t *testing.T) {
@@ -6010,6 +6019,14 @@ func TestReflectMethodTraceback(t *testing.T) {
}
}
+func TestSmallZero(t *testing.T) {
+ type T [10]byte
+ typ := TypeOf(T{})
+ if allocs := testing.AllocsPerRun(100, func() { Zero(typ) }); allocs > 0 {
+ t.Errorf("Creating small zero values caused %f allocs, want 0", allocs)
+ }
+}
+
func TestBigZero(t *testing.T) {
const size = 1 << 10
var v [size]byte
@@ -6021,6 +6038,27 @@ func TestBigZero(t *testing.T) {
}
}
+func TestZeroSet(t *testing.T) {
+ type T [16]byte
+ type S struct {
+ a uint64
+ T T
+ b uint64
+ }
+ v := S{
+ a: 0xaaaaaaaaaaaaaaaa,
+ T: T{9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9},
+ b: 0xbbbbbbbbbbbbbbbb,
+ }
+ ValueOf(&v).Elem().Field(1).Set(Zero(TypeOf(T{})))
+ if v != (S{
+ a: 0xaaaaaaaaaaaaaaaa,
+ b: 0xbbbbbbbbbbbbbbbb,
+ }) {
+ t.Fatalf("Setting a field to a Zero value didn't work")
+ }
+}
+
func TestFieldByIndexNil(t *testing.T) {
type P struct {
F int
@@ -6480,12 +6518,9 @@ func verifyGCBitsSlice(t *testing.T, typ Type, cap int, bits []byte) {
// Repeat the bitmap for the slice size, trimming scalars in
// the last element.
bits = rep(cap, bits)
- for len(bits) > 2 && bits[len(bits)-1] == 0 {
+ for len(bits) > 0 && bits[len(bits)-1] == 0 {
bits = bits[:len(bits)-1]
}
- if len(bits) == 2 && bits[0] == 0 && bits[1] == 0 {
- bits = bits[:0]
- }
if !bytes.Equal(heapBits, bits) {
t.Errorf("heapBits incorrect for make(%v, 0, %v)\nhave %v\nwant %v", typ, cap, heapBits, bits)
}
@@ -7169,6 +7204,176 @@ func TestMapIterDelete1(t *testing.T) {
}
}
+func TestStructTagLookup(t *testing.T) {
+ var tests = []struct {
+ tag StructTag
+ key string
+ expectedValue string
+ expectedOK bool
+ }{
+ {
+ tag: `json:"json_value_1"`,
+ key: "json",
+ expectedValue: "json_value_1",
+ expectedOK: true,
+ },
+ {
+ tag: `json:"json_value_2" xml:"xml_value_2"`,
+ key: "json",
+ expectedValue: "json_value_2",
+ expectedOK: true,
+ },
+ {
+ tag: `json:"json_value_3" xml:"xml_value_3"`,
+ key: "xml",
+ expectedValue: "xml_value_3",
+ expectedOK: true,
+ },
+ {
+ tag: `bson json:"shared_value_4"`,
+ key: "json",
+ expectedValue: "shared_value_4",
+ expectedOK: true,
+ },
+ {
+ tag: `bson json:"shared_value_5"`,
+ key: "bson",
+ expectedValue: "shared_value_5",
+ expectedOK: true,
+ },
+ {
+ tag: `json bson xml form:"field_1,omitempty" other:"value_1"`,
+ key: "xml",
+ expectedValue: "field_1,omitempty",
+ expectedOK: true,
+ },
+ {
+ tag: `json bson xml form:"field_2,omitempty" other:"value_2"`,
+ key: "form",
+ expectedValue: "field_2,omitempty",
+ expectedOK: true,
+ },
+ {
+ tag: `json bson xml form:"field_3,omitempty" other:"value_3"`,
+ key: "other",
+ expectedValue: "value_3",
+ expectedOK: true,
+ },
+ {
+ tag: `json bson xml form:"field_4" other:"value_4"`,
+ key: "json",
+ expectedValue: "field_4",
+ expectedOK: true,
+ },
+ {
+ tag: `json bson xml form:"field_5" other:"value_5"`,
+ key: "non_existing",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ {
+ tag: `json "json_6"`,
+ key: "json",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ {
+ tag: `json:"json_7" bson "bson_7"`,
+ key: "json",
+ expectedValue: "json_7",
+ expectedOK: true,
+ },
+ {
+ tag: `json:"json_8" xml "xml_8"`,
+ key: "xml",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ {
+ tag: `json bson xml form "form_9" other:"value_9"`,
+ key: "bson",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ {
+ tag: `json bson xml form "form_10" other:"value_10"`,
+ key: "other",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ {
+ tag: `json bson xml form:"form_11" other "value_11"`,
+ key: "json",
+ expectedValue: "form_11",
+ expectedOK: true,
+ },
+ {
+ tag: `tag1`,
+ key: "tag1",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ {
+ tag: `tag2 :"hello_2"`,
+ key: "tag2",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ {
+ tag: `tag3: "hello_3"`,
+ key: "tag3",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ {
+ tag: "json\x7fbson: \"hello_4\"",
+ key: "json",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ {
+ tag: "json\x7fbson: \"hello_5\"",
+ key: "bson",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ {
+ tag: "json bson:\x7f\"hello_6\"",
+ key: "json",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ {
+ tag: "json bson:\x7f\"hello_7\"",
+ key: "bson",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ {
+ tag: "json\x09bson:\"hello_8\"",
+ key: "json",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ {
+ tag: "a\x7fb json:\"val\"",
+ key: "json",
+ expectedValue: "",
+ expectedOK: false,
+ },
+ }
+
+ for _, test := range tests {
+ v, ok := test.tag.Lookup(test.key)
+ if v != test.expectedValue {
+ t.Errorf("struct tag lookup failed, got %s, want %s", v, test.expectedValue)
+ }
+ if ok != test.expectedOK {
+ t.Errorf("struct tag lookup failed, got %t, want %t", ok, test.expectedOK)
+ }
+ }
+}
+
// iterateToString returns the set of elements
// returned by an iterator in readable form.
func iterateToString(it *MapIter) string {
diff --git a/libgo/go/reflect/deepequal.go b/libgo/go/reflect/deepequal.go
index b99c345e7bf..d951d8d9997 100644
--- a/libgo/go/reflect/deepequal.go
+++ b/libgo/go/reflect/deepequal.go
@@ -21,7 +21,7 @@ type visit struct {
// Tests for deep equality using reflected types. The map argument tracks
// comparisons that have already been seen, which allows short circuiting on
// recursive types.
-func deepValueEqual(v1, v2 Value, visited map[visit]bool, depth int) bool {
+func deepValueEqual(v1, v2 Value, visited map[visit]bool) bool {
if !v1.IsValid() || !v2.IsValid() {
return v1.IsValid() == v2.IsValid()
}
@@ -29,8 +29,6 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool, depth int) bool {
return false
}
- // if depth > 10 { panic("deepValueEqual") } // for debugging
-
// We want to avoid putting more in the visited map than we need to.
// For any possible reference cycle that might be encountered,
// hard(v1, v2) needs to return true for at least one of the types in the cycle,
@@ -89,7 +87,7 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool, depth int) bool {
switch v1.Kind() {
case Array:
for i := 0; i < v1.Len(); i++ {
- if !deepValueEqual(v1.Index(i), v2.Index(i), visited, depth+1) {
+ if !deepValueEqual(v1.Index(i), v2.Index(i), visited) {
return false
}
}
@@ -105,7 +103,7 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool, depth int) bool {
return true
}
for i := 0; i < v1.Len(); i++ {
- if !deepValueEqual(v1.Index(i), v2.Index(i), visited, depth+1) {
+ if !deepValueEqual(v1.Index(i), v2.Index(i), visited) {
return false
}
}
@@ -114,15 +112,15 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool, depth int) bool {
if v1.IsNil() || v2.IsNil() {
return v1.IsNil() == v2.IsNil()
}
- return deepValueEqual(v1.Elem(), v2.Elem(), visited, depth+1)
+ return deepValueEqual(v1.Elem(), v2.Elem(), visited)
case Ptr:
if v1.Pointer() == v2.Pointer() {
return true
}
- return deepValueEqual(v1.Elem(), v2.Elem(), visited, depth+1)
+ return deepValueEqual(v1.Elem(), v2.Elem(), visited)
case Struct:
for i, n := 0, v1.NumField(); i < n; i++ {
- if !deepValueEqual(v1.Field(i), v2.Field(i), visited, depth+1) {
+ if !deepValueEqual(v1.Field(i), v2.Field(i), visited) {
return false
}
}
@@ -140,7 +138,7 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool, depth int) bool {
for _, k := range v1.MapKeys() {
val1 := v1.MapIndex(k)
val2 := v2.MapIndex(k)
- if !val1.IsValid() || !val2.IsValid() || !deepValueEqual(val1, val2, visited, depth+1) {
+ if !val1.IsValid() || !val2.IsValid() || !deepValueEqual(val1, val2, visited) {
return false
}
}
@@ -217,5 +215,5 @@ func DeepEqual(x, y interface{}) bool {
if v1.Type() != v2.Type() {
return false
}
- return deepValueEqual(v1, v2, make(map[visit]bool), 0)
+ return deepValueEqual(v1, v2, make(map[visit]bool))
}
diff --git a/libgo/go/reflect/type.go b/libgo/go/reflect/type.go
index 73c09d4bb34..1118088fb42 100644
--- a/libgo/go/reflect/type.go
+++ b/libgo/go/reflect/type.go
@@ -49,13 +49,13 @@ type Type interface {
// It panics if i is not in the range [0, NumMethod()).
//
// For a non-interface type T or *T, the returned Method's Type and Func
- // fields describe a function whose first argument is the receiver.
+ // fields describe a function whose first argument is the receiver,
+ // and only exported methods are accessible.
//
// For an interface type, the returned Method's Type field gives the
// method signature, without a receiver, and the Func field is nil.
//
- // Only exported methods are accessible and they are sorted in
- // lexicographic order.
+ // Methods are sorted in lexicographic order.
Method(int) Method
// MethodByName returns the method with that name in the type's
@@ -68,7 +68,9 @@ type Type interface {
// method signature, without a receiver, and the Func field is nil.
MethodByName(string) (Method, bool)
- // NumMethod returns the number of exported methods in the type's method set.
+ // NumMethod returns the number of methods accessible using Method.
+ //
+ // Note that NumMethod counts unexported methods only for interface types.
NumMethod() int
// Name returns the type's name within its package for a defined type.
@@ -840,12 +842,16 @@ type StructField struct {
// A StructTag is the tag string in a struct field.
//
-// By convention, tag strings are a concatenation of
-// optionally space-separated key:"value" pairs.
-// Each key is a non-empty string consisting of non-control
-// characters other than space (U+0020 ' '), quote (U+0022 '"'),
-// and colon (U+003A ':'). Each value is quoted using U+0022 '"'
-// characters and Go string literal syntax.
+// By convention, tag strings are a mapping of keys to values.
+// The format is key:"value". Each key is a non-empty string consisting
+// of non-control characters other than space (U+0020 ' '),
+// quote (U+0022 '"'), and colon (U+003A ':'). Each value is quoted
+// using U+0022 '"' characters and Go string literal syntax.
+// Multiple key-value mappings are separated by zero or more spaces, as in
+// key1:"value1" key2:"value2"
+// Multiple keys may map to a single shared value by separating the keys
+// with spaces, as in
+// key1 key2:"value"
type StructTag string
// Get returns the value associated with key in the tag string.
@@ -868,6 +874,9 @@ func (tag StructTag) Lookup(key string) (value string, ok bool) {
// When modifying this code, also update the validateStructTag code
// in cmd/vet/structtag.go.
+ // keyFound indicates that such key on the left side has already been found.
+ var keyFound bool
+
for tag != "" {
// Skip leading space.
i := 0
@@ -887,11 +896,29 @@ func (tag StructTag) Lookup(key string) (value string, ok bool) {
for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f {
i++
}
- if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' {
+ if i == 0 || i+1 >= len(tag) || tag[i] < ' ' || tag[i] == 0x7f {
break
}
name := string(tag[:i])
- tag = tag[i+1:]
+ tag = tag[i:]
+
+ // If we found a space char here - assume that we have a tag with
+ // multiple keys.
+ if tag[0] == ' ' {
+ if name == key {
+ keyFound = true
+ }
+ continue
+ }
+
+ // Spaces were filtered above so we assume that here we have
+ // only valid tag value started with `:"`.
+ if tag[0] != ':' || tag[1] != '"' {
+ break
+ }
+
+ // Remove the colon leaving tag at the start of the quoted string.
+ tag = tag[1:]
// Scan quoted string to find value.
i = 1
@@ -907,7 +934,7 @@ func (tag StructTag) Lookup(key string) (value string, ok bool) {
qvalue := string(tag[:i+1])
tag = tag[i+1:]
- if key == name {
+ if key == name || keyFound {
value, err := strconv.Unquote(qvalue)
if err != nil {
break
@@ -1438,7 +1465,6 @@ func ChanOf(dir ChanDir, t Type) Type {
}
// Look in known types.
- // TODO: Precedence when constructing string.
var s string
switch dir {
default:
@@ -1448,7 +1474,16 @@ func ChanOf(dir ChanDir, t Type) Type {
case RecvDir:
s = "<-chan " + *typ.string
case BothDir:
- s = "chan " + *typ.string
+ typeStr := *typ.string
+ if typeStr[0] == '<' {
+ // typ is recv chan, need parentheses as "<-" associates with leftmost
+ // chan possible, see:
+ // * https://golang.org/ref/spec#Channel_types
+ // * https://github.com/golang/go/issues/39897
+ s = "chan (" + typeStr + ")"
+ } else {
+ s = "chan " + typeStr
+ }
}
if tt := lookupType(s); tt != nil {
ch := (*chanType)(unsafe.Pointer(toType(tt).(*rtype)))
diff --git a/libgo/go/reflect/value.go b/libgo/go/reflect/value.go
index 1394dd308c4..0b4f094f1b7 100644
--- a/libgo/go/reflect/value.go
+++ b/libgo/go/reflect/value.go
@@ -1364,7 +1364,11 @@ func (v Value) Set(x Value) {
}
x = x.assignTo("reflect.Set", v.typ, target)
if x.flag&flagIndir != 0 {
- typedmemmove(v.typ, v.ptr, x.ptr)
+ if x.ptr == unsafe.Pointer(&zeroVal[0]) {
+ typedmemclr(v.typ, v.ptr)
+ } else {
+ typedmemmove(v.typ, v.ptr, x.ptr)
+ }
} else {
*(*unsafe.Pointer)(v.ptr) = x.ptr
}
@@ -2171,11 +2175,24 @@ func Zero(typ Type) Value {
t := typ.(*rtype)
fl := flag(t.Kind())
if ifaceIndir(t) {
- return Value{t, unsafe_New(t), fl | flagIndir}
+ var p unsafe.Pointer
+ if t.size <= maxZero {
+ p = unsafe.Pointer(&zeroVal[0])
+ } else {
+ p = unsafe_New(t)
+ }
+ return Value{t, p, fl | flagIndir}
}
return Value{t, nil, fl}
}
+// must match declarations in runtime/map.go.
+const maxZero = 1024
+
+// Using linkname here doesn't work for gofrontend.
+// //go:linkname zeroVal runtime.zeroVal
+var zeroVal [maxZero]byte
+
// New returns a Value representing a pointer to a new zero value
// for the specified type. That is, the returned Value's Type is PtrTo(typ).
func New(typ Type) Value {
@@ -2466,12 +2483,20 @@ func cvtComplex(v Value, t Type) Value {
// convertOp: intXX -> string
func cvtIntString(v Value, t Type) Value {
- return makeString(v.flag.ro(), string(rune(v.Int())), t)
+ s := "\uFFFD"
+ if x := v.Int(); int64(rune(x)) == x {
+ s = string(rune(x))
+ }
+ return makeString(v.flag.ro(), s, t)
}
// convertOp: uintXX -> string
func cvtUintString(v Value, t Type) Value {
- return makeString(v.flag.ro(), string(rune(v.Uint())), t)
+ s := "\uFFFD"
+ if x := v.Uint(); uint64(rune(x)) == x {
+ s = string(rune(x))
+ }
+ return makeString(v.flag.ro(), s, t)
}
// convertOp: []byte -> string
@@ -2592,6 +2617,10 @@ func memmove(dst, src unsafe.Pointer, size uintptr)
//go:noescape
func typedmemmove(t *rtype, dst, src unsafe.Pointer)
+// typedmemclr zeros the value at ptr of type t.
+//go:noescape
+func typedmemclr(t *rtype, ptr unsafe.Pointer)
+
// typedslicecopy copies a slice of elemType values from src to dst,
// returning the number of elements copied.
//go:noescape