Skip to content
6 changes: 6 additions & 0 deletions c2rust-transpile/src/c_ast/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,12 @@ impl ConversionContext {
self.add_type(new_id, not_located(rust_type_kind));
self.processed_nodes
.insert(new_id, self::node_types::OTHER_TYPE);

let complex_kind = CTypeKind::Complex(CTypeId(new_id));
let new_id = self.id_mapper.fresh_id();
self.add_type(new_id, not_located(complex_kind));
self.processed_nodes
.insert(new_id, self::node_types::OTHER_TYPE);
}

// Continue popping Clang nodes off of the stack of nodes we have promised to visit
Expand Down
114 changes: 95 additions & 19 deletions c2rust-transpile/src/c_ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1589,22 +1589,95 @@ impl TypedAstContext {
let lhs_kind = &self.ast_context.c_exprs[&lhs].kind;
let lhs_type_id = lhs_kind.get_qual_type().unwrap();

let lhs_resolved_ty = self.ast_context.resolve_type(lhs_type_id.ctype);
let rhs_resolved_ty = self.ast_context.resolve_type(rhs_type_id.ctype);
let lhs_type_kind = &self.ast_context.resolve_type(lhs_type_id.ctype).kind;
let rhs_type_kind = &self.ast_context.resolve_type(rhs_type_id.ctype).kind;

if op == CBinOp::Subtract
&& lhs_resolved_ty.kind.is_pointer()
&& rhs_resolved_ty.kind.is_pointer()
&& lhs_type_kind.is_pointer()
&& rhs_type_kind.is_pointer()
{
// Pointer difference operator should return `ptrdiff_t`.
let new_type_id = self.ast_context.type_for_kind(&CTypeKind::PtrDiff);
Some(CQualTypeId::new(new_type_id))
} else {
let neither_ptr = !lhs_resolved_ty.kind.is_pointer()
&& !rhs_resolved_ty.kind.is_pointer();
let neither_ptr =
!lhs_type_kind.is_pointer() && !rhs_type_kind.is_pointer();

if op.is_arithmetic() && neither_ptr {
struct Operand {
type_id: CQualTypeId,
is_complex: bool,
is_pullback_kind: bool,
}

if op.all_types_same() && neither_ptr {
if CTypeKind::PULLBACK_KINDS.contains(&lhs_resolved_ty.kind) {
let operand = |type_id, type_kind: &CTypeKind| -> Operand {
let real_type_id = type_kind.complex_real_type();
let is_complex = real_type_id.is_some();
let is_pullback_kind = CTypeKind::PULLBACK_KINDS.contains(
real_type_id.map_or(type_kind, |type_id| {
&self.ast_context.resolve_type(type_id).kind
}),
);

Operand {
type_id,
is_complex,
is_pullback_kind,
}
};

let operands = [
&operand(lhs_type_id, lhs_type_kind),
&operand(rhs_type_id, rhs_type_kind),
];

// For complex arithmetic, complex and real operands can be mixed,
// and the result will be `Complex` if at least one of the operands
// is `Complex`.
let is_complex_arithmetic =
operands.iter().any(|operand| operand.is_complex);

let pullback_operand =
match operands.map(|operand| operand.is_pullback_kind) {
[true, true] => {
// If both operands are pullback kinds, then we can
// choose freely, so just pick the lhs. But if the rhs
// is `Complex` and the lhs is not, then prefer the rhs.
if operands[1].is_complex && !operands[0].is_complex {
operands[1]
} else {
operands[0]
}
}
[true, false] => operands[0],
[false, true] => operands[1],
[false, false] => return,
};
let mut new_result_type_id = pullback_operand.type_id;

// If we are doing complex arithmetic, but the chosen operand is not
// complex itself, then we need to wrap it in a `Complex` ourselves.
if is_complex_arithmetic && !pullback_operand.is_complex {
let real_type_id = new_result_type_id.ctype;
let new_type_id = self
.ast_context
.try_type_for_kind(&CTypeKind::Complex(real_type_id))
.unwrap_or_else(|| {
// We are guaranteed to have a `Complex` version of all
// pullback kinds, but that does not necessarily include
// typedefs. If a type doesn't exist with the original
// real type, resolve it.
let real_type_id =
self.ast_context.resolve_type_id(real_type_id);
self.ast_context
.type_for_kind(&CTypeKind::Complex(real_type_id))
});
new_result_type_id = new_type_id.into();
}

Some(new_result_type_id)
} else if op.is_bitwise() {
if CTypeKind::PULLBACK_KINDS.contains(lhs_type_kind) {
Some(lhs_type_id)
} else {
Some(rhs_type_id)
Expand Down Expand Up @@ -2571,17 +2644,6 @@ impl CBinOp {
_ => return None,
})
}

/// Does the rust equivalent of this operator have type (T, T) -> U?
pub fn input_types_same(&self) -> bool {
self.all_types_same() || self.is_logical() || self.is_comparison() || self.is_assignment()
}

/// Does the rust equivalent of this operator have type (T, T) -> T?
/// This ignores cases where one argument is a pointer and we translate to `.offset()`.
pub fn all_types_same(&self) -> bool {
self.is_arithmetic() || self.is_bitwise()
}
}

impl From<CBinOp> for BinOp {
Expand Down Expand Up @@ -2825,6 +2887,12 @@ impl CQualTypeId {
}
}

impl From<CTypeId> for CQualTypeId {
fn from(value: CTypeId) -> Self {
CQualTypeId::new(value)
}
}

// TODO: these may be interesting, but I'm not sure if they fit here:
//
// * UnaryTransformType <http://clang.llvm.org/doxygen/classclang_1_1UnaryTransformType.html>
Expand Down Expand Up @@ -3186,6 +3254,14 @@ impl CTypeKind {
};
Some(ty)
}

fn complex_real_type(&self) -> Option<CTypeId> {
if let Self::Complex(real_type_id) = *self {
Some(real_type_id)
} else {
None
}
}
}

impl Display for CTypeKind {
Expand Down
Loading
Loading