Fix SF bug 574207 (chained __slots__ dealloc segfault).

This is inspired by SF patch 581742 (by Jonathan Hogg, who also
submitted the bug report, and two other suggested patches), but
separates the non-GC case from the GC case to avoid testing for GC
several times.

Had to fix an assert() from call_finalizer() that asserted that the
object wasn't untracked, because it's possible that the object isn't
GC'ed!
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index f7069a0..b7edb14 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -401,7 +401,8 @@
 		_Py_NewReference(self);
 		self->ob_refcnt = refcnt;
 	}
-	assert(_Py_AS_GC(self)->gc.gc_refs != _PyGC_REFS_UNTRACKED);
+	assert(!PyType_IS_GC(self->ob_type) ||
+	       _Py_AS_GC(self)->gc.gc_refs != _PyGC_REFS_UNTRACKED);
 	/* If Py_REF_DEBUG, the original decref dropped _Py_RefTotal, but
 	 * _Py_NewReference bumped it again, so that's a wash.
 	 * If Py_TRACE_REFS, _Py_NewReference re-added self to the object
@@ -423,14 +424,55 @@
 	PyTypeObject *type, *base;
 	destructor basedealloc;
 
-	/* This exists so we can DECREF self->ob_type */
+	/* Extract the type; we expect it to be a heap type */
+	type = self->ob_type;
+	assert(type->tp_flags & Py_TPFLAGS_HEAPTYPE);
 
+	/* Test whether the type has GC exactly once */
+
+	if (!PyType_IS_GC(type)) {
+		/* It's really rare to find a dynamic type that doesn't have
+		   GC; it can only happen when deriving from 'object' and not
+		   adding any slots or instance variables.  This allows
+		   certain simplifications: there's no need to call
+		   clear_slots(), or DECREF the dict, or clear weakrefs. */
+
+		/* Maybe call finalizer; exit early if resurrected */
+		if (call_finalizer(self) < 0)
+			return;
+
+		/* Find the nearest base with a different tp_dealloc */
+		base = type;
+		while ((basedealloc = base->tp_dealloc) == subtype_dealloc) {
+			assert(base->ob_size == 0);
+			base = base->tp_base;
+			assert(base);
+		}
+
+		/* Call the base tp_dealloc() */
+		assert(basedealloc);
+		basedealloc(self);
+
+		/* Can't reference self beyond this point */
+		Py_DECREF(type);
+
+		/* Done */
+		return;
+	}
+
+	/* We get here only if the type has GC */
+
+	/* UnTrack and re-Track around the trashcan macro, alas */
+	_PyObject_GC_UNTRACK(self);
+	Py_TRASHCAN_SAFE_BEGIN(self);
+	_PyObject_GC_TRACK(self); /* We'll untrack for real later */
+
+	/* Maybe call finalizer; exit early if resurrected */
 	if (call_finalizer(self) < 0)
 		return;
 
 	/* Find the nearest base with a different tp_dealloc
 	   and clear slots while we're at it */
-	type = self->ob_type;
 	base = type;
 	while ((basedealloc = base->tp_dealloc) == subtype_dealloc) {
 		if (base->ob_size)
@@ -456,7 +498,7 @@
 		PyObject_ClearWeakRefs(self);
 
 	/* Finalize GC if the base doesn't do GC and we do */
-	if (PyType_IS_GC(type) && !PyType_IS_GC(base))
+	if (!PyType_IS_GC(base))
 		_PyObject_GC_UNTRACK(self);
 
 	/* Call the base tp_dealloc() */
@@ -464,9 +506,9 @@
 	basedealloc(self);
 
 	/* Can't reference self beyond this point */
-	if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) {
-		Py_DECREF(type);
-	}
+	Py_DECREF(type);
+
+	Py_TRASHCAN_SAFE_END(self);
 }
 
 static PyTypeObject *solid_base(PyTypeObject *type);
@@ -2807,7 +2849,7 @@
 	Py_INCREF(Py_None);
 	return Py_None;
 }
-  
+
 static PyObject *
 wrap_descr_delete(PyObject *self, PyObject *args, void *wrapped)
 {
@@ -2992,7 +3034,7 @@
 	if (len == -1 && PyErr_Occurred())
 		return -1;
 	if (len < 0) {
-		PyErr_SetString(PyExc_ValueError, 
+		PyErr_SetString(PyExc_ValueError,
 				"__len__() should return >= 0");
 		return -1;
 	}
@@ -4039,7 +4081,7 @@
 	return p;
 }
 
-static int recurse_down_subclasses(PyTypeObject *type, slotdef **pp, 
+static int recurse_down_subclasses(PyTypeObject *type, slotdef **pp,
 				   PyObject *name);
 
 /* In the type, update the slots whose slotdefs are gathered in the pp0 array,