mirror of https://github.com/golang/go.git
cmd/compile/internal/types2: slightly relax notion of structural type
If we have all channel types in a constraint, there is no structural type if they don't all have the same channel direction (and identical element types, of course). By allowing different channel types for the purposes of the structural type, as long as there is not a send-only _and_ a receive- only channel in the type set, we make it possible to find a useful, if restricted by channel direction, structural type where before there was none. So if we have unrestricted and send-only channels, the structural type is the send-only channel, and vice versa. For all operations on channels that rely on a structural type, it's always ok to have an unrestricted channel, so this is not affecting their behavior. But it makes those operations more flexible in the presence of type parameters containing mixed channel types. For constraint type inference, where we currently may not infer a channel at all, this change allows us to infer a more restricted channel (send- or receive-only). If the inferred channel type is a valid type argument we win; if not we haven't lost anything. Use structuralType for send and receive operations and adjust related error messages (the error message that change are the ones involving type parameters, so historic error messages are preserved). Fixes #45920. Change-Id: If3a64d29c37e7734d3163df330f8b02dd032bc60 Reviewed-on: https://go-review.googlesource.com/c/go/+/363075 Trust: Robert Griesemer <gri@golang.org> Run-TryBot: Robert Griesemer <gri@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
229b909313
commit
96c94c2c83
|
|
@ -25,10 +25,12 @@ func AsTypeParam(t Type) *TypeParam {
|
|||
return u
|
||||
}
|
||||
|
||||
// If t is a type parameter, StructuralType returns the single underlying
|
||||
// type of all types in the type parameter's type constraint if it exists,
|
||||
// or nil otherwise. If t is not a type parameter, StructuralType returns
|
||||
// the underlying type of t.
|
||||
// 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 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(t Type) Type {
|
||||
return structuralType(t)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -187,29 +187,25 @@ func (check *Checker) unary(x *operand, e *syntax.Operation) {
|
|||
return
|
||||
|
||||
case syntax.Recv:
|
||||
var elem Type
|
||||
if !underIs(x.typ, func(u Type) bool {
|
||||
ch, _ := u.(*Chan)
|
||||
if ch == nil {
|
||||
check.errorf(x, invalidOp+"cannot receive from non-channel %s", x)
|
||||
return false
|
||||
}
|
||||
if ch.dir == SendOnly {
|
||||
check.errorf(x, invalidOp+"cannot receive from send-only channel %s", x)
|
||||
return false
|
||||
}
|
||||
if elem != nil && !Identical(ch.elem, elem) {
|
||||
check.errorf(x, invalidOp+"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.errorf(x, invalidOp+"cannot receive from %s: no structural type", x)
|
||||
x.mode = invalid
|
||||
return
|
||||
}
|
||||
ch, _ := u.(*Chan)
|
||||
if ch == nil {
|
||||
check.errorf(x, invalidOp+"cannot receive from non-channel %s", x)
|
||||
x.mode = invalid
|
||||
return
|
||||
}
|
||||
if ch.dir == SendOnly {
|
||||
check.errorf(x, invalidOp+"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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -408,27 +408,21 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.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.errorf(s, invalidOp+"cannot send to non-channel %s", &ch)
|
||||
return false
|
||||
}
|
||||
if uch.dir == RecvOnly {
|
||||
check.errorf(s, invalidOp+"cannot send to receive-only channel %s", &ch)
|
||||
return false
|
||||
}
|
||||
if elem != nil && !Identical(uch.elem, elem) {
|
||||
check.errorf(s, invalidOp+"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.errorf(s, invalidOp+"cannot send to %s: no structural type", &ch)
|
||||
return
|
||||
}
|
||||
check.assignment(&val, elem, "send")
|
||||
uch, _ := u.(*Chan)
|
||||
if uch == nil {
|
||||
check.errorf(s, invalidOp+"cannot send to non-channel %s", &ch)
|
||||
return
|
||||
}
|
||||
if uch.dir == RecvOnly {
|
||||
check.errorf(s, invalidOp+"cannot send to receive-only channel %s", &ch)
|
||||
return
|
||||
}
|
||||
check.assignment(&val, uch.elem, "send")
|
||||
|
||||
case *syntax.AssignStmt:
|
||||
lhs := unpackExpr(s.Lhs)
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
// 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
|
||||
}) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue