]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: allow slicing for operands with []byte|string type sets
authorRobert Findley <rfindley@google.com>
Thu, 18 Nov 2021 00:26:37 +0000 (19:26 -0500)
committerRobert Findley <rfindley@google.com>
Thu, 18 Nov 2021 02:17:23 +0000 (02:17 +0000)
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>
src/go/types/builtins.go
src/go/types/index.go
src/go/types/testdata/check/typeparams.go2
src/go/types/type.go

index b547cddeb1a5c98a7cdc8812e75d5bf785549ab8..daeed81ed8fcb3620abdccc7aa62c0630af804c8 100644 (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)
index 54398ad19bd8cee1ba5c1a631f40ea5d39d7731c..ace9ee06ab4e6a5e09452de33bcd466b2db19d37 100644 (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]
                        }
                }
index fdbb7a27404899054b8b07613bda954f2ad6fbe7..0d3b6ea5274a2db76ed70021db63881b0db4b2a8 100644 (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 */ ) }
index e283c65289e5807ad64f6608bef84102869a1381..099449c8b9c8ff9baba2598880fe59de80c72d26 100644 (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
+}