mirror of
https://github.com/golang/go
synced 2024-09-15 22:20:06 +00:00
[dev.typeparams] go/types: clean up index expr implementation for type parameters
This is a port of CL 332553 to go/types. The "expr" variable is renamed to "e" in Checker.indexExpr to be consistent with types2. Change-Id: I7905bebf2e8dab47256361362b16becf7596cf95 Reviewed-on: https://go-review.googlesource.com/c/go/+/335110 Reviewed-by: Robert Griesemer <gri@golang.org> Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Go Bot <gobot@golang.org> Trust: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
c4cd76fbbb
commit
e9836fe318
|
@ -15,18 +15,18 @@ import (
|
||||||
// If e is a valid function instantiation, indexExpr returns true.
|
// If e is a valid function instantiation, indexExpr returns true.
|
||||||
// In that case x represents the uninstantiated function value and
|
// In that case x represents the uninstantiated function value and
|
||||||
// it is the caller's responsibility to instantiate the function.
|
// it is the caller's responsibility to instantiate the function.
|
||||||
func (check *Checker) indexExpr(x *operand, expr *typeparams.IndexExpr) (isFuncInst bool) {
|
func (check *Checker) indexExpr(x *operand, e *typeparams.IndexExpr) (isFuncInst bool) {
|
||||||
check.exprOrType(x, expr.X)
|
check.exprOrType(x, e.X)
|
||||||
|
|
||||||
switch x.mode {
|
switch x.mode {
|
||||||
case invalid:
|
case invalid:
|
||||||
check.use(expr.Indices...)
|
check.use(e.Indices...)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
case typexpr:
|
case typexpr:
|
||||||
// type instantiation
|
// type instantiation
|
||||||
x.mode = invalid
|
x.mode = invalid
|
||||||
x.typ = check.varType(expr.Orig)
|
x.typ = check.varType(e.Orig)
|
||||||
if x.typ != Typ[Invalid] {
|
if x.typ != Typ[Invalid] {
|
||||||
x.mode = typexpr
|
x.mode = typexpr
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ func (check *Checker) indexExpr(x *operand, expr *typeparams.IndexExpr) (isFuncI
|
||||||
|
|
||||||
valid := false
|
valid := false
|
||||||
length := int64(-1) // valid if >= 0
|
length := int64(-1) // valid if >= 0
|
||||||
switch typ := optype(x.typ).(type) {
|
switch typ := under(x.typ).(type) {
|
||||||
case *Basic:
|
case *Basic:
|
||||||
if isString(typ) {
|
if isString(typ) {
|
||||||
valid = true
|
valid = true
|
||||||
|
@ -77,10 +77,10 @@ func (check *Checker) indexExpr(x *operand, expr *typeparams.IndexExpr) (isFuncI
|
||||||
x.typ = typ.elem
|
x.typ = typ.elem
|
||||||
|
|
||||||
case *Map:
|
case *Map:
|
||||||
index := check.singleIndex(expr)
|
index := check.singleIndex(e)
|
||||||
if index == nil {
|
if index == nil {
|
||||||
x.mode = invalid
|
x.mode = invalid
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
var key operand
|
var key operand
|
||||||
check.expr(&key, index)
|
check.expr(&key, index)
|
||||||
|
@ -88,88 +88,81 @@ func (check *Checker) indexExpr(x *operand, expr *typeparams.IndexExpr) (isFuncI
|
||||||
// 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 = typ.elem
|
x.typ = typ.elem
|
||||||
x.expr = expr.Orig
|
x.expr = e.Orig
|
||||||
return
|
return false
|
||||||
|
|
||||||
case *Union:
|
|
||||||
// A union type can be indexed if all of the union's terms
|
|
||||||
// support indexing and have the same index and element
|
|
||||||
// type. Special rules apply for maps in the union type.
|
|
||||||
var tkey, telem Type // key is for map types only
|
|
||||||
nmaps := 0 // number of map types in union type
|
|
||||||
if typ.underIs(func(t Type) bool {
|
|
||||||
var e Type
|
|
||||||
switch t := t.(type) {
|
|
||||||
case *Basic:
|
|
||||||
if isString(t) {
|
|
||||||
e = universeByte
|
|
||||||
}
|
|
||||||
case *Array:
|
|
||||||
e = t.elem
|
|
||||||
case *Pointer:
|
|
||||||
if t := asArray(t.base); t != nil {
|
|
||||||
e = t.elem
|
|
||||||
}
|
|
||||||
case *Slice:
|
|
||||||
e = t.elem
|
|
||||||
case *Map:
|
|
||||||
// If there are multiple maps in the union type,
|
|
||||||
// they must have identical key types.
|
|
||||||
// TODO(gri) We may be able to relax this rule
|
|
||||||
// but it becomes complicated very quickly.
|
|
||||||
if tkey != nil && !Identical(t.key, tkey) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
tkey = t.key
|
|
||||||
e = t.elem
|
|
||||||
nmaps++
|
|
||||||
case *TypeParam:
|
case *TypeParam:
|
||||||
check.errorf(x, 0, "type of %s contains a type parameter - cannot index (implementation restriction)", x)
|
// TODO(gri) report detailed failure cause for better error messages
|
||||||
case *instance:
|
var tkey, telem Type // tkey != nil if we have maps
|
||||||
panic("unimplemented")
|
if typ.underIs(func(u Type) bool {
|
||||||
}
|
var key, elem Type
|
||||||
if e == nil || telem != nil && !Identical(e, telem) {
|
alen := int64(-1) // valid if >= 0
|
||||||
|
switch t := u.(type) {
|
||||||
|
case *Basic:
|
||||||
|
if !isString(t) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
telem = e
|
elem = universeByte
|
||||||
|
case *Array:
|
||||||
|
elem = t.elem
|
||||||
|
alen = t.len
|
||||||
|
case *Pointer:
|
||||||
|
a, _ := under(t.base).(*Array)
|
||||||
|
if a == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
elem = a.elem
|
||||||
|
alen = a.len
|
||||||
|
case *Slice:
|
||||||
|
elem = t.elem
|
||||||
|
case *Map:
|
||||||
|
key = t.key
|
||||||
|
elem = t.elem
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
assert(elem != nil)
|
||||||
|
if telem == 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
|
||||||
|
}
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}) {
|
}) {
|
||||||
// If there are maps, the index expression must be assignable
|
// For maps, the index expression must be assignable to the map key type.
|
||||||
// to the map key type (as for simple map index expressions).
|
if tkey != nil {
|
||||||
if nmaps > 0 {
|
index := check.singleIndex(e)
|
||||||
index := check.singleIndex(expr)
|
|
||||||
if index == nil {
|
if index == nil {
|
||||||
x.mode = invalid
|
x.mode = invalid
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
var key operand
|
var key operand
|
||||||
check.expr(&key, index)
|
check.expr(&key, index)
|
||||||
check.assignment(&key, tkey, "map index")
|
check.assignment(&key, tkey, "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
|
||||||
|
|
||||||
// If there are only maps, we are done.
|
|
||||||
if nmaps == typ.NumTerms() {
|
|
||||||
x.mode = mapindex
|
x.mode = mapindex
|
||||||
x.typ = telem
|
x.typ = telem
|
||||||
x.expr = expr.Orig
|
x.expr = e
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise we have mix of maps and other types. For
|
|
||||||
// now we require that the map key be an integer type.
|
|
||||||
// TODO(gri) This is probably not good enough.
|
|
||||||
valid = isInteger(tkey)
|
|
||||||
// avoid 2nd indexing error if indexing failed above
|
|
||||||
if !valid && key.mode == invalid {
|
|
||||||
x.mode = invalid
|
|
||||||
return
|
|
||||||
}
|
|
||||||
x.mode = value // map index expressions are not addressable
|
|
||||||
} else {
|
|
||||||
// no maps
|
// no maps
|
||||||
valid = true
|
valid = true
|
||||||
x.mode = variable
|
x.mode = variable
|
||||||
}
|
|
||||||
x.typ = telem
|
x.typ = telem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,13 +170,13 @@ func (check *Checker) indexExpr(x *operand, expr *typeparams.IndexExpr) (isFuncI
|
||||||
if !valid {
|
if !valid {
|
||||||
check.invalidOp(x, _NonIndexableOperand, "cannot index %s", x)
|
check.invalidOp(x, _NonIndexableOperand, "cannot index %s", x)
|
||||||
x.mode = invalid
|
x.mode = invalid
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
index := check.singleIndex(expr)
|
index := check.singleIndex(e)
|
||||||
if index == nil {
|
if index == nil {
|
||||||
x.mode = invalid
|
x.mode = invalid
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// In pathological (invalid) cases (e.g.: type T1 [][[]T1{}[0][0]]T0)
|
// In pathological (invalid) cases (e.g.: type T1 [][[]T1{}[0][0]]T0)
|
||||||
|
|
17
src/go/types/testdata/check/typeparams.go2
vendored
17
src/go/types/testdata/check/typeparams.go2
vendored
|
@ -98,18 +98,23 @@ func _[T any] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
|
||||||
func _[T interface{ ~int }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
|
func _[T interface{ ~int }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
|
||||||
func _[T interface{ ~string }] (x T, i int) { _ = x[i] }
|
func _[T interface{ ~string }] (x T, i int) { _ = x[i] }
|
||||||
func _[T interface{ ~[]int }] (x T, i int) { _ = x[i] }
|
func _[T interface{ ~[]int }] (x T, i int) { _ = x[i] }
|
||||||
func _[T interface{ ~[10]int | ~*[20]int | ~map[int]int }] (x T, i int) { _ = x[i] }
|
func _[T interface{ ~[10]int | ~*[20]int | ~map[int]int }] (x T, i int) { _ = x /* ERROR cannot index */ [i] } // map and non-map types
|
||||||
func _[T interface{ ~string | ~[]byte }] (x T, i int) { _ = x[i] }
|
func _[T interface{ ~string | ~[]byte }] (x T, i int) { _ = x[i] }
|
||||||
func _[T interface{ ~[]int | ~[1]rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
|
func _[T interface{ ~[]int | ~[1]rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
|
||||||
func _[T interface{ ~string | ~[]rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
|
func _[T interface{ ~string | ~[]rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
|
||||||
|
|
||||||
// indexing with various combinations of map types in type lists (see issue #42616)
|
// indexing with various combinations of map types in type sets (see issue #42616)
|
||||||
func _[T interface{ ~[]E | ~map[int]E }, E any](x T, i int) { _ = x[i] }
|
func _[T interface{ ~[]E | ~map[int]E }, E any](x T, i int) { _ = x /* ERROR cannot index */ [i] } // map and non-map types
|
||||||
func _[T interface{ ~[]E }, E any](x T, i int) { _ = &x[i] }
|
func _[T interface{ ~[]E }, E any](x T, i int) { _ = &x[i] }
|
||||||
func _[T interface{ ~map[int]E }, E any](x T, i int) { _, _ = x[i] } // comma-ok permitted
|
func _[T interface{ ~map[int]E }, E any](x T, i int) { _, _ = x[i] } // comma-ok permitted
|
||||||
func _[T interface{ ~[]E | ~map[int]E }, E any](x T, i int) { _ = &x /* ERROR cannot take address */ [i] }
|
func _[T interface{ ~map[int]E }, E any](x T, i int) { _ = &x /* ERROR cannot take address */ [i] }
|
||||||
func _[T interface{ ~[]E | ~map[int]E | ~map[uint]E }, E any](x T, i int) { _ = x /* ERROR cannot index */ [i] } // different map element types
|
func _[T interface{ ~map[int]E | ~map[uint]E }, E any](x T, i int) { _ = x /* ERROR cannot index */ [i] } // different map element types
|
||||||
func _[T interface{ ~[]E | ~map[string]E }, E any](x T, i int) { _ = x[i /* ERROR cannot use i */ ] }
|
func _[T interface{ ~[]E | ~map[string]E }, E any](x T, i int) { _ = x /* ERROR cannot index */ [i] } // map and non-map types
|
||||||
|
|
||||||
|
// indexing with various combinations of array and other types in type sets
|
||||||
|
func _[T interface{ [10]int }](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 */ ] }
|
||||||
|
|
||||||
// slicing
|
// slicing
|
||||||
// TODO(gri) implement this
|
// TODO(gri) implement this
|
||||||
|
|
|
@ -13,7 +13,7 @@ type N[T any] struct{}
|
||||||
var _ N [] // ERROR expected type argument list
|
var _ N [] // ERROR expected type argument list
|
||||||
|
|
||||||
type I interface {
|
type I interface {
|
||||||
~map[int]int | ~[]int
|
~[]int
|
||||||
}
|
}
|
||||||
|
|
||||||
func _[T I](i, j int) {
|
func _[T I](i, j int) {
|
||||||
|
@ -27,6 +27,5 @@ func _[T I](i, j int) {
|
||||||
_ = s[i, j /* ERROR "more than one index" */ ]
|
_ = s[i, j /* ERROR "more than one index" */ ]
|
||||||
|
|
||||||
var t T
|
var t T
|
||||||
// TODO(rFindley) Fix the duplicate error below.
|
_ = t[i, j /* ERROR "more than one index" */ ]
|
||||||
_ = t[i, j /* ERROR "more than one index" */ /* ERROR "more than one index" */ ]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,3 +70,10 @@ func (t *TypeParam) Bound() *Interface {
|
||||||
|
|
||||||
func (t *TypeParam) Underlying() Type { return t }
|
func (t *TypeParam) Underlying() Type { return t }
|
||||||
func (t *TypeParam) String() string { return TypeString(t, nil) }
|
func (t *TypeParam) String() string { return TypeString(t, nil) }
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Implementation
|
||||||
|
|
||||||
|
func (t *TypeParam) underIs(f func(Type) bool) bool {
|
||||||
|
return t.Bound().typeSet().underIs(f)
|
||||||
|
}
|
||||||
|
|
|
@ -75,6 +75,21 @@ func (s *TypeSet) String() string {
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Implementation
|
// Implementation
|
||||||
|
|
||||||
|
// underIs reports whether f returned true for the underlying types of the
|
||||||
|
// enumerable types in the type set s. If the type set comprises all types
|
||||||
|
// f is called once with the top type; if the type set is empty, the result
|
||||||
|
// is false.
|
||||||
|
func (s *TypeSet) underIs(f func(Type) bool) bool {
|
||||||
|
switch t := s.types.(type) {
|
||||||
|
case nil:
|
||||||
|
return f(theTop)
|
||||||
|
default:
|
||||||
|
return f(t)
|
||||||
|
case *Union:
|
||||||
|
return t.underIs(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// topTypeSet may be used as type set for the empty interface.
|
// topTypeSet may be used as type set for the empty interface.
|
||||||
var topTypeSet TypeSet
|
var topTypeSet TypeSet
|
||||||
|
|
||||||
|
|
|
@ -165,7 +165,7 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
|
||||||
}
|
}
|
||||||
for i, e := range t.types {
|
for i, e := range t.types {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
buf.WriteString("|")
|
buf.WriteByte('|')
|
||||||
}
|
}
|
||||||
if t.tilde[i] {
|
if t.tilde[i] {
|
||||||
buf.WriteByte('~')
|
buf.WriteByte('~')
|
||||||
|
|
|
@ -60,7 +60,7 @@ func (u *Union) is(f func(Type, bool) bool) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// is reports whether f returned true for the underlying types of all terms of u.
|
// underIs reports whether f returned true for the underlying types of all terms of u.
|
||||||
func (u *Union) underIs(f func(Type) bool) bool {
|
func (u *Union) underIs(f func(Type) bool) bool {
|
||||||
if u.IsEmpty() {
|
if u.IsEmpty() {
|
||||||
return false
|
return false
|
||||||
|
|
Loading…
Reference in a new issue