Skip to content

Commit a4fcfaa

Browse files
griesemergopherbot
authored andcommitted
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 <[email protected]> Reviewed-by: Robert Findley <[email protected]> Reviewed-by: Robert Griesemer <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent ff627d2 commit a4fcfaa

File tree

6 files changed

+134
-73
lines changed

6 files changed

+134
-73
lines changed

src/cmd/compile/internal/types2/expr.go

Lines changed: 61 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -148,26 +148,13 @@ func (check *Checker) unary(x *operand, e *syntax.Operation) {
148148
return
149149

150150
case syntax.Recv:
151-
u := coreType(x.typ)
152-
if u == nil {
153-
check.errorf(x, InvalidReceive, invalidOp+"cannot receive from %s (no core type)", x)
154-
x.mode = invalid
151+
if elem := check.chanElem(x, x, true); elem != nil {
152+
x.mode = commaok
153+
x.typ = elem
154+
check.hasCallOrRecv = true
155155
return
156156
}
157-
ch, _ := u.(*Chan)
158-
if ch == nil {
159-
check.errorf(x, InvalidReceive, invalidOp+"cannot receive from non-channel %s", x)
160-
x.mode = invalid
161-
return
162-
}
163-
if ch.dir == SendOnly {
164-
check.errorf(x, InvalidReceive, invalidOp+"cannot receive from send-only channel %s", x)
165-
x.mode = invalid
166-
return
167-
}
168-
x.mode = commaok
169-
x.typ = ch.elem
170-
check.hasCallOrRecv = true
157+
x.mode = invalid
171158
return
172159

173160
case syntax.Tilde:
@@ -205,6 +192,62 @@ func (check *Checker) unary(x *operand, e *syntax.Operation) {
205192
// x.typ remains unchanged
206193
}
207194

195+
// chanElem returns the channel element type of x for a receive from x (recv == true)
196+
// or send to x (recv == false) operation. If the operation is not valid, chanElem
197+
// reports an error and returns nil.
198+
func (check *Checker) chanElem(pos poser, x *operand, recv bool) Type {
199+
var elem Type
200+
var cause string
201+
typeset(x.typ, func(t, u Type) bool {
202+
if u == nil {
203+
// Type set contains no explicit terms.
204+
// It is either empty or contains all types (any)
205+
cause = "no specific channel type"
206+
return false
207+
}
208+
ch, _ := u.(*Chan)
209+
if ch == nil {
210+
cause = check.sprintf("non-channel %s", t)
211+
return false
212+
}
213+
if recv && ch.dir == SendOnly {
214+
cause = check.sprintf("send-only channel %s", t)
215+
return false
216+
}
217+
if !recv && ch.dir == RecvOnly {
218+
cause = check.sprintf("receive-only channel %s", t)
219+
return false
220+
}
221+
if elem != nil && !Identical(elem, ch.elem) {
222+
cause = check.sprintf("channels with different element types %s and %s", elem, ch.elem)
223+
return false
224+
}
225+
elem = ch.elem
226+
return true
227+
})
228+
229+
if cause == "" {
230+
return elem
231+
}
232+
233+
if recv {
234+
if isTypeParam(x.typ) {
235+
check.errorf(pos, InvalidReceive, invalidOp+"cannot receive from %s: type set contains %s", x, cause)
236+
} else {
237+
// In this case, only the non-channel and send-only channel error are possible.
238+
check.errorf(pos, InvalidReceive, invalidOp+"cannot receive from %s %s", cause, x)
239+
}
240+
} else {
241+
if isTypeParam(x.typ) {
242+
check.errorf(pos, InvalidSend, invalidOp+"cannot send to %s: type set contains %s", x, cause)
243+
} else {
244+
// In this case, only the non-channel and receive-only channel error are possible.
245+
check.errorf(pos, InvalidSend, invalidOp+"cannot send to %s %s", cause, x)
246+
}
247+
}
248+
return nil
249+
}
250+
208251
func isShift(op syntax.Operator) bool {
209252
return op == syntax.Shl || op == syntax.Shr
210253
}

src/cmd/compile/internal/types2/stmt.go

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -465,21 +465,9 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) {
465465
if ch.mode == invalid || val.mode == invalid {
466466
return
467467
}
468-
u := coreType(ch.typ)
469-
if u == nil {
470-
check.errorf(s, InvalidSend, invalidOp+"cannot send to %s: no core type", &ch)
471-
return
472-
}
473-
uch, _ := u.(*Chan)
474-
if uch == nil {
475-
check.errorf(s, InvalidSend, invalidOp+"cannot send to non-channel %s", &ch)
476-
return
477-
}
478-
if uch.dir == RecvOnly {
479-
check.errorf(s, InvalidSend, invalidOp+"cannot send to receive-only channel %s", &ch)
480-
return
468+
if elem := check.chanElem(s, &ch, false); elem != nil {
469+
check.assignment(&val, elem, "send")
481470
}
482-
check.assignment(&val, uch.elem, "send")
483471

484472
case *syntax.AssignStmt:
485473
if s.Rhs == nil {

src/go/types/expr.go

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -147,27 +147,13 @@ func (check *Checker) unary(x *operand, e *ast.UnaryExpr) {
147147
return
148148

149149
case token.ARROW:
150-
u := coreType(x.typ)
151-
if u == nil {
152-
check.errorf(x, InvalidReceive, invalidOp+"cannot receive from %s (no core type)", x)
153-
x.mode = invalid
150+
if elem := check.chanElem(x, x, true); elem != nil {
151+
x.mode = commaok
152+
x.typ = elem
153+
check.hasCallOrRecv = true
154154
return
155155
}
156-
ch, _ := u.(*Chan)
157-
if ch == nil {
158-
check.errorf(x, InvalidReceive, invalidOp+"cannot receive from non-channel %s", x)
159-
x.mode = invalid
160-
return
161-
}
162-
if ch.dir == SendOnly {
163-
check.errorf(x, InvalidReceive, invalidOp+"cannot receive from send-only channel %s", x)
164-
x.mode = invalid
165-
return
166-
}
167-
168-
x.mode = commaok
169-
x.typ = ch.elem
170-
check.hasCallOrRecv = true
156+
x.mode = invalid
171157
return
172158

173159
case token.TILDE:
@@ -205,6 +191,62 @@ func (check *Checker) unary(x *operand, e *ast.UnaryExpr) {
205191
// x.typ remains unchanged
206192
}
207193

194+
// chanElem returns the channel element type of x for a receive from x (recv == true)
195+
// or send to x (recv == false) operation. If the operation is not valid, chanElem
196+
// reports an error and returns nil.
197+
func (check *Checker) chanElem(pos positioner, x *operand, recv bool) Type {
198+
var elem Type
199+
var cause string
200+
typeset(x.typ, func(t, u Type) bool {
201+
if u == nil {
202+
// Type set contains no explicit terms.
203+
// It is either empty or contains all types (any)
204+
cause = "no specific channel type"
205+
return false
206+
}
207+
ch, _ := u.(*Chan)
208+
if ch == nil {
209+
cause = check.sprintf("non-channel %s", t)
210+
return false
211+
}
212+
if recv && ch.dir == SendOnly {
213+
cause = check.sprintf("send-only channel %s", t)
214+
return false
215+
}
216+
if !recv && ch.dir == RecvOnly {
217+
cause = check.sprintf("receive-only channel %s", t)
218+
return false
219+
}
220+
if elem != nil && !Identical(elem, ch.elem) {
221+
cause = check.sprintf("channels with different element types %s and %s", elem, ch.elem)
222+
return false
223+
}
224+
elem = ch.elem
225+
return true
226+
})
227+
228+
if cause == "" {
229+
return elem
230+
}
231+
232+
if recv {
233+
if isTypeParam(x.typ) {
234+
check.errorf(pos, InvalidReceive, invalidOp+"cannot receive from %s: type set contains %s", x, cause)
235+
} else {
236+
// In this case, only the non-channel and send-only channel error are possible.
237+
check.errorf(pos, InvalidReceive, invalidOp+"cannot receive from %s %s", cause, x)
238+
}
239+
} else {
240+
if isTypeParam(x.typ) {
241+
check.errorf(pos, InvalidSend, invalidOp+"cannot send to %s: type set contains %s", x, cause)
242+
} else {
243+
// In this case, only the non-channel and receive-only channel error are possible.
244+
check.errorf(pos, InvalidSend, invalidOp+"cannot send to %s %s", cause, x)
245+
}
246+
}
247+
return nil
248+
}
249+
208250
func isShift(op token.Token) bool {
209251
return op == token.SHL || op == token.SHR
210252
}

src/go/types/stmt.go

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -466,21 +466,9 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
466466
if ch.mode == invalid || val.mode == invalid {
467467
return
468468
}
469-
u := coreType(ch.typ)
470-
if u == nil {
471-
check.errorf(inNode(s, s.Arrow), InvalidSend, invalidOp+"cannot send to %s: no core type", &ch)
472-
return
473-
}
474-
uch, _ := u.(*Chan)
475-
if uch == nil {
476-
check.errorf(inNode(s, s.Arrow), InvalidSend, invalidOp+"cannot send to non-channel %s", &ch)
477-
return
478-
}
479-
if uch.dir == RecvOnly {
480-
check.errorf(inNode(s, s.Arrow), InvalidSend, invalidOp+"cannot send to receive-only channel %s", &ch)
481-
return
469+
if elem := check.chanElem(inNode(s, s.Arrow), &ch, false); elem != nil {
470+
check.assignment(&val, elem, "send")
482471
}
483-
check.assignment(&val, uch.elem, "send")
484472

485473
case *ast.IncDecStmt:
486474
var op token.Token

src/internal/types/testdata/fixedbugs/issue43671.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ type C4 interface{ chan int | chan<- int }
1212
type C5[T any] interface{ ~chan T | <-chan T }
1313

1414
func _[T any](ch T) {
15-
<-ch // ERRORx `cannot receive from ch .* \(no core type\)`
15+
<-ch // ERRORx `cannot receive from ch .*: type set contains no specific channel type`
1616
}
1717

1818
func _[T C0](ch T) {
19-
<-ch // ERROR "cannot receive from non-channel ch"
19+
<-ch // ERRORx `cannot receive from ch .*: type set contains non-channel int`
2020
}
2121

2222
func _[T C1](ch T) {
@@ -28,11 +28,11 @@ func _[T C2](ch T) {
2828
}
2929

3030
func _[T C3](ch T) {
31-
<-ch // ERRORx `cannot receive from ch .* \(no core type\)`
31+
<-ch // ERRORx `cannot receive from ch .*: type set contains channels with different element types int and float32`
3232
}
3333

3434
func _[T C4](ch T) {
35-
<-ch // ERROR "cannot receive from send-only channel"
35+
<-ch // ERRORx `cannot receive from ch .*: type set contains send-only channel chan<- int`
3636
}
3737

3838
func _[T C5[X], X any](ch T, x X) {

src/internal/types/testdata/fixedbugs/issue47115.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,23 @@ type C4 interface{ chan int | chan<- int }
1212
type C5[T any] interface{ ~chan T | chan<- T }
1313

1414
func _[T any](ch T) {
15-
ch <- /* ERRORx `cannot send to ch .* no core type` */ 0
15+
ch <- /* ERRORx `cannot send to ch .*: type set contains no specific channel type` */ 0
1616
}
1717

1818
func _[T C0](ch T) {
19-
ch <- /* ERROR "cannot send to non-channel" */ 0
19+
ch <- /* ERRORx `cannot send to ch .*: type set contains non-channel int` */ 0
2020
}
2121

2222
func _[T C1](ch T) {
2323
ch <- 0
2424
}
2525

2626
func _[T C2](ch T) {
27-
ch <-/* ERROR "cannot send to receive-only channel" */ 0
27+
ch <- /* ERRORx `cannot send to ch .*: type set contains receive-only channel <-chan int` */ 0
2828
}
2929

3030
func _[T C3](ch T) {
31-
ch <- /* ERRORx `cannot send to ch .* no core type` */ 0
31+
ch <- /* ERRORx `cannot send to ch .*: type set contains channels with different element types` */ 0
3232
}
3333

3434
func _[T C4](ch T) {

0 commit comments

Comments
 (0)