Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion unittests/CppInterOp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ add_cppinterop_unittest(CppInterOpTests
CAPITest.cpp
CommentReflectionTest.cpp
CppInterOpThunksTest.cpp
CppInterOpTypesTest.cpp
DiagnosticTest.cpp
EnumReflectionTest.cpp
FunctionReflectionTest.cpp
HandleTypesTest.cpp
InterpreterTest.cpp
JitTest.cpp
ResultTest.cpp
Expand Down Expand Up @@ -127,6 +127,11 @@ endif()
add_subdirectory(TestSharedLib)
add_dependencies(DynamicLibraryManagerTests TestSharedLib)

# Regression guard that CppInterOpTypes.h stays a self-contained header
# (no out-of-line .cpp dependency from libclangCppInterOp). See
# unittests/CppInterOp/TypesSelfContainedTest/ for the rationale.
add_subdirectory(TypesSelfContainedTest)

# Cross-TU VTableOverlay perf check, built as part of the test suite.
# Standalone-only (its Google Benchmark dependency is provisioned only then;
# keeps it out of embedded host builds such as ROOT). Excluded on WASM
Expand Down
42 changes: 42 additions & 0 deletions unittests/CppInterOp/TypesSelfContainedTest/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Regression guard for CppInterOpTypes.h staying header-only. This
# executable includes only CppInterOpTypes.h, exercises every helper,
# and DELIBERATELY does not link libclangCppInterOp or any LLVM library
# — if any header helper needs an out-of-line symbol, the link fails.
# See TypesSelfContainedTest.cpp for the full rationale.
#
# Not a runtime test — the built binary is not invoked. Success is
# purely that the link stage completes; ctest just runs it to confirm
# the process starts.

add_executable(CppInterOpTypesSelfContainedTest EXCLUDE_FROM_ALL
TypesSelfContainedTest.cpp)

# The one and only include CppInterOpTypes.h needs is its own header
# directory. NOTHING from libclangCppInterOp or LLVM.
target_include_directories(CppInterOpTypesSelfContainedTest PRIVATE
${CMAKE_SOURCE_DIR}/include)

# Match the Release-ish flags real Dispatch consumers see so any
# NDEBUG-guarded regressions surface here too.
target_compile_definitions(CppInterOpTypesSelfContainedTest PRIVATE NDEBUG)

# Route the executable to the same bin dir the other cppinterop
# unittests land in, so ctest's default search path finds it. Follow
# the pattern from add_cppinterop_unittest (see unittests/CMakeLists.txt).
set_output_directory(CppInterOpTypesSelfContainedTest
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/../bin/$<CONFIG>/
LIBRARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/../bin/$<CONFIG>/
)

# Register with ctest — the invocation just checks the process
# launches; the load-bearing signal is that the link succeeded at
# build time.
add_test(NAME cppinterop-CppInterOpTypesSelfContainedTest
COMMAND CppInterOpTypesSelfContainedTest)

# Ensure the target is built as part of CppInterOpUnitTests (parallel
# to how add_cppinterop_unittest chains dependencies).
add_dependencies(CppInterOpUnitTests CppInterOpTypesSelfContainedTest)

set_target_properties(CppInterOpTypesSelfContainedTest PROPERTIES
FOLDER "Tests")
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Regression guard: CppInterOpTypes.h must stay a fully self-contained
// header — every helper's implementation lives either inline in the
// header or in a plain-libc-shaped body. If a future contributor adds
// a helper backed by an out-of-line .cpp definition in
// libclangCppInterOp, this translation unit will fail to LINK
// (undefined symbol at ld time) because the executable deliberately
// does not link against libclangCppInterOp.
//
// Consumers reaching the CppInterOp API through Dispatch.h
// (cppyy-backend, xeus-cpp) do not link against libclangCppInterOp —
// they dlopen it lazily via LoadDispatchAPI. A CppInterOpTypes.h that
// referenced an out-of-line symbol from libclangCppInterOp would
// either force those consumers into a DT_NEEDED against the full
// library (the exact class of hazard we hit with
// Cpp::ResultAbort_UncheckedOnDtor) or require routing the helper
// through the dispatch table.
//
// This binary is not run — it's a compile-and-link probe. Success ==
// build succeeded. Failure looks like:
// undefined reference to `some_new_helper_that_snuck_in'
//
// If you land such a symbol here, either (a) make it a static inline
// in the header, (b) route it through the dispatch table, or (c) split
// the ABI-helper prelude into a truly no-op runtime archive that
// downstream Dispatch consumers can link cheaply.
//
// Follow-up commits extend the probe body as new helpers land in
// CppInterOpTypes.h. Today only the pre-existing types are exercised:
// the opaque handles, TemplateArgInfo, and the CppInterOp{Array,
// StringArray} PODs.

#include "CppInterOp/CppInterOpTypes.h"

#include <cstddef>

// Force ODR-use of every currently-provided CppInterOpTypes.h helper

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

warning: included header cstddef is not used directly [misc-include-cleaner]

Suggested change
// Force ODR-use of every currently-provided CppInterOpTypes.h helper
s.h"
d

// so the linker resolves each one. If any helper picks up an
// out-of-line dependency, this function fails to link.
extern "C" int cppinterop_types_self_contained_probe(int seed) {
// Handle types (Cpp::DeclRef etc.) — POD trivially copyable; a
// default-constructed handle compares equal to nullptr and yields
// false in a bool context. All inline, no external symbols.
Cpp::DeclRef d;
if (d)
return -1;

// TemplateArgInfo — standard-layout POD with a #ifdef __cplusplus
// constructor. Instantiation touches the ctor without pulling any
// implementation symbol.
Cpp::TemplateArgInfo tai(nullptr);
(void)tai;

// Array/StringArray helpers — pure PODs. No methods, no ctors that
// reach out-of-line code.
Cpp::CppInterOpArray arr = {nullptr, 0};
Cpp::CppInterOpStringArray sarr = {nullptr, 0};
return static_cast<int>(seed + arr.size + sarr.size);
}

int main(int argc, char** /*argv*/) {
// Volatile / argc mixing keeps the compiler from optimizing the probe
// call away in a Release build. The returned value is uninteresting;
// the load-bearing signal is that the link succeeded.
volatile int r = cppinterop_types_self_contained_probe(argc);
(void)r;
return 0;
}
Loading