Skip to content
Open
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
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 @@ -454,6 +454,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
38 changes: 27 additions & 11 deletions c2rust-transpile/src/c_ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1361,32 +1361,48 @@ impl TypedAstContext {
None
}
}
CExprKind::Binary(_ty, op, lhs, rhs, _, _) => {
CExprKind::Binary(result_type_id, op, lhs, rhs, _, _) => {
let rhs_type_id =
self.ast_context.c_exprs[&rhs].kind.get_qual_type().unwrap();
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.all_types_same() && neither_ptr {
if CTypeKind::PULLBACK_KINDS.contains(&lhs_resolved_ty.kind) {
Some(lhs_type_id)
} else {
Some(rhs_type_id)
let result_type_kind =
&self.ast_context.resolve_type(result_type_id.ctype).kind;

let mut result_type_id =
if CTypeKind::PULLBACK_KINDS.contains(lhs_type_kind) {
lhs_type_id
} else {
rhs_type_id
};

// For complex arithmetic, complex and real values can be mixed.
// See if a `Complex` version of the new result type exists.
if matches!(result_type_kind, CTypeKind::Complex(..)) {
let new_result_type_kind =
CTypeKind::Complex(result_type_id.ctype);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if result_type_id is already a Complex? Would something like double f(double _Complex a, double _Complex b) { ... a + b ... } panic here? That could be a new test.

let new_type_id =
self.ast_context.type_for_kind(&new_result_type_kind);
result_type_id.ctype = new_type_id;
}

Some(result_type_id)
} else if op.is_bitshift() {
Some(lhs_type_id)
} else {
Expand Down
57 changes: 38 additions & 19 deletions c2rust-transpile/src/translator/operators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,30 +87,41 @@ impl<'c> Translation<'c> {

// If this operation will (in Rust) take args of the same type, then propagate our
// expected type down to the translation of our argument expressions.
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 expr_ty_kind = &self.ast_context.index(expr_type_id.ctype).kind;
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;
let expr_type_kind = &self.ast_context.index(expr_type_id.ctype).kind;

// Addition and subtraction can accept one pointer argument for .offset(), in which
// case we don't want to homogenize arg types.
if !lhs_resolved_ty.kind.is_pointer()
&& !rhs_resolved_ty.kind.is_pointer()
&& !expr_ty_kind.is_pointer()
if !lhs_type_kind.is_pointer()
&& !rhs_type_kind.is_pointer()
&& !expr_type_kind.is_pointer()
{
if op.all_types_same() {
// Ops like division and bitxor accept inputs of their expected result type.
lhs_type_id = expr_type_id;
rhs_type_id = expr_type_id;
} else if op.input_types_same() && lhs_resolved_ty.kind != rhs_resolved_ty.kind
{

// For complex arithmetic, complex and real values can be mixed.
if let &CTypeKind::Complex(result_scalar_type_id) = expr_type_kind {
if !matches!(lhs_type_kind, CTypeKind::Complex(..)) {
lhs_type_id.ctype = result_scalar_type_id;
}

if !matches!(rhs_type_kind, CTypeKind::Complex(..)) {
rhs_type_id.ctype = result_scalar_type_id;
}
}
} else if op.input_types_same() && lhs_type_kind != rhs_type_kind {
// Ops like comparisons require argument types to match, but the result type
// doesn't inform us what type to choose. Select a synthetic definition of a
// portable rust type (e.g. u64 or usize) if either arg is one.
trace!(
"Binary op arg types differ: {:?} vs {:?}",
lhs_resolved_ty.kind,
rhs_resolved_ty.kind
lhs_type_kind,
rhs_type_kind
);
let ty = if CTypeKind::PULLBACK_KINDS.contains(&lhs_resolved_ty.kind) {
let ty = if CTypeKind::PULLBACK_KINDS.contains(lhs_type_kind) {
lhs_type_id
} else {
rhs_type_id
Expand Down Expand Up @@ -225,15 +236,15 @@ impl<'c> Translation<'c> {
.get_qual_type()
.ok_or_else(|| format_err!("bad initial lhs type"))?;

// First, translate the rhs. Then, if it must match the lhs but doesn't, add a cast.
// First, translate the rhs.
let mut rhs_translation = self.convert_expr(ctx.used(), rhs, Some(rhs_type_id))?;
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;

let lhs_rhs_types_must_match = {
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);
// Addition and subtraction can accept one pointer argument for .offset(), in which
// case we don't want to homogenize arg types.
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();

op.underlying_assignment().map_or(true, |op| {
if op.is_pointer_arithmetic() {
Expand All @@ -244,11 +255,19 @@ impl<'c> Translation<'c> {
})
};
if lhs_rhs_types_must_match {
// If the rhs must match the lhs but doesn't, add a cast.
// For compound assignment, use the compute type; for regular assignment, use lhs type
let effective_lhs_ty = compute_lhs_type_id.unwrap_or(lhs_type_id);
let mut effective_lhs_ty = compute_lhs_type_id.unwrap_or(lhs_type_id);

// For complex arithmetic, complex and real values can be mixed.
if let &CTypeKind::Complex(lhs_scalar_type_id) = lhs_type_kind {
if !matches!(rhs_type_kind, CTypeKind::Complex(..)) {
effective_lhs_ty.ctype = lhs_scalar_type_id;
}
}

if effective_lhs_ty.ctype != rhs_type_id.ctype {
let new_rhs_ty =
self.convert_type(compute_lhs_type_id.unwrap_or(lhs_type_id).ctype)?;
let new_rhs_ty = self.convert_type(effective_lhs_ty.ctype)?;
rhs_translation = rhs_translation.map(|val| mk().cast_expr(val, new_rhs_ty));
}
}
Expand Down
Loading