mirror of
https://github.com/golang/go
synced 2024-11-24 08:10:09 -07:00
go/types: slightly relax notion of structural type
This is a port of CL 363075 from types2 to go/types, adjusted for the different error reporting API, and to adjust positions of error messages in tests. Change-Id: Ic6bfedf1152eff94bad20725b56e6ba804b2e3e8 Reviewed-on: https://go-review.googlesource.com/c/go/+/363991 Trust: Robert Findley <rfindley@google.com> Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
46e98d489f
commit
289c930750
@ -174,29 +174,26 @@ func (check *Checker) unary(x *operand, e *ast.UnaryExpr) {
|
||||
return
|
||||
|
||||
case token.ARROW:
|
||||
var elem Type
|
||||
if !underIs(x.typ, func(u Type) bool {
|
||||
ch, _ := u.(*Chan)
|
||||
if ch == nil {
|
||||
check.invalidOp(x, _InvalidReceive, "cannot receive from non-channel %s", x)
|
||||
return false
|
||||
}
|
||||
if ch.dir == SendOnly {
|
||||
check.invalidOp(x, _InvalidReceive, "cannot receive from send-only channel %s", x)
|
||||
return false
|
||||
}
|
||||
if elem != nil && !Identical(ch.elem, elem) {
|
||||
check.invalidOp(x, _InvalidReceive, "channels of %s must have the same element type", x)
|
||||
return false
|
||||
}
|
||||
elem = ch.elem
|
||||
return true
|
||||
}) {
|
||||
u := structuralType(x.typ)
|
||||
if u == nil {
|
||||
check.invalidOp(x, _InvalidReceive, "cannot receive from %s: no structural type", x)
|
||||
x.mode = invalid
|
||||
return
|
||||
}
|
||||
ch, _ := u.(*Chan)
|
||||
if ch == nil {
|
||||
check.invalidOp(x, _InvalidReceive, "cannot receive from non-channel %s", x)
|
||||
x.mode = invalid
|
||||
return
|
||||
}
|
||||
if ch.dir == SendOnly {
|
||||
check.invalidOp(x, _InvalidReceive, "cannot receive from send-only channel %s", x)
|
||||
x.mode = invalid
|
||||
return
|
||||
}
|
||||
|
||||
x.mode = commaok
|
||||
x.typ = elem
|
||||
x.typ = ch.elem
|
||||
check.hasCallOrRecv = true
|
||||
return
|
||||
}
|
||||
|
@ -417,27 +417,21 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
|
||||
if ch.mode == invalid || val.mode == invalid {
|
||||
return
|
||||
}
|
||||
var elem Type
|
||||
if !underIs(ch.typ, func(u Type) bool {
|
||||
u := structuralType(ch.typ)
|
||||
if u == nil {
|
||||
check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to %s: no structural type", &ch)
|
||||
return
|
||||
}
|
||||
uch, _ := u.(*Chan)
|
||||
if uch == nil {
|
||||
check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to non-channel %s", &ch)
|
||||
return false
|
||||
return
|
||||
}
|
||||
if uch.dir == RecvOnly {
|
||||
check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to receive-only channel %s", &ch)
|
||||
return false
|
||||
}
|
||||
if elem != nil && !Identical(uch.elem, elem) {
|
||||
check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "channels of %s must have the same element type", &ch)
|
||||
return false
|
||||
}
|
||||
elem = uch.elem
|
||||
return true
|
||||
}) {
|
||||
return
|
||||
}
|
||||
check.assignment(&val, elem, "send")
|
||||
check.assignment(&val, uch.elem, "send")
|
||||
|
||||
case *ast.IncDecStmt:
|
||||
var op token.Token
|
||||
|
2
src/go/types/testdata/check/typeparams.go2
vendored
2
src/go/types/testdata/check/typeparams.go2
vendored
@ -210,7 +210,7 @@ func _[
|
||||
for _, _ /* ERROR permits only one iteration variable */ = range c1 {}
|
||||
|
||||
var c2 C2
|
||||
for range c2 /* ERROR cannot range over c2.*no structural type */ {}
|
||||
for range c2 {}
|
||||
|
||||
var c3 C3
|
||||
for range c3 /* ERROR receive from send-only channel */ {}
|
||||
|
@ -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 // ERROR cannot receive from non-channel
|
||||
<-ch // ERROR cannot receive from ch .* no structural type
|
||||
}
|
||||
|
||||
func _[T C0](ch T) {
|
||||
<-ch // ERROR cannot receive from non-channel
|
||||
<-ch // ERROR cannot receive from non-channel ch
|
||||
}
|
||||
|
||||
func _[T C1](ch T) {
|
||||
@ -28,7 +28,7 @@ func _[T C2](ch T) {
|
||||
}
|
||||
|
||||
func _[T C3](ch T) {
|
||||
<-ch // ERROR channels of ch .* must have the same element type
|
||||
<-ch // ERROR cannot receive from ch .* no structural type
|
||||
}
|
||||
|
||||
func _[T C4](ch T) {
|
||||
|
17
src/go/types/testdata/fixedbugs/issue45920.go2
vendored
Normal file
17
src/go/types/testdata/fixedbugs/issue45920.go2
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package p
|
||||
|
||||
func f1[T any, C chan T | <-chan T](ch C) {}
|
||||
|
||||
func _(ch chan int) { f1(ch) }
|
||||
func _(ch <-chan int) { f1(ch) }
|
||||
func _(ch chan<- int) { f1 /* ERROR chan<- int does not satisfy chan T\|<-chan T */ (ch) }
|
||||
|
||||
func f2[T any, C chan T | chan<- T](ch C) {}
|
||||
|
||||
func _(ch chan int) { f2(ch) }
|
||||
func _(ch <-chan int) { f2 /* ERROR <-chan int does not satisfy chan T\|chan<- T */ (ch)}
|
||||
func _(ch chan<- int) { f2(ch) }
|
@ -12,7 +12,7 @@ type C4 interface{ chan int | chan<- int }
|
||||
type C5[T any] interface{ ~chan T | chan<- T }
|
||||
|
||||
func _[T any](ch T) {
|
||||
ch <- /* ERROR cannot send to non-channel */ 0
|
||||
ch <- /* ERROR cannot send to ch .* no structural type */ 0
|
||||
}
|
||||
|
||||
func _[T C0](ch T) {
|
||||
@ -28,7 +28,7 @@ func _[T C2](ch T) {
|
||||
}
|
||||
|
||||
func _[T C3](ch T) {
|
||||
ch <- /* ERROR channels of ch .* must have the same element type */ 0
|
||||
ch <- /* ERROR cannot send to ch .* no structural type */ 0
|
||||
}
|
||||
|
||||
func _[T C4](ch T) {
|
||||
|
@ -27,17 +27,51 @@ func under(t Type) Type {
|
||||
return t
|
||||
}
|
||||
|
||||
// If x and y are identical, match returns x.
|
||||
// If x and y are identical channels but for their direction
|
||||
// and one of them is unrestricted, match returns the channel
|
||||
// with the restricted direction.
|
||||
// In all other cases, match returns nil.
|
||||
func match(x, y Type) Type {
|
||||
// Common case: we don't have channels.
|
||||
if Identical(x, y) {
|
||||
return x
|
||||
}
|
||||
|
||||
// We may have channels that differ in direction only.
|
||||
if x, _ := x.(*Chan); x != nil {
|
||||
if y, _ := y.(*Chan); y != nil && Identical(x.elem, y.elem) {
|
||||
// We have channels that differ in direction only.
|
||||
// If there's an unrestricted channel, select the restricted one.
|
||||
switch {
|
||||
case x.dir == SendRecv:
|
||||
return y
|
||||
case y.dir == SendRecv:
|
||||
return x
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// types are different
|
||||
return nil
|
||||
}
|
||||
|
||||
// If typ is a type parameter, structuralType returns the single underlying
|
||||
// type of all types in the corresponding type constraint if it exists,
|
||||
// or nil otherwise. If typ is not a type parameter, structuralType returns
|
||||
// the underlying type.
|
||||
// type of all types in the corresponding type constraint 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.
|
||||
// If typ is not a type parameter, structuralType returns the underlying type.
|
||||
func structuralType(typ Type) Type {
|
||||
var su Type
|
||||
if underIs(typ, func(u Type) bool {
|
||||
if su != nil && !Identical(su, u) {
|
||||
if su != nil {
|
||||
u = match(su, u)
|
||||
if u == nil {
|
||||
return false
|
||||
}
|
||||
// su == nil || Identical(su, u)
|
||||
}
|
||||
// su == nil || match(su, u) != nil
|
||||
su = u
|
||||
return true
|
||||
}) {
|
||||
@ -55,10 +89,13 @@ func structuralString(typ Type) Type {
|
||||
if isString(u) {
|
||||
u = NewSlice(universeByte)
|
||||
}
|
||||
if su != nil && !Identical(su, u) {
|
||||
if su != nil {
|
||||
u = match(su, u)
|
||||
if u == nil {
|
||||
return false
|
||||
}
|
||||
// su == nil || Identical(su, u)
|
||||
}
|
||||
// su == nil || match(su, u) != nil
|
||||
su = u
|
||||
return true
|
||||
}) {
|
||||
|
Loading…
Reference in New Issue
Block a user