diff --git a/lib/CppInterOp/CppInterOp.cpp b/lib/CppInterOp/CppInterOp.cpp index b00684a19..f85a0ec66 100644 --- a/lib/CppInterOp/CppInterOp.cpp +++ b/lib/CppInterOp/CppInterOp.cpp @@ -132,6 +132,21 @@ #if !defined(CPPINTEROP_USE_CLING) && !defined(EMSCRIPTEN) struct __clang_Interpreter_NewTag { } __ci_newtag; + +// Local forwarder for `operator new(size_t, void*, +// __clang_Interpreter_NewTag)`. clang-repl's Runtimes string declares it (LLVM +// 18+, llvm/llvm-project@1566f1ffc6b5) but the definition lives in +// libclangInterpreter -- embedders that dlopen libclangCppInterOp with +// RTLD_LOCAL (cppyy, the DispatchTests binary) have no path to that +// symbol. Registered against the mangled name via DefineAbsoluteSymbol +// at interpreter creation; see DispatchSmokeTest.TaggedPlacementNewResolvable. +namespace { +void* CppInterOpPlacementNew(std::size_t, void* __p, + __clang_Interpreter_NewTag) noexcept { + return __p; +} +} // namespace + #if CLANG_VERSION_MAJOR > 21 extern "C" void* __clang_Interpreter_SetValueWithAlloc(void* This, void* OutVal, void* OpaqueType); @@ -3034,6 +3049,34 @@ void make_narg_call(const FunctionDecl* FD, const std::string& return_type, callbuf << ")"; } +// Tag appended inside `::new (ptr) T(...)` when emitting a scalar +// placement new in a JitCall wrapper. +// +// clang-repl's Runtimes string declares the scalar tagged overload +// `operator new(size_t, void*, __clang_Interpreter_NewTag)` (introduced +// in llvm/llvm-project@1566f1ffc6b5, LLVM 18), so the spelling +// `::new (p, __ci_newtag) T(...)` binds without the user's TU having +// `#include ` in scope. Array placement in +// `make_narg_ctor_with_return` is implemented as a loop of scalar tagged +// placements for the same reason. +// +// Cling has no such tag; its runtime makes `` available by default, +// so the empty tag suffices there (plain scalar placement new). +// +// Every placement-new emission uses the unary `::` form so name lookup +// skips any class-scope `operator new`. Per [class.free]/2, if the +// allocated type is a class with a class-scope `operator new`, global +// scope is not consulted as a fallback -- overload resolution against +// the class's single-arg allocator then fails and the whole wrapper +// refuses to compile (observed on cppyy's test14_new_overloader). +inline const char* PlacementTag() { +#ifdef CPPINTEROP_USE_CLING + return ""; +#else + return ", __ci_newtag"; +#endif +} + void make_narg_ctor_with_return(const FunctionDecl* FD, const unsigned N, const std::string& class_name, std::ostringstream& buf, int indent_level) { @@ -3057,44 +3100,65 @@ void make_narg_ctor_with_return(const FunctionDecl* FD, const unsigned N, indent(callbuf, indent_level); const auto* CD = dyn_cast(FD); - // Activate this block only if array new is possible - // if (nary) { - // (*(ClassName**)ret) = (obj) ? new (*(ClassName**)ret) ClassName[nary] - // : new ClassName[nary]; - // } - // else { + // Array branch. The is_arena side emits a loop of scalar placement + // calls rather than `new (p) T[n]`. Two alternatives were considered + // and rejected: + // + // (a) Forward-declare a tagged + // `operator new[](size_t, void*, __clang_Interpreter_NewTag)` + // and emit `new (p, __ci_newtag) T[n]`. Cheapest in per-wrapper + // emission, but clang does not recognise a custom-signature + // array allocator as the standard placement form and inserts + // an array cookie for types with non-trivial destructors + // (Itanium C++ ABI §2.7), breaking the + // `Cpp::Construct(scope, arena, n) == arena` contract. + // + // (b) Forward-declare the STANDARD-signature + // `operator new[](size_t, void*)`. Clang would signature-match + // this as the placement form (no cookie), but the declaration + // is not portably replicable: libstdc++, libc++, and the MSVC + // STL decorate it with different noexcept macros, calling + // conventions, and `[[nodiscard]]` attributes. A + // user-supplied `#include ` after interpreter creation + // would clash with our declaration and crash the parse. + // + // The loop binds against the already-declared scalar tagged + // placement operator (PlacementTag()), adds only O(6) lines per + // wrapper, and works on cling too (`` is pre-included there, + // so the empty tag is equivalent to plain scalar placement new). if (CD->isDefaultConstructor()) { callbuf << "if (nary > 1) {\n"; indent(callbuf, indent_level); - callbuf << "(*(" << class_name << "**)ret) = "; - callbuf << "(is_arena) ? new (*(" << class_name << "**)ret) "; - make_narg_ctor(FD, N, typedefbuf, callbuf, class_name, indent_level, - true); - - callbuf << ": new "; - // - // Write the actual expression. - // + callbuf << "if (is_arena)\n"; + indent(callbuf, indent_level + 1); + callbuf << "for (unsigned long __i = 0; __i < nary; ++__i)\n"; + indent(callbuf, indent_level + 2); + callbuf << "::new ((void*)(*(" << class_name << "**)ret + __i)" + << PlacementTag() << ") " << class_name << "();\n"; + indent(callbuf, indent_level); + callbuf << "else (*(" << class_name << "**)ret) = new "; make_narg_ctor(FD, N, typedefbuf, callbuf, class_name, indent_level, true); - // - // End the new expression statement. - // callbuf << ";\n"; indent(callbuf, indent_level); callbuf << "}\n"; callbuf << "else {\n"; } - // Standard branch: - // (*(ClassName**)ret) = (obj) ? new (*(ClassName**)ret) ClassName(args...) - // : new ClassName(args...); + // Standard (scalar) branch: + // (*(ClassName**)ret) = (is_arena) + // ? (::new (*(ClassName**)ret[, __ci_newtag]) ClassName(args...)) + // : new ClassName(args...); + // Parens around `::new` are required: clang mis-parses `? ::new` + // inside a conditional expression and reports `expected expression` + // at the `:`. indent(callbuf, indent_level); callbuf << "(*(" << class_name << "**)ret) = "; - callbuf << "(is_arena) ? new (*(" << class_name << "**)ret) "; + callbuf << "(is_arena) ? (::new (*(" << class_name << "**)ret" + << PlacementTag() << ") "; make_narg_ctor(FD, N, typedefbuf, callbuf, class_name, indent_level); - callbuf << ": new "; + callbuf << ") : new "; // // Write the actual expression. // @@ -3182,7 +3246,8 @@ void make_narg_call_with_return(compat::Interpreter& I, const FunctionDecl* FD, // Write the placement part of the placement new. // indent(callbuf, indent_level); - callbuf << "new (ret) "; + // See PlacementTag for the rationale of the tag and the `::`. + callbuf << "::new (ret" << PlacementTag() << ") "; // // Write the type part of the placement new. // @@ -4168,6 +4233,38 @@ TInterp_t CreateInterpreter(const std::vector& Args /*={}*/, #if !defined(CPPINTEROP_USE_CLING) && !defined(EMSCRIPTEN) DefineAbsoluteSymbol(*I, "__ci_newtag", reinterpret_cast(&__ci_newtag)); + + // Register our forwarding definition of the tagged placement + // `operator new(size_t, void*, __clang_Interpreter_NewTag)`. Look up + // the Decl introduced by clang-repl's Runtimes string, compute its + // mangled name, and point it at CppInterOpPlacementNew above. This + // shields embedders that do not expose libclangInterpreter's symbols + // (e.g. cppyy, or the DispatchTests binary which dlopens us with + // RTLD_LOCAL) from `Symbols not found: + // [ _ZnwmPv26__clang_Interpreter_NewTag ]` at JIT link time. The + // regression is caught by DispatchSmokeTest.PlacementConstructTaggedNew. + { + Sema& S = I->getSema(); + ASTContext& Ctx = S.getASTContext(); + DeclarationName DN = Ctx.DeclarationNames.getCXXOperatorName(OO_New); + LookupResult R(S, DN, SourceLocation(), Sema::LookupOrdinaryName); + S.LookupQualifiedName(R, Ctx.getTranslationUnitDecl()); + for (auto* D : R) { + auto* FD = dyn_cast(D); + if (!FD || FD->getNumParams() != 3) + continue; + QualType LastTy = FD->getParamDecl(2)->getType(); + const auto* RD = LastTy->getAsCXXRecordDecl(); + if (!RD || RD->getName() != "__clang_Interpreter_NewTag") + continue; + std::string mangled; + compat::maybeMangleDeclName(GlobalDecl(FD), mangled); + DefineAbsoluteSymbol(*I, mangled.c_str(), + reinterpret_cast(&CppInterOpPlacementNew)); + break; + } + } + // llvm >= 21 has this defined as a C symbol that does not require mangling #if CLANG_VERSION_MAJOR >= 21 DefineAbsoluteSymbol( diff --git a/unittests/CppInterOp/DispatchSmokeTest.cpp b/unittests/CppInterOp/DispatchSmokeTest.cpp index 63924449c..3024e2a84 100644 --- a/unittests/CppInterOp/DispatchSmokeTest.cpp +++ b/unittests/CppInterOp/DispatchSmokeTest.cpp @@ -103,7 +103,7 @@ TEST(DispatchSmokeTest, TemplateInstantiation) { // --- Construct / Destruct --- TEST(DispatchSmokeTest, ConstructDestruct) { - Cpp::CreateInterpreter({"-include", "new"}); + Cpp::CreateInterpreter({}); Cpp::Declare("struct DispObj { int x = 7; };"); auto* scope = Cpp::GetNamed("DispObj"); @@ -114,6 +114,59 @@ TEST(DispatchSmokeTest, ConstructDestruct) { Cpp::Destruct(obj, scope, /*withFree=*/true); } +// End-to-end guard: after the JitCall wrapper is switched to emit +// `, __ci_newtag` in scalar placement-new expressions, `Cpp::Construct` +// on a user-supplied arena must land the object at the provided address +// (no array cookie, no extra indirection). TaggedPlacementNewResolvable +// above already pins the JIT-link side; this test pins the wrapper +// emission side. Fires if a future change drops the tag, emits a +// custom-signature array allocator that inserts an Itanium ABI cookie +// (Itanium C++ ABI S2.7), or otherwise violates `Construct(s,a) == a`. +TEST(DispatchSmokeTest, PlacementConstructTaggedNew) { + Cpp::CreateInterpreter({}); + Cpp::Declare("struct DispPlace { int x = 42; };"); + + auto* scope = Cpp::GetNamed("DispPlace"); + ASSERT_NE(scope, nullptr); + + void* arena = Cpp::Allocate(scope); + ASSERT_NE(arena, nullptr); + + EXPECT_EQ(Cpp::Construct(scope, arena), arena); + EXPECT_EQ(*reinterpret_cast(arena), 42); + + Cpp::Destruct(arena, scope, /*withFree=*/false, 0); + Cpp::Deallocate(scope, arena); +} + +// Regression guard for the tagged placement-new JIT-link path. clang-repl's +// Runtimes string declares `operator new(size_t, void*, +// __clang_Interpreter_NewTag)`, and the definition lives in +// libclangInterpreter. This binary loads libclangCppInterOp via +// dlopen(RTLD_LOCAL) and does not link it directly, so the definition is +// NOT reachable through the process symbol table. The only resolution +// path is the DefineAbsoluteSymbol registration CppInterOp performs at +// interpreter creation; if that registration is lost (or its name is +// interned without the platform's global prefix), JIT link fails here +// with `Symbols not found: [ _ZnwmPv26__clang_Interpreter_NewTag ]`. +// The test drives the lookup directly via user-level code rather than +// through a JitCall wrapper so it fires whether or not the wrapper +// emitter has been switched to emit the tagged form. +TEST(DispatchSmokeTest, TaggedPlacementNewResolvable) { +#ifdef CPPINTEROP_USE_CLING + GTEST_SKIP() << "Cling does not use the __ci_newtag overload."; +#endif + Cpp::CreateInterpreter({}); + ASSERT_EQ(0, Cpp::Declare("struct DispTagProbe { int x = 0; };")); + EXPECT_EQ(0, Cpp::Process("char __buf[sizeof(DispTagProbe)];\n" + "new (__buf, __ci_newtag) DispTagProbe();\n")) + << "Tagged placement-new resolution failed. If the JIT reports " + "'Symbols not found: _ZnwmPv26__clang_Interpreter_NewTag', the " + "CppInterOpPlacementNew forwarding definition is no longer " + "registered with the JIT dylib (or the name is interned without " + "the target's global-symbol prefix)."; +} + // --- Enum --- TEST(DispatchSmokeTest, EnumReflection) { diff --git a/unittests/CppInterOp/FunctionReflectionTest.cpp b/unittests/CppInterOp/FunctionReflectionTest.cpp index 8b0771a77..e6d8d7aa4 100644 --- a/unittests/CppInterOp/FunctionReflectionTest.cpp +++ b/unittests/CppInterOp/FunctionReflectionTest.cpp @@ -693,8 +693,11 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_ExistsFunctionTemplate) { TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_InstantiateTemplateFunctionFromString) { - std::vector interpreter_args = { "-include", "new" }; - TestFixture::CreateInterpreter(interpreter_args); +#if CLANG_VERSION_MAJOR < 22 + if (llvm::sys::RunningOnValgrind()) + GTEST_SKIP() << "XFAIL due to Valgrind report"; +#endif + TestFixture::CreateInterpreter(); std::string code = R"(#include )"; Interp->process(code); const char* str = "std::make_unique"; @@ -1536,7 +1539,8 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_GetFunctionAddress) { std::vector Decls; std::string code = "int f1(int i) { return i * i; }"; - std::vector interpreter_args = {"-include", "new", "-Xclang", "-iwithsysroot/include/compat"}; + std::vector interpreter_args = {"-Xclang", + "-iwithsysroot/include/compat"}; GetAllTopLevelDecls(code, Decls, /*filter_implicitGenerated=*/false, interpreter_args); @@ -1592,6 +1596,35 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_IsVirtualMethod) { EXPECT_FALSE(Cpp::IsVirtualMethod(Decls[0])); } +// Regression guard: JitCall wrappers must not require the user's TU to +// pull in . This test adds an explicit premise check and one +// scalar-return JitCall so a refactor that accidentally makes +// reachable cannot silently mask a future revert of the wrapper contract. +TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_JitCallNoNewHeader) { +#ifdef CPPINTEROP_USE_CLING + GTEST_SKIP() << "Cling pre-includes ."; +#endif + if (TypeParam::isOutOfProcess) + GTEST_SKIP() << "Test fails for OOP JIT builds"; + + TestFixture::CreateInterpreter(); + + // Per [new.delete.placement] the standard placement overloads are + // declared `noexcept`; this non-noexcept redeclaration only parses + // if is NOT in scope. + ASSERT_EQ(0, Cpp::Declare("void* operator new(__SIZE_TYPE__, void*);")); + + // One JitCall is enough — if the wrapper regresses to plain placement + // new, MakeFunctionCallable fails to compile without . + Cpp::Declare("int jc_sq(int x) { return x * x; }"); + auto JC = Cpp::MakeFunctionCallable(Cpp::GetNamed("jc_sq")); + ASSERT_TRUE(JC.isValid()); + int arg = 5, ret = 0; + void* args[] = {&arg}; + JC.Invoke(&ret, {args, 1}); + EXPECT_EQ(ret, 25); +} + TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_JitCallAdvanced) { #if CLANG_VERSION_MAJOR == 20 && defined(CPPINTEROP_USE_CLING) && defined(_WIN32) GTEST_SKIP() << "Test fails with Cling on Windows"; @@ -1615,10 +1648,7 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_JitCallAdvanced) { } name; )"; - std::vector interpreter_args = {"-include", "new"}; - - GetAllTopLevelDecls(code, Decls, /*filter_implicitGenerated=*/false, - interpreter_args); + GetAllTopLevelDecls(code, Decls, /*filter_implicitGenerated=*/false); auto *CtorD = (clang::CXXConstructorDecl*)Cpp::GetDefaultConstructor(Decls[0]); auto Ctor = Cpp::MakeFunctionCallable(CtorD); @@ -1646,8 +1676,7 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_JitCallDebug) { } };)"; - std::vector interpreter_args = {"-include", "new", - "-debug-only=jitcall"}; + std::vector interpreter_args = {"-debug-only=jitcall"}; GetAllTopLevelDecls(code, Decls, /*filter_implicitGenerated=*/false, interpreter_args); @@ -1747,8 +1776,7 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_GetFunctionCallWrapper) { int f1(int i) { return i * i; } )"; - std::vector interpreter_args = {"-include", "new"}; - + std::vector interpreter_args; GetAllTopLevelDecls(code, Decls, /*filter_implicitGenerated=*/false, interpreter_args); @@ -2402,11 +2430,9 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_Construct) { #endif if (TypeParam::isOutOfProcess) GTEST_SKIP() << "Test fails for OOP JIT builds"; - std::vector interpreter_args = {"-include", "new"}; std::vector Decls, SubDecls; std::string code = R"( - #include extern "C" int printf(const char*,...); class C { public: @@ -2419,7 +2445,7 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_Construct) { void construct() { return; } )"; - GetAllTopLevelDecls(code, Decls, false, interpreter_args); + GetAllTopLevelDecls(code, Decls, false); GetAllSubDecls(Decls[1], SubDecls); testing::internal::CaptureStdout(); Cpp::TCppScope_t scope = Cpp::GetNamed("C"); @@ -2477,8 +2503,7 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_ConstructPOD) { #endif if (TypeParam::isOutOfProcess) GTEST_SKIP() << "Test fails for OOP JIT builds"; - std::vector interpreter_args = {"-include", "new"}; - TestFixture::CreateInterpreter(interpreter_args); + TestFixture::CreateInterpreter(); Interp->declare(R"( namespace PODS { @@ -2523,11 +2548,9 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_ConstructNested) { if (TypeParam::isOutOfProcess) GTEST_SKIP() << "Test fails for OOP JIT builds"; - std::vector interpreter_args = {"-include", "new"}; - TestFixture::CreateInterpreter(interpreter_args); + TestFixture::CreateInterpreter(); Interp->declare(R"( - #include extern "C" int printf(const char*,...); class A { public: @@ -2587,7 +2610,6 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_ConstructArray) { TestFixture::CreateInterpreter(); Interp->declare(R"( - #include extern "C" int printf(const char*,...); class C { int x; @@ -2637,11 +2659,9 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_Destruct) { if (TypeParam::isOutOfProcess) GTEST_SKIP() << "Test fails for OOP JIT builds"; - std::vector interpreter_args = {"-include", "new"}; - TestFixture::CreateInterpreter(interpreter_args); + TestFixture::CreateInterpreter(); Interp->declare(R"( - #include extern "C" int printf(const char*,...); class C { C() {} @@ -2699,11 +2719,9 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_DestructArray) { if (TypeParam::isOutOfProcess) GTEST_SKIP() << "Test fails for OOP JIT builds"; - std::vector interpreter_args = {"-include", "new"}; - TestFixture::CreateInterpreter(interpreter_args); + TestFixture::CreateInterpreter(); Interp->declare(R"( - #include extern "C" int printf(const char*,...); class C { int x; @@ -2765,6 +2783,70 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_DestructArray) { output.clear(); } +// Regression guard for Itanium C++ ABI §2.7 array cookies. Any +// placement `operator new[]` that clang does not recognise as the +// standard placement form (standard signature, declared in ) +// causes clang to insert an array cookie before the storage when the +// element type has a non-trivial destructor. That would shift the +// pointer returned by `Cpp::Construct(scope, arena, n)` past the +// cookie, breaking the documented contract that the return equals +// `arena`. The JitCall ctor wrapper works around this by emitting a +// loop of scalar placement news rather than a single +// `new (p) T[n]` with a custom-signature allocator — see the comment +// on `make_narg_ctor_with_return` in lib/CppInterOp/CppInterOp.cpp. +// This test fails immediately if that loop is ever replaced with a +// tagged `operator new[]` or a non-standard forward declaration. +TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_ArrayConstructNoCookie) { +#ifdef EMSCRIPTEN + GTEST_SKIP() << "Test fails for Emscripten builds"; +#endif +#if CLANG_VERSION_MAJOR < 22 + if (llvm::sys::RunningOnValgrind()) + GTEST_SKIP() << "XFAIL due to Valgrind report"; +#endif +#ifdef _WIN32 + GTEST_SKIP() << "Disabled on Windows. Needs fixing."; +#endif + if (TypeParam::isOutOfProcess) + GTEST_SKIP() << "Test fails for OOP JIT builds"; + + TestFixture::CreateInterpreter(); + + // CookieProbe has a user-provided destructor, which makes it + // non-trivially destructible. Any non-placement-form `operator new[]` + // must reserve space for an array cookie in front of the storage. + Interp->declare(R"( + struct CookieProbe { + int v; + CookieProbe() : v(0xC0DE) {} + ~CookieProbe() {} + }; + )"); + + auto* scope = Cpp::GetNamed("CookieProbe"); + ASSERT_NE(scope, nullptr); + + constexpr size_t kN = 4; + void* arena = Cpp::Allocate(scope, kN); + ASSERT_NE(arena, nullptr); + + // Placement array construction must return the arena as-is. + EXPECT_EQ(Cpp::Construct(scope, arena, kN), arena) + << "Construct returned a pointer offset from arena; an array " + "cookie has been inserted. Placement-new signature regressed."; + + // Cross-check: the i-th element's field lives at offset i*sizeof(T) + // from the arena base (not past a cookie header). + const size_t T = Cpp::SizeOf(scope); + for (size_t i = 0; i < kN; ++i) { + int* slot = reinterpret_cast(reinterpret_cast(arena) + i * T); + EXPECT_EQ(*slot, 0xC0DE) << "element " << i << " not at expected offset"; + } + + Cpp::Destruct(arena, scope, /*withFree=*/false, kN); + Cpp::Deallocate(scope, arena, kN); +} + TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_UndoTest) { #ifdef _WIN32 GTEST_SKIP() << "Disabled on Windows. Needs fixing."; @@ -2934,7 +3016,7 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_IsExplicitTemplated) { TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_IsExplicitDeductionGuide) { // Deduction guides are a C++17 feature - std::vector interpreter_args = {"-include", "new", "-std=c++17"}; + std::vector interpreter_args = {"-std=c++17"}; Cpp::CreateInterpreter(interpreter_args, {}); Interp->declare(R"( diff --git a/unittests/CppInterOp/InterpreterTest.cpp b/unittests/CppInterOp/InterpreterTest.cpp index 8b527793a..a8e537e0b 100644 --- a/unittests/CppInterOp/InterpreterTest.cpp +++ b/unittests/CppInterOp/InterpreterTest.cpp @@ -252,7 +252,12 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, Interpreter_Process) { #endif if (TypeParam::isOutOfProcess) GTEST_SKIP() << "Test fails for OOP JIT builds"; - std::vector interpreter_args = { "-include", "new", "-Xclang", "-iwithsysroot/include/compat" }; +#if CLANG_VERSION_MAJOR < 22 + if (llvm::sys::RunningOnValgrind()) + GTEST_SKIP() << "XFAIL due to Valgrind report"; +#endif + std::vector interpreter_args = {"-Xclang", + "-iwithsysroot/include/compat"}; TestFixture::CreateInterpreter(interpreter_args); EXPECT_TRUE(Cpp::Process("") == 0); EXPECT_TRUE(Cpp::Process("int a = 12;") == 0); @@ -611,7 +616,6 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, Interpreter_WrapperCacheIsPerInterpreter) { auto* AddDecl1 = Cpp::GetNamed("add"); ASSERT_NE(AddDecl1, nullptr); - Cpp::Declare("#include "); // Needed by JitCall auto JC1 = Cpp::MakeFunctionCallable(AddDecl1); ASSERT_TRUE(JC1.isValid()); @@ -628,7 +632,6 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, Interpreter_WrapperCacheIsPerInterpreter) { auto* AddDecl2 = Cpp::GetNamed("add"); ASSERT_NE(AddDecl2, nullptr); - Cpp::Declare("#include "); // Needed by JitCall auto JC2 = Cpp::MakeFunctionCallable(AddDecl2); ASSERT_TRUE(JC2.isValid()); diff --git a/unittests/CppInterOp/ScopeReflectionTest.cpp b/unittests/CppInterOp/ScopeReflectionTest.cpp index 36e4ea432..e9ae7318f 100644 --- a/unittests/CppInterOp/ScopeReflectionTest.cpp +++ b/unittests/CppInterOp/ScopeReflectionTest.cpp @@ -313,9 +313,7 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, ScopeReflection_IsBuiltin) { // "int", "unsigned int", "long", "unsigned long", "long long", "unsigned long long", // "float", "double", "long double", "void"} - std::vector interpreter_args = { "-include", "new" }; - - TestFixture::CreateInterpreter(interpreter_args); + TestFixture::CreateInterpreter(); ASTContext &C = Interp->getCI()->getASTContext(); EXPECT_TRUE(Cpp::IsBuiltin(C.BoolTy.getAsOpaquePtr())); EXPECT_TRUE(Cpp::IsBuiltin(C.CharTy.getAsOpaquePtr())); @@ -675,9 +673,7 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, ScopeReflection_GetNamed) { } )"; - std::vector interpreter_args = {"-include", "new"}; - - TestFixture::CreateInterpreter(interpreter_args); + TestFixture::CreateInterpreter(); Interp->declare(code); Cpp::TCppScope_t ns_N1 = Cpp::GetNamed("N1", nullptr); @@ -1141,8 +1137,11 @@ template T TrivialFnTemplate() { return T(); } TYPED_TEST(CPPINTEROP_TEST_MODE, ScopeReflection_InstantiateTemplateFunctionFromString) { - std::vector interpreter_args = {"-include", "new"}; - TestFixture::CreateInterpreter(interpreter_args); +#if CLANG_VERSION_MAJOR < 22 + if (llvm::sys::RunningOnValgrind()) + GTEST_SKIP() << "XFAIL due to Valgrind report"; +#endif + TestFixture::CreateInterpreter(); std::string code = R"(#include )"; Interp->process(code); const char* str = "std::make_unique"; @@ -1317,8 +1316,7 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, ScopeReflection_IncludeVector) { #include #include )"; - std::vector interpreter_args = {"-include", "new"}; - TestFixture::CreateInterpreter(interpreter_args); + TestFixture::CreateInterpreter(); Interp->declare(code); } diff --git a/unittests/CppInterOp/TracingTests.cpp b/unittests/CppInterOp/TracingTests.cpp index 2d5c3d965..30d80280f 100644 --- a/unittests/CppInterOp/TracingTests.cpp +++ b/unittests/CppInterOp/TracingTests.cpp @@ -1221,8 +1221,6 @@ TEST_F(TracingTest, JitCallWrapperSourceLogged) { Cpp::CreateInterpreter({}); ASSERT_NE(TheTraceInfo, nullptr); - // Placement new requires . - Cpp::Declare("#include "); Cpp::Declare("namespace WrapNS { int add(int a, int b) { return a + b; } }"); auto* Func = static_cast(Cpp::GetNamed("add", Cpp::GetScope("WrapNS"))); @@ -1246,7 +1244,6 @@ TEST_F(TracingTest, JitCallInvokeLogged) { Cpp::CreateInterpreter({}); ASSERT_NE(TheTraceInfo, nullptr); - Cpp::Declare("#include "); Cpp::Declare("namespace InvNS { int square(int x) { return x * x; } }"); auto* Func = static_cast(Cpp::GetNamed("square", Cpp::GetScope("InvNS"))); diff --git a/unittests/CppInterOp/TypeReflectionTest.cpp b/unittests/CppInterOp/TypeReflectionTest.cpp index 6df0a6c6f..5145ac722 100644 --- a/unittests/CppInterOp/TypeReflectionTest.cpp +++ b/unittests/CppInterOp/TypeReflectionTest.cpp @@ -583,8 +583,7 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, TypeReflection_IsSmartPtrType) { GTEST_SKIP() << "Test fails with Cling on Windows"; #endif - std::vector interpreter_args = {"-include", "new"}; - TestFixture::CreateInterpreter(interpreter_args); + TestFixture::CreateInterpreter(); Interp->declare(R"( #include @@ -622,8 +621,7 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, TypeReflection_IsSmartPtrType) { } TYPED_TEST(CPPINTEROP_TEST_MODE, TypeReflection_IsFunctionPointerType) { - std::vector interpreter_args = {"-include", "new"}; - TestFixture::CreateInterpreter(interpreter_args); + TestFixture::CreateInterpreter(); Interp->declare(R"( typedef int (*int_func)(int, int); diff --git a/unittests/CppInterOp/VariableReflectionTest.cpp b/unittests/CppInterOp/VariableReflectionTest.cpp index a9dc9be89..d39b18daa 100644 --- a/unittests/CppInterOp/VariableReflectionTest.cpp +++ b/unittests/CppInterOp/VariableReflectionTest.cpp @@ -382,8 +382,7 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, VariableReflection_VariableOffsetsWithInheritan GTEST_SKIP() << "Test fails with Cling on Windows"; #endif - std::vector interpreter_args = {"-include", "new"}; - TestFixture::CreateInterpreter(interpreter_args); + TestFixture::CreateInterpreter(); Cpp::Declare("#include");