[fix] Null-guard the cling.printValue lookup in op_str - #56
Conversation
cc6b1ab to
06c8899
Compare
| PyObject* cl = PyObject_GetAttrString(gbl, (char*)"cling"); | ||
| printValue = PyObject_GetAttrString(cl, (char*)"printValue"); | ||
| Py_DECREF(cl); | ||
| // no cling namespace exists unless user code declares one |
There was a problem hiding this comment.
Perhaps we can just short-circuit this path completely if we know the interpreter is clang-repl, since that lookup would always return null. We could use the compile-time definition CPPJIT_USE_CLING, or a runtime check like in test/support.py
There was a problem hiding this comment.
Good call — done. The whole pretty-printing block now sits behind #ifdef CPPJIT_USE_CLING, so clang-repl builds compile it out and str() falls straight through to the generic repr; strings on the clang-repl libcppjit.so shows the printValue lookup gone, and the object shrinks slightly. One thing the gate needed: CPPJIT_USE_CLING was only ever a CMake option and never reached the sources, so I added the matching target_compile_definitions entry — without it the #ifdef would have been false on the cling build too. The null-guards stay inside the cling branch, in case an odd cling state leaves the lookup empty. Worth naming the trade-off: a user-declared namespace cling { printValue } is no longer honored on clang-repl, which I think is right given the native value-printing direction in compiler-research/CppInterOp#1100.
06c8899 to
40e8086
Compare
The in-process test52 crashed the llvm22 and cling CI lanes with a SIGSEGV, hiding the rest of the suite. Match compiler-research#56's head: the cling pretty-print path in op_str now compiles out entirely behind CPPJIT_USE_CLING on clang-repl, with the matching CMake compile-definition wiring, and the regression test runs its repro in a subprocess so a crash fails the assertion instead of the runner. Co-developed-with-the-help-of: Claude Code (Sonnet 5, human in the loop)
40e8086 to
e043129
Compare
| // reasons) | ||
| // reasons). Cling only: clang-repl has no cling namespace to look up, so the | ||
| // whole path compiles out and str() falls through to the generic repr. | ||
| #ifdef CPPJIT_USE_CLING |
There was a problem hiding this comment.
This should probably be required to CppInterOp’s toString interface which will still crash for clang-repl but this time we can fix it there.
There was a problem hiding this comment.
Happy to make that change here: route str() through Cpp::ObjToString and drop the cling-only block. clang-repl would then keep failing in toString until compiler-research/CppInterOp#1100 lands the fix there. Do you want that in this PR, or should we keep the short-circuit and switch once #1100 is fixed?
e043129 to
e5f3418
Compare
e5f3418 to
53ae578
Compare
| CPPINTEROP_INCLUDE_DIR="${CPPJIT_INTEROP_RUNTIME_INCLUDES}" | ||
| CPPJIT_CLANG_MAJOR="${CPPJIT_INTEROP_CLANG_MAJOR}" | ||
| CPPJIT_CLANG_INCLUDE_DIR="${CPPJIT_INTEROP_CLANG_DIR}" | ||
| # cling-only code paths need the flavor at compile time, not just in cmake |
There was a problem hiding this comment.
We can probably drop this comment here.
| with raises(TypeError): | ||
| cppjit.gbl.std.vector[object()] | ||
|
|
||
| def test53_str_fallback_without_ostream_insertion(self): |
There was a problem hiding this comment.
We do have existing tests that are marked crashing with clang-repl due to ToString. I am not sure if adding a Python test that spawns a process guaranteed to crash is a good idea, especially since this does not live on the API surface, and this test suite is meant to be user-facing. Imo this can be moved into a CppInterOp only reproducer. If compat::GetClingPrintValue() returns null, can we prevent the crash and gracefully error out instead?
There was a problem hiding this comment.
The null return was already safe. op_str falls through to the generic repr there.
The crash the test caught happens one step earlier. op_str calls Cpp::ObjToString, and Interpreter::toString is an assert(0) stub. It aborts with assertions on and returns "" without them. The stub is not backend specific, so the existing markers say "and on Cling".
I moved that call behind the same compatibility header and made it return the empty string on every build, so op_str reaches the generic repr everywhere. #1100 turns the helper back into a one-line forward.
The subprocess test is gone. The new test53 runs in process and checks that str(obj) equals repr(obj). The three xfail markers that named this crash now pass on an assertions-on build, so I removed them.
Prevent a null dereference when str() cannot find cling.printValue. Keep the interpreter check and cached lookup in Compatibility.h. The helper returns nullptr on clang-repl and preserves the Cling fallback. Pass CPPJIT_USE_CLING from CMake to the sources. Skip the CppInterOp ObjToString call. Its toString is an assert(0) stub, so the call aborts with assertions on and returns "" without them. op_str now falls back to the generic repr on every build. Test the repr fallback in process. The earlier subprocess test is no longer necessary, because the path does not crash. Retire the three xfail markers that named the toString crash. Co-developed-with-the-help-of: Claude Code (Fable 5, human in the loop) Co-developed-with-the-help-of: Claude Code (Opus 5, human in the loop) Co-developed-with-the-help-of: OpenAI Codex (GPT-6, human in the loop)
0697f8d
53ae578 to
0697f8d
Compare
str()on any instance without anostreaminserter segfaults on currentmain. Commit5a33027dropped the fakedcling::runtime::gCling, and thepretty-print fallback in
op_strthen dereferenced the failedcppjit.gbl.clinglookup. This guards both lookups, clears theAttributeError, and falls back to the generic repr.Repro:
A regression test is included. The crash kills the interpreter, so the test
asserts the fixed behaviour rather than the crash.