recv *Var // nil if not a method
params *Tuple // (incoming) parameters from left to right; or nil
results *Tuple // (outgoing) results from left to right; or nil
- variadic bool // true if the last parameter's type is of the form ...T (or string, for append built-in only)
+ variadic bool // true if the last parameter's type is of the form ...T
+
+ // If variadic, the last element of params ordinarily has an
+ // unnamed Slice type. As a special case, in a call to append,
+ // it may be string, or a TypeParam T whose typeset ⊇ {string, []byte}.
+ // It may even be a named []byte type if a client instantiates
+ // T at such a type.
}
// NewSignatureType creates a new function type for the given receiver,
// receiver type parameters, type parameters, parameters, and results.
+//
// If variadic is set, params must hold at least one parameter and the
// last parameter must be an unnamed slice or a type parameter whose
// type set has an unnamed slice as common underlying type.
-// As a special case, for variadic signatures the last parameter may
-// also be a string type, or a type parameter containing a mix of byte
-// slices and string types in its type set.
+//
+// As a special case, to support append([]byte, str...), for variadic
+// signatures the last parameter may also be a string type, or a type
+// parameter containing a mix of byte slices and string types in its
+// type set. It may even be a named []byte slice type resulting from
+// substitution of such a type parameter.
+//
// If recv is non-nil, typeParams must be empty. If recvTypeParams is
// non-empty, recv must be non-nil.
func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params, results *Tuple, variadic bool) *Signature {
if isString(t) {
s = NewSlice(universeByte)
} else {
- s, _ = Unalias(t).(*Slice) // don't accept a named slice type
+ // Variadic Go functions have a last parameter of type []T,
+ // suggesting we should reject a named slice type B here.
+ //
+ // However, a call to built-in append(slice, x...)
+ // where x has a TypeParam type [T ~string | ~[]byte],
+ // has the type func([]byte, T). Since a client may
+ // instantiate this type at T=B, we must permit
+ // named slice types, even when this results in a
+ // signature func([]byte, B) where type B []byte.
+ //
+ // (The caller of NewSignatureType may have no way to
+ // know that it is dealing with the append special case.)
+ s, _ = t.Underlying().(*Slice)
}
if S == nil {
S = s
- } else if !Identical(S, s) {
+ } else if s == nil || !Identical(S, s) {
S = nil
break
}
}
if S == nil {
- panic(fmt.Sprintf("got %s, want variadic parameter of unnamed slice or string type", last))
+ panic(fmt.Sprintf("got %s, want variadic parameter of slice or string type", last))
}
}
sig := &Signature{recv: recv, params: params, results: results, variadic: variadic}
func (s *Signature) RecvTypeParams() *TypeParamList { return s.rparams }
// Params returns the parameters of signature s, or nil.
+// See [NewSignatureType] for details of variadic functions.
func (s *Signature) Params() *Tuple { return s.params }
// Results returns the results of signature s, or nil.
}
typ := v.typ
if variadic && i == len(tup.vars)-1 {
- if s, ok := typ.(*Slice); ok {
+ if slice, ok := typ.(*Slice); ok {
w.string("...")
- typ = s.elem
+ w.typ(slice.elem)
} else {
- // special case:
- // append(s, "foo"...) leads to signature func([]byte, string...)
- if t, _ := typ.Underlying().(*Basic); t == nil || t.kind != String {
- w.error("expected string type")
- continue
- }
+ // append(slice, str...) entails various special
+ // cases, especially in conjunction with generics.
+ // str may be:
+ // - a string,
+ // - a TypeParam whose typeset includes string, or
+ // - a named []byte slice type B resulting from
+ // a client instantiating append([]byte, T) at T=B.
+ // For such cases we use the irregular notation
+ // func([]byte, T...), with the dots after the type.
w.typ(typ)
w.string("...")
- continue
}
+ } else {
+ w.typ(typ)
}
- w.typ(typ)
}
}
w.byte(')')
func f[T ~[]byte](y T) []byte { return append([]byte(nil), y...) }
-// for illustration only:
type B []byte
var _ = f[B]
`
// Check type inferred for 'append'.
//
- // Before the fix, the inferred type of append's y parameter
+ // Before CL 688815, the inferred type of append's y parameter
// was T. When a client such as x/tools/go/ssa instantiated T=B,
// it would result in the Signature "func([]byte, B)" with the
// variadic flag set, an invalid combination that caused
// NewSignatureType to panic.
append := f.Decls[0].(*ast.FuncDecl).Body.List[0].(*ast.ReturnStmt).Results[0].(*ast.CallExpr).Fun
tAppend := info.TypeOf(append).(*Signature)
- want := "func([]byte, ...byte) []byte"
- if got := fmt.Sprint(tAppend); got != want {
- // Before the fix, tAppend was func([]byte, T) []byte,
+ if got, want := fmt.Sprint(tAppend), "func([]byte, ...byte) []byte"; got != want {
+ // Before CL 688815, tAppend was func([]byte, T) []byte,
// where T prints as "<expected string type>".
- t.Errorf("for append, inferred type %s, want %s", tAppend, want)
+ t.Errorf("append: got type %s, want %s", got, want)
+ }
+
+ // Instantiate the type inferred for append(...) at T=B.
+ // Before the fix in CL 689277, NewSignatureType would panic.
+ params := slices.Collect(tAppend.Params().Variables())
+ params[1] = NewVar(token.NoPos, pkg, "", pkg.Scope().Lookup("B").Type())
+ sig := NewSignatureType(nil, nil, nil, NewTuple(params...), tAppend.Results(), true)
+ if got, want := fmt.Sprint(sig), "func([]byte, p.B...) []byte"; got != want {
+ t.Errorf("instantiated: got type %s, want %s", got, want)
}
}
recv *Var // nil if not a method
params *Tuple // (incoming) parameters from left to right; or nil
results *Tuple // (outgoing) results from left to right; or nil
- variadic bool // true if the last parameter's type is of the form ...T (or string, for append built-in only)
+ variadic bool // true if the last parameter's type is of the form ...T
+
+ // If variadic, the last element of params ordinarily has an
+ // unnamed Slice type. As a special case, in a call to append,
+ // it may be string, or a TypeParam T whose typeset ⊇ {string, []byte}.
+ // It may even be a named []byte type if a client instantiates
+ // T at such a type.
}
// NewSignature returns a new function type for the given receiver, parameters,
// NewSignatureType creates a new function type for the given receiver,
// receiver type parameters, type parameters, parameters, and results.
+//
// If variadic is set, params must hold at least one parameter and the
// last parameter must be an unnamed slice or a type parameter whose
// type set has an unnamed slice as common underlying type.
-// As a special case, for variadic signatures the last parameter may
-// also be a string type, or a type parameter containing a mix of byte
-// slices and string types in its type set.
+//
+// As a special case, to support append([]byte, str...), for variadic
+// signatures the last parameter may also be a string type, or a type
+// parameter containing a mix of byte slices and string types in its
+// type set. It may even be a named []byte slice type resulting from
+// instantiation of such a type parameter.
+//
// If recv is non-nil, typeParams must be empty. If recvTypeParams is
// non-empty, recv must be non-nil.
func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params, results *Tuple, variadic bool) *Signature {
if isString(t) {
s = NewSlice(universeByte)
} else {
- s, _ = Unalias(t).(*Slice) // don't accept a named slice type
+ // Variadic Go functions have a last parameter of type []T,
+ // suggesting we should reject a named slice type B here.
+ //
+ // However, a call to built-in append(slice, x...)
+ // where x has a TypeParam type [T ~string | ~[]byte],
+ // has the type func([]byte, T). Since a client may
+ // instantiate this type at T=B, we must permit
+ // named slice types, even when this results in a
+ // signature func([]byte, B) where type B []byte.
+ //
+ // (The caller of NewSignatureType may have no way to
+ // know that it is dealing with the append special case.)
+ s, _ = t.Underlying().(*Slice)
}
if S == nil {
S = s
- } else if !Identical(S, s) {
+ } else if s == nil || !Identical(S, s) {
S = nil
break
}
}
if S == nil {
- panic(fmt.Sprintf("got %s, want variadic parameter of unnamed slice or string type", last))
+ panic(fmt.Sprintf("got %s, want variadic parameter of slice or string type", last))
}
}
sig := &Signature{recv: recv, params: params, results: results, variadic: variadic}
func (s *Signature) RecvTypeParams() *TypeParamList { return s.rparams }
// Params returns the parameters of signature s, or nil.
+// See [NewSignatureType] for details of variadic functions.
func (s *Signature) Params() *Tuple { return s.params }
// Results returns the results of signature s, or nil.
}
typ := v.typ
if variadic && i == len(tup.vars)-1 {
- if s, ok := typ.(*Slice); ok {
+ if slice, ok := typ.(*Slice); ok {
w.string("...")
- typ = s.elem
+ w.typ(slice.elem)
} else {
- // special case:
- // append(s, "foo"...) leads to signature func([]byte, string...)
- if t, _ := typ.Underlying().(*Basic); t == nil || t.kind != String {
- w.error("expected string type")
- continue
- }
+ // append(slice, str...) entails various special
+ // cases, especially in conjunction with generics.
+ // str may be:
+ // - a string,
+ // - a TypeParam whose typeset includes string, or
+ // - a named []byte slice type B resulting from
+ // a client instantiating append([]byte, T) at T=B.
+ // For such cases we use the irregular notation
+ // func([]byte, T...), with the dots after the type.
w.typ(typ)
w.string("...")
- continue
}
+ } else {
+ w.typ(typ)
}
- w.typ(typ)
}
}
w.byte(')')