Skip to content

Add Cpp::Box, the typed result carrier for the AOT/JIT boundary#1023

Merged
vgvassilev merged 1 commit into
compiler-research:mainfrom
vgvassilev:cpp-value-evaluate
Jun 4, 2026
Merged

Add Cpp::Box, the typed result carrier for the AOT/JIT boundary#1023
vgvassilev merged 1 commit into
compiler-research:mainfrom
vgvassilev:cpp-value-evaluate

Conversation

@vgvassilev
Copy link
Copy Markdown
Contributor

@vgvassilev vgvassilev commented Jun 3, 2026

Add a small tagged-union type, Cpp::Box, that bindings and the interpreter use to exchange typed values in both directions. A binding constructs one with Box::Create(x) and hands it to the runtime; the interpreter returns one from Evaluate and the binding extracts it with unbox(), convertTo(), or visit(v). The same shape works for the fundamental scalar types (stored inline) and for object payloads (stored behind a refcounted pointer the producer attaches via a {retain, release} ops table). Without Box every binding has to write its own Kind switch and duplicate the per-type storage logic.

Evaluate now returns a Box (replacing the old intptr_t + bool* out-param). Object payloads round-trip through a small ValueRefCount wrapper in Compatibility.h next to compat::Value; the wrapper uses clang::Value's copy ctor instead of move ctor to work around an upstream bug fixed by llvm/llvm-project#200888, and a static_assert(LLVM_VERSION_MAJOR < 23) makes sure we revisit it.

Box.h itself is kept cheap to parse since the JIT pulls it whenever a catalog thunk or evaluated snippet references Cpp::Box. We avoid CppInterOpTypes.h (it transitively brings in , , ), use unsigned char for the Kind underlying type instead of , gate behind NDEBUG, and inline the cross-platform always_inline macro locally. Under NDEBUG the header pulls in zero project or standard headers; with asserts on, only . Tests cover the fundamentals and K_PtrOrObj round-trip (POD, non-trivial destructor, class-with-static-member), refcounted copy + move semantics, and visit / convertTo on both AOT and runtime paths.

@vgvassilev vgvassilev force-pushed the cpp-value-evaluate branch from b77b6d4 to 7ec9635 Compare June 3, 2026 18:54
@vgvassilev vgvassilev requested a review from Vipul-Cariappa June 3, 2026 18:56
@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 3, 2026

Codecov Report

❌ Patch coverage is 97.22222% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.30%. Comparing base (b92d31d) to head (96dc713).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
lib/CppInterOp/CppInterOp.cpp 96.07% 2 Missing ⚠️
include/CppInterOp/Box.h 97.05% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1023      +/-   ##
==========================================
+ Coverage   86.17%   86.30%   +0.12%     
==========================================
  Files          17       18       +1     
  Lines        5121     5227     +106     
