go/types: disable type assertions on variables of generic type

This matches the latest design draft. It's unclear what the
correct approach is here, and there is an easy work-around:
just assign to an interface variable first.

Passes all.bash.

Change-Id: Ic1d6ed3de6333505e2044a318ea05fb984b408cf
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go2-dev/+/764001
Reviewed-by: Robert Griesemer <gri@google.com>
This commit is contained in:
Robert Griesemer 2020-06-05 11:37:26 -07:00
parent 35eefe6b79
commit d3a2e0e245
3 changed files with 81 additions and 49 deletions

View File

@ -1529,12 +1529,15 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
switch t := x.typ.Under().(type) {
case *Interface:
xtyp = t
case *TypeParam:
// TODO(gri) disable for now
xtyp = t.Bound()
strict = true
// Disabled for now. It is not clear what the right approach is
// here. Also, the implementation below is inconsistent because
// the underlying type of a type parameter is either itself or
// a sum type if the corresponding type bound contains a type list.
// case *TypeParam:
// xtyp = t.Bound()
// strict = true
default:
check.invalidOp(x.pos(), "%s is not an interface or generic type", x)
check.invalidOp(x.pos(), "%s is not an interface type", x)
goto Error
}
// x.(type) expressions are handled explicitly in type switches

View File

@ -615,12 +615,12 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
switch t := x.typ.Under().(type) {
case *Interface:
xtyp = t
case *TypeParam:
// TODO(gri) disable
xtyp = t.Bound()
strict = true
// Disabled for now. See comment in the implementation of type assertions (expr.go).
// case *TypeParam:
// xtyp = t.Bound()
// strict = true
default:
check.errorf(x.pos(), "%s is not an interface or generic type", &x)
check.errorf(x.pos(), "%s is not an interface type", &x)
return
}

View File

@ -4,7 +4,7 @@
package p
import "io" // for type assertion tests
// import "io" // for type assertion tests
func identity(type T)(x T) T { return x }
@ -323,49 +323,78 @@ func (_ R2(X, Y)) m1(X)
func (_ R2(X, Y)) m2(X) Y
// type assertions and type switches over generic types
// NOTE: These are currently disabled because it's unclear what the correct
// approach is, and one can always work around by assigning the variable to
// an interface first.
// ReadByte1 corresponds to the ReadByte example in the contracts draft design.
func ReadByte1(type T io.Reader)(r T) (byte, error) {
if br, ok := r.(io.ByteReader); ok {
return br.ReadByte()
// // ReadByte1 corresponds to the ReadByte example in the contracts draft design.
// func ReadByte1(type T io.Reader)(r T) (byte, error) {
// if br, ok := r.(io.ByteReader); ok {
// return br.ReadByte()
// }
// var b [1]byte
// _, err := r.Read(b[:])
// return b[0], err
// }
//
// // ReadBytes2 is like ReadByte1 but uses a type switch instead.
// func ReadByte2(type T io.Reader)(r T) (byte, error) {
// switch br := r.(type) {
// case io.ByteReader:
// return br.ReadByte()
// }
// var b [1]byte
// _, err := r.Read(b[:])
// return b[0], err
// }
//
// // type assertions and type switches over generic types are strict
// type I3 interface {
// m(int)
// }
//
// type I4 interface {
// m() int // different signature from I3.m
// }
//
// func _(type T I3)(x I3, p T) {
// // type assertions and type switches over interfaces are not strict
// _ = x.(I4)
// switch x.(type) {
// case I4:
// }
//
// // type assertions and type switches over generic types are strict
// _ = p /* ERROR cannot have dynamic type I4 */.(I4)
// switch p.(type) {
// case I4 /* ERROR cannot have dynamic type I4 */ :
// }
// }
// type assertions and type switches over generic types lead to errors for now
func _(type T)(x T) {
_ = x /* ERROR not an interface */ .(int)
switch x /* ERROR not an interface */ .(type) {
}
// work-around
var t interface{} = x
_ = t.(int)
switch t.(type) {
}
var b [1]byte
_, err := r.Read(b[:])
return b[0], err
}
// ReadBytes2 is like ReadByte1 but uses a type switch instead.
func ReadByte2(type T io.Reader)(r T) (byte, error) {
switch br := r.(type) {
case io.ByteReader:
return br.ReadByte()
}
var b [1]byte
_, err := r.Read(b[:])
return b[0], err
}
func _(type T interface{type int})(x T) {
_ = x /* ERROR not an interface */ .(int)
switch x /* ERROR not an interface */ .(type) {
}
// type assertions and type switches over generic types are strict
type I3 interface {
m(int)
}
type I4 interface {
m() int // different signature from I3.m
}
func _(type T I3)(x I3, p T) {
// type assertions and type switches over interfaces are not strict
_ = x.(I4)
switch x.(type) {
case I4:
}
// type assertions and type switches over generic types are strict
_ = p /* ERROR cannot have dynamic type I4 */.(I4)
switch p.(type) {
case I4 /* ERROR cannot have dynamic type I4 */ :
}
// work-around
var t interface{} = x
_ = t.(int)
switch t.(type) {
}
}
// error messages related to type bounds mention those bounds