diff --git a/c2rust-ast-exporter/src/AstExporter.cpp b/c2rust-ast-exporter/src/AstExporter.cpp index 0aa6ecb09a..a713327c1d 100644 --- a/c2rust-ast-exporter/src/AstExporter.cpp +++ b/c2rust-ast-exporter/src/AstExporter.cpp @@ -711,7 +711,7 @@ class TypeEncoder final : public TypeVisitor { class TranslateASTVisitor final : public RecursiveASTVisitor { - struct MacroExpansionInfo { + struct MacroDeclInfo { StringRef Name; }; @@ -723,13 +723,20 @@ class TranslateASTVisitor final // Mapping from SourceManager FileID to index in files DenseMap file_id_mapping; std::set> exportedTags; - std::unordered_map macros; + std::unordered_map macros; + + struct MacroInvocationInfo { + uint32_t key; + MacroInfo* macro; + StringRef parameter; + }; // This stores a raw encoding of the macro call site SourceLocation, since // SourceLocation isn't hashable. - std::unordered_set macroCallSites; - SmallVector curMacroExpansionStack; - StringRef curMacroExpansionSource; + // TODO: Can this be made to use `unordered_set` for speed? + std::set> macroCallSites; + SmallVector curMacroInvocationStack; + StringRef curMacroInvocationSource; // Returns true when a new entry is added to exportedTags bool markForExport(void *ptr, ASTEntryTag tag) { @@ -769,7 +776,7 @@ class TranslateASTVisitor final // Template required because Decl and Stmt don't share a common base class void encode_entry_raw(void *ast, ASTEntryTag tag, SourceRange loc, const QualType ty, bool rvalue, - bool isVaList, bool encodeMacroExpansions, + bool isVaList, bool encodeMacroInvocations, const std::vector &childIds, std::function extra) { if (!markForExport(ast, tag)) @@ -808,21 +815,33 @@ class TranslateASTVisitor final // 9 - Is Rvalue (only for expressions) cbor_encode_boolean(&local, rvalue); - // 10 - Macro expansion stack, starting with initial macro call and ending + // 10 - Macro invocation stack, starting with initial macro call and ending // with the innermost replacement. cbor_encoder_create_array(&local, &childEnc, - encodeMacroExpansions ? curMacroExpansionStack.size() : 0); - if (encodeMacroExpansions) { - for (auto I = curMacroExpansionStack.rbegin(), E = curMacroExpansionStack.rend(); + encodeMacroInvocations ? curMacroInvocationStack.size() : 0); + if (encodeMacroInvocations) { + for (auto I = curMacroInvocationStack.rbegin(), E = curMacroInvocationStack.rend(); I != E; ++I) { - cbor_encode_uint(&childEnc, uintptr_t(*I)); + CborEncoder expansionEnc; + cbor_encoder_create_array(&childEnc, &expansionEnc, 3); + + cbor_encode_uint(&expansionEnc, I->key); + cbor_encode_uint(&expansionEnc, uintptr_t(I->macro)); + + if (!I->parameter.empty()) { + cbor_encode_string(&expansionEnc, I->parameter.str()); + } else { + cbor_encode_null(&expansionEnc); + } + + cbor_encoder_close_container(&childEnc, &expansionEnc); } } cbor_encoder_close_container(&local, &childEnc); - // 11 - Macro expansion source string, if applicable. - if (!curMacroExpansionSource.empty()) { - cbor_encode_string(&local, curMacroExpansionSource.str()); + // 11 - Macro invocation source string, if applicable. + if (!curMacroInvocationSource.empty()) { + cbor_encode_string(&local, curMacroInvocationSource.str()); } else { cbor_encode_null(&local); } @@ -846,7 +865,7 @@ class TranslateASTVisitor final std::function extra = [](CborEncoder *) {}) { auto ty = ast->getType(); auto isVaList = false; - auto encodeMacroExpansions = true; + auto encodeMacroInvocations = true; #if CLANG_VERSION_MAJOR < 13 bool isRValue = ast->isRValue(); #else @@ -860,7 +879,7 @@ class TranslateASTVisitor final auto span = ast->getSourceRange(); expandSpanToFinalChar(span, Context); encode_entry_raw(ast, tag, span, ty, isRValue, isVaList, - encodeMacroExpansions, childIds, extra); + encodeMacroInvocations, childIds, extra); typeEncoder.VisitQualTypeOf(ty, ast); } @@ -870,11 +889,11 @@ class TranslateASTVisitor final QualType s = QualType(static_cast(nullptr), 0); auto rvalue = false; auto isVaList = false; - auto encodeMacroExpansions = false; + auto encodeMacroInvocations = false; auto span = ast->getSourceRange(); expandSpanToFinalChar(span, Context); encode_entry_raw(ast, tag, span, s, rvalue, isVaList, - encodeMacroExpansions, childIds, extra); + encodeMacroInvocations, childIds, extra); } void encode_entry( @@ -882,11 +901,11 @@ class TranslateASTVisitor final const QualType T, std::function extra = [](CborEncoder *) {}) { auto rvalue = false; - auto encodeMacroExpansions = false; + auto encodeMacroInvocations = false; auto span = ast->getSourceRange(); expandSpanToFinalChar(span, Context); encode_entry_raw(ast, tag, span, T, rvalue, - isVaList(ast, T), encodeMacroExpansions, childIds, extra); + isVaList(ast, T), encodeMacroInvocations, childIds, extra); } /// Explicitly override the source location of this decl for cases where the @@ -897,54 +916,21 @@ class TranslateASTVisitor final const std::vector &childIds, const QualType T, std::function extra = [](CborEncoder *) {}) { auto rvalue = false; - auto encodeMacroExpansions = false; + auto encodeMacroInvocations = false; expandSpanToFinalChar(loc, Context); encode_entry_raw(ast, tag, loc, T, rvalue, - isVaList(ast, T), encodeMacroExpansions, childIds, extra); - } - - MacroInfo* getMacroInfo(SourceLocation loc, StringRef &name) const { - auto &Mgr = Context->getSourceManager(); - Token Result; - if (!Lexer::getRawToken(Mgr.getSpellingLoc(loc), Result, - Mgr, Context->getLangOpts(), false)) { - if (Result.is(tok::raw_identifier)) { - PP.LookUpIdentifierInfo(Result); - } - IdentifierInfo *IdentifierInfo = Result.getIdentifierInfo(); - if (IdentifierInfo && IdentifierInfo->hadMacroDefinition()) { - std::pair DecLoc = - Mgr.getDecomposedExpansionLoc(loc); - // Get the definition just before the searched location - // so that a macro referenced in a '#undef MACRO' can - // still be found. - SourceLocation BeforeSearchedLocation = - Mgr.getMacroArgExpandedLocation( - Mgr.getLocForStartOfFile(DecLoc.first) - .getLocWithOffset(DecLoc.second - 1)); - MacroDefinition MacroDef = PP.getMacroDefinitionAtLoc( - IdentifierInfo, BeforeSearchedLocation); - MacroInfo *MacroInf = MacroDef.getMacroInfo(); - if (MacroInf) { - LLVM_DEBUG(dbgs() << IdentifierInfo->getName() << "\n"); - LLVM_DEBUG(MacroInf->dump()); - LLVM_DEBUG(dbgs() << "\n"); - name = IdentifierInfo->getName(); - return MacroInf; - } - } - } - return nullptr; + isVaList(ast, T), encodeMacroInvocations, childIds, extra); } - bool VisitMacro(StringRef name, SourceLocation loc, MacroInfo *mac, Expr *E) { + bool VisitMacro(StringRef name, StringRef parameter, SourceLocation loc, MacroInfo *mac, Expr *E) { // TODO: handle builtin macros if (mac->isBuiltinMacro()) return false; // If this isn't the first time we've seen this macro call site, we // shouldn't associate this expression with the macro as it is a subexpr // of a previously seen expression. - if (!macroCallSites.insert(loc.getRawEncoding()).second) + std::pair key { loc.getRawEncoding(), parameter }; + if (!macroCallSites.insert(key).second) return false; auto &info = macros[mac]; if (info.Name.empty()) @@ -1007,11 +993,11 @@ class TranslateASTVisitor final void encodeMacros() { // Sort macros by source location - std::vector> macro_vec( + std::vector> macro_vec( macros.begin(), macros.end()); std::sort(macro_vec.begin(), macro_vec.end(), - [](const std::pair &a, - const std::pair &b) { + [](const std::pair &a, + const std::pair &b) { return a.first->getDefinitionLoc() < b.first->getDefinitionLoc(); }); @@ -1403,8 +1389,8 @@ class TranslateASTVisitor final // bool VisitExpr(Expr *E) { - curMacroExpansionStack.clear(); - curMacroExpansionSource = StringRef(); + curMacroInvocationStack.clear(); + curMacroInvocationSource = StringRef(); // We only translate constant macro objects to Rust consts, so this // expression must be constant. @@ -1438,39 +1424,73 @@ class TranslateASTVisitor final return true; } - curMacroExpansionSource = - Lexer::getSourceText(ExpansionStack[0], Mgr, Context->getLangOpts()); + curMacroInvocationSource = + Lexer::getSourceText(ExpansionStack[0].range, Mgr, Context->getLangOpts()); + + for (auto &elem : ExpansionStack) { + auto loc = elem.range.getBegin(); + StringRef parameter; + + if (elem.isParameter) { + auto IdentifierInfo = getIdentifierInfo(loc); + + if (!IdentifierInfo) { + return true; + } + + parameter = IdentifierInfo->getName(); + loc = Mgr.getImmediateExpansionRange(loc).getBegin(); + } + + auto IdentifierInfo = getIdentifierInfo(loc); + + if (!IdentifierInfo) { + return true; + } - for (auto &ExpansionRange : ExpansionStack) { - StringRef name; - MacroInfo *mac = getMacroInfo(ExpansionRange.getBegin(), name); + auto MacroInfo = getMacroInfo(loc, IdentifierInfo); - if (!mac || mac->getNumTokens() == 0) { + if (!MacroInfo || MacroInfo->getNumTokens() == 0) { return true; } - if (VisitMacro(name, ExpansionRange.getBegin(), mac, E)) { - curMacroExpansionStack.push_back(mac); + if (VisitMacro(IdentifierInfo->getName(), parameter, loc, MacroInfo, E)) { + MacroInvocationInfo info = { loc.getRawEncoding(), MacroInfo, parameter }; + curMacroInvocationStack.push_back(info); } } return true; } - std::vector getMacroExpansionStack(SourceRange Range) const { + struct ExpansionRange { + CharSourceRange range; + bool isParameter; + + bool operator== (const ExpansionRange &rhs) const { + return this->range.getAsRange() == rhs.range.getAsRange() + && this->range.isTokenRange() == rhs.range.isTokenRange() + && this->isParameter == rhs.isParameter; + } + }; + + std::vector getMacroExpansionStack(SourceRange Range) const { auto &Mgr = Context->getSourceManager(); auto Begin = Range.getBegin(); auto End = Range.getEnd(); - std::vector ExpansionStack; + std::vector ExpansionStack; do { if (!isAtStartOfImmediateMacroExpansion(Begin)) { break; } - auto ExpansionRange = Mgr.getImmediateExpansionRange(Begin); - ExpansionStack.push_back(ExpansionRange); - Begin = ExpansionRange.getBegin(); + ExpansionRange elem = { + Mgr.getImmediateExpansionRange(Begin), + Mgr.isMacroArgExpansion(Begin) + }; + Begin = elem.range.getBegin(); + ExpansionStack.push_back(elem); } while (Begin.isMacroID()); // Find the point at which `Begin` and `End` converge on the same expansion range. @@ -1482,16 +1502,12 @@ class TranslateASTVisitor final break; } - auto ExpansionRange = Mgr.getImmediateExpansionRange(End); - ConvergencePoint = std::find_if( - ExpansionStack.begin(), - ExpansionStack.end(), - [ExpansionRange](auto &R) { - return R.getAsRange() == ExpansionRange.getAsRange() && - R.isTokenRange() == ExpansionRange.isTokenRange(); - } - ); - End = ExpansionRange.getEnd(); + ExpansionRange elem = { + Mgr.getImmediateExpansionRange(End), + Mgr.isMacroArgExpansion(End) + }; + End = elem.range.getEnd(); + ConvergencePoint = std::find(ExpansionStack.begin(), ExpansionStack.end(), elem); } while (End.isMacroID() && ConvergencePoint == ExpansionStack.end()); // Remove all elements before the convergence point. @@ -1501,8 +1517,8 @@ class TranslateASTVisitor final auto EraseAfter = std::find_if( ExpansionStack.begin(), ExpansionStack.end(), - [this](auto &R) { - auto End = R.getEnd(); + [this](auto &elem) { + auto End = elem.range.getEnd(); return End.isMacroID() && !isAtEndOfImmediateMacroExpansion(End); } ); @@ -1527,6 +1543,54 @@ class TranslateASTVisitor final return Mgr.isAtEndOfImmediateMacroExpansion(loc.getLocWithOffset(len)); } + IdentifierInfo *getIdentifierInfo(SourceLocation loc) const { + auto &Mgr = Context->getSourceManager(); + Token Result; + + if (!Lexer::getRawToken( + Mgr.getSpellingLoc(loc), + Result, + Mgr, + Context->getLangOpts(), + false + )) { + if (Result.is(tok::raw_identifier)) { + PP.LookUpIdentifierInfo(Result); + } + + return Result.getIdentifierInfo(); + } + + return nullptr; + } + + MacroInfo* getMacroInfo(SourceLocation loc, IdentifierInfo *IdentifierInfo) const { + if (!IdentifierInfo->hadMacroDefinition()) { + return nullptr; + } + + auto &Mgr = Context->getSourceManager(); + std::pair DecLoc = Mgr.getDecomposedExpansionLoc(loc); + // Get the definition just before the searched location + // so that a macro referenced in a '#undef MACRO' can + // still be found. + SourceLocation BeforeSearchedLocation = Mgr.getMacroArgExpandedLocation( + Mgr.getLocForStartOfFile(DecLoc.first).getLocWithOffset(DecLoc.second - 1)); + MacroDefinition MacroDef = PP.getMacroDefinitionAtLoc( + IdentifierInfo, BeforeSearchedLocation); + MacroInfo *MacroInf = MacroDef.getMacroInfo(); + + if (!MacroInf) { + return nullptr; + } + + LLVM_DEBUG(dbgs() << IdentifierInfo->getName() << "\n"); + LLVM_DEBUG(MacroInf->dump()); + LLVM_DEBUG(dbgs() << "\n"); + + return MacroInf; + } + bool VisitVAArgExpr(VAArgExpr *E) { std::vector childIds{E->getSubExpr()}; encode_entry(E, TagVAArgExpr, childIds); diff --git a/c2rust-ast-exporter/src/clang_ast.rs b/c2rust-ast-exporter/src/clang_ast.rs index bac54c9302..a96ee1c5cc 100644 --- a/c2rust-ast-exporter/src/clang_ast.rs +++ b/c2rust-ast-exporter/src/clang_ast.rs @@ -1,3 +1,4 @@ +use serde::Deserialize; use serde_bytes::ByteBuf; use serde_cbor::error; use std::collections::{HashMap, VecDeque}; @@ -108,11 +109,18 @@ pub struct AstNode { // Stack of macros this node was expanded from, beginning with the initial // macro call and ending with the leaf. This needs to be a stack for nested // macro definitions. - pub macro_expansions: Vec, - pub macro_expansion_text: Option, + pub macro_invocations: Vec, + pub macro_invocation_text: Option, pub extras: Vec, } +#[derive(Debug, Clone, Deserialize)] +pub struct MacroInvocationInfoRaw { + pub key: u32, + pub macro_id: u64, + pub parameter: Option, +} + #[derive(Debug, Clone)] pub struct TypeNode { pub tag: TypeTag, @@ -260,9 +268,10 @@ pub fn process(items: Value) -> error::Result { }; // entry[10] - let macro_expansions = from_value::>(entry.pop_front().unwrap()).unwrap(); + let macro_invocations = + from_value::>(entry.pop_front().unwrap()).unwrap(); - let macro_expansion_text = expect_opt_str(&entry.pop_front().unwrap()) + let macro_invocation_text = expect_opt_str(&entry.pop_front().unwrap()) .unwrap() .map(|s| s.to_string()); @@ -278,8 +287,8 @@ pub fn process(items: Value) -> error::Result { }, type_id, rvalue, - macro_expansions, - macro_expansion_text, + macro_invocations, + macro_invocation_text, extras: entry.into_iter().collect(), }; diff --git a/c2rust-transpile/src/c_ast/conversion.rs b/c2rust-transpile/src/c_ast/conversion.rs index e6e7225491..37e2fcf76a 100644 --- a/c2rust-transpile/src/c_ast/conversion.rs +++ b/c2rust-transpile/src/c_ast/conversion.rs @@ -1069,8 +1069,8 @@ impl ConversionContext { }; if expected_ty & EXPR != 0 { - for mac_id in &node.macro_expansions { - let mac = CDeclId(self.visit_node_type(*mac_id, MACRO_DECL)); + for info in &node.macro_invocations { + let mac = CDeclId(self.visit_node_type(info.macro_id, MACRO_DECL)); self.typed_context .macro_invocations .entry(CExprId(new_id)) @@ -1079,7 +1079,7 @@ impl ConversionContext { } } - if let Some(text) = &node.macro_expansion_text { + if let Some(text) = &node.macro_invocation_text { self.typed_context .macro_expansion_text .insert(CExprId(new_id), text.clone()); diff --git a/c2rust-transpile/tests/snapshots.rs b/c2rust-transpile/tests/snapshots.rs index 5b72ce1293..92bf7bf53d 100644 --- a/c2rust-transpile/tests/snapshots.rs +++ b/c2rust-transpile/tests/snapshots.rs @@ -440,6 +440,11 @@ fn test_lift_const() { transpile("lift_const.c").run(); } +#[test] +fn test_macro_fns() { + transpile("macro_fns.c").run(); +} + #[test] fn test_macrocase() { transpile("macrocase.c").run(); diff --git a/c2rust-transpile/tests/snapshots/macro_fns.c b/c2rust-transpile/tests/snapshots/macro_fns.c new file mode 100644 index 0000000000..e6a2a55284 --- /dev/null +++ b/c2rust-transpile/tests/snapshots/macro_fns.c @@ -0,0 +1,27 @@ +#define CONST 42 + +#define ID(x) x +#define PAREN(x) (x) +#define LIT_ID ID(42) +#define LIT_PAREN PAREN(42) +#define CONST_ID ID(CONST) +#define CONST_PAREN PAREN(CONST) +#define NESTED_ID(x) ID(x) +#define NESTED_PAREN(x) PAREN(x) + +void basic(void) { + int lit_id = LIT_ID; + int lit_paren = LIT_PAREN; + int const_id = CONST_ID; + int const_paren = CONST_PAREN; + + int id_with_lit = ID(42); + int paren_with_lit = PAREN(42); + int id_with_const = ID(CONST); + int paren_with_const = PAREN(CONST); + + int nested_id_with_lit = NESTED_ID(42); + int nested_paren_with_lit = NESTED_PAREN(42); + int nested_id_with_const = NESTED_ID(CONST); + int nested_paren_with_const = NESTED_PAREN(CONST); +} diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@macro_fns.c.2021.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@macro_fns.c.2021.clang15.snap new file mode 100644 index 0000000000..b4120ee024 --- /dev/null +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@macro_fns.c.2021.clang15.snap @@ -0,0 +1,32 @@ +--- +source: c2rust-transpile/tests/snapshots.rs +expression: cat tests/snapshots/macro_fns.2021.clang15.rs +--- +#![allow( + clippy::missing_safety_doc, + dead_code, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unused_assignments, + unused_mut +)] +pub const LIT_ID: ::core::ffi::c_int = 42 as ::core::ffi::c_int; +pub const LIT_PAREN: ::core::ffi::c_int = 42 as ::core::ffi::c_int; +pub const CONST_ID: ::core::ffi::c_int = 42 as ::core::ffi::c_int; +pub const CONST_PAREN: ::core::ffi::c_int = 42 as ::core::ffi::c_int; +#[no_mangle] +pub unsafe extern "C" fn basic() { + let mut lit_id: ::core::ffi::c_int = LIT_ID; + let mut lit_paren: ::core::ffi::c_int = LIT_PAREN; + let mut const_id: ::core::ffi::c_int = CONST_ID; + let mut const_paren: ::core::ffi::c_int = CONST_PAREN; + let mut id_with_lit: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut paren_with_lit: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut id_with_const: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut paren_with_const: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut nested_id_with_lit: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut nested_paren_with_lit: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut nested_id_with_const: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut nested_paren_with_const: ::core::ffi::c_int = 42 as ::core::ffi::c_int; +} diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@macro_fns.c.2024.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@macro_fns.c.2024.clang15.snap new file mode 100644 index 0000000000..e1ed38afc8 --- /dev/null +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@macro_fns.c.2024.clang15.snap @@ -0,0 +1,33 @@ +--- +source: c2rust-transpile/tests/snapshots.rs +expression: cat tests/snapshots/macro_fns.2024.clang15.rs +--- +#![allow( + clippy::missing_safety_doc, + dead_code, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unsafe_op_in_unsafe_fn, + unused_assignments, + unused_mut +)] +pub const LIT_ID: ::core::ffi::c_int = 42 as ::core::ffi::c_int; +pub const LIT_PAREN: ::core::ffi::c_int = 42 as ::core::ffi::c_int; +pub const CONST_ID: ::core::ffi::c_int = 42 as ::core::ffi::c_int; +pub const CONST_PAREN: ::core::ffi::c_int = 42 as ::core::ffi::c_int; +#[unsafe(no_mangle)] +pub unsafe extern "C" fn basic() { + let mut lit_id: ::core::ffi::c_int = LIT_ID; + let mut lit_paren: ::core::ffi::c_int = LIT_PAREN; + let mut const_id: ::core::ffi::c_int = CONST_ID; + let mut const_paren: ::core::ffi::c_int = CONST_PAREN; + let mut id_with_lit: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut paren_with_lit: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut id_with_const: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut paren_with_const: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut nested_id_with_lit: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut nested_paren_with_lit: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut nested_id_with_const: ::core::ffi::c_int = 42 as ::core::ffi::c_int; + let mut nested_paren_with_const: ::core::ffi::c_int = 42 as ::core::ffi::c_int; +}