]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: remove references to core type in append
authorRobert Griesemer <gri@golang.org>
Wed, 26 Feb 2025 17:57:31 +0000 (09:57 -0800)
committerGopher Robot <gobot@golang.org>
Mon, 3 Mar 2025 19:07:14 +0000 (11:07 -0800)
Writing explicit code for this case turned out to be simpler
and easier to reason about then relying on a helper functions
(except for typeset).

While at it, make append error messages more consistent.

For #70128.

Change-Id: I3dc79774249929de5061b4301ab2506d4b3da0d0
Reviewed-on: https://go-review.googlesource.com/c/go/+/653095
Reviewed-by: Robert Findley <rfindley@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
src/cmd/compile/internal/types2/builtins.go
src/go/types/builtins.go
src/internal/types/testdata/fixedbugs/issue49735.go

index 1d5b67946ba0069f3a1330a122f0768687a447ac..e7d5f56c81e4de24ef26a05847d2ec348eb18bcf 100644 (file)
@@ -82,41 +82,61 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
 
        switch id {
        case _Append:
-               // append(s S, x ...T) S, where T is the element type of S
-               // spec: "The variadic function append appends zero or more values x to s of type
-               // S, which must be a slice type, and returns the resulting slice, also of type S.
-               // The values x are passed to a parameter of type ...T where T is the element type
+               // append(s S, x ...E) S, where E is the element type of S
+               // spec: "The variadic function append appends zero or more values x to a slice s
+               // of type S and returns the resulting slice, also of type S.
+               // The values x are passed to a parameter of type ...E where E is the element type
                // of S and the respective parameter passing rules apply."
                S := x.typ
-               var T Type
-               if s, _ := coreType(S).(*Slice); s != nil {
-                       T = s.elem
-               } else {
-                       var cause string
-                       switch {
-                       case x.isNil():
-                               cause = "have untyped nil"
-                       case isTypeParam(S):
-                               if u := coreType(S); u != nil {
-                                       cause = check.sprintf("%s has core type %s", x, u)
+
+               // determine E
+               var E Type
+               typeset(S, func(_, u Type) bool {
+                       s, _ := u.(*Slice)
+                       if s == nil {
+                               var cause string
+                               if x.isNil() {
+                                       // Printing x in this case would just print "nil".
+                                       // Special case this so we can emphasize "untyped".
+                                       cause = "untyped nil"
                                } else {
-                                       cause = check.sprintf("%s has no core type", x)
+                                       cause = check.sprintf("%s", x)
                                }
-                       default:
-                               cause = check.sprintf("have %s", x)
+                               check.errorf(x, InvalidAppend, "invalid append: first argument must be a slice; have %s", cause)
+                               E = nil
+                               return false
                        }
-                       // don't use invalidArg prefix here as it would repeat "argument" in the error message
-                       check.errorf(x, InvalidAppend, "first argument to append must be a slice; %s", cause)
+                       if E == nil {
+                               E = s.elem
+                       } else if !Identical(E, s.elem) {
+                               check.errorf(x, InvalidAppend, "invalid append: mismatched slice element types %s and %s in %s", E, s.elem, x)
+                               E = nil
+                               return false
+                       }
+                       return true
+               })
+               if E == nil {
                        return
                }
 
                // spec: "As a special case, append also accepts a first argument assignable
                // to type []byte with a second argument of string type followed by ... .
-               // This form appends the bytes of the string.
+               // This form appends the bytes of the string."
                if nargs == 2 && hasDots(call) {
                        if ok, _ := x.assignableTo(check, NewSlice(universeByte), nil); ok {
-                               y := args[1]
-                               if t := coreString(y.typ); t != nil && isString(t) {
+                               y := args[1] // valid if != nil
+                               typeset(y.typ, func(_, u Type) bool {
+                                       if s, _ := u.(*Slice); s != nil && Identical(s.elem, universeByte) {
+                                               return true
+                                       }
+                                       if isString(u) {
+                                               return true
+                                       }
+                                       y = nil
+                                       return false
+                               })
+
+                               if y != nil {
                                        if check.recordTypes() {
                                                sig := makeSig(S, S, y.typ)
                                                sig.variadic = true
@@ -130,7 +150,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
                }
 
                // check general case by creating custom signature
-               sig := makeSig(S, S, NewSlice(T)) // []T required for variadic signature
+               sig := makeSig(S, S, NewSlice(E)) // []E required for variadic signature
                sig.variadic = true
                check.arguments(call, sig, nil, nil, args, nil) // discard result (we know the result type)
                // ok to continue even if check.arguments reported errors
index 4a6dcedb54ef5f86cb9d95350fe1972e66ffaf43..786c0d5ea4ef9508cc9f9ebea9be2720db4ffb7f 100644 (file)
@@ -85,41 +85,61 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
 
        switch id {
        case _Append:
-               // append(s S, x ...T) S, where T is the element type of S
-               // spec: "The variadic function append appends zero or more values x to s of type
-               // S, which must be a slice type, and returns the resulting slice, also of type S.
-               // The values x are passed to a parameter of type ...T where T is the element type
+               // append(s S, x ...E) S, where E is the element type of S
+               // spec: "The variadic function append appends zero or more values x to a slice s
+               // of type S and returns the resulting slice, also of type S.
+               // The values x are passed to a parameter of type ...E where E is the element type
                // of S and the respective parameter passing rules apply."
                S := x.typ
-               var T Type
-               if s, _ := coreType(S).(*Slice); s != nil {
-                       T = s.elem
-               } else {
-                       var cause string
-                       switch {
-                       case x.isNil():
-                               cause = "have untyped nil"
-                       case isTypeParam(S):
-                               if u := coreType(S); u != nil {
-                                       cause = check.sprintf("%s has core type %s", x, u)
+
+               // determine E
+               var E Type
+               typeset(S, func(_, u Type) bool {
+                       s, _ := u.(*Slice)
+                       if s == nil {
+                               var cause string
+                               if x.isNil() {
+                                       // Printing x in this case would just print "nil".
+                                       // Special case this so we can emphasize "untyped".
+                                       cause = "untyped nil"
                                } else {
-                                       cause = check.sprintf("%s has no core type", x)
+                                       cause = check.sprintf("%s", x)
                                }
-                       default:
-                               cause = check.sprintf("have %s", x)
+                               check.errorf(x, InvalidAppend, "invalid append: first argument must be a slice; have %s", cause)
+                               E = nil
+                               return false
                        }
-                       // don't use invalidArg prefix here as it would repeat "argument" in the error message
-                       check.errorf(x, InvalidAppend, "first argument to append must be a slice; %s", cause)
+                       if E == nil {
+                               E = s.elem
+                       } else if !Identical(E, s.elem) {
+                               check.errorf(x, InvalidAppend, "invalid append: mismatched slice element types %s and %s in %s", E, s.elem, x)
+                               E = nil
+                               return false
+                       }
+                       return true
+               })
+               if E == nil {
                        return
                }
 
                // spec: "As a special case, append also accepts a first argument assignable
                // to type []byte with a second argument of string type followed by ... .
-               // This form appends the bytes of the string.
+               // This form appends the bytes of the string."
                if nargs == 2 && hasDots(call) {
                        if ok, _ := x.assignableTo(check, NewSlice(universeByte), nil); ok {
-                               y := args[1]
-                               if t := coreString(y.typ); t != nil && isString(t) {
+                               y := args[1] // valid if != nil
+                               typeset(y.typ, func(_, u Type) bool {
+                                       if s, _ := u.(*Slice); s != nil && Identical(s.elem, universeByte) {
+                                               return true
+                                       }
+                                       if isString(u) {
+                                               return true
+                                       }
+                                       y = nil
+                                       return false
+                               })
+
+                               if y != nil {
                                        if check.recordTypes() {
                                                sig := makeSig(S, S, y.typ)
                                                sig.variadic = true
@@ -133,7 +153,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
                }
 
                // check general case by creating custom signature
-               sig := makeSig(S, S, NewSlice(T)) // []T required for variadic signature
+               sig := makeSig(S, S, NewSlice(E)) // []E required for variadic signature
                sig.variadic = true
                check.arguments(call, sig, nil, nil, args, nil) // discard result (we know the result type)
                // ok to continue even if check.arguments reported errors
index 0fcc778a066529bd7edfe0fb71dad17a5a3a298f..07603b712c51823d408ab0a4339b26ba83816ebd 100644 (file)
@@ -4,8 +4,9 @@
 
 package p
 
-func _[P1 any, P2 ~byte](s1 P1, s2 P2) {
-        _ = append(nil /* ERROR "first argument to append must be a slice; have untyped nil" */ , 0)
-        _ = append(s1 /* ERRORx `s1 .* has no core type` */ , 0)
-        _ = append(s2 /* ERRORx `s2 .* has core type byte` */ , 0)
+func _[P1 any, P2 ~byte, P3 []int | []byte](s1 P1, s2 P2, s3 P3) {
+       _ = append(nil /* ERROR "invalid append: first argument must be a slice; have untyped nil" */, 0)
+       _ = append(s1 /* ERROR "invalid append: first argument must be a slice; have s1 (variable of type P1 constrained by any)" */, 0)
+       _ = append(s2 /* ERROR "invalid append: first argument must be a slice; have s2 (variable of type P2 constrained by ~byte)" */, 0)
+       _ = append(s3 /* ERROR "invalid append: mismatched slice element types int and byte in s3 (variable of type P3 constrained by []int | []byte)" */, 0)
 }