//===--- ImportMacro.cpp - Import Clang preprocessor macros ---------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file implements support for translating some kinds of C preprocessor // macros into Swift declarations. // //===----------------------------------------------------------------------===// #include "ImporterImpl.h" #include "llvm/ADT/SmallString.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Expr.h" #include "clang/Lex/MacroInfo.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/DelayedDiagnostic.h" #include "clang/Sema/Sema.h" #include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Expr.h" #include "swift/AST/Stmt.h" #include "swift/AST/Types.h" #include "swift/Basic/PrettyStackTrace.h" #include "swift/ClangImporter/ClangModule.h" using namespace swift; using namespace importer; template static const T * parseNumericLiteral(ClangImporter::Implementation &impl, const clang::Token &tok) { auto result = impl.getClangSema().ActOnNumericConstant(tok); if (result.isUsable()) return dyn_cast(result.get()); return nullptr; } // FIXME: Duplicated from ImportDecl.cpp. static bool isInSystemModule(DeclContext *D) { return cast(D->getModuleScopeContext())->isSystemModule(); } static ValueDecl * createMacroConstant(ClangImporter::Implementation &Impl, const clang::MacroInfo *macro, Identifier name, DeclContext *dc, Type type, const clang::APValue &value, ConstantConvertKind convertKind, bool isStatic, ClangNode ClangN) { Impl.ImportedMacroConstants[macro] = {value, type}; return Impl.createConstant(name, dc, type, value, convertKind, isStatic, ClangN); } static ValueDecl *importNumericLiteral(ClangImporter::Implementation &Impl, DeclContext *DC, const clang::MacroInfo *MI, Identifier name, const clang::Token *signTok, const clang::Token &tok, ClangNode ClangN, clang::QualType castType) { assert(tok.getKind() == clang::tok::numeric_constant && "not a numeric token"); { // Temporary hack to reject literals with ud-suffix. // FIXME: remove this when the following radar is implemented: // Swift should set up a DiagnosticConsumer for // Clang llvm::SmallString<32> SpellingBuffer; bool Invalid = false; StringRef TokSpelling = Impl.getClangPreprocessor().getSpelling(tok, SpellingBuffer, &Invalid); if (Invalid) return nullptr; if (TokSpelling.find('_') != StringRef::npos) return nullptr; } if (const clang::Expr *parsed = parseNumericLiteral<>(Impl, tok)) { auto clangTy = parsed->getType(); auto literalType = Impl.importTypeIgnoreIUO( clangTy, ImportTypeKind::Value, isInSystemModule(DC), Bridgeability::None); if (!literalType) return nullptr; Type constantType; if (castType.isNull()) { constantType = literalType; } else { constantType = Impl.importTypeIgnoreIUO( castType, ImportTypeKind::Value, isInSystemModule(DC), Bridgeability::None); if (!constantType) return nullptr; } if (auto *integer = dyn_cast(parsed)) { // Determine the value. llvm::APSInt value{integer->getValue(), clangTy->isUnsignedIntegerType()}; // If there was a - sign, negate the value. // If there was a ~, flip all bits. if (signTok) { if (signTok->is(clang::tok::minus)) { if (!value.isMinSignedValue()) value = -value; } else if (signTok->is(clang::tok::tilde)) { value.flipAllBits(); } } return createMacroConstant(Impl, MI, name, DC, constantType, clang::APValue(value), ConstantConvertKind::None, /*static*/ false, ClangN); } if (auto *floating = dyn_cast(parsed)) { // ~ doesn't make sense with floating-point literals. if (signTok && signTok->is(clang::tok::tilde)) return nullptr; llvm::APFloat value = floating->getValue(); // If there was a - sign, negate the value. if (signTok && signTok->is(clang::tok::minus)) { value.changeSign(); } return createMacroConstant(Impl, MI, name, DC, constantType, clang::APValue(value), ConstantConvertKind::None, /*static*/ false, ClangN); } // TODO: Other numeric literals (complex, imaginary, etc.) } return nullptr; } static bool isStringToken(const clang::Token &tok) { return tok.is(clang::tok::string_literal) || tok.is(clang::tok::utf8_string_literal); } // Describes the kind of string literal we're importing. enum class MappedStringLiteralKind { CString, // "string" NSString, // @"string" CFString // CFSTR("string") }; static ValueDecl *importStringLiteral(ClangImporter::Implementation &Impl, DeclContext *DC, const clang::MacroInfo *MI, Identifier name, const clang::Token &tok, MappedStringLiteralKind kind, ClangNode ClangN) { assert(isStringToken(tok)); clang::ActionResult result = Impl.getClangSema().ActOnStringLiteral(tok); if (!result.isUsable()) return nullptr; auto parsed = dyn_cast(result.get()); if (!parsed) return nullptr; Type importTy = Impl.getNamedSwiftType(Impl.getStdlibModule(), "String"); if (!importTy) return nullptr; return Impl.createConstant(name, DC, importTy, parsed->getString(), ConstantConvertKind::None, /*static*/ false, ClangN); } static ValueDecl *importLiteral(ClangImporter::Implementation &Impl, DeclContext *DC, const clang::MacroInfo *MI, Identifier name, const clang::Token &tok, ClangNode ClangN, clang::QualType castType) { switch (tok.getKind()) { case clang::tok::numeric_constant: return importNumericLiteral(Impl, DC, MI, name, /*signTok*/nullptr, tok, ClangN, castType); case clang::tok::string_literal: case clang::tok::utf8_string_literal: return importStringLiteral(Impl, DC, MI, name, tok, MappedStringLiteralKind::CString, ClangN); // TODO: char literals. default: return nullptr; } } static ValueDecl *importNil(ClangImporter::Implementation &Impl, DeclContext *DC, Identifier name, ClangNode clangN) { // We use a dummy type since we don't have a convenient type for 'nil'. Any // use of this will be an error anyway. auto type = TupleType::getEmpty(Impl.SwiftContext); return Impl.createUnavailableDecl(name, DC, type, "use 'nil' instead of this imported macro", /*isStatic=*/false, clangN); } static bool isSignToken(const clang::Token &tok) { return tok.is(clang::tok::plus) || tok.is(clang::tok::minus) || tok.is(clang::tok::tilde); } static Optional builtinTypeForToken(const clang::Token &tok, const clang::ASTContext &context) { switch (tok.getKind()) { case clang::tok::kw_short: return clang::QualType(context.ShortTy); case clang::tok::kw_long: return clang::QualType(context.LongTy); case clang::tok::kw___int64: return clang::QualType(context.LongLongTy); case clang::tok::kw___int128: return clang::QualType(context.Int128Ty); case clang::tok::kw_signed: return clang::QualType(context.IntTy); case clang::tok::kw_unsigned: return clang::QualType(context.UnsignedIntTy); case clang::tok::kw_void: return clang::QualType(context.VoidTy); case clang::tok::kw_char: return clang::QualType(context.CharTy); case clang::tok::kw_int: return clang::QualType(context.IntTy); case clang::tok::kw_float: return clang::QualType(context.FloatTy); case clang::tok::kw_double: return clang::QualType(context.DoubleTy); case clang::tok::kw_wchar_t: return clang::QualType(context.WCharTy); case clang::tok::kw_bool: return clang::QualType(context.BoolTy); case clang::tok::kw_char16_t: return clang::QualType(context.Char16Ty); case clang::tok::kw_char32_t: return clang::QualType(context.Char32Ty); default: return llvm::None; } } static Optional> getIntegerConstantForMacroToken(ClangImporter::Implementation &impl, DeclContext *DC, const clang::Token &token) { // Integer literal. if (token.is(clang::tok::numeric_constant)) { if (auto literal = parseNumericLiteral(impl,token)) { auto value = llvm::APSInt { literal->getValue(), literal->getType()->isUnsignedIntegerType() }; auto type = impl.importTypeIgnoreIUO( literal->getType(), ImportTypeKind::Value, isInSystemModule(DC), Bridgeability::None); return {{ value, type }}; } // Macro identifier. } else if (token.is(clang::tok::identifier) && token.getIdentifierInfo()->hasMacroDefinition()) { auto rawID = token.getIdentifierInfo(); auto definition = impl.getClangPreprocessor().getMacroDefinition(rawID); if (!definition) return None; ClangNode macroNode; const clang::MacroInfo *macroInfo; if (definition.getModuleMacros().empty()) { macroInfo = definition.getMacroInfo(); macroNode = macroInfo; } else { // Follow MacroDefinition::getMacroInfo in preferring the last ModuleMacro // rather than the first. const clang::ModuleMacro *moduleMacro = definition.getModuleMacros().back(); macroInfo = moduleMacro->getMacroInfo(); macroNode = moduleMacro; } auto importedID = impl.getNameImporter().importMacroName(rawID, macroInfo); (void)impl.importMacro(importedID, macroNode); auto searcher = impl.ImportedMacroConstants.find(macroInfo); if (searcher == impl.ImportedMacroConstants.end()) { return None; } auto importedConstant = searcher->second; if (!importedConstant.first.isInt()) { return None; } return {{ importedConstant.first.getInt(), importedConstant.second }}; } return None; } static ValueDecl *importMacro(ClangImporter::Implementation &impl, DeclContext *DC, Identifier name, const clang::MacroInfo *macro, ClangNode ClangN, clang::QualType castType) { if (name.empty()) return nullptr; auto numTokens = macro->getNumTokens(); auto tokenI = macro->tokens_begin(), tokenE = macro->tokens_end(); // Drop one layer of parentheses. if (numTokens > 2 && tokenI[0].is(clang::tok::l_paren) && tokenE[-1].is(clang::tok::r_paren)) { ++tokenI; --tokenE; numTokens -= 2; } // 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; } // FIXME: Ask Clang to try to parse and evaluate the expansion as a constant // expression instead of doing these special-case pattern matches. switch (numTokens) { case 1: { // Check for a single-token expansion of the form . // TODO: or . const clang::Token &tok = *tokenI; if (castTypeIsId && tok.is(clang::tok::numeric_constant)) { auto *integerLiteral = parseNumericLiteral(impl, tok); if (integerLiteral && integerLiteral->getValue() == 0) return importNil(impl, DC, name, ClangN); } // If it's a literal token, we might be able to translate the literal. if (tok.isLiteral()) { return importLiteral(impl, DC, macro, name, tok, ClangN, castType); } if (tok.is(clang::tok::identifier)) { auto clangID = tok.getIdentifierInfo(); // If it's an identifier that is itself a macro, look into that macro. if (clangID->hasMacroDefinition()) { auto isNilMacro = llvm::StringSwitch(clangID->getName()) #define NIL_MACRO(NAME) .Case(#NAME, true) #include "MacroTable.def" .Default(false); if (isNilMacro) return importNil(impl, DC, name, ClangN); auto macroID = impl.getClangPreprocessor().getMacroInfo(clangID); if (macroID && macroID != macro) { // FIXME: This was clearly intended to pass the cast type down, but // doing so would be a behavior change. return importMacro(impl, DC, name, macroID, ClangN, /*castType*/{}); } } // FIXME: If the identifier refers to a declaration, alias it? } return nullptr; } case 2: { // Check for a two-token expansion of the form + or -. // These are technically subtly wrong without parentheses because they // allow things like: // #define EOF -1 // int pred(int x) { return x EOF; } // but are pervasive in C headers anyway. clang::Token const &first = tokenI[0]; clang::Token const &second = tokenI[1]; if (isSignToken(first) && second.is(clang::tok::numeric_constant)) return importNumericLiteral(impl, DC, macro, name, &first, second, ClangN, castType); // We also allow @"string". if (first.is(clang::tok::at) && isStringToken(second)) return importStringLiteral(impl, DC, macro, name, second, MappedStringLiteralKind::NSString, ClangN); break; } case 3: { // Check for infix operations between two integer constants. // Import the result as another integer constant: // #define INT3 (INT1 INT2) // Doesn't allow inner parentheses. // Parse INT1. llvm::APSInt firstValue; Type firstSwiftType = nullptr; if (auto firstInt = getIntegerConstantForMacroToken(impl, DC, tokenI[0])) { firstValue = firstInt->first; firstSwiftType = firstInt->second; } else { return nullptr; } // Parse INT2. llvm::APSInt secondValue; Type secondSwiftType = nullptr; if (auto secondInt = getIntegerConstantForMacroToken(impl, DC, tokenI[2])) { secondValue = secondInt->first; secondSwiftType = secondInt->second; } else { return nullptr; } llvm::APSInt resultValue; Type resultSwiftType = nullptr; // Resolve width and signedness differences and find the type of the result. auto firstIntSpec = clang::ento::APSIntType(firstValue); auto secondIntSpec = clang::ento::APSIntType(secondValue); if (firstIntSpec == std::max(firstIntSpec, secondIntSpec)) { firstIntSpec.apply(secondValue); resultSwiftType = firstSwiftType; } else { secondIntSpec.apply(firstValue); resultSwiftType = secondSwiftType; } // Addition. if (tokenI[1].is(clang::tok::plus)) { resultValue = firstValue + secondValue; // Subtraction. } else if (tokenI[1].is(clang::tok::minus)) { resultValue = firstValue - secondValue; // Multiplication. } else if (tokenI[1].is(clang::tok::star)) { resultValue = firstValue * secondValue; // Division. } else if (tokenI[1].is(clang::tok::slash)) { if (secondValue == 0) { return nullptr; } resultValue = firstValue / secondValue; // Left-shift. } else if (tokenI[1].is(clang::tok::lessless)) { // Shift by a negative number is UB in C. Don't import. if (secondValue.isNegative()) { return nullptr; } resultValue = llvm::APSInt { firstValue.shl(secondValue), firstValue.isUnsigned() }; // Right-shift. } else if (tokenI[1].is(clang::tok::greatergreater)) { // Shift by a negative number is UB in C. Don't import. if (secondValue.isNegative()) { return nullptr; } if (firstValue.isUnsigned()) { resultValue = llvm::APSInt { firstValue.lshr(secondValue), /*isUnsigned*/ true }; } else { resultValue = llvm::APSInt { firstValue.ashr(secondValue), /*isUnsigned*/ false }; } // Bitwise OR. } else if (tokenI[1].is(clang::tok::pipe)) { firstValue.setIsUnsigned(true); secondValue.setIsUnsigned(true); resultValue = llvm::APSInt { firstValue | secondValue, /*isUnsigned*/ true }; // Bitwise AND. } else if (tokenI[1].is(clang::tok::amp)) { firstValue.setIsUnsigned(true); secondValue.setIsUnsigned(true); resultValue = llvm::APSInt { firstValue & secondValue, /*isUnsigned*/ true }; // XOR. } else if (tokenI[1].is(clang::tok::caret)) { firstValue.setIsUnsigned(true); secondValue.setIsUnsigned(true); resultValue = llvm::APSInt { firstValue ^ secondValue, /*isUnsigned*/ true }; // Logical OR. } else if (tokenI[1].is(clang::tok::pipepipe)) { bool result = firstValue.getBoolValue() || secondValue.getBoolValue(); resultValue = llvm::APSInt::get(result); resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType(); // Logical AND. } else if (tokenI[1].is(clang::tok::ampamp)) { bool result = firstValue.getBoolValue() && secondValue.getBoolValue(); resultValue = llvm::APSInt::get(result); resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType(); // Equality. } else if (tokenI[1].is(clang::tok::equalequal)) { resultValue = llvm::APSInt::get(firstValue == secondValue); resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType(); // Less than. } else if (tokenI[1].is(clang::tok::less)) { resultValue = llvm::APSInt::get(firstValue < secondValue); resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType(); // Less than or equal. } else if (tokenI[1].is(clang::tok::lessequal)) { resultValue = llvm::APSInt::get(firstValue <= secondValue); resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType(); // Greater than. } else if (tokenI[1].is(clang::tok::greater)) { resultValue = llvm::APSInt::get(firstValue > secondValue); resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType(); // Greater than or equal. } else if (tokenI[1].is(clang::tok::greaterequal)) { resultValue = llvm::APSInt::get(firstValue >= secondValue); resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType(); // Unhandled operators. } else { return nullptr; } return createMacroConstant(impl, macro, name, DC, resultSwiftType, clang::APValue(resultValue), ConstantConvertKind::None, /*isStatic=*/false, ClangN); } case 4: { // Check for a CFString literal of the form CFSTR("string"). if (tokenI[0].is(clang::tok::identifier) && tokenI[0].getIdentifierInfo()->isStr("CFSTR") && tokenI[1].is(clang::tok::l_paren) && isStringToken(tokenI[2]) && tokenI[3].is(clang::tok::r_paren)) { return importStringLiteral(impl, DC, macro, name, tokenI[2], MappedStringLiteralKind::CFString, ClangN); } // FIXME: Handle BIT_MASK(pos) helper macros which expand to a constant? break; } case 5: // Check for the literal series of tokens (void*)0. (We've already stripped // one layer of parentheses.) if (tokenI[0].is(clang::tok::l_paren) && tokenI[1].is(clang::tok::kw_void) && tokenI[2].is(clang::tok::star) && tokenI[3].is(clang::tok::r_paren) && tokenI[4].is(clang::tok::numeric_constant)) { auto *integerLiteral = parseNumericLiteral(impl, tokenI[4]); if (!integerLiteral || integerLiteral->getValue() != 0) break; return importNil(impl, DC, name, ClangN); } break; default: break; } return nullptr; } ValueDecl *ClangImporter::Implementation::importMacro(Identifier name, ClangNode macroNode) { const clang::MacroInfo *macro = macroNode.getAsMacro(); if (!macro) return nullptr; PrettyStackTraceStringAction stackRAII{"importing macro", name.str()}; // Look for macros imported with the same name. auto known = ImportedMacros.find(name); if (known == ImportedMacros.end()) { // Push in a placeholder to break circularity. ImportedMacros[name].push_back({macro, nullptr}); } else { // Check whether this macro has already been imported. for (const auto &entry : known->second) { if (entry.first == macro) return entry.second; } // Otherwise, check whether this macro is identical to a macro that has // already been imported. auto &clangPP = getClangPreprocessor(); for (const auto &entry : known->second) { // If the macro is equal to an existing macro, map down to the same // declaration. if (macro->isIdenticalTo(*entry.first, clangPP, true)) { ValueDecl *result = entry.second; known->second.push_back({macro, result}); return result; } } // If not, push in a placeholder to break circularity. known->second.push_back({macro, nullptr}); } startedImportingEntity(); // We haven't tried to import this macro yet. Do so now, and cache the // result. DeclContext *DC; if (const clang::Module *module = getClangOwningModule(macroNode)) { // Get the parent module because currently we don't model Clang submodules // in Swift. DC = getWrapperForModule(module->getTopLevelModule()); } else { DC = ImportedHeaderUnit; } auto valueDecl = ::importMacro(*this, DC, name, macro, macroNode, /*castType*/{}); // Update the entry for the value we just imported. // It's /probably/ the last entry in ImportedMacros[name], but there's an // outside chance more macros with the same name have been imported // re-entrantly since this method started. if (valueDecl) { auto entryIter = llvm::find_if(llvm::reverse(ImportedMacros[name]), [macro](std::pair entry) { return entry.first == macro; }); assert(entryIter != llvm::reverse(ImportedMacros[name]).end() && "placeholder not found"); entryIter->second = valueDecl; } return valueDecl; }