go/types, types2: first argument to append must never be be nil

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>
This commit is contained in:
Robert Griesemer 2025-11-07 15:50:14 -08:00 committed by Gopher Robot
parent a0eb4548cf
commit cdf64106f6
3 changed files with 39 additions and 14 deletions

View 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

View 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

View file

@ -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)
}