Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a libclang-style C API #337

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

Gnimuc
Copy link
Contributor

@Gnimuc Gnimuc commented Oct 27, 2024

Since there is a substantial overlap with libclang’s API, I am only exposing the API I currently use in this PR.

This PR introduces two types, CXScope and CXQualType, which wrap TCppScope_t and TCppType_t, respectively. The structure of these two types is the same as libclang's CXCursor/CXType, with the only difference being that they store a handle to the interpreter instead of a CXTranslationUnit. This paves the way for upstreaming changes to libclang.

Copy link

codecov bot commented Oct 27, 2024

Codecov Report

Attention: Patch coverage is 22.46575% with 283 lines in your changes missing coverage. Please review.

Project coverage is 69.02%. Comparing base (9b5f4c5) to head (7be80d8).
Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
lib/Interpreter/CXCppInterOp.cpp 22.46% 283 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #337      +/-   ##
==========================================
- Coverage   74.43%   69.02%   -5.42%     
==========================================
  Files           8        9       +1     
  Lines        3204     3574     +370     
==========================================
+ Hits         2385     2467      +82     
- Misses        819     1107     +288     
Files with missing lines Coverage Δ
lib/Interpreter/CXCppInterOp.cpp 22.46% <22.46%> (ø)

... and 2 files with indirect coverage changes

Files with missing lines Coverage Δ
lib/Interpreter/CXCppInterOp.cpp 22.46% <22.46%> (ø)

... and 2 files with indirect coverage changes

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

clang-tidy made some suggestions

There were too many comments to post at once. Showing the first 10 out of 41. Check the log or trigger a new build to see more.

include/clang-c/CXCppInterOp.h Show resolved Hide resolved
include/clang-c/CXCppInterOp.h Show resolved Hide resolved
include/clang-c/CXCppInterOp.h Show resolved Hide resolved
include/clang-c/CXCppInterOp.h Show resolved Hide resolved
include/clang-c/CXCppInterOp.h Show resolved Hide resolved
include/clang-c/CXCppInterOp.h Show resolved Hide resolved
include/clang-c/CXCppInterOp.h Show resolved Hide resolved
include/clang-c/CXCppInterOp.h Show resolved Hide resolved
include/clang-c/CXCppInterOp.h Show resolved Hide resolved
include/clang-c/CXCppInterOp.h Show resolved Hide resolved
Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

clang-tidy made some suggestions

There were too many comments to post at once. Showing the first 10 out of 24. Check the log or trigger a new build to see more.

include/clang-c/CXCppInterOp.h Show resolved Hide resolved
lib/Interpreter/CXCppInterOp.cpp Show resolved Hide resolved
lib/Interpreter/CXCppInterOp.cpp Show resolved Hide resolved
lib/Interpreter/CXCppInterOp.cpp Show resolved Hide resolved
lib/Interpreter/CXCppInterOp.cpp Show resolved Hide resolved
lib/Interpreter/CXCppInterOp.cpp Show resolved Hide resolved
lib/Interpreter/CXCppInterOp.cpp Show resolved Hide resolved
static inline bool isNull(const CXScope& S) { return !S.data[0]; }

static inline clang::Decl* getDecl(const CXScope& S) {
return const_cast<clang::Decl*>(static_cast<const clang::Decl*>(S.data[0]));

Choose a reason for hiding this comment

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

warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]

  return const_cast<clang::Decl*>(static_cast<const clang::Decl*>(S.data[0]));
         ^

