]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: first argument to append must never be be nil
authorRobert Griesemer <gri@google.com>
Fri, 7 Nov 2025 23:50:14 +0000 (15:50 -0800)
committerGopher Robot <gobot@golang.org>
Mon, 10 Nov 2025 20:48:02 +0000 (12:48 -0800)
The current implementation followed the spec faithfully for
the special case for append. Per the spec:

As a special case, append also accepts a first argument assignable to
type []byte with a second argument of string type followed by ... .

As it happens, nil is assignable to []byte, so append(nil, ""...)
didn't get an error message but a subsequent assertion failed.

This CL ensures that the first argument to append is never nil and
always a slice. We should make the spec more precise (separate CL).

Fixes #76220.

Change-Id: I581d11827a75afbb257077814beea813d4fe2441
Reviewed-on: https://go-review.googlesource.com/c/go/+/718860
Auto-Submit: Robert Griesemer <gri@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
Reviewed-by: Brett Howell <devbrett90@gmail.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

src/cmd/compile/internal/types2/builtins.go
src/go/types/builtins.go
src/internal/types/testdata/fixedbugs/issue76220.go [new file with mode: 0644]

index d4463bc4b09a5ff727134aa4846f9568a8dec0f9..549d94615bcceca653ebe0646489f4fd7ce9a766 100644 (file)
@@ -91,6 +91,17 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
                // to type []byte with a second argument of string type followed by ... .
                // This form appends the bytes of the string."
 
+               // In either case, the first argument must be a slice; in particular it
+               // cannot be the predeclared nil value. Note that nil is not excluded by
+               // the assignability requirement alone for the special case (go.dev/issue/76220).
+               // spec: "If S is a type parameter, all types in its type set
+               // must have the same underlying slice type []E."
+               E, err := sliceElem(x)
+               if err != nil {
+                       check.errorf(x, InvalidAppend, "invalid append: %s", err.format(check))
+                       return
+               }
+
                // Handle append(bytes, y...) special case, where
                // the type set of y is {string} or {string, []byte}.
                var sig *Signature
@@ -119,13 +130,6 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
 
                // general case
                if sig == nil {
-                       // spec: "If S is a type parameter, all types in its type set
-                       // must have the same underlying slice type []E."
-                       E, err := sliceElem(x)
-                       if err != nil {
-                               check.errorf(x, InvalidAppend, "invalid append: %s", err.format(check))
-                               return
-                       }
                        // check arguments by creating custom signature
                        sig = makeSig(x.typ, x.typ, NewSlice(E)) // []E required for variadic signature
                        sig.variadic = true
index 04d294db0c11e6eb6c91195e0d5fae8da143fa03..90a3b4a901f8e71ecb23e41fb7871ec7ca3537a8 100644 (file)
@@ -94,6 +94,17 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
                // to type []byte with a second argument of string type followed by ... .
                // This form appends the bytes of the string."
 
+               // In either case, the first argument must be a slice; in particular it
+               // cannot be the predeclared nil value. Note that nil is not excluded by
+               // the assignability requirement alone for the special case (go.dev/issue/76220).
+               // spec: "If S is a type parameter, all types in its type set
+               // must have the same underlying slice type []E."
+               E, err := sliceElem(x)
+               if err != nil {
+                       check.errorf(x, InvalidAppend, "invalid append: %s", err.format(check))
+                       return
+               }
+
                // Handle append(bytes, y...) special case, where
                // the type set of y is {string} or {string, []byte}.
                var sig *Signature
@@ -122,13 +133,6 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
 
                // general case
                if sig == nil {
-                       // spec: "If S is a type parameter, all types in its type set
-                       // must have the same underlying slice type []E."
-                       E, err := sliceElem(x)
-                       if err != nil {
-                               check.errorf(x, InvalidAppend, "invalid append: %s", err.format(check))
-                               return
-                       }
                        // check arguments by creating custom signature
                        sig = makeSig(x.typ, x.typ, NewSlice(E)) // []E required for variadic signature
                        sig.variadic = true
diff --git a/src/internal/types/testdata/fixedbugs/issue76220.go b/src/internal/types/testdata/fixedbugs/issue76220.go
new file mode 100644 (file)
index 0000000..ad46501
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+func _() {
+       append(nil /* ERROR "argument must be a slice; have untyped nil" */, ""...)
+}
+
+// test case from issue
+
+func main() {
+       s := "hello"
+       msg := append(nil /* ERROR "argument must be a slice; have untyped nil" */, s...)
+       print(msg)
+}