==========================================
+ Hits         4413     4511      +98     
- Misses        708      716       +8     
Files with missing lines Coverage Δ
include/CppInterOp/CppInterOp.h 100.00% <ø> (ø)
include/CppInterOp/Dispatch.h 88.57% <ø> (ø)
lib/CppInterOp/CXCppInterOp.cpp 100.00% <100.00%> (ø)
lib/CppInterOp/Compatibility.h 84.27% <100.00%> (+0.71%) ⬆️
include/CppInterOp/Box.h 97.05% <97.05%> (ø)
lib/CppInterOp/CppInterOp.cpp 89.52% <96.07%> (-0.07%) ⬇️
Files with missing lines Coverage Δ
include/CppInterOp/CppInterOp.h 100.00% <ø> (ø)
include/CppInterOp/Dispatch.h 88.57% <ø> (ø)
lib/CppInterOp/CXCppInterOp.cpp 100.00% <100.00%> (ø)
lib/CppInterOp/Compatibility.h 84.27% <100.00%> (+0.71%) ⬆️
include/CppInterOp/Box.h 97.05% <97.05%> (ø)
lib/CppInterOp/CppInterOp.cpp 89.52% <96.07%> (-0.07%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@vgvassilev vgvassilev force-pushed the cpp-value-evaluate branch 4 times, most recently from f3e4d98 to 18e25c8 Compare June 4, 2026 11:19
Copy link
Copy Markdown
Collaborator

@Vipul-Cariappa Vipul-Cariappa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me! Modulo minor comments!

Comment thread include/CppInterOp/Box.h
#undef X
struct {
void* m_Ptr;
const ObjectOps* m_Ops;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const ObjectOps* m_Ops;
const ObjectOps m_Ops;

Is storing the struct itself possible?

Comment on lines +104 to +106
Cpp::Box sV = Cpp::Evaluate("struct S{} s; s");
EXPECT_EQ(sV.getKind(), Cpp::Box::K_PtrOrObj);
EXPECT_NE(sV.getObjectPtr(), nullptr);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also test the QualType stored? Using getType. I could not see getType being tested.

@Vipul-Cariappa
Copy link
Copy Markdown
Collaborator

I guess the cppyy failure is expected. We will need to update all usages of Evaluate to the new overload.

Add a small tagged-union type, Cpp::Box, that bindings and the
interpreter use to exchange typed values in both directions. A binding
constructs one with Box::Create<T>(x) and hands it to the runtime; the
interpreter returns one from Evaluate and the binding extracts it with
unbox<T>(), convertTo<T>(), or visit(v). The same shape works for the
fundamental scalar types (stored inline) and for object payloads
(stored behind a refcounted pointer the producer attaches via a
{retain, release} ops table). Without Box every binding has to write
its own Kind switch and duplicate the per-type storage logic.

Evaluate gains a Box-returning C++ overload as the new primary form;
the body classifies the result by clang::QualType so the same code
serves both the clang-repl and cling backends. The legacy
intptr_t Evaluate(const char*, bool*) overload is preserved -- moved
into CXCppInterOp.cpp next to the other hand-written C-ABI bridges
along with its cppinterop_Evaluate C wrapper -- so existing C
consumers (cppyy, ROOT) keep working unchanged. Object payloads round-
trip through a small ValueRefCount wrapper in Compatibility.h next to
compat::Value; the wrapper uses clang::Value's copy ctor instead of
move ctor to work around an upstream bug fixed by
llvm/llvm-project#200888, and a static_assert(LLVM_VERSION_MAJOR < 23)
makes sure we revisit it.

Box.h itself is kept cheap to parse since the JIT pulls it whenever a
catalog thunk or evaluated snippet references Cpp::Box. We avoid
CppInterOpTypes.h (it transitively brings in <vector>, <string>,
<set>), use unsigned char for the Kind underlying type instead of
<cstdint>, gate <cassert> behind NDEBUG, and inline the cross-platform
always_inline and unreachable macros locally. Under NDEBUG the header
pulls in zero project or standard headers; with asserts on, only
<cassert>. Tests cover the fundamentals and K_PtrOrObj round-trip
(POD, non-trivial destructor, class-with-static-member), refcounted
copy + move semantics, and visit / convertTo on both AOT and runtime
paths.
@vgvassilev vgvassilev force-pushed the cpp-value-evaluate branch from 18e25c8 to 96dc713 Compare June 4, 2026 13:14
@vgvassilev
Copy link
Copy Markdown
Contributor Author

osx26-x86-clang-cling-llvm20-cppyy seems stuck -- moving forward, the fixup pr is here compiler-research/cppyy#222

@vgvassilev vgvassilev merged commit 8f45a63 into compiler-research:main Jun 4, 2026
19 of 26 checks passed
@vgvassilev vgvassilev deleted the cpp-value-evaluate branch June 4, 2026 13:40
@vgvassilev vgvassilev restored the cpp-value-evaluate branch June 4, 2026 14:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants