1
0
mirror of https://github.com/golang/go synced 2024-11-24 09:20:02 -07:00

go/types: fix indexing of generic types

This is a clean port of CL 360603 to go/types.

Change-Id: Iadb312f07e509ff83339d5525765b7b7987bf233
Reviewed-on: https://go-review.googlesource.com/c/go/+/360936
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Robert Findley 2021-11-02 18:36:36 -04:00
parent d6f7203a3c
commit 2b81b863a2
2 changed files with 54 additions and 43 deletions

View File

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