Skip to content

Commit 53da95a

Browse files
committed
Rework the way we find builtins.
The builtin types can be platform- and target-dependent (see ASTContext::InitBuiltinTypes). The new approach iterates over all registered builtin types and adds their string representation to a map which can be searched efficiently.
1 parent d7361d9 commit 53da95a

5 files changed

Lines changed: 234 additions & 71 deletions

File tree

include/CppInterOp/CppInterOp.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ using TCppIndex_t = size_t;
3838
using TCppScope_t = void*;
3939
using TCppConstScope_t = const void*;
4040
using TCppType_t = void*;
41+
using TCppConstType_t = const void*;
4142
using TCppFunction_t = void*;
4243
using TCppConstFunction_t = const void*;
4344
using TCppFuncAddr_t = void*;
@@ -368,7 +369,7 @@ CPPINTEROP_API bool IsComplete(TCppScope_t scope);
368369
CPPINTEROP_API size_t SizeOf(TCppScope_t scope);
369370

370371
/// Checks if it is a "built-in" or a "complex" type.
371-
CPPINTEROP_API bool IsBuiltin(TCppType_t type);
372+
CPPINTEROP_API bool IsBuiltin(TCppConstType_t type);
372373

373374
/// Checks if it is a templated class.
374375
CPPINTEROP_API bool IsTemplate(TCppScope_t handle);

lib/CppInterOp/CppInterOp.cpp

Lines changed: 107 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "CppInterOp/CppInterOp.h"
1111

1212
#include "Compatibility.h"
13+
#include "Sins.h" // for access to private members
1314

