mirror of
https://github.com/golang/go.git
synced 2025-05-06 08:03:03 +00:00
go/types, types2: better error messages for channel sends and receives
Use the same code pattern for sends and receives and factor it out into a new helper method Checker.chanElem. Provide the exact error cause rather than simply referring to the core type. For #70128. Change-Id: I4a0b597a487b78c057eebe06c4ac28f9bf1f7719 Reviewed-on: https://go-review.googlesource.com/c/go/+/647455 Auto-Submit: Robert Griesemer <gri@google.com> Reviewed-by: Robert Findley <rfindley@google.com> Reviewed-by: Robert Griesemer <gri@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
ff627d28db
commit
a4fcfaa167
@ -148,27 +148,14 @@ func (check *Checker) unary(x *operand, e *syntax.Operation) {
|
|||||||
return
|
return
|
||||||
|
|
||||||
case syntax.Recv:
|
case syntax.Recv:
|
||||||
u := coreType(x.typ)
|
if elem := check.chanElem(x, x, true); elem != nil {
|
||||||
if u == nil {
|
|
||||||
check.errorf(x, InvalidReceive, invalidOp+"cannot receive from %s (no core type)", x)
|
|
||||||
x.mode = invalid
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ch, _ := u.(*Chan)
|
|
||||||
if ch == nil {
|
|
||||||
check.errorf(x, InvalidReceive, invalidOp+"cannot receive from non-channel %s", x)
|
|
||||||
x.mode = invalid
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if ch.dir == SendOnly {
|
|
||||||
check.errorf(x, InvalidReceive, invalidOp+"cannot receive from send-only channel %s", x)
|
|
||||||
x.mode = invalid
|
|
||||||
return
|
|
||||||
}
|
|
||||||
x.mode = commaok
|
x.mode = commaok
|
||||||
x.typ = ch.elem
|
x.typ = elem
|
||||||
check.hasCallOrRecv = true
|
check.hasCallOrRecv = true
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
x.mode = invalid
|
||||||
|
return
|
||||||
|
|
||||||
case syntax.Tilde:
|
case syntax.Tilde:
|
||||||
// Provide a better error position and message than what check.op below would do.
|
// Provide a better error position and message than what check.op below would do.
|
||||||
@ -205,6 +192,62 @@ func (check *Checker) unary(x *operand, e *syntax.Operation) {
|
|||||||
// x.typ remains unchanged
|
// x.typ remains unchanged
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// chanElem returns the channel element type of x for a receive from x (recv == true)
|
||||||
|
// 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 {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
ch, _ := u.(*Chan)
|
||||||
|
if ch == nil {
|
||||||
|
cause = check.sprintf("non-channel %s", t)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if recv && ch.dir == SendOnly {
|
||||||
|
cause = check.sprintf("send-only channel %s", t)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !recv && ch.dir == RecvOnly {
|
||||||
|
cause = check.sprintf("receive-only channel %s", t)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
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
|
||||||
|
})
|
||||||
|
|
||||||
|
if cause == "" {
|
||||||
|
return elem
|
||||||
|
}
|
||||||
|
|
||||||
|
if recv {
|
||||||
|
if isTypeParam(x.typ) {
|
||||||
|
check.errorf(pos, InvalidReceive, invalidOp+"cannot receive from %s: type set contains %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)
|
||||||
|
} 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func isShift(op syntax.Operator) bool {
|
func isShift(op syntax.Operator) bool {
|
||||||
return op == syntax.Shl || op == syntax.Shr
|
return op == syntax.Shl || op == syntax.Shr
|
||||||
}
|
}
|
||||||
|
@ -465,21 +465,9 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) {
|
|||||||
if ch.mode == invalid || val.mode == invalid {
|
if ch.mode == invalid || val.mode == invalid {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
u := coreType(ch.typ)
|
if elem := check.chanElem(s, &ch, false); elem != nil {
|
||||||
if u == nil {
|
check.assignment(&val, elem, "send")
|
||||||
check.errorf(s, InvalidSend, invalidOp+"cannot send to %s: no core type", &ch)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
uch, _ := u.(*Chan)
|
|
||||||
if uch == nil {
|
|
||||||
check.errorf(s, InvalidSend, invalidOp+"cannot send to non-channel %s", &ch)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if uch.dir == RecvOnly {
|
|
||||||
check.errorf(s, InvalidSend, invalidOp+"cannot send to receive-only channel %s", &ch)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
check.assignment(&val, uch.elem, "send")
|
|
||||||
|
|
||||||
case *syntax.AssignStmt:
|
case *syntax.AssignStmt:
|
||||||
if s.Rhs == nil {
|
if s.Rhs == nil {
|
||||||
|
@ -147,28 +147,14 @@ func (check *Checker) unary(x *operand, e *ast.UnaryExpr) {
|
|||||||
return
|
return
|
||||||
|
|
||||||
case token.ARROW:
|
case token.ARROW:
|
||||||
u := coreType(x.typ)
|
if elem := check.chanElem(x, x, true); elem != nil {
|
||||||
if u == nil {
|
|
||||||
check.errorf(x, InvalidReceive, invalidOp+"cannot receive from %s (no core type)", x)
|
|
||||||
x.mode = invalid
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ch, _ := u.(*Chan)
|
|
||||||
if ch == nil {
|
|
||||||
check.errorf(x, InvalidReceive, invalidOp+"cannot receive from non-channel %s", x)
|
|
||||||
x.mode = invalid
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if ch.dir == SendOnly {
|
|
||||||
check.errorf(x, InvalidReceive, invalidOp+"cannot receive from send-only channel %s", x)
|
|
||||||
x.mode = invalid
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
x.mode = commaok
|
x.mode = commaok
|
||||||
x.typ = ch.elem
|
x.typ = elem
|
||||||
check.hasCallOrRecv = true
|
check.hasCallOrRecv = true
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
x.mode = invalid
|
||||||
|
return
|
||||||
|
|
||||||
case token.TILDE:
|
case token.TILDE:
|
||||||
// Provide a better error position and message than what check.op below would do.
|
// Provide a better error position and message than what check.op below would do.
|
||||||
@ -205,6 +191,62 @@ func (check *Checker) unary(x *operand, e *ast.UnaryExpr) {
|
|||||||
// x.typ remains unchanged
|
// x.typ remains unchanged
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// chanElem returns the channel element type of x for a receive from x (recv == true)
|
||||||
|
// 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 {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
ch, _ := u.(*Chan)
|
||||||
|
if ch == nil {
|
||||||
|
cause = check.sprintf("non-channel %s", t)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if recv && ch.dir == SendOnly {
|
||||||
|
cause = check.sprintf("send-only channel %s", t)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !recv && ch.dir == RecvOnly {
|
||||||
|
cause = check.sprintf("receive-only channel %s", t)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
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
|
||||||
|
})
|
||||||
|
|
||||||
|
if cause == "" {
|
||||||
|
return elem
|
||||||
|
}
|
||||||
|
|
||||||
|
if recv {
|
||||||
|
if isTypeParam(x.typ) {
|
||||||
|
check.errorf(pos, InvalidReceive, invalidOp+"cannot receive from %s: type set contains %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)
|
||||||
|
} 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func isShift(op token.Token) bool {
|
func isShift(op token.Token) bool {
|
||||||
return op == token.SHL || op == token.SHR
|
return op == token.SHL || op == token.SHR
|
||||||
}
|
}
|
||||||
|
@ -466,21 +466,9 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
|||||||
if ch.mode == invalid || val.mode == invalid {
|
if ch.mode == invalid || val.mode == invalid {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
u := coreType(ch.typ)
|
if elem := check.chanElem(inNode(s, s.Arrow), &ch, false); elem != nil {
|
||||||
if u == nil {
|
check.assignment(&val, elem, "send")
|
||||||
check.errorf(inNode(s, s.Arrow), InvalidSend, invalidOp+"cannot send to %s: no core type", &ch)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
uch, _ := u.(*Chan)
|
|
||||||
if uch == nil {
|
|
||||||
check.errorf(inNode(s, s.Arrow), InvalidSend, invalidOp+"cannot send to non-channel %s", &ch)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if uch.dir == RecvOnly {
|
|
||||||
check.errorf(inNode(s, s.Arrow), InvalidSend, invalidOp+"cannot send to receive-only channel %s", &ch)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
check.assignment(&val, uch.elem, "send")
|
|
||||||
|
|
||||||
case *ast.IncDecStmt:
|
case *ast.IncDecStmt:
|
||||||
var op token.Token
|
var op token.Token
|
||||||
|
@ -12,11 +12,11 @@ type C4 interface{ chan int | chan<- int }
|
|||||||
type C5[T any] interface{ ~chan T | <-chan T }
|
type C5[T any] interface{ ~chan T | <-chan T }
|
||||||
|
|
||||||
func _[T any](ch T) {
|
func _[T any](ch T) {
|
||||||
<-ch // ERRORx `cannot receive from ch .* \(no core type\)`
|
<-ch // ERRORx `cannot receive from ch .*: type set contains no specific channel type`
|
||||||
}
|
}
|
||||||
|
|
||||||
func _[T C0](ch T) {
|
func _[T C0](ch T) {
|
||||||
<-ch // ERROR "cannot receive from non-channel ch"
|
<-ch // ERRORx `cannot receive from ch .*: type set contains non-channel int`
|
||||||
}
|
}
|
||||||
|
|
||||||
func _[T C1](ch T) {
|
func _[T C1](ch T) {
|
||||||
@ -28,11 +28,11 @@ func _[T C2](ch T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func _[T C3](ch T) {
|
func _[T C3](ch T) {
|
||||||
<-ch // ERRORx `cannot receive from ch .* \(no core type\)`
|
<-ch // ERRORx `cannot receive from ch .*: type set contains channels with different element types int and float32`
|
||||||
}
|
}
|
||||||
|
|
||||||
func _[T C4](ch T) {
|
func _[T C4](ch T) {
|
||||||
<-ch // ERROR "cannot receive from send-only channel"
|
<-ch // ERRORx `cannot receive from ch .*: type set contains send-only channel chan<- int`
|
||||||
}
|
}
|
||||||
|
|
||||||
func _[T C5[X], X any](ch T, x X) {
|
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 }
|
type C5[T any] interface{ ~chan T | chan<- T }
|
||||||
|
|
||||||
func _[T any](ch T) {
|
func _[T any](ch T) {
|
||||||
ch <- /* ERRORx `cannot send to ch .* no core type` */ 0
|
ch <- /* ERRORx `cannot send to ch .*: type set contains no specific channel type` */ 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func _[T C0](ch T) {
|
func _[T C0](ch T) {
|
||||||
ch <- /* ERROR "cannot send to non-channel" */ 0
|
ch <- /* ERRORx `cannot send to ch .*: type set contains non-channel int` */ 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func _[T C1](ch T) {
|
func _[T C1](ch T) {
|
||||||
@ -24,11 +24,11 @@ func _[T C1](ch T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func _[T C2](ch T) {
|
func _[T C2](ch T) {
|
||||||
ch <-/* ERROR "cannot send to receive-only channel" */ 0
|
ch <- /* ERRORx `cannot send to ch .*: type set contains receive-only channel <-chan int` */ 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func _[T C3](ch T) {
|
func _[T C3](ch T) {
|
||||||
ch <- /* ERRORx `cannot send to ch .* no core type` */ 0
|
ch <- /* ERRORx `cannot send to ch .*: type set contains channels with different element types` */ 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func _[T C4](ch T) {
|
func _[T C4](ch T) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user