Issue #18594: Fix the fast path for collections.Counter().

The path wasn't being taken due to an over-restrictive type check.
diff --git a/Include/object.h b/Include/object.h
index 387cadb..20c4780 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -482,6 +482,7 @@
                                                PyObject *, PyObject *);
 #ifndef Py_LIMITED_API
 PyAPI_FUNC(PyObject *) _PyType_Lookup(PyTypeObject *, PyObject *);
+PyAPI_FUNC(PyObject *) _PyType_LookupId(PyTypeObject *, _Py_Identifier *);
 PyAPI_FUNC(PyObject *) _PyObject_LookupSpecial(PyObject *, _Py_Identifier *);
 PyAPI_FUNC(PyTypeObject *) _PyType_CalculateMetaclass(PyTypeObject *, PyObject *);
 #endif
diff --git a/Misc/NEWS b/Misc/NEWS
index c787537..7898b9b 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -74,6 +74,9 @@
 - Issue #12641: Avoid passing "-mno-cygwin" to the mingw32 compiler, except
   when necessary.  Patch by Oscar Benjamin.
 
+- Issue #18594: The fast path for collections.Counter() was never taken
+  due to an over-restrictive type check.
+
 - Properly initialize all fields of a SSL object after allocation.
 
 - Issue #4366: Fix building extensions on all platforms when --enable-shared
diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c
index 34a1a90..cb16748 100644
--- a/Modules/_collectionsmodule.c
+++ b/Modules/_collectionsmodule.c
@@ -1689,10 +1689,16 @@
 static PyObject *
 _count_elements(PyObject *self, PyObject *args)
 {
+    _Py_IDENTIFIER(__getitem__);
+    _Py_IDENTIFIER(__setitem__);
     PyObject *it, *iterable, *mapping, *oldval;
     PyObject *newval = NULL;
     PyObject *key = NULL;
     PyObject *one = NULL;
+    PyObject *mapping_getitem;
+    PyObject *mapping_setitem;
+    PyObject *dict_getitem;
+    PyObject *dict_setitem;
 
     if (!PyArg_UnpackTuple(args, "_count_elements", 2, 2, &mapping, &iterable))
         return NULL;
@@ -1707,7 +1713,15 @@
         return NULL;
     }
 
-    if (PyDict_CheckExact(mapping)) {
+    mapping_getitem = _PyType_LookupId(Py_TYPE(mapping), &PyId___getitem__);
+    dict_getitem = _PyType_LookupId(&PyDict_Type, &PyId___getitem__);
+    mapping_setitem = _PyType_LookupId(Py_TYPE(mapping), &PyId___setitem__);
+    dict_setitem = _PyType_LookupId(&PyDict_Type, &PyId___setitem__);
+
+    if (mapping_getitem != NULL &&
+        mapping_getitem == dict_getitem &&
+        mapping_setitem != NULL &&
+        mapping_setitem == dict_setitem) {
         while (1) {
             key = PyIter_Next(it);
             if (key == NULL)
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index a55d977..3dcbc86 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -50,9 +50,6 @@
 _Py_IDENTIFIER(__new__);
 
 static PyObject *
-_PyType_LookupId(PyTypeObject *type, struct _Py_Identifier *name);
-
-static PyObject *
 slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
 
 unsigned int
@@ -2589,7 +2586,7 @@
     return res;
 }
 
-static PyObject *
+PyObject *
 _PyType_LookupId(PyTypeObject *type, struct _Py_Identifier *name)
 {
     PyObject *oname;