mirror of
https://github.com/golang/go.git
synced 2025-10-19 11:03:18 +00:00
go/types, types2: factor out single commonUnder function
Combine commonUnder and commonUnderOrChan: - Provide an optional cond(ition) function argument to commonUnder to establish additional type set conditions. - Instead of a *Checker and *string argument for error reporting, return an error cause that is only allocated in the presence of an error. - Streamline some error messages. Replace all calls to coreType with calls to commonUnder. Change-Id: I81ac86d0d532cddc09164309acced61d90718b44 Reviewed-on: https://go-review.googlesource.com/c/go/+/654455 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Robert Griesemer <gri@google.com> Reviewed-by: Robert Griesemer <gri@google.com> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
f55bb135d2
commit
8b7e376e71
23 changed files with 250 additions and 303 deletions
|
@ -377,7 +377,8 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
|||
|
||||
case _Copy:
|
||||
// copy(x, y []T) int
|
||||
dst, _ := commonUnder(check, x.typ, nil).(*Slice)
|
||||
u, _ := commonUnder(x.typ, nil)
|
||||
dst, _ := u.(*Slice)
|
||||
|
||||
y := args[1]
|
||||
src0 := coreString(y.typ)
|
||||
|
@ -514,7 +515,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
|||
}
|
||||
|
||||
var min int // minimum number of arguments
|
||||
switch coreType(T).(type) {
|
||||
switch u, _ := commonUnder(T, nil); u.(type) {
|
||||
case *Slice:
|
||||
min = 2
|
||||
case *Map, *Chan:
|
||||
|
@ -818,7 +819,8 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
|||
// unsafe.Slice(ptr *T, len IntegerType) []T
|
||||
check.verifyVersionf(call.Fun, go1_17, "unsafe.Slice")
|
||||
|
||||
ptr, _ := commonUnder(check, x.typ, nil).(*Pointer)
|
||||
u, _ := commonUnder(x.typ, nil)
|
||||
ptr, _ := u.(*Pointer)
|
||||
if ptr == nil {
|
||||
check.errorf(x, InvalidUnsafeSlice, invalidArg+"%s is not a pointer", x)
|
||||
return
|
||||
|
@ -839,7 +841,8 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
|||
// unsafe.SliceData(slice []T) *T
|
||||
check.verifyVersionf(call.Fun, go1_20, "unsafe.SliceData")
|
||||
|
||||
slice, _ := commonUnder(check, x.typ, nil).(*Slice)
|
||||
u, _ := commonUnder(x.typ, nil)
|
||||
slice, _ := u.(*Slice)
|
||||
if slice == nil {
|
||||
check.errorf(x, InvalidUnsafeSliceData, invalidArg+"%s is not a slice", x)
|
||||
return
|
||||
|
|
|
@ -244,11 +244,12 @@ func (check *Checker) callExpr(x *operand, call *syntax.CallExpr) exprKind {
|
|||
|
||||
// If the operand type is a type parameter, all types in its type set
|
||||
// must have a common underlying type, which must be a signature.
|
||||
var cause string
|
||||
sig, _ := commonUnder(check, x.typ, &cause).(*Signature)
|
||||
// TODO(gri) use commonUnder condition for better error message
|
||||
u, err := commonUnder(x.typ, nil)
|
||||
sig, _ := u.(*Signature)
|
||||
if sig == nil {
|
||||
if cause != "" {
|
||||
check.errorf(x, InvalidCall, invalidOp+"cannot call %s: %s", x, cause)
|
||||
if err != nil {
|
||||
check.errorf(x, InvalidCall, invalidOp+"cannot call %s: %s", x, err.format(check))
|
||||
} else {
|
||||
check.errorf(x, InvalidCall, invalidOp+"cannot call non-function %s", x)
|
||||
}
|
||||
|
|
|
@ -26,7 +26,8 @@ func AsSignature(t Type) *Signature {
|
|||
// is the restricted channel type if the restrictions are always the same.
|
||||
// If typ is not a type parameter, CoreType returns the underlying type.
|
||||
func CoreType(t Type) Type {
|
||||
return coreType(t)
|
||||
u, _ := commonUnder(t, nil)
|
||||
return u
|
||||
}
|
||||
|
||||
// RangeKeyVal returns the key and value types for a range over typ.
|
||||
|
|
|
@ -196,50 +196,38 @@ func (check *Checker) unary(x *operand, e *syntax.Operation) {
|
|||
// or send to x (recv == false) operation. If the operation is not valid, chanElem
|
||||
// reports an error and returns nil.
|
||||
func (check *Checker) chanElem(pos poser, x *operand, recv bool) Type {
|
||||
var elem Type
|
||||
var cause string
|
||||
typeset(x.typ, func(t, u Type) bool {
|
||||
u, err := commonUnder(x.typ, func(t, u Type) *errorCause {
|
||||
if u == nil {
|
||||
// Type set contains no explicit terms.
|
||||
// It is either empty or contains all types (any)
|
||||
cause = "no specific channel type"
|
||||
return false
|
||||
return newErrorCause("no specific channel type")
|
||||
}
|
||||
ch, _ := u.(*Chan)
|
||||
if ch == nil {
|
||||
cause = check.sprintf("non-channel %s", t)
|
||||
return false
|
||||
return newErrorCause("non-channel %s", t)
|
||||
}
|
||||
if recv && ch.dir == SendOnly {
|
||||
cause = check.sprintf("send-only channel %s", t)
|
||||
return false
|
||||
return newErrorCause("send-only channel %s", t)
|
||||
}
|
||||
if !recv && ch.dir == RecvOnly {
|
||||
cause = check.sprintf("receive-only channel %s", t)
|
||||
return false
|
||||
return newErrorCause("receive-only channel %s", t)
|
||||
}
|
||||
if elem != nil && !Identical(elem, ch.elem) {
|
||||
cause = check.sprintf("channels with different element types %s and %s", elem, ch.elem)
|
||||
return false
|
||||
}
|
||||
elem = ch.elem
|
||||
return true
|
||||
return nil
|
||||
})
|
||||
|
||||
if cause == "" {
|
||||
return elem
|
||||
if u != nil {
|
||||
return u.(*Chan).elem
|
||||
}
|
||||
|
||||
cause := err.format(check)
|
||||
if recv {
|
||||
if isTypeParam(x.typ) {
|
||||
check.errorf(pos, InvalidReceive, invalidOp+"cannot receive from %s: type set contains %s", x, cause)
|
||||
check.errorf(pos, InvalidReceive, invalidOp+"cannot receive from %s: %s", x, cause)
|
||||
} else {
|
||||
// In this case, only the non-channel and send-only channel error are possible.
|
||||
check.errorf(pos, InvalidReceive, invalidOp+"cannot receive from %s %s", cause, x)
|
||||
}
|
||||
} else {
|
||||
if isTypeParam(x.typ) {
|
||||
check.errorf(pos, InvalidSend, invalidOp+"cannot send to %s: type set contains %s", x, cause)
|
||||
check.errorf(pos, InvalidSend, invalidOp+"cannot send to %s: %s", x, cause)
|
||||
} else {
|
||||
// In this case, only the non-channel and receive-only channel error are possible.
|
||||
check.errorf(pos, InvalidSend, invalidOp+"cannot send to %s %s", cause, x)
|
||||
|
|
|
@ -667,11 +667,12 @@ func coreTerm(tpar *TypeParam) (*term, bool) {
|
|||
})
|
||||
if n == 1 {
|
||||
if debug {
|
||||
assert(debug && under(single.typ) == coreType(tpar))
|
||||
u, _ := commonUnder(tpar, nil)
|
||||
assert(under(single.typ) == u)
|
||||
}
|
||||
return single, true
|
||||
}
|
||||
if typ := coreType(tpar); typ != nil {
|
||||
if typ, _ := commonUnder(tpar, nil); typ != nil {
|
||||
// A core type is always an underlying type.
|
||||
// If any term of tpar has a tilde, we don't
|
||||
// have a precise core type and we must return
|
||||
|
|
|
@ -129,7 +129,8 @@ func (check *Checker) compositeLit(x *operand, e *syntax.CompositeLit, hint Type
|
|||
typ = hint
|
||||
base = typ
|
||||
// *T implies &T{}
|
||||
if b, ok := deref(commonUnder(check, base, nil)); ok {
|
||||
u, _ := commonUnder(base, nil)
|
||||
if b, ok := deref(u); ok {
|
||||
base = b
|
||||
}
|
||||
isElem = true
|
||||
|
@ -142,7 +143,7 @@ func (check *Checker) compositeLit(x *operand, e *syntax.CompositeLit, hint Type
|
|||
base = typ
|
||||
}
|
||||
|
||||
switch utyp := commonUnder(check, base, nil).(type) {
|
||||
switch u, _ := commonUnder(base, nil); utyp := u.(type) {
|
||||
case *Struct:
|
||||
// Prevent crash if the struct referred to is not yet set up.
|
||||
// See analogous comment for *Array.
|
||||
|
|
|
@ -73,7 +73,7 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string, fo
|
|||
// we are ok here because only fields are accepted as results.
|
||||
const enableTParamFieldLookup = false // see go.dev/issue/51576
|
||||
if enableTParamFieldLookup && obj == nil && isTypeParam(T) {
|
||||
if t := commonUnder(nil, T, nil); t != nil {
|
||||
if t, _ := commonUnder(T, nil); t != nil {
|
||||
obj, index, indirect = lookupFieldOrMethodImpl(t, addressable, pkg, name, foldCase)
|
||||
if _, ok := obj.(*Var); !ok {
|
||||
obj, index, indirect = nil, nil, false // accept fields (variables) only
|
||||
|
|
|
@ -39,8 +39,6 @@ func isBasic(t Type, info BasicInfo) bool {
|
|||
// The allX predicates below report whether t is an X.
|
||||
// If t is a type parameter the result is true if isX is true
|
||||
// for all specified types of the type parameter's type set.
|
||||
// allX is an optimized version of isX(coreType(t)) (which
|
||||
// is the same as underIs(t, isX)).
|
||||
|
||||
func allBoolean(t Type) bool { return allBasic(t, IsBoolean) }
|
||||
func allInteger(t Type) bool { return allBasic(t, IsInteger) }
|
||||
|
@ -53,7 +51,6 @@ func allNumericOrString(t Type) bool { return allBasic(t, IsNumeric|IsString) }
|
|||
// allBasic reports whether under(t) is a basic type with the specified info.
|
||||
// If t is a type parameter, the result is true if isBasic(t, info) is true
|
||||
// for all specific types of the type parameter's type set.
|
||||
// allBasic(t, info) is an optimized version of isBasic(coreType(t), info).
|
||||
func allBasic(t Type, info BasicInfo) bool {
|
||||
if tpar, _ := Unalias(t).(*TypeParam); tpar != nil {
|
||||
return tpar.is(func(t *term) bool { return t != nil && isBasic(t.typ, info) })
|
||||
|
|
|
@ -1004,10 +1004,15 @@ func rangeKeyVal(check *Checker, orig Type, allowVersion func(goVersion) bool) (
|
|||
return Typ[Invalid], Typ[Invalid], cause, false
|
||||
}
|
||||
|
||||
var cause1 string
|
||||
rtyp := commonUnderOrChan(check, orig, &cause1)
|
||||
rtyp, err := commonUnder(orig, func(t, u Type) *errorCause {
|
||||
// A channel must permit receive operations.
|
||||
if ch, _ := u.(*Chan); ch != nil && ch.dir == SendOnly {
|
||||
return newErrorCause("receive from send-only channel %s", t)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if rtyp == nil {
|
||||
return bad(cause1)
|
||||
return bad(err.format(check))
|
||||
}
|
||||
|
||||
switch typ := arrayPtrDeref(rtyp).(type) {
|
||||
|
@ -1043,12 +1048,12 @@ func rangeKeyVal(check *Checker, orig Type, allowVersion func(goVersion) bool) (
|
|||
}
|
||||
assert(typ.Recv() == nil)
|
||||
// check iterator argument type
|
||||
var cause2 string
|
||||
cb, _ := commonUnder(check, typ.Params().At(0).Type(), &cause2).(*Signature)
|
||||
u, err := commonUnder(typ.Params().At(0).Type(), nil)
|
||||
cb, _ := u.(*Signature)
|
||||
switch {
|
||||
case cb == nil:
|
||||
if cause2 != "" {
|
||||
return bad(check.sprintf("func must be func(yield func(...) bool): in yield type, %s", cause2))
|
||||
if err != nil {
|
||||
return bad(check.sprintf("func must be func(yield func(...) bool): in yield type, %s", err.format(check)))
|
||||
} else {
|
||||
return bad("func must be func(yield func(...) bool): argument is not func")
|
||||
}
|
||||
|
|
|
@ -40,117 +40,94 @@ func typeset(t Type, yield func(t, u Type) bool) {
|
|||
yield(t, under(t))
|
||||
}
|
||||
|
||||
// TODO(gri) commonUnder, commonUnderOrChan, and Checker.chanElem (expr.go)
|
||||
// have a lot of similarities. Maybe we can find common ground
|
||||
// between them and distill a better factorization.
|
||||
// A errorCause describes an error cause.
|
||||
type errorCause struct {
|
||||
format_ string
|
||||
args []any
|
||||
}
|
||||
|
||||
// If t is not a type parameter, commonUnder returns the underlying type.
|
||||
// If t is a type parameter, commonUnder returns the common underlying
|
||||
// type of all types in its type set if it exists.
|
||||
// Otherwise the result is nil, and *cause reports the error if a non-nil
|
||||
// cause is provided.
|
||||
// The check parameter is only used if *cause reports an error; it may be nil.
|
||||
func commonUnder(check *Checker, t Type, cause *string) Type {
|
||||
var s, su Type
|
||||
func newErrorCause(format string, args ...any) *errorCause {
|
||||
return &errorCause{format, args}
|
||||
}
|
||||
|
||||
bad := func(s string) bool {
|
||||
if cause != nil {
|
||||
*cause = s
|
||||
}
|
||||
su = nil
|
||||
// format formats a cause as a string.
|
||||
// check may be nil.
|
||||
func (err *errorCause) format(check *Checker) string {
|
||||
return check.sprintf(err.format_, err.args...)
|
||||
}
|
||||
|
||||
// If t is a type parameter, cond is nil, and t's type set contains no channel types,
|
||||
// commonUnder returns the common underlying type of all types in t's type set if
|
||||
// it exists, or nil and an error cause otherwise.
|
||||
//
|
||||
// If t is a type parameter, cond is nil, and there are channel types, t's type set
|
||||
// must only contain channel types, they must all have the same element types,
|
||||
// channel directions must not conflict, and commonUnder returns one of the most
|
||||
// restricted channels. Otherwise, the function returns nil and an error cause.
|
||||
//
|
||||
// If cond != nil, each pair (t, u) of type and underlying type in t's type set
|
||||
// must satisfy the condition expressed by cond. If the result of cond is != nil,
|
||||
// commonUnder returns nil and the error cause reported by cond.
|
||||
// Note that cond is called before any other conditions are checked; specifically
|
||||
// cond may be called with (nil, nil) if the type set contains no specific types.
|
||||
//
|
||||
// If t is not a type parameter, commonUnder behaves as if t was a type parameter
|
||||
// with the single type t in its type set.
|
||||
func commonUnder(t Type, cond func(t, u Type) *errorCause) (Type, *errorCause) {
|
||||
var ct, cu Type // type and respective common underlying type
|
||||
var err *errorCause
|
||||
|
||||
bad := func(format string, args ...any) bool {
|
||||
cu = nil
|
||||
err = newErrorCause(format, args...)
|
||||
return false
|
||||
}
|
||||
|
||||
typeset(t, func(t, u Type) bool {
|
||||
if u == nil {
|
||||
return bad("no specific type")
|
||||
}
|
||||
if su != nil && !Identical(su, u) {
|
||||
return bad(check.sprintf("%s and %s have different underlying types", s, t))
|
||||
}
|
||||
// su == nil || Identical(su, u)
|
||||
s, su = t, u
|
||||
return true
|
||||
})
|
||||
|
||||
return su
|
||||
}
|
||||
|
||||
// If t is not a type parameter, commonUnderOrChan returns the underlying type;
|
||||
// if that type is a channel type it must permit receive operations.
|
||||
// If t is a type parameter, commonUnderOrChan returns the common underlying
|
||||
// type of all types in its type set if it exists, or, if the type set contains
|
||||
// only channel types permitting receive operations and with identical element
|
||||
// types, commonUnderOrChan returns one of those channel types.
|
||||
// Otherwise the result is nil, and *cause reports the error if a non-nil cause
|
||||
// is provided.
|
||||
// The check parameter is only used if *cause reports an error; it may be nil.
|
||||
func commonUnderOrChan(check *Checker, t Type, cause *string) Type {
|
||||
var s, su Type
|
||||
var sc *Chan
|
||||
|
||||
bad := func(s string) bool {
|
||||
if cause != nil {
|
||||
*cause = s
|
||||
}
|
||||
su = nil
|
||||
return false
|
||||
}
|
||||
|
||||
typeset(t, func(t, u Type) bool {
|
||||
if u == nil {
|
||||
return bad("no specific type")
|
||||
}
|
||||
c, _ := u.(*Chan)
|
||||
if c != nil && c.dir == SendOnly {
|
||||
return bad(check.sprintf("receive from send-only channel %s", t))
|
||||
}
|
||||
if su == nil {
|
||||
s, su = t, u
|
||||
sc = c // possibly nil
|
||||
return true
|
||||
}
|
||||
// su != nil
|
||||
if sc != nil && c != nil {
|
||||
if !Identical(sc.elem, c.elem) {
|
||||
return bad(check.sprintf("channels with different element types %s and %s", sc.elem, c.elem))
|
||||
}
|
||||
return true
|
||||
}
|
||||
// sc == nil
|
||||
if !Identical(su, u) {
|
||||
return bad(check.sprintf("%s and %s have different underlying types", s, t))
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return su
|
||||
}
|
||||
|
||||
// If t is not a type parameter, coreType returns the underlying type.
|
||||
// If t is a type parameter, coreType returns the single underlying
|
||||
// type of all types in its type set if it exists, or nil otherwise. If the
|
||||
// type set contains only unrestricted and restricted channel types (with
|
||||
// identical element types), the single underlying type is the restricted
|
||||
// channel type if the restrictions are always the same, or nil otherwise.
|
||||
func coreType(t Type) Type {
|
||||
var su Type
|
||||
typeset(t, func(_, u Type) bool {
|
||||
if u == nil {
|
||||
return false
|
||||
}
|
||||
if su != nil {
|
||||
u = match(su, u)
|
||||
if u == nil {
|
||||
su = nil
|
||||
if cond != nil {
|
||||
if err = cond(t, u); err != nil {
|
||||
cu = nil
|
||||
return false
|
||||
}
|
||||
}
|
||||
// su == nil || match(su, u) != nil
|
||||
su = u
|
||||
|
||||
if u == nil {
|
||||
return bad("no specific type")
|
||||
}
|
||||
|
||||
// If this is the first type we're seeing, we're done.
|
||||
if cu == nil {
|
||||
ct, cu = t, u
|
||||
return true
|
||||
}
|
||||
|
||||
// If we've seen a channel before, and we have a channel now, they must be compatible.
|
||||
if chu, _ := cu.(*Chan); chu != nil {
|
||||
if ch, _ := u.(*Chan); ch != nil {
|
||||
if !Identical(chu.elem, ch.elem) {
|
||||
return bad("channels %s and %s have different element types", ct, t)
|
||||
}
|
||||
// If we have different channel directions, keep the restricted one
|
||||
// and complain if they conflict.
|
||||
if chu.dir == SendRecv {
|
||||
ct, cu = t, u // switch to current, possibly restricted channel
|
||||
} else if chu.dir != ch.dir {
|
||||
return bad("channels %s and %s have conflicting directions", ct, t)
|
||||
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, the current type must have the same underlying type as all previous types.
|
||||
if !Identical(cu, u) {
|
||||
return bad("%s and %s have different underlying types", ct, t)
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
return su
|
||||
|
||||
return cu, err
|
||||
}
|
||||
|
||||
// coreString is like coreType but also considers []byte
|
||||
|
|
|
@ -773,7 +773,7 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
|
|||
// If y is also an unbound type parameter, we will end
|
||||
// up here again with x and y swapped, so we don't
|
||||
// need to take care of that case separately.
|
||||
if cx := coreType(x); cx != nil {
|
||||
if cx, _ := commonUnder(x, nil); cx != nil {
|
||||
if traceInference {
|
||||
u.tracef("core %s ≡ %s", xorig, yorig)
|
||||
}
|
||||
|
|
|
@ -380,7 +380,8 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
|
||||
case _Copy:
|
||||
// copy(x, y []T) int
|
||||
dst, _ := commonUnder(check, x.typ, nil).(*Slice)
|
||||
u, _ := commonUnder(x.typ, nil)
|
||||
dst, _ := u.(*Slice)
|
||||
|
||||
y := args[1]
|
||||
src0 := coreString(y.typ)
|
||||
|
@ -517,7 +518,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
}
|
||||
|
||||
var min int // minimum number of arguments
|
||||
switch coreType(T).(type) {
|
||||
switch u, _ := commonUnder(T, nil); u.(type) {
|
||||
case *Slice:
|
||||
min = 2
|
||||
case *Map, *Chan:
|
||||
|
@ -821,7 +822,8 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
// unsafe.Slice(ptr *T, len IntegerType) []T
|
||||
check.verifyVersionf(call.Fun, go1_17, "unsafe.Slice")
|
||||
|
||||
ptr, _ := commonUnder(check, x.typ, nil).(*Pointer)
|
||||
u, _ := commonUnder(x.typ, nil)
|
||||
ptr, _ := u.(*Pointer)
|
||||
if ptr == nil {
|
||||
check.errorf(x, InvalidUnsafeSlice, invalidArg+"%s is not a pointer", x)
|
||||
return
|
||||
|
@ -842,7 +844,8 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
// unsafe.SliceData(slice []T) *T
|
||||
check.verifyVersionf(call.Fun, go1_20, "unsafe.SliceData")
|
||||
|
||||
slice, _ := commonUnder(check, x.typ, nil).(*Slice)
|
||||
u, _ := commonUnder(x.typ, nil)
|
||||
slice, _ := u.(*Slice)
|
||||
if slice == nil {
|
||||
check.errorf(x, InvalidUnsafeSliceData, invalidArg+"%s is not a slice", x)
|
||||
return
|
||||
|
|
|
@ -246,11 +246,12 @@ func (check *Checker) callExpr(x *operand, call *ast.CallExpr) exprKind {
|
|||
|
||||
// If the operand type is a type parameter, all types in its type set
|
||||
// must have a common underlying type, which must be a signature.
|
||||
var cause string
|
||||
sig, _ := commonUnder(check, x.typ, &cause).(*Signature)
|
||||
// TODO(gri) use commonUnder condition for better error message
|
||||
u, err := commonUnder(x.typ, nil)
|
||||
sig, _ := u.(*Signature)
|
||||
if sig == nil {
|
||||
if cause != "" {
|
||||
check.errorf(x, InvalidCall, invalidOp+"cannot call %s: %s", x, cause)
|
||||
if err != nil {
|
||||
check.errorf(x, InvalidCall, invalidOp+"cannot call %s: %s", x, err.format(check))
|
||||
} else {
|
||||
check.errorf(x, InvalidCall, invalidOp+"cannot call non-function %s", x)
|
||||
}
|
||||
|
|
|
@ -195,50 +195,38 @@ func (check *Checker) unary(x *operand, e *ast.UnaryExpr) {
|
|||
// or send to x (recv == false) operation. If the operation is not valid, chanElem
|
||||
// reports an error and returns nil.
|
||||
func (check *Checker) chanElem(pos positioner, x *operand, recv bool) Type {
|
||||
var elem Type
|
||||
var cause string
|
||||
typeset(x.typ, func(t, u Type) bool {
|
||||
u, err := commonUnder(x.typ, func(t, u Type) *errorCause {
|
||||
if u == nil {
|
||||
// Type set contains no explicit terms.
|
||||
// It is either empty or contains all types (any)
|
||||
cause = "no specific channel type"
|
||||
return false
|
||||
return newErrorCause("no specific channel type")
|
||||
}
|
||||
ch, _ := u.(*Chan)
|
||||
if ch == nil {
|
||||
cause = check.sprintf("non-channel %s", t)
|
||||
return false
|
||||
return newErrorCause("non-channel %s", t)
|
||||
}
|
||||
if recv && ch.dir == SendOnly {
|
||||
cause = check.sprintf("send-only channel %s", t)
|
||||
return false
|
||||
return newErrorCause("send-only channel %s", t)
|
||||
}
|
||||
if !recv && ch.dir == RecvOnly {
|
||||
cause = check.sprintf("receive-only channel %s", t)
|
||||
return false
|
||||
return newErrorCause("receive-only channel %s", t)
|
||||
}
|
||||
if elem != nil && !Identical(elem, ch.elem) {
|
||||
cause = check.sprintf("channels with different element types %s and %s", elem, ch.elem)
|
||||
return false
|
||||
}
|
||||
elem = ch.elem
|
||||
return true
|
||||
return nil
|
||||
})
|
||||
|
||||
if cause == "" {
|
||||
return elem
|
||||
if u != nil {
|
||||
return u.(*Chan).elem
|
||||
}
|
||||
|
||||
cause := err.format(check)
|
||||
if recv {
|
||||
if isTypeParam(x.typ) {
|
||||
check.errorf(pos, InvalidReceive, invalidOp+"cannot receive from %s: type set contains %s", x, cause)
|
||||
check.errorf(pos, InvalidReceive, invalidOp+"cannot receive from %s: %s", x, cause)
|
||||
} else {
|
||||
// In this case, only the non-channel and send-only channel error are possible.
|
||||
check.errorf(pos, InvalidReceive, invalidOp+"cannot receive from %s %s", cause, x)
|
||||
}
|
||||
} else {
|
||||
if isTypeParam(x.typ) {
|
||||
check.errorf(pos, InvalidSend, invalidOp+"cannot send to %s: type set contains %s", x, cause)
|
||||
check.errorf(pos, InvalidSend, invalidOp+"cannot send to %s: %s", x, cause)
|
||||
} else {
|
||||
// In this case, only the non-channel and receive-only channel error are possible.
|
||||
check.errorf(pos, InvalidSend, invalidOp+"cannot send to %s %s", cause, x)
|
||||
|
|
|
@ -670,11 +670,12 @@ func coreTerm(tpar *TypeParam) (*term, bool) {
|
|||
})
|
||||
if n == 1 {
|
||||
if debug {
|
||||
assert(debug && under(single.typ) == coreType(tpar))
|
||||
u, _ := commonUnder(tpar, nil)
|
||||
assert(under(single.typ) == u)
|
||||
}
|
||||
return single, true
|
||||
}
|
||||
if typ := coreType(tpar); typ != nil {
|
||||
if typ, _ := commonUnder(tpar, nil); typ != nil {
|
||||
// A core type is always an underlying type.
|
||||
// If any term of tpar has a tilde, we don't
|
||||
// have a precise core type and we must return
|
||||
|
|
|
@ -133,7 +133,8 @@ func (check *Checker) compositeLit(x *operand, e *ast.CompositeLit, hint Type) {
|
|||
typ = hint
|
||||
base = typ
|
||||
// *T implies &T{}
|
||||
if b, ok := deref(commonUnder(check, base, nil)); ok {
|
||||
u, _ := commonUnder(base, nil)
|
||||
if b, ok := deref(u); ok {
|
||||
base = b
|
||||
}
|
||||
isElem = true
|
||||
|
@ -146,7 +147,7 @@ func (check *Checker) compositeLit(x *operand, e *ast.CompositeLit, hint Type) {
|
|||
base = typ
|
||||
}
|
||||
|
||||
switch utyp := commonUnder(check, base, nil).(type) {
|
||||
switch u, _ := commonUnder(base, nil); utyp := u.(type) {
|
||||
case *Struct:
|
||||
// Prevent crash if the struct referred to is not yet set up.
|
||||
// See analogous comment for *Array.
|
||||
|
|
|
@ -76,7 +76,7 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string, fo
|
|||
// we are ok here because only fields are accepted as results.
|
||||
const enableTParamFieldLookup = false // see go.dev/issue/51576
|
||||
if enableTParamFieldLookup && obj == nil && isTypeParam(T) {
|
||||
if t := commonUnder(nil, T, nil); t != nil {
|
||||
if t, _ := commonUnder(T, nil); t != nil {
|
||||
obj, index, indirect = lookupFieldOrMethodImpl(t, addressable, pkg, name, foldCase)
|
||||
if _, ok := obj.(*Var); !ok {
|
||||
obj, index, indirect = nil, nil, false // accept fields (variables) only
|
||||
|
|
|
@ -42,8 +42,6 @@ func isBasic(t Type, info BasicInfo) bool {
|
|||
// The allX predicates below report whether t is an X.
|
||||
// If t is a type parameter the result is true if isX is true
|
||||
// for all specified types of the type parameter's type set.
|
||||
// allX is an optimized version of isX(coreType(t)) (which
|
||||
// is the same as underIs(t, isX)).
|
||||
|
||||
func allBoolean(t Type) bool { return allBasic(t, IsBoolean) }
|
||||
func allInteger(t Type) bool { return allBasic(t, IsInteger) }
|
||||
|
@ -56,7 +54,6 @@ func allNumericOrString(t Type) bool { return allBasic(t, IsNumeric|IsString) }
|
|||
// allBasic reports whether under(t) is a basic type with the specified info.
|
||||
// If t is a type parameter, the result is true if isBasic(t, info) is true
|
||||
// for all specific types of the type parameter's type set.
|
||||
// allBasic(t, info) is an optimized version of isBasic(coreType(t), info).
|
||||
func allBasic(t Type, info BasicInfo) bool {
|
||||
if tpar, _ := Unalias(t).(*TypeParam); tpar != nil {
|
||||
return tpar.is(func(t *term) bool { return t != nil && isBasic(t.typ, info) })
|
||||
|
|
|
@ -1025,10 +1025,15 @@ func rangeKeyVal(check *Checker, orig Type, allowVersion func(goVersion) bool) (
|
|||
return Typ[Invalid], Typ[Invalid], cause, false
|
||||
}
|
||||
|
||||
var cause1 string
|
||||
rtyp := commonUnderOrChan(check, orig, &cause1)
|
||||
rtyp, err := commonUnder(orig, func(t, u Type) *errorCause {
|
||||
// A channel must permit receive operations.
|
||||
if ch, _ := u.(*Chan); ch != nil && ch.dir == SendOnly {
|
||||
return newErrorCause("receive from send-only channel %s", t)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if rtyp == nil {
|
||||
return bad(cause1)
|
||||
return bad(err.format(check))
|
||||
}
|
||||
|
||||
switch typ := arrayPtrDeref(rtyp).(type) {
|
||||
|
@ -1064,12 +1069,12 @@ func rangeKeyVal(check *Checker, orig Type, allowVersion func(goVersion) bool) (
|
|||
}
|
||||
assert(typ.Recv() == nil)
|
||||
// check iterator argument type
|
||||
var cause2 string
|
||||
cb, _ := commonUnder(check, typ.Params().At(0).Type(), &cause2).(*Signature)
|
||||
u, err := commonUnder(typ.Params().At(0).Type(), nil)
|
||||
cb, _ := u.(*Signature)
|
||||
switch {
|
||||
case cb == nil:
|
||||
if cause2 != "" {
|
||||
return bad(check.sprintf("func must be func(yield func(...) bool): in yield type, %s", cause2))
|
||||
if err != nil {
|
||||
return bad(check.sprintf("func must be func(yield func(...) bool): in yield type, %s", err.format(check)))
|
||||
} else {
|
||||
return bad("func must be func(yield func(...) bool): argument is not func")
|
||||
}
|
||||
|
|
|
@ -43,117 +43,94 @@ func typeset(t Type, yield func(t, u Type) bool) {
|
|||
yield(t, under(t))
|
||||
}
|
||||
|
||||
// TODO(gri) commonUnder, commonUnderOrChan, and Checker.chanElem (expr.go)
|
||||
// have a lot of similarities. Maybe we can find common ground
|
||||
// between them and distill a better factorization.
|
||||
// A errorCause describes an error cause.
|
||||
type errorCause struct {
|
||||
format_ string
|
||||
args []any
|
||||
}
|
||||
|
||||
// If t is not a type parameter, commonUnder returns the underlying type.
|
||||
// If t is a type parameter, commonUnder returns the common underlying
|
||||
// type of all types in its type set if it exists.
|
||||
// Otherwise the result is nil, and *cause reports the error if a non-nil
|
||||
// cause is provided.
|
||||
// The check parameter is only used if *cause reports an error; it may be nil.
|
||||
func commonUnder(check *Checker, t Type, cause *string) Type {
|
||||
var s, su Type
|
||||
func newErrorCause(format string, args ...any) *errorCause {
|
||||
return &errorCause{format, args}
|
||||
}
|
||||
|
||||
bad := func(s string) bool {
|
||||
if cause != nil {
|
||||
*cause = s
|
||||
}
|
||||
su = nil
|
||||
// format formats a cause as a string.
|
||||
// check may be nil.
|
||||
func (err *errorCause) format(check *Checker) string {
|
||||
return check.sprintf(err.format_, err.args...)
|
||||
}
|
||||
|
||||
// If t is a type parameter, cond is nil, and t's type set contains no channel types,
|
||||
// commonUnder returns the common underlying type of all types in t's type set if
|
||||
// it exists, or nil and an error cause otherwise.
|
||||
//
|
||||
// If t is a type parameter, cond is nil, and there are channel types, t's type set
|
||||
// must only contain channel types, they must all have the same element types,
|
||||
// channel directions must not conflict, and commonUnder returns one of the most
|
||||
// restricted channels. Otherwise, the function returns nil and an error cause.
|
||||
//
|
||||
// If cond != nil, each pair (t, u) of type and underlying type in t's type set
|
||||
// must satisfy the condition expressed by cond. If the result of cond is != nil,
|
||||
// commonUnder returns nil and the error cause reported by cond.
|
||||
// Note that cond is called before any other conditions are checked; specifically
|
||||
// cond may be called with (nil, nil) if the type set contains no specific types.
|
||||
//
|
||||
// If t is not a type parameter, commonUnder behaves as if t was a type parameter
|
||||
// with the single type t in its type set.
|
||||
func commonUnder(t Type, cond func(t, u Type) *errorCause) (Type, *errorCause) {
|
||||
var ct, cu Type // type and respective common underlying type
|
||||
var err *errorCause
|
||||
|
||||
bad := func(format string, args ...any) bool {
|
||||
cu = nil
|
||||
err = newErrorCause(format, args...)
|
||||
return false
|
||||
}
|
||||
|
||||
typeset(t, func(t, u Type) bool {
|
||||
if u == nil {
|
||||
return bad("no specific type")
|
||||
}
|
||||
if su != nil && !Identical(su, u) {
|
||||
return bad(check.sprintf("%s and %s have different underlying types", s, t))
|
||||
}
|
||||
// su == nil || Identical(su, u)
|
||||
s, su = t, u
|
||||
return true
|
||||
})
|
||||
|
||||
return su
|
||||
}
|
||||
|
||||
// If t is not a type parameter, commonUnderOrChan returns the underlying type;
|
||||
// if that type is a channel type it must permit receive operations.
|
||||
// If t is a type parameter, commonUnderOrChan returns the common underlying
|
||||
// type of all types in its type set if it exists, or, if the type set contains
|
||||
// only channel types permitting receive operations and with identical element
|
||||
// types, commonUnderOrChan returns one of those channel types.
|
||||
// Otherwise the result is nil, and *cause reports the error if a non-nil cause
|
||||
// is provided.
|
||||
// The check parameter is only used if *cause reports an error; it may be nil.
|
||||
func commonUnderOrChan(check *Checker, t Type, cause *string) Type {
|
||||
var s, su Type
|
||||
var sc *Chan
|
||||
|
||||
bad := func(s string) bool {
|
||||
if cause != nil {
|
||||
*cause = s
|
||||
}
|
||||
su = nil
|
||||
return false
|
||||
}
|
||||
|
||||
typeset(t, func(t, u Type) bool {
|
||||
if u == nil {
|
||||
return bad("no specific type")
|
||||
}
|
||||
c, _ := u.(*Chan)
|
||||
if c != nil && c.dir == SendOnly {
|
||||
return bad(check.sprintf("receive from send-only channel %s", t))
|
||||
}
|
||||
if su == nil {
|
||||
s, su = t, u
|
||||
sc = c // possibly nil
|
||||
return true
|
||||
}
|
||||
// su != nil
|
||||
if sc != nil && c != nil {
|
||||
if !Identical(sc.elem, c.elem) {
|
||||
return bad(check.sprintf("channels with different element types %s and %s", sc.elem, c.elem))
|
||||
}
|
||||
return true
|
||||
}
|
||||
// sc == nil
|
||||
if !Identical(su, u) {
|
||||
return bad(check.sprintf("%s and %s have different underlying types", s, t))
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return su
|
||||
}
|
||||
|
||||
// If t is not a type parameter, coreType returns the underlying type.
|
||||
// If t is a type parameter, coreType returns the single underlying
|
||||
// type of all types in its type set if it exists, or nil otherwise. If the
|
||||
// type set contains only unrestricted and restricted channel types (with
|
||||
// identical element types), the single underlying type is the restricted
|
||||
// channel type if the restrictions are always the same, or nil otherwise.
|
||||
func coreType(t Type) Type {
|
||||
var su Type
|
||||
typeset(t, func(_, u Type) bool {
|
||||
if u == nil {
|
||||
return false
|
||||
}
|
||||
if su != nil {
|
||||
u = match(su, u)
|
||||
if u == nil {
|
||||
su = nil
|
||||
if cond != nil {
|
||||
if err = cond(t, u); err != nil {
|
||||
cu = nil
|
||||
return false
|
||||
}
|
||||
}
|
||||
// su == nil || match(su, u) != nil
|
||||
su = u
|
||||
|
||||
if u == nil {
|
||||
return bad("no specific type")
|
||||
}
|
||||
|
||||
// If this is the first type we're seeing, we're done.
|
||||
if cu == nil {
|
||||
ct, cu = t, u
|
||||
return true
|
||||
}
|
||||
|
||||
// If we've seen a channel before, and we have a channel now, they must be compatible.
|
||||
if chu, _ := cu.(*Chan); chu != nil {
|
||||
if ch, _ := u.(*Chan); ch != nil {
|
||||
if !Identical(chu.elem, ch.elem) {
|
||||
return bad("channels %s and %s have different element types", ct, t)
|
||||
}
|
||||
// If we have different channel directions, keep the restricted one
|
||||
// and complain if they conflict.
|
||||
if chu.dir == SendRecv {
|
||||
ct, cu = t, u // switch to current, possibly restricted channel
|
||||
} else if chu.dir != ch.dir {
|
||||
return bad("channels %s and %s have conflicting directions", ct, t)
|
||||
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, the current type must have the same underlying type as all previous types.
|
||||
if !Identical(cu, u) {
|
||||
return bad("%s and %s have different underlying types", ct, t)
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
return su
|
||||
|
||||
return cu, err
|
||||
}
|
||||
|
||||
// coreString is like coreType but also considers []byte
|
||||
|
|
|
@ -776,7 +776,7 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
|
|||
// If y is also an unbound type parameter, we will end
|
||||
// up here again with x and y swapped, so we don't
|
||||
// need to take care of that case separately.
|
||||
if cx := coreType(x); cx != nil {
|
||||
if cx, _ := commonUnder(x, nil); cx != nil {
|
||||
if traceInference {
|
||||
u.tracef("core %s ≡ %s", xorig, yorig)
|
||||
}
|
||||
|
|
|
@ -12,11 +12,11 @@ type C4 interface{ chan int | chan<- int }
|
|||
type C5[T any] interface{ ~chan T | <-chan T }
|
||||
|
||||
func _[T any](ch T) {
|
||||
<-ch // ERRORx `cannot receive from ch .*: type set contains no specific channel type`
|
||||
<-ch // ERRORx `cannot receive from ch .*: no specific channel type`
|
||||
}
|
||||
|
||||
func _[T C0](ch T) {
|
||||
<-ch // ERRORx `cannot receive from ch .*: type set contains non-channel int`
|
||||
<-ch // ERRORx `cannot receive from ch .*: non-channel int`
|
||||
}
|
||||
|
||||
func _[T C1](ch T) {
|
||||
|
@ -28,11 +28,11 @@ func _[T C2](ch T) {
|
|||
}
|
||||
|
||||
func _[T C3](ch T) {
|
||||
<-ch // ERRORx `cannot receive from ch .*: type set contains channels with different element types int and float32`
|
||||
<-ch // ERRORx `cannot receive from ch .*: channels chan int and chan float32 have different element types`
|
||||
}
|
||||
|
||||
func _[T C4](ch T) {
|
||||
<-ch // ERRORx `cannot receive from ch .*: type set contains send-only channel chan<- int`
|
||||
<-ch // ERRORx `cannot receive from ch .*: send-only channel chan<- int`
|
||||
}
|
||||
|
||||
func _[T C5[X], X any](ch T, x X) {
|
||||
|
|
|
@ -12,11 +12,11 @@ type C4 interface{ chan int | chan<- int }
|
|||
type C5[T any] interface{ ~chan T | chan<- T }
|
||||
|
||||
func _[T any](ch T) {
|
||||
ch <- /* ERRORx `cannot send to ch .*: type set contains no specific channel type` */ 0
|
||||
ch <- /* ERRORx `cannot send to ch .*: no specific channel type` */ 0
|
||||
}
|
||||
|
||||
func _[T C0](ch T) {
|
||||
ch <- /* ERRORx `cannot send to ch .*: type set contains non-channel int` */ 0
|
||||
ch <- /* ERRORx `cannot send to ch .*: non-channel int` */ 0
|
||||
}
|
||||
|
||||
func _[T C1](ch T) {
|
||||
|
@ -24,11 +24,11 @@ func _[T C1](ch T) {
|
|||
}
|
||||
|
||||
func _[T C2](ch T) {
|
||||
ch <- /* ERRORx `cannot send to ch .*: type set contains receive-only channel <-chan int` */ 0
|
||||
ch <- /* ERRORx `cannot send to ch .*: receive-only channel <-chan int` */ 0
|
||||
}
|
||||
|
||||
func _[T C3](ch T) {
|
||||
ch <- /* ERRORx `cannot send to ch .*: type set contains channels with different element types` */ 0
|
||||
ch <- /* ERRORx `cannot send to ch .*: channels chan int and chan float32 have different element types` */ 0
|
||||
}
|
||||
|
||||
func _[T C4](ch T) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue