[Armin Rigo, on
x = [] x.extend(-y for y in x) Segmentation fault ]
The segfault is immediate. And the example is different, as Ronald pointed out: the list 'x' is empty!
Good eye! I overlooked that too.
Uh oh. We have a real bug in listextend(): the list being extended is in a semi-invalid state when it's calling tp_iternext() on the 2nd iterable. This might call back Python code, which can inspect the list. The above example does just that. Crash.
"Semi-invalid" means that all invariants are respected but the final items in the list are NULL. Reading them crashes. And I'm not even talking about the nasty things you can do if you modify the list while it's being extended :-)
Yup. The code doesn't check for C int overflow of m+n either.
The safest solution would be to use a regular app1() to add each item as the iterable produce them instead of optimizing this case. I'm not sure we need the high-flying optimization of listextend() in this case (this is the case where the iterable we extend the list with is neither a list nor a tuple). I believe that the speed of app1() would be acceptable, given the fixed bug and the overall decrease of code complexity (though that should be measured).
I think it's easy to fix. "The usual rule" applies: you can't assume anything about a mutable object after potentially calling back into Python. So trying to save info in "i", "m", or "n" across loop iterations can't work, and the list can never be left in an insane state ("semi" or not) at any time user code may get invoked. But since we have both "num allocated" and "num used" members in the list struct now, it's easy to use those instead of trying to carry info in locals. Patch attached. Anyone object? Of course in the example at the start of this msg, it leaves x empty.