func TestIssue55030(t *testing.T) {
// makeSig makes the signature func(typ...)
- makeSig := func(typ Type) {
+ // If valid is not set, making that signature is expected to panic.
+ makeSig := func(typ Type, valid bool) {
+ if !valid {
+ defer func() {
+ if recover() == nil {
+ panic("NewSignatureType panic expected")
+ }
+ }()
+ }
par := NewParam(nopos, nil, "", typ)
params := NewTuple(par)
NewSignatureType(nil, nil, nil, params, nil, true)
// makeSig must not panic for the following (example) types:
// []int
- makeSig(NewSlice(Typ[Int]))
+ makeSig(NewSlice(Typ[Int]), true)
// string
- makeSig(Typ[String])
+ makeSig(Typ[String], true)
- // P where P's core type is string
+ // P where P's common underlying type is string
{
P := NewTypeName(nopos, nil, "P", nil) // [P string]
- makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{Typ[String]})))
+ makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{Typ[String]})), true)
}
- // P where P's core type is an (unnamed) slice
+ // P where P's common underlying type is an (unnamed) slice
{
P := NewTypeName(nopos, nil, "P", nil) // [P []int]
- makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{NewSlice(Typ[Int])})))
+ makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{NewSlice(Typ[Int])})), true)
}
- // P where P's core type is bytestring (i.e., string or []byte)
+ // P where P's type set contains strings and []byte
{
t1 := NewTerm(true, Typ[String]) // ~string
t2 := NewTerm(false, NewSlice(Typ[Byte])) // []byte
u := NewUnion([]*Term{t1, t2}) // ~string | []byte
P := NewTypeName(nopos, nil, "P", nil) // [P ~string | []byte]
- makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{u})))
+ makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{u})), true)
+ }
+
+ // makeSig must panic for the following (example) types:
+ // int
+ makeSig(Typ[Int], false)
+
+ // P where P's type set doesn't have any specific types
+ {
+ P := NewTypeName(nopos, nil, "P", nil) // [P any]
+ makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{Universe.Lookup("any").Type()})), false)
+ }
+
+ // P where P's type set doesn't have any slice or string types
+ {
+ P := NewTypeName(nopos, nil, "P", nil) // [P any]
+ makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{Typ[Int]})), false)
}
}
}
// 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's core type must be of unnamed slice or bytestring type.
+// 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.
// 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 n == 0 {
panic("variadic function must have at least one parameter")
}
- core := coreString(params.At(n - 1).typ)
- if _, ok := core.(*Slice); !ok && !isString(core) {
- panic(fmt.Sprintf("got %s, want variadic parameter with unnamed slice type or string as common underlying type", core.String()))
+ last := params.At(n - 1).typ
+ var S *Slice
+ typeset(last, func(t, _ Type) bool {
+ var s *Slice
+ if isString(t) {
+ s = NewSlice(universeByte)
+ } else {
+ s, _ = Unalias(t).(*Slice) // don't accept a named slice type
+ }
+ if S == nil {
+ S = s
+ } else if !Identical(S, s) {
+ S = nil
+ return false
+ }
+ return true
+ })
+ if S == nil {
+ panic(fmt.Sprintf("got %s, want variadic parameter of unnamed slice or string type", last))
}
}
sig := &Signature{recv: recv, params: params, results: results, variadic: variadic}
func TestIssue55030(t *testing.T) {
// makeSig makes the signature func(typ...)
- makeSig := func(typ Type) {
+ // If valid is not set, making that signature is expected to panic.
+ makeSig := func(typ Type, valid bool) {
+ if !valid {
+ defer func() {
+ if recover() == nil {
+ panic("NewSignatureType panic expected")
+ }
+ }()
+ }
par := NewParam(nopos, nil, "", typ)
params := NewTuple(par)
NewSignatureType(nil, nil, nil, params, nil, true)
// makeSig must not panic for the following (example) types:
// []int
- makeSig(NewSlice(Typ[Int]))
+ makeSig(NewSlice(Typ[Int]), true)
// string
- makeSig(Typ[String])
+ makeSig(Typ[String], true)
- // P where P's core type is string
+ // P where P's common underlying type is string
{
P := NewTypeName(nopos, nil, "P", nil) // [P string]
- makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{Typ[String]})))
+ makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{Typ[String]})), true)
}
- // P where P's core type is an (unnamed) slice
+ // P where P's common underlying type is an (unnamed) slice
{
P := NewTypeName(nopos, nil, "P", nil) // [P []int]
- makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{NewSlice(Typ[Int])})))
+ makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{NewSlice(Typ[Int])})), true)
}
- // P where P's core type is bytestring (i.e., string or []byte)
+ // P where P's type set contains strings and []byte
{
t1 := NewTerm(true, Typ[String]) // ~string
t2 := NewTerm(false, NewSlice(Typ[Byte])) // []byte
u := NewUnion([]*Term{t1, t2}) // ~string | []byte
P := NewTypeName(nopos, nil, "P", nil) // [P ~string | []byte]
- makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{u})))
+ makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{u})), true)
+ }
+
+ // makeSig must panic for the following (example) types:
+ // int
+ makeSig(Typ[Int], false)
+
+ // P where P's type set doesn't have any specific types
+ {
+ P := NewTypeName(nopos, nil, "P", nil) // [P any]
+ makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{Universe.Lookup("any").Type()})), false)
+ }
+
+ // P where P's type set doesn't have any slice or string types
+ {
+ P := NewTypeName(nopos, nil, "P", nil) // [P any]
+ makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{Typ[Int]})), false)
}
}
}
// 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's core type must be of unnamed slice or bytestring type.
+// 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.
// 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 n == 0 {
panic("variadic function must have at least one parameter")
}
- core := coreString(params.At(n - 1).typ)
- if _, ok := core.(*Slice); !ok && !isString(core) {
- panic(fmt.Sprintf("got %s, want variadic parameter with unnamed slice type or string as common underlying type", core.String()))
+ last := params.At(n - 1).typ
+ var S *Slice
+ typeset(last, func(t, _ Type) bool {
+ var s *Slice
+ if isString(t) {
+ s = NewSlice(universeByte)
+ } else {
+ s, _ = Unalias(t).(*Slice) // don't accept a named slice type
+ }
+ if S == nil {
+ S = s
+ } else if !Identical(S, s) {
+ S = nil
+ return false
+ }
+ return true
+ })
+ if S == nil {
+ panic(fmt.Sprintf("got %s, want variadic parameter of unnamed slice or string type", last))
}
}
sig := &Signature{recv: recv, params: params, results: results, variadic: variadic}