1
0
mirror of https://github.com/golang/go synced 2024-09-30 02:34:40 -06:00

cmd/compile/internal/types2: fix indexing of generic types

Correctly track if the index expression is addressable.
Rewrote code slightly.

Fixes #49275.

Change-Id: Ic54edd0213a091173ff5403ab0e3e1f1fca0e361
Reviewed-on: https://go-review.googlesource.com/c/go/+/360603
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Robert Griesemer 2021-11-01 16:05:14 -07:00
parent 599de4b2c3
commit f801da7855
2 changed files with 54 additions and 43 deletions

View File

@ -101,77 +101,80 @@ func (check *Checker) indexExpr(x *operand, e *syntax.IndexExpr) (isFuncInst boo
case *TypeParam:
// TODO(gri) report detailed failure cause for better error messages
var tkey, telem Type // tkey != nil if we have maps
var key, elem Type // key != nil: we must have all maps
mode := variable // non-maps result mode
// TODO(gri) factor out closure and use it for non-typeparam cases as well
if typ.underIs(func(u Type) bool {
var key, elem Type
alen := int64(-1) // valid if >= 0
l := int64(-1) // valid if >= 0
var k, e Type // k is only set for maps
switch t := u.(type) {
case *Basic:
if !isString(t) {
return false
if isString(t) {
e = universeByte
mode = value
}
elem = universeByte
case *Array:
elem = t.elem
alen = t.len
case *Pointer:
a, _ := under(t.base).(*Array)
if a == nil {
return false
l = t.len
e = t.elem
if x.mode != variable {
mode = value
}
case *Pointer:
if t := asArray(t.base); t != nil {
l = t.len
e = t.elem
}
elem = a.elem
alen = a.len
case *Slice:
elem = t.elem
e = t.elem
case *Map:
key = t.key
elem = t.elem
default:
k = t.key
e = t.elem
}
if e == nil {
return false
}
assert(elem != nil)
if telem == nil {
if elem == nil {
// first type
tkey, telem = key, elem
length = alen
} else {
// all map keys must be identical (incl. all nil)
if !Identical(key, tkey) {
return false
}
// all element types must be identical
if !Identical(elem, telem) {
return false
}
tkey, telem = key, elem
// track the minimal length for arrays
if alen >= 0 && alen < length {
length = alen
}
length = l
key, elem = k, e
return true
}
// all map keys must be identical (incl. all nil)
// (that is, we cannot mix maps with other types)
if !Identical(key, k) {
return false
}
// all element types must be identical
if !Identical(elem, e) {
return false
}
// track the minimal length for arrays, if any
if l >= 0 && l < length {
length = l
}
return true
}) {
// For maps, the index expression must be assignable to the map key type.
if tkey != nil {
if key != nil {
index := check.singleIndex(e)
if index == nil {
x.mode = invalid
return false
}
var key operand
check.expr(&key, index)
check.assignment(&key, tkey, "map index")
var k operand
check.expr(&k, index)
check.assignment(&k, key, "map index")
// ok to continue even if indexing failed - map element type is known
x.mode = mapindex
x.typ = telem
x.typ = elem
x.expr = e
return false
}
// no maps
valid = true
x.mode = variable
x.typ = telem
x.mode = mode
x.typ = elem
}
}

View File

@ -114,6 +114,14 @@ func _[T interface{ [10]int }](x T, i int) { _ = x[i]; _ = x[9]; _ = x[10 /* ERR
func _[T interface{ [10]byte | string }](x T, i int) { _ = x[i]; _ = x[9]; _ = x[10 /* ERROR out of bounds */ ] }
func _[T interface{ [10]int | *[20]int | []int }](x T, i int) { _ = x[i]; _ = x[9]; _ = x[10 /* ERROR out of bounds */ ] }
// indexing with strings and non-variable arrays (assignment not permitted)
func _[T string](x T) { _ = x[0]; x /* ERROR cannot assign */ [0] = 0 }
func _[T []byte | string](x T) { x /* ERROR cannot assign */ [0] = 0 }
func _[T [10]byte]() { f := func() (x T) { return }; f /* ERROR cannot assign */ ()[0] = 0 }
func _[T [10]byte]() { f := func() (x *T) { return }; f /* ERROR cannot index */ ()[0] = 0 }
func _[T [10]byte]() { f := func() (x *T) { return }; (*f())[0] = 0 }
func _[T *[10]byte]() { f := func() (x T) { return }; f()[0] = 0 }
// slicing
func _[T interface{ ~[10]E }, E any] (x T, i, j, k int) { var _ []E = x[i:j] }