From d3a2e0e24516204759f72cb5e2cd6970fbf4a82b Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Fri, 5 Jun 2020 11:37:26 -0700 Subject: [PATCH] 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 --- src/go/types/expr.go | 13 ++-- src/go/types/stmt.go | 10 +-- src/go/types/testdata/typeparams.go2 | 107 +++++++++++++++++---------- 3 files changed, 81 insertions(+), 49 deletions(-) diff --git a/src/go/types/expr.go b/src/go/types/expr.go index f75593fd9a..a348f84295 100644 --- a/src/go/types/expr.go +++ b/src/go/types/expr.go @@ -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 diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go index bb6e6b6766..354ab7b3dc 100644 --- a/src/go/types/stmt.go +++ b/src/go/types/stmt.go @@ -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 } diff --git a/src/go/types/testdata/typeparams.go2 b/src/go/types/testdata/typeparams.go2 index af4ee85c8a..87f213a173 100644 --- a/src/go/types/testdata/typeparams.go2 +++ b/src/go/types/testdata/typeparams.go2 @@ -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