From: Robert Griesemer Date: Fri, 12 Nov 2021 23:08:00 +0000 (-0800) Subject: cmd/compile/internal/types2: allow slicing for operands with []byte|string type sets X-Git-Tag: go1.18beta1~317 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=cfcd71790f0ca8c2ec1ae5989cd60ad1e83ee40c;p=gostls13.git cmd/compile/internal/types2: allow slicing for operands with []byte|string type sets Fixes #49566. Change-Id: I80ff4ca661f82b0981d51e0997d5988a9b82f508 Reviewed-on: https://go-review.googlesource.com/c/go/+/363662 Trust: Robert Griesemer Run-TryBot: Robert Griesemer Reviewed-by: Robert Findley --- diff --git a/src/cmd/compile/internal/types2/builtins.go b/src/cmd/compile/internal/types2/builtins.go index 5b4ffd0dad..53d834507a 100644 --- a/src/cmd/compile/internal/types2/builtins.go +++ b/src/cmd/compile/internal/types2/builtins.go @@ -337,26 +337,11 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) ( 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.errorf(x, invalidArg+"copy expects slice arguments; found %s and %s", x, &y) diff --git a/src/cmd/compile/internal/types2/index.go b/src/cmd/compile/internal/types2/index.go index 648c7abe6f..524d1957b5 100644 --- a/src/cmd/compile/internal/types2/index.go +++ b/src/cmd/compile/internal/types2/index.go @@ -213,7 +213,7 @@ func (check *Checker) sliceExpr(x *operand, e *syntax.SliceExpr) { valid := false length := int64(-1) // valid if >= 0 - switch u := structuralType(x.typ).(type) { + switch u := structuralString(x.typ).(type) { case nil: check.errorf(x, invalidOp+"cannot slice %s: %s has no structural type", x, x.typ) x.mode = invalid @@ -232,7 +232,7 @@ func (check *Checker) sliceExpr(x *operand, e *syntax.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] } } diff --git a/src/cmd/compile/internal/types2/testdata/check/typeparams.go2 b/src/cmd/compile/internal/types2/testdata/check/typeparams.go2 index 03c3f9a0b5..f77d09391b 100644 --- a/src/cmd/compile/internal/types2/testdata/check/typeparams.go2 +++ b/src/cmd/compile/internal/types2/testdata/check/typeparams.go2 @@ -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 */ ) } diff --git a/src/cmd/compile/internal/types2/type.go b/src/cmd/compile/internal/types2/type.go index 39737d47a7..3ab738eb19 100644 --- a/src/cmd/compile/internal/types2/type.go +++ b/src/cmd/compile/internal/types2/type.go @@ -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 +}