It looks to me like there is a limitation in the clang importer when importing simple constants defined like #define X_STD_SIZE 78
.
If they are typecast like this...
#define X_STD_SIZE (char)78
...then it works fine but if they are typecast like this...
#define X_STD_SIZE (unsigned char)78
...then it does not work, the X_STD_SIZE
constant is not imported and is not available to Swift.
I have tracked this down to code in lib/ClangImporter/ImportMacro.cpp
the importMacro
function (line 326 in my code).
This code is the problem...
// Handle tokens starting with a type cast
bool castTypeIsId = false;
if (numTokens > 3 &&
tokenI[0].is(clang::tok::l_paren) &&
(tokenI[1].is(clang::tok::identifier) ||
impl.getClangSema().isSimpleTypeSpecifier(tokenI[1].getKind())) &&
tokenI[2].is(clang::tok::r_paren)) {
if (!castType.isNull()) {
// this is a nested cast
return nullptr;
}
if (tokenI[1].is(clang::tok::identifier)) {
auto identifierInfo = tokenI[1].getIdentifierInfo();
if (identifierInfo->isStr("id")) {
castTypeIsId = true;
}
auto identifierName = identifierInfo->getName();
auto &identifier = impl.getClangASTContext().Idents.get(identifierName);
clang::sema::DelayedDiagnosticPool diagPool{
impl.getClangSema().DelayedDiagnostics.getCurrentPool()};
auto diagState = impl.getClangSema().DelayedDiagnostics.push(diagPool);
auto parsedType = impl.getClangSema().getTypeName(identifier,
clang::SourceLocation(),
/*scope*/nullptr);
impl.getClangSema().DelayedDiagnostics.popWithoutEmitting(diagState);
if (parsedType && diagPool.empty()) {
castType = parsedType.get();
} else {
return nullptr;
}
if (!castType->isBuiltinType() && !castTypeIsId) {
return nullptr;
}
} else {
auto builtinType = builtinTypeForToken(tokenI[1],
impl.getClangASTContext());
if (builtinType) {
castType = builtinType.getValue();
} else {
return nullptr;
}
}
tokenI += 3;
numTokens -= 3;
}
The problem is fairly obvious when you see it.
In the case when you're casting to char
, there are four tokens in total, '(', 'char', ')', '78' and it all works. When you're casting to unsigned char
, there are five tokens, '(', 'unsigned', 'char', ')', '78', so token 3 is 'char', not ')' and the test fails.
In order to fix this, the test needs to be more general, like...
if (numTokens > 3 &&
tokenI[0].is(clang::tok::l_paren) &&
(tokenI[1].is(clang::tok::identifier) ||
impl.getClangSema().isSimpleTypeSpecifier(tokenI[1].getKind())) &&
(tokenI[2].is(clang::tok::r_paren) || tokenE[-2].is(clang::tok::r_paren))) {
...but I'm not sure how this code works and what is best.
What do people think?