wlav / cppyy

Other
401 stars 41 forks source link

Exceptions sometimes lead to "terminate called" #13

Open saraedum opened 2 years ago

saraedum commented 2 years ago

When running the following code snippet, a C++ exception is correctly reported in an interactive Python session:

>>> from pyintervalxt import IntervalExchangeTransformation
>>> iet = IntervalExchangeTransformation((18, 3), (1, 0))
>>> iet
[a: 18] [b: 3] / [b] [a]
>>> from pyeantic import eantic
>>> L = eantic.renf("a^3 - a^2 - a - 1", "a", "[1.84 +/- 0.01]")
>>> lengths = [L.gen(), eantic.renf_elem(L, "-3*a^2 + 2*a - 1")]
>>> IntervalExchangeTransformation(lengths, [1, 0])
---------------------------------------------------------------------------
TypeError: none of the 4 overloaded methods succeeded. Full details:
  Lengths<std::vector<eantic::renf_elem_class> >::Lengths<std::vector<eantic::renf_elem_class> >(intervalxt::cppyy::Lengths<std::vector<eantic::renf_elem_class> >&&) =>
    ValueError: could not convert argument 1 (object is not an rvalue)
  Lengths<std::vector<eantic::renf_elem_class> >::Lengths<std::vector<eantic::renf_elem_class> >(const intervalxt::cppyy::Lengths<std::vector<eantic::renf_elem_class> >&) =>
    TypeError: could not convert argument 1
  Lengths<std::vector<eantic::renf_elem_class> >::Lengths<std::vector<eantic::renf_elem_class> >(const std::vector<eantic::renf_elem_class>&) =>
    invalid_argument: all lengths must be non-negative
  Lengths<std::vector<eantic::renf_elem_class> >::Lengths<std::vector<eantic::renf_elem_class> >() =>
    TypeError: takes at most 0 arguments (1 given)

However, later in the same session, the same invocation causes a crash:

>>> iet = IntervalExchangeTransformation((18, 3), (1, 0))
>>> from pickle import loads, dumps
>>> loads(dumps(iet))
[a: 18] [b: 3] / [b] [a]
>>> lengths = [L.gen(), eantic.renf_elem(L, "-3*a^2 + 2*a - 1")]
>>> IntervalExchangeTransformation(lengths, [1, 0])
terminate called after throwing an instance of 'std::invalid_argument'
  what():  all lengths must be non-negative

Since the same exception was apparently thrown, I don't understand why the loads and dumps on an unrelated object, made it so that this time the exception could not be caught and handed over to Python.

Unfortunately, I could not find a short reproducer for this yet. I can reproduce the above on Linux in the following conda environment:

conda create -n terminate -c flatsurf pyintervalxt pyeantic
conda activate terminate
python
wlav commented 2 years ago

I'll have a look, but all method calls into C++ have a catch (...), so the only reason I can think of, is that the C++ exception has two type_info's. Nominally, the easiest way to "achieve" that is to have one JIT-ed and one linked, but a) I haven't seen that to be a problem on Linux; b) a standard exception is a weird one for this to happen to; and c) I don't see where this JIT-ing would occur. So, I'm a bit at a loss as to how this can occur ...