1
0
mirror of https://github.com/golang/go synced 2024-11-24 04:50:07 -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:
Robert Findley 2021-11-15 22:42:41 -05:00
parent 46e98d489f
commit 289c930750
7 changed files with 98 additions and 53 deletions

View File

@ -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
}

View File

@ -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 {
uch, _ := u.(*Chan)
if uch == nil {
check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to non-channel %s", &ch)
return false
}
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
}) {
u := structuralType(ch.typ)
if u == nil {
check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to %s: no structural type", &ch)
return
}
check.assignment(&val, elem, "send")
uch, _ := u.(*Chan)
if uch == nil {
check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to non-channel %s", &ch)
return
}
if uch.dir == RecvOnly {
check.invalidOp(inNode(s, s.Arrow), _InvalidSend, "cannot send to receive-only channel %s", &ch)
return
}
check.assignment(&val, uch.elem, "send")
case *ast.IncDecStmt:
var op token.Token

View File

@ -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 */ {}

View File

@ -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) {

View 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) }

View File

@ -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) {

View File

@ -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) {
return false
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) {
return false
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
}) {