1
0
mirror of https://github.com/golang/go synced 2024-09-29 02:24:33 -06:00

go/types: allow slicing for operands with []byte|string type sets

This is a port of CL 363662 from types2 to go/types. An error message
was adjusted to be on the operand in test data.

Change-Id: I4d2d69976f4f05e0d89ba1c6bf8b3e4cf1a82316
Reviewed-on: https://go-review.googlesource.com/c/go/+/364899
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-17 19:26:37 -05:00
parent 353cb71ea2
commit ce7e5013a6
4 changed files with 47 additions and 21 deletions

View File

@ -342,26 +342,11 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
if y.mode == invalid {
return
}
// src, _ := structuralType(y.typ).(*Slice); but also accepts strings
var src *Slice
var elem Type // == src.elem if valid
if underIs(y.typ, func(u Type) bool {
switch u := u.(type) {
case *Basic:
if isString(u) && (elem == nil || Identical(elem, universeByte)) {
elem = universeByte
return true
}
case *Slice:
if elem == nil || Identical(elem, u.elem) {
elem = u.elem
return true
}
}
return false
}) {
src = NewSlice(elem)
src0 := structuralString(y.typ)
if src0 != nil && isString(src0) {
src0 = NewSlice(universeByte)
}
src, _ := src0.(*Slice)
if dst == nil || src == nil {
check.invalidArg(x, _InvalidCopy, "copy expects slice arguments; found %s and %s", x, &y)

View File

@ -214,7 +214,7 @@ func (check *Checker) sliceExpr(x *operand, e *ast.SliceExpr) {
valid := false
length := int64(-1) // valid if >= 0
switch u := structuralType(x.typ).(type) {
switch u := structuralString(x.typ).(type) {
case nil:
check.invalidOp(x, _NonSliceableOperand, "cannot slice %s: %s has no structural type", x, x.typ)
x.mode = invalid
@ -233,7 +233,7 @@ func (check *Checker) sliceExpr(x *operand, e *ast.SliceExpr) {
}
// spec: "For untyped string operands the result
// is a non-constant value of type string."
if u.kind == UntypedString {
if isUntyped(x.typ) {
x.typ = Typ[String]
}
}

View File

@ -136,6 +136,10 @@ type myByte2 []byte
func _[T interface{ []byte | myByte1 | myByte2 }] (x T, i, j, k int) { var _ T = x[i:j:k] }
func _[T interface{ []byte | myByte1 | []int }] (x T, i, j, k int) { var _ T = x /* ERROR no structural type */ [i:j:k] }
func _[T interface{ []byte | myByte1 | myByte2 | string }] (x T, i, j, k int) { var _ T = x[i:j] }
func _[T interface{ []byte | myByte1 | myByte2 | string }] (x T, i, j, k int) { var _ T = x /* ERROR 3-index slice of string */ [i:j:k] }
func _[T interface{ []byte | myByte1 | []int | string }] (x T, i, j, k int) { var _ T = x /* ERROR no structural type */ [i:j] }
// len/cap built-ins
func _[T any](x T) { _ = len(x /* ERROR invalid argument */ ) }

View File

@ -87,3 +87,40 @@ func structuralType(t Type) Type {
}
return nil
}
// structuralString is like structuralType but also considers []byte
// and strings as identical. In this case, if successful and we saw
// a string, the result is of type (possibly untyped) string.
func structuralString(t Type) Type {
tpar, _ := t.(*TypeParam)
if tpar == nil {
return under(t) // string or untyped string
}
var su Type
hasString := false
if tpar.underIs(func(u Type) bool {
if u == nil {
return false
}
if isString(u) {
u = NewSlice(universeByte)
hasString = true
}
if su != nil {
u = match(su, u)
if u == nil {
return false
}
}
// su == nil || match(su, u) != nil
su = u
return true
}) {
if hasString {
return Typ[String]
}
return su
}
return nil
}