Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 86 additions & 45 deletions c2rust-ast-exporter/src/AstExporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1426,65 +1426,106 @@ class TranslateASTVisitor final
LLVM_DEBUG(Range.getBegin().dump(Mgr));
LLVM_DEBUG(Range.getEnd().dump(Mgr));

auto Begin = Range.getBegin();
auto End = Range.getEnd();

// Check that we are only expanding a single macro call.
if (!Begin.isMacroID() || !End.isMacroID() ||
Mgr.getImmediateMacroCallerLoc(Begin) != Mgr.getImmediateMacroCallerLoc(End))
if (!Range.getBegin().isMacroID() || !Range.getEnd().isMacroID()) {
return true;
}

if (Begin.isMacroID()) {
#if CLANG_VERSION_MAJOR < 7
// getImmediateExpansionRange in LLVM<7 returns a
// std::pair<SourceLocation, SourceLocation>, which we need to
// translate to a CharSourceRange for Lexer::getSourceText
auto LocPair = Mgr.getImmediateExpansionRange(Begin);
auto ExpansionRange = CharSourceRange::getCharRange(LocPair.first, LocPair.second);
#else // CLANG_VERSION_MAJOR >= 7
auto ExpansionRange = Mgr.getImmediateExpansionRange(Begin);
#endif
curMacroExpansionSource =
Lexer::getSourceText(ExpansionRange, Mgr, Context->getLangOpts());
// Holds the stack of ranges of macro expansions that expand to this expression.
// The last element is the top-level macro call.
auto ExpansionStack = getMacroExpansionStack(Range);

if (ExpansionStack.empty()) {
return true;
}

// The macro stack unwound by getImmediateMacroCallerLoc and friends
// starts with literal replacement and works it's way to the macro call
// that was replaced.
while (Begin.isMacroID()) {
#if CLANG_VERSION_MAJOR < 7
auto ExpansionRange = Mgr.getImmediateExpansionRange(Begin);
auto ExpansionBegin = ExpansionRange.first;
auto ExpansionEnd = ExpansionRange.second;
#else // CLANG_VERSION_MAJOR >= 7
auto ExpansionRange = Mgr.getImmediateExpansionRange(Begin).getAsRange();
auto ExpansionBegin = ExpansionRange.getBegin();
auto ExpansionEnd = ExpansionRange.getEnd();
#endif
curMacroExpansionSource =
Lexer::getSourceText(ExpansionStack[0], Mgr, Context->getLangOpts());

for (auto &ExpansionRange : ExpansionStack) {
StringRef name;
MacroInfo *mac = getMacroInfo(ExpansionBegin, name);
MacroInfo *mac = getMacroInfo(ExpansionRange.getBegin(), name);

if (!mac || mac->getNumTokens() == 0)
if (!mac || mac->getNumTokens() == 0) {
return true;
auto ReplacementBegin = mac->getReplacementToken(0).getLocation();
auto ReplacementEnd = mac->getDefinitionEndLoc();
// Verify that this expansion covers the entire macro replacement
// definition, i.e. E is not a subexpression of the macro
// replacement.
if (Mgr.getSpellingLoc(Begin) != ReplacementBegin ||
Mgr.getSpellingLoc(End) != ReplacementEnd)
return true;

Begin = ExpansionBegin;
End = ExpansionEnd;
}

if (VisitMacro(name, Begin, mac, E)) {
if (VisitMacro(name, ExpansionRange.getBegin(), mac, E)) {
curMacroExpansionStack.push_back(mac);
}
}

return true;
}

std::vector<CharSourceRange> getMacroExpansionStack(SourceRange Range) const {
auto &Mgr = Context->getSourceManager();
auto Begin = Range.getBegin();
auto End = Range.getEnd();
std::vector<CharSourceRange> ExpansionStack;

do {
if (!isAtStartOfImmediateMacroExpansion(Begin)) {
break;
}

auto ExpansionRange = Mgr.getImmediateExpansionRange(Begin);
ExpansionStack.push_back(ExpansionRange);
Begin = ExpansionRange.getBegin();
} while (Begin.isMacroID());

// Find the point at which `Begin` and `End` converge on the same expansion range.
// This is where the expression in `Range` first corresponds to a single macro call.
auto ConvergencePoint = ExpansionStack.end();

do {
if (!isAtEndOfImmediateMacroExpansion(End)) {
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();
} while (End.isMacroID() && ConvergencePoint == ExpansionStack.end());

// Remove all elements before the convergence point.
ExpansionStack.erase(ExpansionStack.begin(), ConvergencePoint);

// Ensure the remaining ranges still correspond to the input `Range`.
auto EraseAfter = std::find_if(
ExpansionStack.begin(),
ExpansionStack.end(),
[this](auto &R) {
auto End = R.getEnd();
return End.isMacroID() && !isAtEndOfImmediateMacroExpansion(End);
}
);
auto EraseFrom = EraseAfter + 1;

if (EraseFrom < ExpansionStack.end()) {
ExpansionStack.erase(EraseFrom, ExpansionStack.end());
}

return ExpansionStack;
}

bool isAtStartOfImmediateMacroExpansion(SourceLocation loc) const {
auto &Mgr = Context->getSourceManager();
return Mgr.isAtStartOfImmediateMacroExpansion(loc);
}

bool isAtEndOfImmediateMacroExpansion(SourceLocation loc) const {
auto &Mgr = Context->getSourceManager();
auto spellingLoc = Mgr.getSpellingLoc(loc);
auto len = Lexer::MeasureTokenLength(spellingLoc, Mgr, Context->getLangOpts());
return Mgr.isAtEndOfImmediateMacroExpansion(loc.getLocWithOffset(len));
}

bool VisitVAArgExpr(VAArgExpr *E) {
std::vector<void *> childIds{E->getSubExpr()};
Expand Down
32 changes: 25 additions & 7 deletions c2rust-transpile/src/c_ast/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,28 @@ impl ConversionContext {
self.visit_node(untyped_context, node_id, new_id, expected_ty)
}

// Check what primitive kinds were emitted by the compiler.
let mut found_kinds: HashMap<_, _> = CTypeKind::PRIMITIVE_KINDS
.into_iter()
.map(|kind| (kind, false))
.collect();

for Located { kind, .. } in self.typed_context.c_types.values() {
if let Some(is_found) = found_kinds.get_mut(kind) {
*is_found = true;
}
}

// If any primitives are missing, add them ourselves.
for (kind, is_found) in found_kinds {
if !is_found {
let new_id = self.id_mapper.fresh_id();
self.add_type(new_id, not_located(kind));
self.processed_nodes
.insert(new_id, self::node_types::OTHER_TYPE);
}
}

// Function declarations' types look through typedefs, but we want to use the types with
// typedefs intact in some cases during translation. To ensure that these types exist in the
// `TypedAstContext`, iterate over all function decls, compute their adjusted type using
Expand All @@ -511,7 +533,7 @@ impl ConversionContext {
for (_decl_id, located_kind) in self.typed_context.c_decls.iter() {
if let kind @ CDeclKind::Function { .. } = &located_kind.kind {
let new_kind = self.typed_context.fn_decl_ty_with_declared_args(kind);
if self.typed_context.type_for_kind(&new_kind).is_none() {
if self.typed_context.try_type_for_kind(&new_kind).is_none() {
// Create and insert fn type
let new_id = CTypeId(self.id_mapper.fresh_id());
self.typed_context
Expand Down Expand Up @@ -2203,9 +2225,7 @@ impl ConversionContext {
}
};
log::trace!("Selected kind {kind} for typedef {name}");
Some(CQualTypeId::new(
self.typed_context.type_for_kind(&kind).unwrap(),
))
Some(CQualTypeId::new(self.typed_context.type_for_kind(&kind)))
})
.unwrap_or(typ);

Expand Down Expand Up @@ -2248,9 +2268,7 @@ impl ConversionContext {
}
};
log::trace!("Selected kind {kind} for typedef {name}");
Some(CQualTypeId::new(
self.typed_context.type_for_kind(&kind).unwrap(),
))
Some(CQualTypeId::new(self.typed_context.type_for_kind(&kind)))
};
let file = self
.typed_context
Expand Down
47 changes: 35 additions & 12 deletions c2rust-transpile/src/c_ast/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::c_ast::iterators::{immediate_children_all_types, NodeVisitor};
use crate::iterators::{DFNodes, SomeId};
use c2rust_ast_builder::properties::Mutability;
use c2rust_ast_exporter::clang_ast::LRValue;
use indexmap::{IndexMap, IndexSet};
use itertools::Itertools;
Expand Down Expand Up @@ -777,12 +778,17 @@ impl TypedAstContext {
ty.map(|ty| (expr_id, ty))
}

pub fn type_for_kind(&self, kind: &CTypeKind) -> Option<CTypeId> {
pub fn try_type_for_kind(&self, kind: &CTypeKind) -> Option<CTypeId> {
self.c_types
.iter()
.find_map(|(id, k)| if kind == &k.kind { Some(*id) } else { None })
}

pub fn type_for_kind(&self, kind: &CTypeKind) -> CTypeId {
self.try_type_for_kind(kind)
.expect("could not find type for CTypeKind::{kind:?}")
}

pub fn resolve_type_id(&self, typ: CTypeId) -> CTypeId {
use CTypeKind::*;
let ty = match self.index(typ).kind {
Expand Down Expand Up @@ -881,9 +887,7 @@ impl TypedAstContext {
pub fn fn_declref_ty_with_declared_args(&self, func_expr: CExprId) -> Option<CQualTypeId> {
if let Some(func_decl @ CDeclKind::Function { .. }) = self.fn_declref_decl(func_expr) {
let kind_with_declared_args = self.fn_decl_ty_with_declared_args(func_decl);
let specific_typ = self
.type_for_kind(&kind_with_declared_args)
.unwrap_or_else(|| panic!("no type for kind {kind_with_declared_args:?}"));
let specific_typ = self.type_for_kind(&kind_with_declared_args);
return Some(CQualTypeId::new(specific_typ));
}
None
Expand Down Expand Up @@ -1383,10 +1387,7 @@ impl TypedAstContext {
CUnTypeOp::AlignOf => CTypeKind::Size,
CUnTypeOp::PreferredAlignOf => CTypeKind::Size,
};
let ty = self
.ast_context
.type_for_kind(&kind)
.expect("CTypeKind::Size should be size_t");
let ty = self.ast_context.type_for_kind(&kind);
Some(CQualTypeId::new(ty))
}
_ => return,
Expand Down Expand Up @@ -2101,7 +2102,7 @@ impl CUnOp {
}
Not => {
return ast_context
.type_for_kind(&CTypeKind::Int)
.try_type_for_kind(&CTypeKind::Int)
.map(CQualTypeId::new)
}
Real | Imag => {
Expand Down Expand Up @@ -2483,7 +2484,7 @@ pub struct AsmOperand {
}

/// Type qualifiers (6.7.3)
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Hash)]
pub struct Qualifiers {
/// The `const` qualifier, which marks lvalues as non-assignable.
///
Expand Down Expand Up @@ -2516,10 +2517,17 @@ impl Qualifiers {
is_volatile: self.is_volatile || other.is_volatile,
}
}

pub fn mutability(self) -> Mutability {
match self.is_const {
true => Mutability::Immutable,
false => Mutability::Mutable,
}
}
}

/// Qualified type
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct CQualTypeId {
pub qualifiers: Qualifiers,
pub ctype: CTypeId,
Expand All @@ -2536,6 +2544,10 @@ impl CQualTypeId {
pub fn with_ctype(self, ctype: CTypeId) -> Self {
Self { ctype, ..self }
}

pub fn mutability(self) -> Mutability {
self.qualifiers.mutability()
}
}

// TODO: these may be interesting, but I'm not sure if they fit here:
Expand All @@ -2546,7 +2558,7 @@ impl CQualTypeId {
/// Represents a type in C (6.2.5 Types)
///
/// Reflects the types in <http://clang.llvm.org/doxygen/classclang_1_1Type.html>
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum CTypeKind {
Void,

Expand Down Expand Up @@ -2670,6 +2682,17 @@ pub enum CTypeKind {
}

impl CTypeKind {
/// Kinds for C primitive types. These are emitted by the compiler, but possibly only if
/// they are actually used in the code.
pub const PRIMITIVE_KINDS: [CTypeKind; 16] = {
use CTypeKind::*;
[
Void, Bool, Char, SChar, Short, Int, Long, LongLong, UChar, UShort, UInt, ULong,
ULongLong, Float, Double, LongDouble,
]
};

/// Kinds for Rust types that are pulled back into C, for more fine-grained translation.
pub const PULLBACK_KINDS: [CTypeKind; 16] = {
use CTypeKind::*;
[
Expand Down
10 changes: 2 additions & 8 deletions c2rust-transpile/src/convert_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::renamer::*;
use crate::translator::variadic::mk_va_list_ty;
use crate::TranspilerConfig;
use crate::{CrateSet, ExternCrate};
use c2rust_ast_builder::{mk, properties::*};
use c2rust_ast_builder::mk;
use c2rust_rust_tools::RustEdition;
use failure::format_err;
use indexmap::IndexSet;
Expand Down Expand Up @@ -170,13 +170,7 @@ impl TypeConverter {
let param = mk().angle_bracketed_args(vec![pointee_ty]);
Ok(mk().path_ty(vec![mk().path_segment_with_args("Option", param)]))
} else {
let mutbl = if qtype.qualifiers.is_const {
Mutability::Immutable
} else {
Mutability::Mutable
};

Ok(mk().set_mutbl(mutbl).ptr_ty(pointee_ty))
Ok(mk().set_mutbl(qtype.mutability()).ptr_ty(pointee_ty))
}
}

Expand Down
6 changes: 1 addition & 5 deletions c2rust-transpile/src/translator/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,11 +341,7 @@ impl<'c> Translation<'c> {
typ: CQualTypeId,
) -> TranslationResult<ConvertedFunctionParam> {
if self.ast_context.is_va_list(typ.ctype) {
let mutbl = if typ.qualifiers.is_const {
Mutability::Immutable
} else {
Mutability::Mutable
};
let mutbl = typ.mutability();
let ty = mk().abs_path_ty(vec!["core", "ffi", "VaList"]);
return Ok(ConvertedFunctionParam { mutbl, ty });
}
Expand Down
7 changes: 1 addition & 6 deletions c2rust-transpile/src/translator/literals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,13 +193,8 @@ impl<'c> Translation<'c> {
}))
} else {
Ok(val.and_then(|val| {
let mutbl = if qty.qualifiers.is_const {
Mutability::Immutable
} else {
Mutability::Mutable
};
let local = mk().local(
mk().set_mutbl(mutbl).ident_pat(&fresh_name),
mk().set_mutbl(qty.mutability()).ident_pat(&fresh_name),
Some(fresh_ty),
Some(val),
);
Expand Down
Loading
Loading