1415
#include "clang/AST/Attrs.inc"
1516
#include "clang/AST/CXXInheritance.h"
@@ -132,6 +133,9 @@ using namespace llvm;
132133
struct InterpreterInfo {
133134
compat::Interpreter* Interpreter = nullptr;
134135
bool isOwned = true;
136+
// Store the list of builtin types.
137+
llvm::StringMap<QualType> BuiltinMap;
138+
135139
InterpreterInfo(compat::Interpreter* I, bool Owned)
136140
: Interpreter(I), isOwned(Owned) {}
137141

@@ -525,7 +529,7 @@ size_t SizeOf(TCppScope_t scope) {
525529
return 0;
526530
}
527531

528-
bool IsBuiltin(TCppType_t type) {
532+
bool IsBuiltin(TCppConstType_t type) {
529533
QualType Ty = QualType::getFromOpaquePtr(type);
530534
if (Ty->isBuiltinType() || Ty->isAnyComplexType())
531535
return true;
@@ -2004,69 +2008,109 @@ TCppType_t AddTypeQualifier(TCppType_t type, QualKind qual) {
20042008
return QT.getAsOpaquePtr();
20052009
}
20062010

2007-
// Internal functions that are not needed outside the library are
2008-
// encompassed in an anonymous namespace as follows. This function converts
2009-
// from a string to the actual type. It is used in the GetType() function.
2010-
namespace {
2011+
// Registers all permutations of a word set
2012+
static void RegisterPerms(llvm::StringMap<QualType>& Map, QualType QT,
2013+
llvm::SmallVectorImpl<llvm::StringRef>& Words) {
2014+
std::sort(Words.begin(), Words.end());
2015+
do {
2016+
std::string Key;
2017+
for (size_t i = 0; i < Words.size(); ++i) {
2018+
if (i > 0)
2019+
Key += ' ';
2020+
Key += Words[i].str();
2021+
}
2022+
Map[Key] = QT;
2023+
} while (std::next_permutation(Words.begin(), Words.end()));
2024+
}
2025+
ALLOW_ACCESS(ASTContext, Types, llvm::SmallVector<clang::Type*, 0>);
2026+
static void PopulateBuiltinMap(ASTContext& Context) {
2027+
const PrintingPolicy Policy(Context.getLangOpts());
2028+
auto& BuiltinMap = sInterpreters->back().BuiltinMap;
2029+
const auto& Types = ACCESS(Context, Types);
2030+
2031+
for (clang::Type* T : Types) {
2032+
auto* BT = llvm::dyn_cast<BuiltinType>(T);
2033+
if (!BT || BT->isPlaceholderType())
2034+
continue;
2035+
2036+
QualType QT(BT, 0);
2037+
std::string Name = QT.getAsString(Policy);
2038+
if (Name.empty() || Name[0] == '<')
2039+
continue;
2040+
2041+
// Initial entry (e.g., "int", "unsigned long")
2042+
BuiltinMap[Name] = QT;
2043+
2044+
llvm::SmallVector<llvm::StringRef, 4> Words;
2045+
llvm::StringRef(Name).split(Words, ' ', -1, false);
2046+
2047+
bool hasInt = false;
2048+
bool hasSigned = false;
2049+
bool hasUnsigned = false;
2050+
bool hasChar = false;
2051+
bool isModifiable = false;
2052+
2053+
for (auto W : Words) {
2054+
if (W == "int")
2055+
hasInt = true;
2056+
else if (W == "signed")
2057+
hasSigned = true;
2058+
else if (W == "unsigned")
2059+
hasUnsigned = true;
2060+
else if (W == "char")
2061+
hasChar = true;
2062+
2063+
if (W == "long" || W == "short" || hasInt)
2064+
isModifiable = true;
2065+
}
2066+
2067+
// Skip things like 'float' or 'double' that aren't combined
2068+
if (!isModifiable && !hasUnsigned && !hasSigned)
2069+
continue;
2070+
2071+
// Register base permutations (e.g., "long long" or "unsigned int")
2072+
if (Words.size() > 1)
2073+
RegisterPerms(BuiltinMap, QT, Words);
2074+
2075+
// Expansion: Add "int" suffix where missing (e.g., "short" -> "short int")
2076+
if (!hasInt && !hasChar) {
2077+
auto WithInt = Words;
2078+
WithInt.push_back("int");
2079+
RegisterPerms(BuiltinMap, QT, WithInt);
2080+
2081+
// If we are adding 'int', we should also try adding 'signed'
2082+
// to cover cases like "short" -> "signed short int"
2083+
if (!hasSigned && !hasUnsigned) {
2084+
auto WithBoth = WithInt;
2085+
WithBoth.push_back("signed");
2086+
RegisterPerms(BuiltinMap, QT, WithBoth);
2087+
}
2088+
}
2089+
2090+
// Expansion: Add "signed" prefix
2091+
// (e.g., "int" -> "signed int", "long" -> "signed long")
2092+
if (!hasSigned && !hasUnsigned) {
2093+
auto WithSigned = Words;
2094+
WithSigned.push_back("signed");
2095+
RegisterPerms(BuiltinMap, QT, WithSigned);
2096+
}
2097+
}
2098+
2099+
// Explicit global synonym
2100+
BuiltinMap["signed"] = Context.IntTy;
2101+
BuiltinMap["unsigned"] = Context.UnsignedIntTy;
2102+
}
20112103
static QualType findBuiltinType(llvm::StringRef typeName, ASTContext& Context) {
2012-
bool issigned = false;
2013-
bool isunsigned = false;
2014-
if (typeName.starts_with("signed ")) {
2015-
issigned = true;
2016-
typeName = StringRef(typeName.data() + 7, typeName.size() - 7);
2017-
}
2018-
if (!issigned && typeName.starts_with("unsigned ")) {
2019-
isunsigned = true;
2020-
typeName = StringRef(typeName.data() + 9, typeName.size() - 9);
2021-
}
2022-
if (typeName == "char") {
2023-
if (isunsigned)
2024-
return Context.UnsignedCharTy;
2025-
return Context.SignedCharTy;
2026-
}
2027-
if (typeName == "short") {
2028-
if (isunsigned)
2029-
return Context.UnsignedShortTy;
2030-
return Context.ShortTy;
2031-
}
2032-
if (typeName == "int") {
2033-
if (isunsigned)
2034-
return Context.UnsignedIntTy;
2035-
return Context.IntTy;
2036-
}
2037-
if (typeName == "long") {
2038-
if (isunsigned)
2039-
return Context.UnsignedLongTy;
2040-
return Context.LongTy;
2041-
}
2042-
if (typeName == "long long") {
2043-
if (isunsigned)
2044-
return Context.UnsignedLongLongTy;
2045-
return Context.LongLongTy;
2046-
}
2047-
if (!issigned && !isunsigned) {
2048-
if (typeName == "bool")
2049-
return Context.BoolTy;
2050-
if (typeName == "float")
2051-
return Context.FloatTy;
2052-
if (typeName == "double")
2053-
return Context.DoubleTy;
2054-
if (typeName == "long double")
2055-
return Context.LongDoubleTy;
2056-
2057-
if (typeName == "wchar_t")
2058-
return Context.WCharTy;
2059-
if (typeName == "char16_t")
2060-
return Context.Char16Ty;
2061-
if (typeName == "char32_t")
2062-
return Context.Char32Ty;
2063-
}
2064-
/* Missing
2065-
CanQualType WideCharTy; // Same as WCharTy in C++, integer type in C99.
2066-
CanQualType WIntTy; // [C99 7.24.1], integer type unchanged by default
2067-
promotions.
2068-
*/
2069-
return QualType();
2104+
llvm::StringMap<QualType>& BuiltinMap = sInterpreters->back().BuiltinMap;
2105+
if (BuiltinMap.empty())
2106+
PopulateBuiltinMap(Context);
2107+
2108+
// Fast Lookup
2109+
auto It = BuiltinMap.find(typeName);
2110+
if (It != BuiltinMap.end())
2111+
return It->second;
2112+
2113+
return QualType(); // Return null if not a builtin
20702114
}
20712115
static std::optional<QualType> GetTypeInternal(Decl* D) {
20722116
if (!D)
@@ -2084,7 +2128,6 @@ static std::optional<QualType> GetTypeInternal(Decl* D) {
20842128

20852129
return {};
20862130
}
2087-
} // namespace
20882131

20892132
TCppType_t GetType(const std::string& name) {
20902133
QualType builtin = findBuiltinType(name, getASTContext());

lib/CppInterOp/Sins.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#ifndef LIB_CPPINTEROP_SINS_H
2+
#define LIB_CPPINTEROP_SINS_H
3+
4+
/// Standard-protected facility allowing access into private members in C++.
5+
/// Use with caution!
6+
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
7+
#define CONCATE_(X, Y) X##Y
8+
#define CONCATE(X, Y) CONCATE_(X, Y)
9+
#define ALLOW_ACCESS(CLASS, MEMBER, ...) \
10+
template <typename Only, __VA_ARGS__ CLASS::*Member> \
11+
struct CONCATE(MEMBER, __LINE__) { \
12+
friend __VA_ARGS__ CLASS::*Access(Only*) { return Member; } \
13+
}; \
14+
template <typename> struct Only_##MEMBER; \
15+
template <> struct Only_##MEMBER<CLASS> { \
16+
friend __VA_ARGS__ CLASS::*Access(Only_##MEMBER<CLASS>*); \
17+
}; \
18+
template struct CONCATE(MEMBER, \
19+
__LINE__)<Only_##MEMBER<CLASS>, &CLASS::MEMBER>
20+
21+
#define ACCESS(OBJECT, MEMBER) \
22+
(OBJECT).* \
23+
Access( \
24+
(Only_##MEMBER<std::remove_reference_t<decltype(OBJECT)>>*)nullptr)
25+
26+
// NOLINTEND(cppcoreguidelines-macro-usage)
27+
28+
#endif // LIB_CPPINTEROP_SINS_H

lib/CppInterOp/exports.ld

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,4 @@
5454
-Wl,--export=_ZN4llvm15SmallVectorBaseIjE8set_sizeEm
5555
-Wl,--export=_ZN5clang11Interpreter6createENSt3__210unique_ptrINS_16CompilerInstanceENS1_14default_deleteIS3_EEEENS2_IN4llvm3orc12LLJITBuilderENS4_IS9_EEEE
5656
-Wl,--export=_ZNK5clang13CXXRecordDecl19isInjectedClassNameEv
57+
-Wl,--export=_ZNK5clang8QualType11getAsStringERKNS_14PrintingPolicyE

unittests/CppInterOp/ScopeReflectionTest.cpp

Lines changed: 96 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,17 @@
44
#include "clang-c/CXCppInterOp.h"
55

66
#include "clang/AST/ASTContext.h"
7-
#include "clang/Basic/Version.h"
8-
#include "clang/Frontend/CompilerInstance.h"
9-
#include "clang/Sema/Sema.h"
10-
117
#include "clang/AST/ASTDumper.h"
128
#include "clang/AST/Decl.h"
139
#include "clang/AST/DeclBase.h"
1410
#include "clang/AST/GlobalDecl.h"
11+
#include "clang/AST/Type.h"
12+
#include "clang/Basic/Version.h"
13+
#include "clang/Frontend/CompilerInstance.h"
14+
#include "clang/Sema/Sema.h"
1515

1616
#include "llvm/Support/Valgrind.h"
1717

18-
#include "clang-c/CXCppInterOp.h"
19-
2018
#include "gtest/gtest.h"
2119

2220
#include <string>
@@ -25,6 +23,98 @@ using namespace TestUtils;
2523
using namespace llvm;
2624
using namespace clang;
2725

26+
TYPED_TEST(CPPINTEROP_TEST_MODE, ScopeReflection_GetTypeOfBuiltins) {
27+
// Structural check: Ensure every valid BuiltinType kind can be resolved
28+
TestFixture::CreateInterpreter();
29+
ASTContext& C = Interp->getCI()->getASTContext(); // for brevity
30+
31+
#define BUILTIN_TYPE(Id, SingletonId) \
32+
{ \
33+
QualType QT = C.SingletonId; \
34+
if (!QT.isNull()) { \
35+
std::string name = QT.getAsString(C.getPrintingPolicy()); \
36+
if (name[0] != '<' && !QT->isPlaceholderType()) { \
37+
const auto* FoundTy = Cpp::GetType(name); \
38+
EXPECT_TRUE(FoundTy) << "Failed to find builtin: '" << name << "'"; \
39+
if (FoundTy) { \
40+
EXPECT_EQ(FoundTy, QT.getAsOpaquePtr()) \
41+
<< "Type mismatch for '" << name << "'"; \
42+
EXPECT_TRUE(Cpp::IsBuiltin(FoundTy)); \
43+
} \
44+
} \
45+
} \
46+
}
47+
#include "clang/AST/BuiltinTypes.def"
48+
#undef BUILTIN_TYPE
49+
50+
auto Verify = [&](const char* Name, QualType Expected) {
51+
void* Found = Cpp::GetType(Name);
52+
EXPECT_EQ(Found, Expected.getAsOpaquePtr()) << "Failed for: " << Name;
53+
};
54+
55+
// --- Integer Variations ---
56+
Verify("int", C.IntTy);
57+
Verify("signed int", C.IntTy);
58+
Verify("signed", C.IntTy);
59+
Verify("int signed", C.IntTy);
60+
61+
// --- Short Variations ---
62+
Verify("short", C.ShortTy);
63+
Verify("short int", C.ShortTy);
64+
Verify("signed short", C.ShortTy);
65+
Verify("signed short int", C.ShortTy);
66+
Verify("int short signed", C.ShortTy);
67+
68+
// --- Long Variations ---
69+
Verify("long", C.LongTy);
70+
Verify("long int", C.LongTy);
71+
Verify("signed long", C.LongTy);
72+
Verify("signed long int", C.LongTy);
73+
Verify("int long", C.LongTy);
74+
75+
// --- Long Long Variations ---
76+
Verify("long long", C.LongLongTy);
77+
Verify("long long int", C.LongLongTy);
78+
Verify("signed long long", C.LongLongTy);
79+
Verify("signed long long int", C.LongLongTy);
80+
Verify("int long long signed", C.LongLongTy);
81+
82+
// --- Unsigned Variations ---
83+
Verify("unsigned", C.UnsignedIntTy);
84+
Verify("unsigned int", C.UnsignedIntTy);
85+
Verify("int unsigned", C.UnsignedIntTy);
86+
87+
Verify("unsigned short", C.UnsignedShortTy);
88+
Verify("unsigned short int", C.UnsignedShortTy);
89+
Verify("int short unsigned", C.UnsignedShortTy);
90+
91+
Verify("unsigned long", C.UnsignedLongTy);
92+
Verify("unsigned long int", C.UnsignedLongTy);
93+
94+
Verify("unsigned long long", C.UnsignedLongLongTy);
95+
Verify("unsigned long long int", C.UnsignedLongLongTy);
96+
97+
// --- Character Variations (No 'int' suffix allowed) ---
98+
Verify("char", C.CharTy);
99+
Verify("signed char", C.SignedCharTy);
100+
Verify("unsigned char", C.UnsignedCharTy);
101+
102+
// Negative check: signed char int is NOT a valid builtin
103+
EXPECT_EQ(Cpp::GetType("signed char int"), nullptr);
104+
105+
// Maybe these should be considered builtins?
106+
// EXPECT_EQ(Cpp::GetType("size_t"), C.getSizeType().getAsOpaquePtr());
107+
// EXPECT_EQ(Cpp::GetType("ptrdiff_t"),
108+
// C.getPointerDiffType().getAsOpaquePtr());
109+
// EXPECT_EQ(Cpp::GetType("intptr_t"), C.getIntPtrType().getAsOpaquePtr());
110+
// EXPECT_EQ(Cpp::GetType("uintptr_t"), C.getUIntPtrType().getAsOpaquePtr());
111+
// EXPECT_EQ(Cpp::GetType("size_t"), C.getSizeType().getAsOpaquePtr());
112+
// EXPECT_EQ(Cpp::GetType("size_t"), C.getSizeType().getAsOpaquePtr());
113+
114+
// EXPECT_EQ(Cpp::GetType("wint_t"), C.WIntTy.getAsOpaquePtr());
115+
// EXPECT_EQ(Cpp::GetType("wchar_t"), C.WCharTy.getAsOpaquePtr());
116+
}
117+
28118
TYPED_TEST(CPPINTEROP_TEST_MODE, ScopeReflection_GetTypeOfTypedef) {
29119
std::string code = R"(
30120
typedef int Type_t;

0 commit comments

Comments
 (0)