}
// AssertableTo reports whether a value of type V can be asserted to have type T.
+// The behavior of AssertableTo is undefined if V is a generalized interface; i.e.,
+// an interface that may only be used as a type constraint in Go code.
func AssertableTo(V *Interface, T Type) bool {
- m, _ := (*Checker)(nil).assertableTo(V, T)
- return m == nil
+ // Checker.newAssertableTo suppresses errors for invalid types, so we need special
+ // handling here.
+ if T.Underlying() == Typ[Invalid] {
+ return false
+ }
+ return (*Checker)(nil).newAssertableTo(V, T) == nil
}
// AssignableTo reports whether a value of type V is assignable to a variable of type T.
conf := Config{Error: func(error) {}}
pkg, _ := conf.Check(f.PkgName.Value, []*syntax.File{f}, nil)
- scope := pkg.Scope()
+ lookup := func(tname string) Type { return pkg.Scope().Lookup(tname).Type() }
var (
- EmptyIface = scope.Lookup("EmptyIface").Type().Underlying().(*Interface)
- I = scope.Lookup("I").Type().(*Named)
+ EmptyIface = lookup("EmptyIface").Underlying().(*Interface)
+ I = lookup("I").(*Named)
II = I.Underlying().(*Interface)
- C = scope.Lookup("C").Type().(*Named)
+ C = lookup("C").(*Named)
CI = C.Underlying().(*Interface)
- Integer = scope.Lookup("Integer").Type().Underlying().(*Interface)
- EmptyTypeSet = scope.Lookup("EmptyTypeSet").Type().Underlying().(*Interface)
- N1 = scope.Lookup("N1").Type()
+ Integer = lookup("Integer").Underlying().(*Interface)
+ EmptyTypeSet = lookup("EmptyTypeSet").Underlying().(*Interface)
+ N1 = lookup("N1")
N1p = NewPointer(N1)
- N2 = scope.Lookup("N2").Type()
+ N2 = lookup("N2")
N2p = NewPointer(N2)
- N3 = scope.Lookup("N3").Type()
- N4 = scope.Lookup("N4").Type()
- Bad = scope.Lookup("Bad").Type()
+ N3 = lookup("N3")
+ N4 = lookup("N4")
+ Bad = lookup("Bad")
)
tests := []struct {
- t Type
- i *Interface
+ V Type
+ T *Interface
want bool
}{
{I, II, true},
}
for _, test := range tests {
- if got := Implements(test.t, test.i); got != test.want {
- t.Errorf("Implements(%s, %s) = %t, want %t", test.t, test.i, got, test.want)
+ if got := Implements(test.V, test.T); got != test.want {
+ t.Errorf("Implements(%s, %s) = %t, want %t", test.V, test.T, got, test.want)
+ }
+
+ // The type assertion x.(T) is valid if T is an interface or if T implements the type of x.
+ // The assertion is never valid if T is a bad type.
+ V := test.T
+ T := test.V
+ want := false
+ if _, ok := T.Underlying().(*Interface); (ok || Implements(T, V)) && T != Bad {
+ want = true
+ }
+ if got := AssertableTo(V, T); got != want {
+ t.Errorf("AssertableTo(%s, %s) = %t, want %t", V, T, got, want)
}
}
}
// debugging/development support
const debug = false // leave on during development
-// 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
-// strict mode are Go 1 compliant, but not all Go 1 programs
-// will pass in strict mode. The additional rules are:
-//
-// - A type assertion x.(T) where T is an interface type
-// is invalid if any (statically known) method that exists
-// for both x and T have different signatures.
-//
-const forceStrict = false
-
// exprInfo stores information about an untyped expression.
type exprInfo struct {
isLhs bool // expression is lhs operand of a shift with delayed type-check
// 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.
-// If the global constant forceStrict is set, assertions that are known to fail
-// are not permitted.
+// TODO(gri) replace calls to this function with calls to newAssertableTo.
func (check *Checker) assertableTo(V *Interface, T Type) (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 IsInterface(T) && !forceStrict {
+ if IsInterface(T) {
return
}
+ // TODO(gri) fix this for generalized interfaces
return check.missingMethod(T, V, false)
}
+// newAssertableTo reports whether a value of type V can be asserted to have type T.
+// It also implements behavior for interfaces that currently are only permitted
+// in constraint position (we have not yet defined that behavior in the spec).
+func (check *Checker) newAssertableTo(V *Interface, T Type) error {
+ // 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 IsInterface(T) {
+ return nil
+ }
+ return check.implements(T, V)
+}
+
// deref dereferences typ if it is a *Pointer and returns its base and true.
// Otherwise it returns (typ, false).
func deref(typ Type) (Type, bool) {
}
// AssertableTo reports whether a value of type V can be asserted to have type T.
+// The behavior of AssertableTo is undefined if V is a generalized interface; i.e.,
+// an interface that may only be used as a type constraint in Go code.
func AssertableTo(V *Interface, T Type) bool {
- m, _ := (*Checker)(nil).assertableTo(V, T)
- return m == nil
+ // Checker.newAssertableTo suppresses errors for invalid types, so we need special
+ // handling here.
+ if T.Underlying() == Typ[Invalid] {
+ return false
+ }
+ return (*Checker)(nil).newAssertableTo(V, T) == nil
}
// AssignableTo reports whether a value of type V is assignable to a variable of type T.
conf := Config{Error: func(error) {}}
pkg, _ := conf.Check(f.Name.Name, fset, []*ast.File{f}, nil)
- scope := pkg.Scope()
+ lookup := func(tname string) Type { return pkg.Scope().Lookup(tname).Type() }
var (
- EmptyIface = scope.Lookup("EmptyIface").Type().Underlying().(*Interface)
- I = scope.Lookup("I").Type().(*Named)
+ EmptyIface = lookup("EmptyIface").Underlying().(*Interface)
+ I = lookup("I").(*Named)
II = I.Underlying().(*Interface)
- C = scope.Lookup("C").Type().(*Named)
+ C = lookup("C").(*Named)
CI = C.Underlying().(*Interface)
- Integer = scope.Lookup("Integer").Type().Underlying().(*Interface)
- EmptyTypeSet = scope.Lookup("EmptyTypeSet").Type().Underlying().(*Interface)
- N1 = scope.Lookup("N1").Type()
+ Integer = lookup("Integer").Underlying().(*Interface)
+ EmptyTypeSet = lookup("EmptyTypeSet").Underlying().(*Interface)
+ N1 = lookup("N1")
N1p = NewPointer(N1)
- N2 = scope.Lookup("N2").Type()
+ N2 = lookup("N2")
N2p = NewPointer(N2)
- N3 = scope.Lookup("N3").Type()
- N4 = scope.Lookup("N4").Type()
- Bad = scope.Lookup("Bad").Type()
+ N3 = lookup("N3")
+ N4 = lookup("N4")
+ Bad = lookup("Bad")
)
tests := []struct {
- t Type
- i *Interface
+ V Type
+ T *Interface
want bool
}{
{I, II, true},
}
for _, test := range tests {
- if got := Implements(test.t, test.i); got != test.want {
- t.Errorf("Implements(%s, %s) = %t, want %t", test.t, test.i, got, test.want)
+ if got := Implements(test.V, test.T); got != test.want {
+ t.Errorf("Implements(%s, %s) = %t, want %t", test.V, test.T, got, test.want)
+ }
+
+ // The type assertion x.(T) is valid if T is an interface or if T implements the type of x.
+ // The assertion is never valid if T is a bad type.
+ V := test.T
+ T := test.V
+ want := false
+ if _, ok := T.Underlying().(*Interface); (ok || Implements(T, V)) && T != Bad {
+ want = true
+ }
+ if got := AssertableTo(V, T); got != want {
+ t.Errorf("AssertableTo(%s, %s) = %t, want %t", V, T, got, want)
}
}
}
compilerErrorMessages = false // match compiler error messages
)
-// 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
-// strict mode are Go 1 compliant, but not all Go 1 programs
-// will pass in strict mode. The additional rules are:
-//
-// - A type assertion x.(T) where T is an interface type
-// is invalid if any (statically known) method that exists
-// for both x and T have different signatures.
-//
-const forceStrict = false
-
// exprInfo stores information about an untyped expression.
type exprInfo struct {
isLhs bool // expression is lhs operand of a shift with delayed type-check
// 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.
-// If the global constant forceStrict is set, assertions that are known to fail
-// are not permitted.
+// TODO(gri) replace calls to this function with calls to newAssertableTo.
func (check *Checker) assertableTo(V *Interface, T Type) (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 IsInterface(T) && !forceStrict {
+ if IsInterface(T) {
return
}
+ // TODO(gri) fix this for generalized interfaces
return check.missingMethod(T, V, false)
}
+// newAssertableTo reports whether a value of type V can be asserted to have type T.
+// It also implements behavior for interfaces that currently are only permitted
+// in constraint position (we have not yet defined that behavior in the spec).
+func (check *Checker) newAssertableTo(V *Interface, T Type) error {
+ // 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 IsInterface(T) {
+ return nil
+ }
+ return check.implements(T, V)
+}
+
// deref dereferences typ if it is a *Pointer and returns its base and true.
// Otherwise it returns (typ, false).
func deref(typ Type) (Type, bool) {