mirror of
https://github.com/golang/go
synced 2024-11-27 03:21:18 -07:00
go/types, types2: detect dupl. map keys in comp. literals with generic key type
For map composite literals where the key type is a suitably constrained type parameter, the existing key duplicate detection mechanism doesn't work when the keys are numeric values of different types but equal value. For instance, given func _[P int64|float64]() { _ = map[P]string{0: "foo", 0.0: "bar"} } the key values 0 and 0.0 have the same numeric value 0 but currently are treated as different values int64(0) and float64(0.0). For any valid instantiation of P, the keys will collide. This CL changes the keyVal function to map numeric types to the "smallest" numeric type in which a value can be represented. For instance, float64(0.0) is mapped to int64(0). This ensures that numerically equal values are always represented the same way so that they can be detected as duplicates. Fixes #51610. Change-Id: I3eb71142bbe6b13453282a7f71ee48950e58ecbd Reviewed-on: https://go-review.googlesource.com/c/go/+/406555 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Robert Griesemer <gri@google.com>
This commit is contained in:
parent
3fa5ab0d83
commit
2138124143
@ -1741,16 +1741,33 @@ Error:
|
||||
return statement // avoid follow-up errors
|
||||
}
|
||||
|
||||
// keyVal maps a complex, float, integer, string or boolean constant value
|
||||
// to the corresponding complex128, float64, int64, uint64, string, or bool
|
||||
// Go value if possible; otherwise it returns x.
|
||||
// A complex constant that can be represented as a float (such as 1.2 + 0i)
|
||||
// is returned as a floating point value; if a floating point value can be
|
||||
// represented as an integer (such as 1.0) it is returned as an integer value.
|
||||
// This ensures that constants of different kind but equal value (such as
|
||||
// 1.0 + 0i, 1.0, 1) result in the same value.
|
||||
func keyVal(x constant.Value) interface{} {
|
||||
// TODO(gri) This function must map 1, 1.0, and 1.0 + 0i to the same (integer) value.
|
||||
// Same for 1.1 and 1.1 + 0i.
|
||||
// Otherwise we won't get duplicate key errors for certain type parameter
|
||||
// key types. See issue #51610.
|
||||
switch x.Kind() {
|
||||
case constant.Bool:
|
||||
return constant.BoolVal(x)
|
||||
case constant.String:
|
||||
return constant.StringVal(x)
|
||||
case constant.Complex:
|
||||
f := constant.ToFloat(x)
|
||||
if f.Kind() != constant.Float {
|
||||
r, _ := constant.Float64Val(constant.Real(x))
|
||||
i, _ := constant.Float64Val(constant.Imag(x))
|
||||
return complex(r, i)
|
||||
}
|
||||
x = f
|
||||
fallthrough
|
||||
case constant.Float:
|
||||
i := constant.ToInt(x)
|
||||
if i.Kind() != constant.Int {
|
||||
v, _ := constant.Float64Val(x)
|
||||
return v
|
||||
}
|
||||
x = i
|
||||
fallthrough
|
||||
case constant.Int:
|
||||
if v, ok := constant.Int64Val(x); ok {
|
||||
return v
|
||||
@ -1758,13 +1775,10 @@ func keyVal(x constant.Value) interface{} {
|
||||
if v, ok := constant.Uint64Val(x); ok {
|
||||
return v
|
||||
}
|
||||
case constant.Float:
|
||||
v, _ := constant.Float64Val(x)
|
||||
return v
|
||||
case constant.Complex:
|
||||
r, _ := constant.Float64Val(constant.Real(x))
|
||||
i, _ := constant.Float64Val(constant.Imag(x))
|
||||
return complex(r, i)
|
||||
case constant.String:
|
||||
return constant.StringVal(x)
|
||||
case constant.Bool:
|
||||
return constant.BoolVal(x)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
9
src/cmd/compile/internal/types2/testdata/fixedbugs/issue51610.go
vendored
Normal file
9
src/cmd/compile/internal/types2/testdata/fixedbugs/issue51610.go
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2022 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 p
|
||||
|
||||
func _[P int | float64 | complex128]() {
|
||||
_ = map[P]int{1: 1, 1.0 /* ERROR duplicate key 1 */ : 2, 1 /* ERROR duplicate key \(1 \+ 0i\) */ + 0i: 3}
|
||||
}
|
@ -1663,16 +1663,33 @@ Error:
|
||||
return statement // avoid follow-up errors
|
||||
}
|
||||
|
||||
func keyVal(x constant.Value) any {
|
||||
// TODO(gri) This function must map 1, 1.0, and 1.0 + 0i to the same (integer) value.
|
||||
// Same for 1.1 and 1.1 + 0i.
|
||||
// Otherwise we won't get duplicate key errors for certain type parameter
|
||||
// key types. See issue #51610.
|
||||
// keyVal maps a complex, float, integer, string or boolean constant value
|
||||
// to the corresponding complex128, float64, int64, uint64, string, or bool
|
||||
// Go value if possible; otherwise it returns x.
|
||||
// A complex constant that can be represented as a float (such as 1.2 + 0i)
|
||||
// is returned as a floating point value; if a floating point value can be
|
||||
// represented as an integer (such as 1.0) it is returned as an integer value.
|
||||
// This ensures that constants of different kind but equal value (such as
|
||||
// 1.0 + 0i, 1.0, 1) result in the same value.
|
||||
func keyVal(x constant.Value) interface{} {
|
||||
switch x.Kind() {
|
||||
case constant.Bool:
|
||||
return constant.BoolVal(x)
|
||||
case constant.String:
|
||||
return constant.StringVal(x)
|
||||
case constant.Complex:
|
||||
f := constant.ToFloat(x)
|
||||
if f.Kind() != constant.Float {
|
||||
r, _ := constant.Float64Val(constant.Real(x))
|
||||
i, _ := constant.Float64Val(constant.Imag(x))
|
||||
return complex(r, i)
|
||||
}
|
||||
x = f
|
||||
fallthrough
|
||||
case constant.Float:
|
||||
i := constant.ToInt(x)
|
||||
if i.Kind() != constant.Int {
|
||||
v, _ := constant.Float64Val(x)
|
||||
return v
|
||||
}
|
||||
x = i
|
||||
fallthrough
|
||||
case constant.Int:
|
||||
if v, ok := constant.Int64Val(x); ok {
|
||||
return v
|
||||
@ -1680,13 +1697,10 @@ func keyVal(x constant.Value) any {
|
||||
if v, ok := constant.Uint64Val(x); ok {
|
||||
return v
|
||||
}
|
||||
case constant.Float:
|
||||
v, _ := constant.Float64Val(x)
|
||||
return v
|
||||
case constant.Complex:
|
||||
r, _ := constant.Float64Val(constant.Real(x))
|
||||
i, _ := constant.Float64Val(constant.Imag(x))
|
||||
return complex(r, i)
|
||||
case constant.String:
|
||||
return constant.StringVal(x)
|
||||
case constant.Bool:
|
||||
return constant.BoolVal(x)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
9
src/go/types/testdata/fixedbugs/issue51610.go
vendored
Normal file
9
src/go/types/testdata/fixedbugs/issue51610.go
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright 2022 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 p
|
||||
|
||||
func _[P int | float64 | complex128]() {
|
||||
_ = map[P]int{1: 1, 1.0 /* ERROR duplicate key 1 */ : 2, 1 /* ERROR duplicate key \(1 \+ 0i\) */ + 0i: 3}
|
||||
}
|
Loading…
Reference in New Issue
Block a user