From 69371925a563908afdaf9b6169624ee628cbf366 Mon Sep 17 00:00:00 2001 From: keremsahn Date: Tue, 16 Jun 2026 19:23:25 +0300 Subject: [PATCH 1/2] Little Fix --- lib/CppInterOp/CppInterOp.cpp | 46 +++++++++++ lib/CppInterOp/CppInterOp.td | 16 ++++ .../CppInterOp/APINotes/TestHeader.apinotes | 6 ++ unittests/CppInterOp/APINotes/TestHeader.h | 7 ++ .../CppInterOp/APINotes/module.modulemap | 1 + unittests/CppInterOp/CMakeLists.txt | 3 + .../CppInterOp/FunctionReflectionTest.cpp | 80 +++++++++++++++++++ 7 files changed, 159 insertions(+) create mode 100644 unittests/CppInterOp/APINotes/TestHeader.apinotes create mode 100644 unittests/CppInterOp/APINotes/TestHeader.h create mode 100644 unittests/CppInterOp/APINotes/module.modulemap diff --git a/lib/CppInterOp/CppInterOp.cpp b/lib/CppInterOp/CppInterOp.cpp index 270487c4e..02645400d 100644 --- a/lib/CppInterOp/CppInterOp.cpp +++ b/lib/CppInterOp/CppInterOp.cpp @@ -53,6 +53,7 @@ #include "clang/AST/Stmt.h" #include "clang/AST/Type.h" #include "clang/AST/VTableBuilder.h" +#include "clang/Basic/Builtins.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticSema.h" @@ -1578,6 +1579,51 @@ TypeRef GetFunctionReturnType(ConstFuncRef func) { return INTEROP_RETURN(nullptr); } +bool IsAllocator(ConstFuncRef Fn) { + INTEROP_TRACE(Fn); + if (Fn) { + const auto* D = unwrap(Fn); + if (const auto* FD = dyn_cast(D)) { + if (FD->getBuiltinID() == Builtin::ID::BImalloc) + return INTEROP_RETURN(true); + if (const auto* FDA = FD->getAttr()) { + if (FDA->getSemanticSpelling() != RestrictAttr::Declspec_restrict) + return INTEROP_RETURN(true); + } + + if (const auto* FDA = FD->getAttr()) { + if (FDA->getOwnKind() == OwnershipAttr::Returns) + return INTEROP_RETURN(true); + } + // FIXME: These attributes are not currently compatible with our memory + // representation, they are added for APINotes' test + if (FD->hasAttr() || + FD->hasAttr() || + FD->hasAttr()) + return INTEROP_RETURN(true); + } + } + + return INTEROP_RETURN(false); +} + +bool IsDeallocator(ConstFuncRef Fn) { + INTEROP_TRACE(Fn); + if (Fn) { + const auto* D = unwrap(Fn); + if (const auto* FD = dyn_cast(D)) { + if (FD->getBuiltinID() == Builtin::ID::BIfree) + return INTEROP_RETURN(true); + if (const auto* FDA = FD->getAttr()) { + if (FDA->getOwnKind() == OwnershipAttr::Takes) + return INTEROP_RETURN(true); + } + } + } + + return INTEROP_RETURN(false); +} + bool IsFunctionProtoType(ConstTypeRef TyRef) { INTEROP_TRACE(TyRef); QualType QT = QualType::getFromOpaquePtr(TyRef.data); diff --git a/lib/CppInterOp/CppInterOp.td b/lib/CppInterOp/CppInterOp.td index e8a57a441..7abf77005 100644 --- a/lib/CppInterOp/CppInterOp.td +++ b/lib/CppInterOp/CppInterOp.td @@ -1262,6 +1262,22 @@ def GetFunctionReturnType : CppInterOpAPI { let Args = [Arg<"ConstFuncRef", "func">]; } +def IsAllocator : CppInterOpAPI { + let Doc = "Checks if the provided function is an allocator function"; + let ReturnType = "bool"; + let Args = [ + Arg<"ConstFuncRef", "func"> + ]; +} + +def IsDeallocator : CppInterOpAPI { + let Doc = "Checks if the provided function is a deallocator function"; + let ReturnType = "bool"; + let Args = [ + Arg<"ConstFuncRef", "func"> + ]; +} + def GetFunctionSignature : CppInterOpAPI { let Doc = [{\\returns a stringified version of a given function signature in the form: void N::f(int i, double d, long l = 0, char ch = 'a').}]; diff --git a/unittests/CppInterOp/APINotes/TestHeader.apinotes b/unittests/CppInterOp/APINotes/TestHeader.apinotes new file mode 100644 index 000000000..5f14e0a6f --- /dev/null +++ b/unittests/CppInterOp/APINotes/TestHeader.apinotes @@ -0,0 +1,6 @@ +Name: TestHeader +Functions: + - Name: testAlloc + RetainCountConvention: CFReturnsRetained + - Name: testNotAlloc + RetainCountConvention: CFReturnsNotRetained diff --git a/unittests/CppInterOp/APINotes/TestHeader.h b/unittests/CppInterOp/APINotes/TestHeader.h new file mode 100644 index 000000000..6478c632f --- /dev/null +++ b/unittests/CppInterOp/APINotes/TestHeader.h @@ -0,0 +1,7 @@ +#ifndef UNITTESTS_CPPINTEROP_APINOTES_TESTHEADER_H +#define UNITTESTS_CPPINTEROP_APINOTES_TESTHEADER_H + +void* testAlloc(int value); +void testNotAlloc(void* ptr); + +#endif diff --git a/unittests/CppInterOp/APINotes/module.modulemap b/unittests/CppInterOp/APINotes/module.modulemap new file mode 100644 index 000000000..27fce239b --- /dev/null +++ b/unittests/CppInterOp/APINotes/module.modulemap @@ -0,0 +1 @@ +module TestHeader {header "TestHeader.h"} diff --git a/unittests/CppInterOp/CMakeLists.txt b/unittests/CppInterOp/CMakeLists.txt index adb2acade..87492280a 100644 --- a/unittests/CppInterOp/CMakeLists.txt +++ b/unittests/CppInterOp/CMakeLists.txt @@ -85,6 +85,9 @@ if(TARGET CppInterOpTableGen) add_dependencies(CAPITestC CppInterOpTableGen) endif() target_link_libraries(CppInterOpTests PRIVATE CAPITestC) +target_compile_definitions(CppInterOpTests PRIVATE + "CPPINTEROP_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/../../\"" +) set_source_files_properties(InterpreterTest.cpp PROPERTIES COMPILE_DEFINITIONS "LLVM_BINARY_DIR=\"${LLVM_BINARY_DIR}\"" diff --git a/unittests/CppInterOp/FunctionReflectionTest.cpp b/unittests/CppInterOp/FunctionReflectionTest.cpp index 48e8609ed..de334de40 100644 --- a/unittests/CppInterOp/FunctionReflectionTest.cpp +++ b/unittests/CppInterOp/FunctionReflectionTest.cpp @@ -575,6 +575,86 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_GetFunctionReturnType) { "double"); } +TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_IsAllocator) { + std::vector Decls; + std::string code = R"( + class Klass{ + int val; + }; + __attribute__((ownership_returns(malloc))) + Klass* Allocator(){ + Klass* obj = new Klass; + return obj; + } + Klass* __attribute__((malloc)) Allocator2(){ + Klass* obj = new Klass; + return obj; + } + void foo(); + )"; + GetAllTopLevelDecls(code, Decls, true); + EXPECT_TRUE(Cpp::IsAllocator(Decls[1])); + EXPECT_TRUE(Cpp::IsAllocator(Decls[2])); + EXPECT_FALSE(Cpp::IsAllocator(Decls[3])); + // Builtin check + code = R"( + //There is nothing lstdlib.h is included at args + )"; + TestFixture::CreateInterpreter({"-include", "stdlib.h"}); + Interp->process(code); + auto mallocDecl = Cpp::GetNamed("malloc"); + EXPECT_TRUE(Cpp::IsAllocator(Cpp::ConstFuncRef{mallocDecl.data})); + Cpp::DeleteInterpreter(); + + // APINotes check +#if !defined(CPPINTEROP_USE_CLING) && !defined(__EMSCRIPTEN__) + std::string include_flag = + "-I" + std::string(CPPINTEROP_DIR) + "unittests/CppInterOp/APINotes"; + std::vector interpreter_args = { + "-fmodules", "-fimplicit-module-maps", "-fapinotes-modules", + include_flag.c_str()}; + TestFixture::CreateInterpreter(interpreter_args); + code = R"( + #include "TestHeader.h" + )"; + Interp->process(code); + auto testAllocDecl = Cpp::GetNamed("testAlloc"); + EXPECT_TRUE(Cpp::IsAllocator(Cpp::ConstFuncRef{testAllocDecl.data})); + + auto testNotAllocDecl = Cpp::GetNamed("testNotAlloc"); + EXPECT_FALSE(Cpp::IsAllocator(Cpp::ConstFuncRef{testNotAllocDecl.data})); + Cpp::DeleteInterpreter(); +#endif +} + +TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_IsDeallocator) { + std::vector Decls; + std::string code = R"( + class Klass{ + int val; + }; + __attribute__((ownership_takes(malloc, 1))) + void Deallocator(Klass* arg){ + delete arg; + } + void foo(); + )"; + GetAllTopLevelDecls(code, Decls, true); + EXPECT_TRUE(Cpp::IsDeallocator(Decls[1])); + EXPECT_FALSE(Cpp::IsDeallocator(Decls[2])); + + code = R"( + #include + void test(){ + //Do Nothing + } + )"; + TestFixture::CreateInterpreter(); + Interp->process(code); + auto freeDecl = Cpp::GetNamed("free"); + EXPECT_TRUE(Cpp::IsDeallocator(Cpp::ConstFuncRef{freeDecl.data})); +} + TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_GetFunctionNumArgs) { std::vector Decls, TemplateSubDecls; std::string code = R"( From a60399fe970d6b6476c78f3b38eb19ad3d1f2b3b Mon Sep 17 00:00:00 2001 From: keremsahn Date: Thu, 25 Jun 2026 21:49:48 +0300 Subject: [PATCH 2/2] Fix: Early return --- lib/CppInterOp/CppInterOp.cpp | 49 +++++++++++++++++------------------ 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/lib/CppInterOp/CppInterOp.cpp b/lib/CppInterOp/CppInterOp.cpp index 02645400d..4b917d4b5 100644 --- a/lib/CppInterOp/CppInterOp.cpp +++ b/lib/CppInterOp/CppInterOp.cpp @@ -1581,27 +1581,26 @@ TypeRef GetFunctionReturnType(ConstFuncRef func) { bool IsAllocator(ConstFuncRef Fn) { INTEROP_TRACE(Fn); - if (Fn) { - const auto* D = unwrap(Fn); - if (const auto* FD = dyn_cast(D)) { - if (FD->getBuiltinID() == Builtin::ID::BImalloc) + if (!Fn) + return INTEROP_RETURN(false); + const auto* D = unwrap(Fn); + if (const auto* FD = dyn_cast(D)) { + if (FD->getBuiltinID() == Builtin::ID::BImalloc) + return INTEROP_RETURN(true); + if (const auto* FDA = FD->getAttr()) { + if (FDA->getSemanticSpelling() != RestrictAttr::Declspec_restrict) return INTEROP_RETURN(true); - if (const auto* FDA = FD->getAttr()) { - if (FDA->getSemanticSpelling() != RestrictAttr::Declspec_restrict) - return INTEROP_RETURN(true); - } + } - if (const auto* FDA = FD->getAttr()) { - if (FDA->getOwnKind() == OwnershipAttr::Returns) - return INTEROP_RETURN(true); - } - // FIXME: These attributes are not currently compatible with our memory - // representation, they are added for APINotes' test - if (FD->hasAttr() || - FD->hasAttr() || - FD->hasAttr()) + if (const auto* FDA = FD->getAttr()) { + if (FDA->getOwnKind() == OwnershipAttr::Returns) return INTEROP_RETURN(true); } + + if (FD->hasAttr() || + FD->hasAttr() || + FD->hasAttr()) + return INTEROP_RETURN(true); } return INTEROP_RETURN(false); @@ -1609,15 +1608,15 @@ bool IsAllocator(ConstFuncRef Fn) { bool IsDeallocator(ConstFuncRef Fn) { INTEROP_TRACE(Fn); - if (Fn) { - const auto* D = unwrap(Fn); - if (const auto* FD = dyn_cast(D)) { - if (FD->getBuiltinID() == Builtin::ID::BIfree) + if (!Fn) + INTEROP_RETURN(false); + const auto* D = unwrap(Fn); + if (const auto* FD = dyn_cast(D)) { + if (FD->getBuiltinID() == Builtin::ID::BIfree) + return INTEROP_RETURN(true); + if (const auto* FDA = FD->getAttr()) { + if (FDA->getOwnKind() == OwnershipAttr::Takes) return INTEROP_RETURN(true); - if (const auto* FDA = FD->getAttr()) { - if (FDA->getOwnKind() == OwnershipAttr::Takes) - return INTEROP_RETURN(true); - } } }