The following trivial code crashes:
from mlir.ir import *
m = ir.Module.create()
m.operation.erase()
because erase erases the module operation, and then the destructor of PyModule attempts to erase it the second time (transitively through the module object).
We should connect PyModule to the corresponding PyOperation in the bindings and avoid calling mlirModuleDestroy if the operation was already destroyed, using the valid flag on the operation.