Wouldn't it suffice to check for tp_dict != NULL (after the call to PyType_Ready of course)?
No, see below (although I must admit that I wrote "Right" here first :-)
Hm. What does Py_TPFLAGS_HAVE_CLASS mean exactly?
According to the documentation, it means that the underlying TypeObject structure has the necessary fields in its C declaration.
Or, better, since TPFLAGS_DEFAULT contains TPFLAGS_HAVE_CLASS, what does it mean when Py_TPFLAGS_HAVE_CLASS is NOT in tp_flags?
It means you have been loading a module from an earlier Python version, which had a different setting for TPFLAGS_DEFAULTS, and a shorter definition of the TypeObject.
If you try to access tp_dict in such an object, you are accessing random memory. This may immediately crash, or only crash when you pass the pointer you got to the dictionary functions.