go/types, types2: remove need for coreString in signature.go

Also, add additional test cases for NewSignatureType
to check expected panic behavior.

Change-Id: If26cd81a2af384bf2084dd09119483c0584715c8
Reviewed-on: https://go-review.googlesource.com/c/go/+/655695
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Griesemer <gri@google.com>
Auto-Submit: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Robert Griesemer 2025-03-06 16:06:55 -08:00 committed by Gopher Robot
parent 2d097e363a
commit e5d3ece35d
4 changed files with 118 additions and 30 deletions

View File

@ -627,7 +627,15 @@ func TestIssue50646(t *testing.T) {
func TestIssue55030(t *testing.T) { func TestIssue55030(t *testing.T) {
// makeSig makes the signature func(typ...) // 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) par := NewParam(nopos, nil, "", typ)
params := NewTuple(par) params := NewTuple(par)
NewSignatureType(nil, nil, nil, params, nil, true) NewSignatureType(nil, nil, nil, params, nil, true)
@ -635,30 +643,46 @@ func TestIssue55030(t *testing.T) {
// makeSig must not panic for the following (example) types: // makeSig must not panic for the following (example) types:
// []int // []int
makeSig(NewSlice(Typ[Int])) makeSig(NewSlice(Typ[Int]), true)
// string // 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] 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] 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 t1 := NewTerm(true, Typ[String]) // ~string
t2 := NewTerm(false, NewSlice(Typ[Byte])) // []byte t2 := NewTerm(false, NewSlice(Typ[Byte])) // []byte
u := NewUnion([]*Term{t1, t2}) // ~string | []byte u := NewUnion([]*Term{t1, t2}) // ~string | []byte
P := NewTypeName(nopos, nil, "P", nil) // [P ~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)
} }
} }

View File

@ -32,9 +32,13 @@ type Signature struct {
} }
// NewSignatureType creates a new function type for the given receiver, // NewSignatureType creates a new function type for the given receiver,
// receiver type parameters, type parameters, parameters, and results. If // receiver type parameters, type parameters, parameters, and results.
// variadic is set, params must hold at least one parameter and the last // If variadic is set, params must hold at least one parameter and the
// parameter's core type must be of unnamed slice or bytestring type. // 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 // If recv is non-nil, typeParams must be empty. If recvTypeParams is
// non-empty, recv must be non-nil. // non-empty, recv must be non-nil.
func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params, results *Tuple, variadic bool) *Signature { func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params, results *Tuple, variadic bool) *Signature {
@ -43,9 +47,25 @@ func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params
if n == 0 { if n == 0 {
panic("variadic function must have at least one parameter") panic("variadic function must have at least one parameter")
} }
core := coreString(params.At(n - 1).typ) last := params.At(n - 1).typ
if _, ok := core.(*Slice); !ok && !isString(core) { var S *Slice
panic(fmt.Sprintf("got %s, want variadic parameter with unnamed slice type or string as common underlying type", core.String())) 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} sig := &Signature{recv: recv, params: params, results: results, variadic: variadic}

View File

@ -637,7 +637,15 @@ func TestIssue50646(t *testing.T) {
func TestIssue55030(t *testing.T) { func TestIssue55030(t *testing.T) {
// makeSig makes the signature func(typ...) // 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) par := NewParam(nopos, nil, "", typ)
params := NewTuple(par) params := NewTuple(par)
NewSignatureType(nil, nil, nil, params, nil, true) NewSignatureType(nil, nil, nil, params, nil, true)
@ -645,30 +653,46 @@ func TestIssue55030(t *testing.T) {
// makeSig must not panic for the following (example) types: // makeSig must not panic for the following (example) types:
// []int // []int
makeSig(NewSlice(Typ[Int])) makeSig(NewSlice(Typ[Int]), true)
// string // 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] 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] 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 t1 := NewTerm(true, Typ[String]) // ~string
t2 := NewTerm(false, NewSlice(Typ[Byte])) // []byte t2 := NewTerm(false, NewSlice(Typ[Byte])) // []byte
u := NewUnion([]*Term{t1, t2}) // ~string | []byte u := NewUnion([]*Term{t1, t2}) // ~string | []byte
P := NewTypeName(nopos, nil, "P", nil) // [P ~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)
} }
} }

View File

@ -45,9 +45,13 @@ func NewSignature(recv *Var, params, results *Tuple, variadic bool) *Signature {
} }
// NewSignatureType creates a new function type for the given receiver, // NewSignatureType creates a new function type for the given receiver,
// receiver type parameters, type parameters, parameters, and results. If // receiver type parameters, type parameters, parameters, and results.
// variadic is set, params must hold at least one parameter and the last // If variadic is set, params must hold at least one parameter and the
// parameter's core type must be of unnamed slice or bytestring type. // 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 // If recv is non-nil, typeParams must be empty. If recvTypeParams is
// non-empty, recv must be non-nil. // non-empty, recv must be non-nil.
func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params, results *Tuple, variadic bool) *Signature { func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params, results *Tuple, variadic bool) *Signature {
@ -56,9 +60,25 @@ func NewSignatureType(recv *Var, recvTypeParams, typeParams []*TypeParam, params
if n == 0 { if n == 0 {
panic("variadic function must have at least one parameter") panic("variadic function must have at least one parameter")
} }
core := coreString(params.At(n - 1).typ) last := params.At(n - 1).typ
if _, ok := core.(*Slice); !ok && !isString(core) { var S *Slice
panic(fmt.Sprintf("got %s, want variadic parameter with unnamed slice type or string as common underlying type", core.String())) 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} sig := &Signature{recv: recv, params: params, results: results, variadic: variadic}