auto* I = getInterpreter(scope);
if (const Cpp::JitCall JC = Cpp::MakeFunctionCallableImpl(I, getDecl(Ctor))) {
if (arena) {
JC.Invoke(&arena, {}, (void*)~0); // Tell Invoke to use placement new.

Choose a reason for hiding this comment

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

warning: do not use C-style cast to convert between unrelated types [cppcoreguidelines-pro-type-cstyle-cast]

      JC.Invoke(&arena, {}, (void*)~0); // Tell Invoke to use placement new.
                            ^

auto* I = getInterpreter(scope);
if (const Cpp::JitCall JC = Cpp::MakeFunctionCallableImpl(I, getDecl(Ctor))) {
if (arena) {
JC.Invoke(&arena, {}, (void*)~0); // Tell Invoke to use placement new.

Choose a reason for hiding this comment

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

warning: integer to pointer cast pessimizes optimization opportunities [performance-no-int-to-ptr]

      JC.Invoke(&arena, {}, (void*)~0); // Tell Invoke to use placement new.
                            ^

Copy link
Contributor

@vgvassilev vgvassilev left a comment

Choose a reason for hiding this comment

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

Looks like we are quite close. Can we silence the codecov somehow without duplicating tests? Can we move the refactorings in CppInterOp.cpp as a separate PR?

*
* \param prepend Whether to prepend the directory to the search path.
*/
void clang_interpreter_addSearchPath(CXInterpreter I, const char* dir,
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we want to shorten these interfaces. Maybe Cpp_AddSearchPath?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since we only expose opaque pointers in the C API, I believe libclang uses this naming style to warn users about the type of the input argument. However, for historical reasons, libclang appears to lack strict rules on capitalization.

The naming convention is like clang_ + ClassName + methodName or clang_ + functionName.

Here are some examples:

typedef void *CXIndexAction;
CINDEX_LINKAGE CXIndexAction clang_IndexAction_create(CXIndex CIdx);
CINDEX_LINKAGE void clang_IndexAction_dispose(CXIndexAction);

CINDEX_LINKAGE CXIdxClientContainer
clang_index_getClientContainer(const CXIdxContainerInfo *);

CINDEX_LINKAGE unsigned clang_CXXMethod_isDeleted(CXCursor C);

CINDEX_LINKAGE CXString clang_Module_getName(CXModule Module);

CINDEX_LINKAGE CXCursor clang_getCanonicalCursor(CXCursor);

CINDEX_LINKAGE unsigned
clang_PrintingPolicy_getProperty(CXPrintingPolicy Policy, enum CXPrintingPolicyProperty Property);

CINDEX_LINKAGE void
clang_PrintingPolicy_setProperty(CXPrintingPolicy Policy,
                                 enum CXPrintingPolicyProperty Property,
                                 unsigned Value);

CINDEX_LINKAGE CXPrintingPolicy clang_getCursorPrintingPolicy(CXCursor);

CINDEX_LINKAGE void clang_PrintingPolicy_dispose(CXPrintingPolicy Policy);

CINDEX_LINKAGE CXString clang_getCursorPrettyPrinted(CXCursor Cursor, CXPrintingPolicy Policy);

@@ -262,16 +264,16 @@ namespace Cpp {

/// This is similar to GetName() function, but besides
/// the name, it also gets the template arguments.
CPPINTEROP_API std::string GetCompleteName(TCppType_t klass);
CPPINTEROP_API std::string GetCompleteName(TCppScope_t klass);
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks! We should probably adopt the CX enum style so that the compiler could help us with finding these wrong cases. Related to compiler-research/CPyCppyy#8.

lib/Interpreter/CXCppInterOp.cpp Show resolved Hide resolved
lib/Interpreter/CppInterOp.cpp Outdated Show resolved Hide resolved
@Gnimuc
Copy link
Contributor Author

Gnimuc commented Oct 27, 2024

Looks like we are quite close. Can we silence the codecov somehow without duplicating tests? Can we move the refactorings in CppInterOp.cpp as a separate PR?

Yes. I'll make another PR for the changes in CppInterOp.cpp.

@Gnimuc Gnimuc force-pushed the add-c-api2 branch 3 times, most recently from 66ccc69 to 1bb9ead Compare October 27, 2024 14:56
Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

clang-tidy made some suggestions

There were too many comments to post at once. Showing the first 10 out of 12. Check the log or trigger a new build to see more.

@@ -98,6 +100,19 @@ TEST(InterpreterTest, CreateInterpreter) {
"#endif");
EXPECT_TRUE(Cpp::GetNamed("cpp17"));
EXPECT_FALSE(Cpp::GetNamed("cppUnknown"));

// C API
const char* args[] = {"-std=c++14"};

Choose a reason for hiding this comment

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

warning: do not declare C-style arrays, use std::array<> instead [cppcoreguidelines-avoid-c-arrays]

  const char* args[] = {"-std=c++14"};
        ^


// C API
const char* args[] = {"-std=c++14"};
auto CXI = clang_createInterpreter(args, 1);

Choose a reason for hiding this comment

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

warning: 'auto CXI' can be declared as 'auto *CXI' [llvm-qualified-auto]

Suggested change
auto CXI = clang_createInterpreter(args, 1);
auto *CXI = clang_createInterpreter(args, 1);


// C API
const char* args[] = {"-std=c++14"};
auto CXI = clang_createInterpreter(args, 1);

Choose a reason for hiding this comment

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

warning: do not implicitly decay an array into a pointer; consider using gsl::array_view or an explicit cast instead [cppcoreguidelines-pro-bounds-array-to-pointer-decay]

  auto CXI = clang_createInterpreter(args, 1);
                                     ^

clang_Interpreter_dispose(CXI);

CXI = clang_createInterpreterFromRawPtr(I);
auto CLI = clang_Interpreter_getClangInterpreter(CXI);

Choose a reason for hiding this comment

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

warning: 'auto CLI' can be declared as 'auto *CLI' [llvm-qualified-auto]

Suggested change
auto CLI = clang_Interpreter_getClangInterpreter(CXI);
auto *CLI = clang_Interpreter_getClangInterpreter(CXI);

CXI = clang_createInterpreterFromRawPtr(I);
auto CLI = clang_Interpreter_getClangInterpreter(CXI);
EXPECT_TRUE(CLI);
auto I2 = clang_Interpreter_takeInterpreterAsPtr(CXI);

Choose a reason for hiding this comment

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

warning: 'auto I2' can be declared as 'auto *I2' [llvm-qualified-auto]

Suggested change
auto I2 = clang_Interpreter_takeInterpreterAsPtr(CXI);
auto *I2 = clang_Interpreter_takeInterpreterAsPtr(CXI);


void dispose_string(CXString string) {
if (string.private_flags == 1 && string.data)
free(const_cast<void*>(string.data));

Choose a reason for hiding this comment

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

warning: calling legacy resource function without passing a 'gsl::owner<>' [cppcoreguidelines-owning-memory]

    free(const_cast<void*>(string.data));
    ^


void dispose_string(CXString string) {
if (string.private_flags == 1 && string.data)
free(const_cast<void*>(string.data));

Choose a reason for hiding this comment

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

warning: do not manage memory manually; use RAII [cppcoreguidelines-no-malloc]

    free(const_cast<void*>(string.data));
    ^


void dispose_string(CXString string) {
if (string.private_flags == 1 && string.data)
free(const_cast<void*>(string.data));

Choose a reason for hiding this comment

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

warning: do not use const_cast [cppcoreguidelines-pro-type-const-cast]

    free(const_cast<void*>(string.data));
         ^

free(const_cast<void*>(string.data));
}

CXScope make_scope(const clang::Decl* D, const CXInterpreter I) {

Choose a reason for hiding this comment

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

warning: 'I' declared with a const-qualified typedef; results in the type being 'CXInterpreterImpl *const' instead of 'const CXInterpreterImpl *' [misc-misplaced-const]

CXScope make_scope(const clang::Decl* D, const CXInterpreter I) {
                                                             ^
Additional context

include/clang-c/CXCppInterOp.h:25: typedef declared here

typedef struct CXInterpreterImpl* CXInterpreter;
                                  ^

#include <memory>
#include <vector>
#include "llvm/Support/Valgrind.h"
#include "clang-c/CXCppInterOp.h"
#include "clang-c/CXString.h"

Choose a reason for hiding this comment

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

warning: #includes are not sorted properly [llvm-include-order]

Suggested change
#include "clang-c/CXString.h"
#include "clang-c/CXCppInterOp.h"
#include "clang-c/CXString.h"
#include "llvm/Support/Valgrind.h"
#include <memory>
#include <vector>

Comment on lines +482 to +491
if (!clang_hasDefaultConstructor(S))
return clang::cxscope::MakeCXScope(nullptr, getNewTU(S));

auto* CXXRD = llvm::dyn_cast_or_null<clang::CXXRecordDecl>(getDecl(S));
if (!CXXRD)
return clang::cxscope::MakeCXScope(nullptr, getNewTU(S));

const auto* Res =
getInterpreter(S)->getSema().LookupDefaultConstructor(CXXRD);
return clang::cxscope::MakeCXScope(Res, getNewTU(S));
Copy link
Contributor

Choose a reason for hiding this comment

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

Why can't we forward to Cpp::GetDefaultConstructor? I see this trend in several interfaces. Is there some limitation on the C++ side?

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

clang-tidy made some suggestions


void dispose_string(CXString string);

CXScope make_scope(const clang::Decl* D, const CXInterpreter I);

Choose a reason for hiding this comment

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

warning: 'I' declared with a const-qualified typedef; results in the type being 'CXInterpreterImpl *const' instead of 'const CXInterpreterImpl *' [misc-misplaced-const]

CXScope make_scope(const clang::Decl* D, const CXInterpreter I);
                                                             ^
Additional context

include/clang-c/CXCppInterOp.h:25: typedef declared here

typedef struct CXInterpreterImpl* CXInterpreter;
                                  ^


void dispose_string(CXString string);

CXScope make_scope(const clang::Decl* D, const CXInterpreter I);

Choose a reason for hiding this comment

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

warning: parameter 'I' is const-qualified in the function declaration; const-qualification of parameters only has an effect in function definitions [readability-avoid-const-params-in-decls]

Suggested change
CXScope make_scope(const clang::Decl* D, const CXInterpreter I);
CXScope make_scope(const clang::Decl* D, CXInterpreter I);

- Add `CXScope` and `CXQualType` as a bridge between libclang types and CppInterOp's types
- Add C API for interpreter and scope manipulations
@vgvassilev
Copy link
Contributor

Do we know why some builds are failing?

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