From e27cae74ec1d7d07692981249e2d62696fa40fae Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Wed, 26 Feb 2020 14:26:53 -0800 Subject: [PATCH] go/types: assertions/type switches on generic types are strict Assertions/type switches that are guaranteed to fail on generic types are not permitted (in contrast to regular type assertions and type switches). Change-Id: Iaa5b96f094585cb206fdadaa501445f96f26c166 --- src/go/types/api.go | 2 +- src/go/types/check.go | 4 ++-- src/go/types/expr.go | 8 +++++--- src/go/types/lookup.go | 6 ++++-- src/go/types/stmt.go | 8 +++++--- src/go/types/testdata/typeparams.go2 | 23 +++++++++++++++++++++++ 6 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/go/types/api.go b/src/go/types/api.go index fcb7d025dc..71f93e236d 100644 --- a/src/go/types/api.go +++ b/src/go/types/api.go @@ -366,7 +366,7 @@ func (conf *Config) Check(path string, fset *token.FileSet, files []*ast.File, i // AssertableTo reports whether a value of type V can be asserted to have type T. func AssertableTo(V *Interface, T Type) bool { - m, _ := (*Checker)(nil).assertableTo(V, T) + m, _ := (*Checker)(nil).assertableTo(V, T, false) return m == nil } diff --git a/src/go/types/check.go b/src/go/types/check.go index 77f191b8ec..9be79f2503 100644 --- a/src/go/types/check.go +++ b/src/go/types/check.go @@ -17,7 +17,7 @@ import ( // debugging/development support const debug = true // leave on during development -// If strict is set, the type-checker enforces additional +// If forceStrict is set, the type-checker enforces additional // rules not specified by the Go 1 spec, but which will // catch guaranteed run-time errors if the respective // code is executed. In other words, programs passing in @@ -28,7 +28,7 @@ const debug = true // leave on during development // is invalid if any (statically known) method that exists // for both x and T have different signatures. // -const strict = false +const forceStrict = false // If methodTypeParamsOk is set, type parameters are // permitted in method declarations (in interfaces, too). diff --git a/src/go/types/expr.go b/src/go/types/expr.go index 46b899be71..a58a432e34 100644 --- a/src/go/types/expr.go +++ b/src/go/types/expr.go @@ -1523,11 +1523,13 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { goto Error } var xtyp *Interface + var strict bool switch t := x.typ.Underlying().(type) { case *Interface: xtyp = t case *TypeParam: xtyp = t.Interface() + strict = true default: check.invalidOp(x.pos(), "%s is not an interface or generic type", x) goto Error @@ -1541,7 +1543,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind { if T == Typ[Invalid] { goto Error } - check.typeAssertion(x.pos(), x, xtyp, T) + check.typeAssertion(x.pos(), x, xtyp, T, strict) x.mode = commaok x.typ = T @@ -1639,8 +1641,8 @@ func keyVal(x constant.Value) interface{} { } // typeAssertion checks that x.(T) is legal; xtyp must be the type of x. -func (check *Checker) typeAssertion(pos token.Pos, x *operand, xtyp *Interface, T Type) { - method, wrongType := check.assertableTo(xtyp, T) +func (check *Checker) typeAssertion(pos token.Pos, x *operand, xtyp *Interface, T Type, strict bool) { + method, wrongType := check.assertableTo(xtyp, T, strict) if method == nil { return } diff --git a/src/go/types/lookup.go b/src/go/types/lookup.go index 8d9dda69a1..e06c0778ef 100644 --- a/src/go/types/lookup.go +++ b/src/go/types/lookup.go @@ -390,11 +390,13 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, // method required by V and whether it is missing or just has the wrong type. // The receiver may be nil if assertableTo is invoked through an exported API call // (such as AssertableTo), i.e., when all methods have been type-checked. -func (check *Checker) assertableTo(V *Interface, T Type) (method, wrongType *Func) { +// If strict (or the global constant forceStrict) is set, assertions are strict; +// i.e., assertions that are guaranteed to fail are not permitted. +func (check *Checker) assertableTo(V *Interface, T Type, strict bool) (method, wrongType *Func) { // no static check is required if T is an interface // spec: "If T is an interface type, x.(T) asserts that the // dynamic type of x implements the interface T." - if _, ok := T.Underlying().(*Interface); ok && !strict { + if _, ok := T.Underlying().(*Interface); ok && !(strict || forceStrict) { return } return check.missingMethod(T, V, false) diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go index fb4e9ed741..f348c4bb1b 100644 --- a/src/go/types/stmt.go +++ b/src/go/types/stmt.go @@ -265,7 +265,7 @@ L: } } -func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []ast.Expr, seen map[Type]token.Pos) (T Type) { +func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []ast.Expr, seen map[Type]token.Pos, strict bool) (T Type) { L: for _, e := range types { T = check.typOrNil(e) @@ -288,7 +288,7 @@ L: } seen[T] = e.Pos() if T != nil { - check.typeAssertion(e.Pos(), x, xtyp, T) + check.typeAssertion(e.Pos(), x, xtyp, T, strict) } } return @@ -611,11 +611,13 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) { return } var xtyp *Interface + var strict bool switch t := x.typ.Underlying().(type) { case *Interface: xtyp = t case *TypeParam: xtyp = t.Interface() + strict = true default: check.errorf(x.pos(), "%s is not an interface or generic type", &x) return @@ -632,7 +634,7 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) { continue } // Check each type in this type switch case. - T := check.caseTypes(&x, xtyp, clause.List, seen) + T := check.caseTypes(&x, xtyp, clause.List, seen, strict) check.openScope(clause, "case") // If lhs exists, declare a corresponding variable in the case-local scope. if lhs != nil { diff --git a/src/go/types/testdata/typeparams.go2 b/src/go/types/testdata/typeparams.go2 index e2ed36d6f8..97ecb271da 100644 --- a/src/go/types/testdata/typeparams.go2 +++ b/src/go/types/testdata/typeparams.go2 @@ -340,3 +340,26 @@ func ReadByte2(type T io.Reader)(r T) (byte, error) { _, 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 */ : + } +} \ No newline at end of file