diff --git a/src/go/types/NOTES b/src/go/types/NOTES index 389d10d2f7..87bbbec6e9 100644 --- a/src/go/types/NOTES +++ b/src/go/types/NOTES @@ -11,7 +11,6 @@ TODO - use []*TypeParam for tparams in subst? (unclear) - should we use nil instead of &emptyInterface for no type bounds (as an optimization)? - TBD: in prose, should we use "generic" or "parameterized" (try to be consistent) -- don't apply receiver type identifier substitution in place (typexpr.go) ---------------------------------------------------------------------------------------------------- KNOWN ISSUES diff --git a/src/go/types/expr.go b/src/go/types/expr.go index 1261f06dfb..46b899be71 100644 --- a/src/go/types/expr.go +++ b/src/go/types/expr.go @@ -1522,9 +1522,14 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { if x.mode == invalid { goto Error } - xtyp, _ := x.typ.Underlying().(*Interface) - if xtyp == nil { - check.invalidOp(x.pos(), "%s is not an interface", x) + var xtyp *Interface + switch t := x.typ.Underlying().(type) { + case *Interface: + xtyp = t + case *TypeParam: + xtyp = t.Interface() + default: + check.invalidOp(x.pos(), "%s is not an interface or generic 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 ad86bd5a82..fb4e9ed741 100644 --- a/src/go/types/stmt.go +++ b/src/go/types/stmt.go @@ -599,7 +599,7 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) { return } - // rhs must be of the form: expr.(type) and expr must be an interface + // rhs must be of the form: expr.(type) and expr must be an interface or generic type expr, _ := rhs.(*ast.TypeAssertExpr) if expr == nil || expr.Type != nil { check.invalidAST(s.Pos(), "incorrect form of type switch guard") @@ -610,9 +610,14 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) { if x.mode == invalid { return } - xtyp, _ := x.typ.Underlying().(*Interface) - if xtyp == nil { - check.errorf(x.pos(), "%s is not an interface", &x) + var xtyp *Interface + switch t := x.typ.Underlying().(type) { + case *Interface: + xtyp = t + case *TypeParam: + xtyp = t.Interface() + default: + check.errorf(x.pos(), "%s is not an interface or generic type", &x) return } diff --git a/src/go/types/testdata/todos.go2 b/src/go/types/testdata/todos.go2 index 675385a51d..7d200c4de5 100644 --- a/src/go/types/testdata/todos.go2 +++ b/src/go/types/testdata/todos.go2 @@ -32,12 +32,6 @@ func _(type T interface{ type *int })(p T) { _ = *p /* ERROR cannot indirect */ } -// type assertions over generic types are not yet supported -func _(type T interface{ type int })(x T) { - _ = T /* ERROR not an expression */ .(int) - _ = x /* ERROR not an interface */ .(int) -} - // calling of a generic variable is not yet supported func _(type T interface{ type func() })(f T) { f /* ERROR cannot call */ () diff --git a/src/go/types/testdata/typeparams.go2 b/src/go/types/testdata/typeparams.go2 index 970db54674..e2ed36d6f8 100644 --- a/src/go/types/testdata/typeparams.go2 +++ b/src/go/types/testdata/typeparams.go2 @@ -4,6 +4,8 @@ package p +import "io" // for type assertion tests + func identity(type T)(x T) T { return x } func _(type)(x int) int @@ -314,4 +316,27 @@ var _ I2(int, float32) = R2(int, float32){} type R2(type P, Q) struct{} func (_ R2(X, Y)) m1(X) -func (_ R2(X, Y)) m2(X) Y \ No newline at end of file +func (_ R2(X, Y)) m2(X) Y + +// type assertions and type switches over generic types + +// 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 +}