mirror of https://github.com/golang/go.git
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
This commit is contained in:
parent
86b6990e9e
commit
e27cae74ec
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 */ :
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue