1
0
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:
Robert Griesemer 2022-05-16 17:43:38 -07:00
parent 3fa5ab0d83
commit 2138124143
4 changed files with 77 additions and 31 deletions

View File

@ -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
}

View 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}
}

View File

@ -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
}

View 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}
}