xerrors: require that error be type assertable to As's target

Document As(err, target) as panicking when *target is not of interface
type and does not implement the error interface. While this prevents
some clever tricks, it permits catching (at runtime or statically
via vet) type errors equivalent to the ones that can be detected
by type assertions.

  if e := os.PathError{}; errors.As(err, &e) {  // error, os.PathError does not implement error
  if e := &os.PathError{}; errors.As(err, e) {  // error, os.PathError does not implement error
  if e := &os.PathError{}; errors.As(err, &e) { // ok

  var e interface{ Timeout() bool }
  if errors.As(&e) && e.Timeout() { // ok

Change-Id: I3607ac62fbe8aebef3c1aeec3fb414fb41afcdda
Reviewed-on: https://go-review.googlesource.com/c/161899
Reviewed-by: Jonathan Amsterdam <jba@google.com>
diff --git a/wrap.go b/wrap.go
index 3e71058..a33459d 100644
--- a/wrap.go
+++ b/wrap.go
@@ -71,7 +71,7 @@
 // points, and if so, sets the target to its value and returns true. An error
 // matches a type if it is assignable to the target type, or if it has a method
 // As(interface{}) bool such that As(target) returns true. As will panic if target
-// is nil or not a pointer.
+// is not a non-nil pointer to a type which implements error or is of interface type.
 //
 // The As method should set the target to its value and return true if err
 // matches the type to which target points.
@@ -84,6 +84,9 @@
 	if typ.Kind() != reflect.Ptr || val.IsNil() {
 		panic("errors: target must be a non-nil pointer")
 	}
+	if e := typ.Elem(); e.Kind() != reflect.Interface && !e.Implements(errorType) {
+		panic("errors: *target must be interface or implement error")
+	}
 	targetType := typ.Elem()
 	for {
 		if reflect.TypeOf(err).AssignableTo(targetType) {
@@ -98,3 +101,5 @@
 		}
 	}
 }
+
+var errorType = reflect.TypeOf((*error)(nil)).Elem()
diff --git a/wrap_test.go b/wrap_test.go
index ef6a9ed..d08c325 100644
--- a/wrap_test.go
+++ b/wrap_test.go
@@ -151,10 +151,12 @@
 }
 
 func TestAsValidation(t *testing.T) {
+	var s string
 	testCases := []interface{}{
 		nil,
 		(*int)(nil),
 		"error",
+		&s,
 	}
 	err := xerrors.New("error")
 	for _, tc := range testCases {