From f801da7855d3a82cc415279262e6b1ca7df39e5f Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Mon, 1 Nov 2021 16:05:14 -0700 Subject: [PATCH] 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 Reviewed-by: Robert Findley --- src/cmd/compile/internal/types2/index.go | 89 ++++++++++--------- .../types2/testdata/check/typeparams.go2 | 8 ++ 2 files changed, 54 insertions(+), 43 deletions(-) diff --git a/src/cmd/compile/internal/types2/index.go b/src/cmd/compile/internal/types2/index.go index 23e433ac6a..bb7033e957 100644 --- a/src/cmd/compile/internal/types2/index.go +++ b/src/cmd/compile/internal/types2/index.go @@ -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 } } diff --git a/src/cmd/compile/internal/types2/testdata/check/typeparams.go2 b/src/cmd/compile/internal/types2/testdata/check/typeparams.go2 index 11adb21d95..a1bf6c262f 100644 --- a/src/cmd/compile/internal/types2/testdata/check/typeparams.go2 +++ b/src/cmd/compile/internal/types2/testdata/check/typeparams.go2 @@ -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] }