bpo-44184: Fix subtype_dealloc() for freed type (GH-26274) (GH-26290)

Fix a crash at Python exit when a deallocator function removes the
last strong reference to a heap type.

Don't read type memory after calling basedealloc() since
basedealloc() can deallocate the type and free its memory.

_PyMem_IsPtrFreed() argument is now constant.
(cherry picked from commit 615069eb08494d089bf24e43547fbc482ed699b8)

Co-authored-by: Victor Stinner <vstinner@python.org>

Co-authored-by: Victor Stinner <vstinner@python.org>
diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py
index ee4fe49..1528cf6 100644
--- a/Lib/test/test_gc.py
+++ b/Lib/test/test_gc.py
@@ -1361,6 +1361,34 @@ def __del__(self):
             # empty __dict__.
             self.assertEqual(x, None)
 
+
+class PythonFinalizationTests(unittest.TestCase):
+    def test_ast_fini(self):
+        # bpo-44184: Regression test for subtype_dealloc() when deallocating
+        # an AST instance also destroy its AST type: subtype_dealloc() must
+        # not access the type memory after deallocating the instance, since
+        # the type memory can be freed as well. The test is also related to
+        # _PyAST_Fini() which clears references to AST types.
+        code = textwrap.dedent("""
+            import ast
+            import codecs
+
+            # Small AST tree to keep their AST types alive
+            tree = ast.parse("def f(x, y): return 2*x-y")
+            x = [tree]
+            x.append(x)
+
+            # Put the cycle somewhere to survive until the last GC collection.
+            # Codec search functions are only cleared at the end of
+            # interpreter_clear().
+            def search_func(encoding):
+                return None
+            search_func.a = x
+            codecs.register(search_func)
+        """)
+        assert_python_ok("-c", code)
+
+
 def test_main():
     enabled = gc.isenabled()
     gc.disable()
@@ -1370,7 +1398,11 @@ def test_main():
 
     try:
         gc.collect() # Delete 2nd generation garbage
-        run_unittest(GCTests, GCTogglingTests, GCCallbackTests)
+        run_unittest(
+            GCTests,
+            GCCallbackTests,
+            GCTogglingTests,
+            PythonFinalizationTests)
     finally:
         gc.set_debug(debug)
         # test gc.enable() even if GC is disabled by default