Issue #13411: memoryview objects are now hashable when the underlying object is hashable.
diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c
index a89798a..88411b7 100644
--- a/Objects/bytesobject.c
+++ b/Objects/bytesobject.c
@@ -860,22 +860,11 @@
 static Py_hash_t
 bytes_hash(PyBytesObject *a)
 {
-    register Py_ssize_t len;
-    register unsigned char *p;
-    register Py_uhash_t x;
-
-    if (a->ob_shash != -1)
-        return a->ob_shash;
-    len = Py_SIZE(a);
-    p = (unsigned char *) a->ob_sval;
-    x = (Py_uhash_t)*p << 7;
-    while (--len >= 0)
-        x = (1000003U*x) ^ (Py_uhash_t)*p++;
-    x ^= (Py_uhash_t)Py_SIZE(a);
-    if (x == -1)
-        x = -2;
-    a->ob_shash = x;
-    return x;
+    if (a->ob_shash == -1) {
+        /* Can't fail */
+        a->ob_shash = _Py_HashBytes((unsigned char *) a->ob_sval, Py_SIZE(a));
+    }
+    return a->ob_shash;
 }
 
 static PyObject*
diff --git a/Objects/memoryobject.c b/Objects/memoryobject.c
index e0d1a89..295a742 100644
--- a/Objects/memoryobject.c
+++ b/Objects/memoryobject.c
@@ -84,6 +84,7 @@
         PyObject_GC_New(PyMemoryViewObject, &PyMemoryView_Type);
     if (mview == NULL)
         return NULL;
+    mview->hash = -1;
     dup_buffer(&mview->view, info);
     /* NOTE: mview->view.obj should already have been incref'ed as
        part of PyBuffer_FillInfo(). */
@@ -512,6 +513,37 @@
         return PyUnicode_FromFormat("<memory at %p>", self);
 }
 
+static Py_hash_t
+memory_hash(PyMemoryViewObject *self)
+{
+    if (self->hash == -1) {
+        Py_buffer *view = &self->view;
+        CHECK_RELEASED_INT(self);
+        if (view->ndim > 1) {
+            PyErr_SetString(PyExc_NotImplementedError,
+                            "can't hash multi-dimensional memoryview object");
+            return -1;
+        }
+        if (view->strides && view->strides[0] != view->itemsize) {
+            PyErr_SetString(PyExc_NotImplementedError,
+                            "can't hash strided memoryview object");
+            return -1;
+        }
+        if (!view->readonly) {
+            PyErr_SetString(PyExc_ValueError,
+                            "can't hash writable memoryview object");
+            return -1;
+        }
+        if (view->obj != NULL && PyObject_Hash(view->obj) == -1) {
+            /* Keep the original error message */
+            return -1;
+        }
+        /* Can't fail */
+        self->hash = _Py_HashBytes((unsigned char *) view->buf, view->len);
+    }
+    return self->hash;
+}
+
 /* Sequence methods */
 static Py_ssize_t
 memory_length(PyMemoryViewObject *self)
@@ -829,7 +861,7 @@
     0,                                        /* tp_as_number */
     &memory_as_sequence,                      /* tp_as_sequence */
     &memory_as_mapping,                       /* tp_as_mapping */
-    0,                                        /* tp_hash */
+    (hashfunc)memory_hash,                    /* tp_hash */
     0,                                        /* tp_call */
     0,                                        /* tp_str */
     PyObject_GenericGetAttr,                  /* tp_getattro */
diff --git a/Objects/object.c b/Objects/object.c
index 25e64e1..00f1716 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -744,6 +744,21 @@
 }
 
 Py_hash_t
+_Py_HashBytes(unsigned char *p, Py_ssize_t len)
+{
+    Py_uhash_t x;
+    Py_ssize_t i;
+
+    x = (Py_uhash_t) *p << 7;
+    for (i = 0; i < len; i++)
+        x = (1000003U * x) ^ (Py_uhash_t) *p++;
+    x ^= (Py_uhash_t) len;
+    if (x == -1)
+        x = -2;
+    return x;
+}
+
+Py_hash_t
 PyObject_HashNotImplemented(PyObject *v)
 {
     PyErr_Format(PyExc_TypeError, "unhashable type: '%.200s'",