Skip to content
216 changes: 191 additions & 25 deletions c2rust-transpile/src/cfg/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use std::ops::Index;
use std::rc::Rc;
use std::{fmt, io};
use syn::Lit;
use syn::{spanned::Spanned, Arm, Expr, Pat, Stmt};
use syn::{punctuated::Punctuated, spanned::Spanned, Arm, Expr, Pat, Stmt};

use failure::format_err;
use indexmap::indexset;
Expand Down Expand Up @@ -1934,31 +1934,27 @@ impl CfgBuilder {
self.add_wip_block(wip, Jump(this_label.clone()));

// Case
let resolved = translator.ast_context.unwrap_cast_expr(case_expr);
let branch = match translator.ast_context.index_unwrap_parens(resolved).kind {
CExprKind::Literal(..) | CExprKind::ConstantExpr(_, _, Some(_)) => {
match translator
.convert_expr(ctx.used(), resolved, None)?
.to_pure_expr()
{
Some(expr) => match *expr {
Expr::Lit(lit) => Some(mk().lit_pat(lit.lit)),
Expr::Path(path) => Some(mk().path_pat(path.path, path.qself)),
_ => None,
},
_ => None,
}
}
_ => None,
};
let pat = translator
.convert_expr(ctx.const_().pattern().used(), case_expr, None)
.map_err(|err| ("convert_expr", err.to_string()))
.and_then(|val| {
val.to_pure_expr()
.ok_or_else(|| ("to_pure_expr", "".to_string()))
})
.and_then(|expr| expr_to_pat(*expr).map_err(|err| ("expr_to_pat", err)))
.unwrap_or_else(|(src, err)| {
log::trace!(
"Converting `case` {:?} failed in {}: {}",
case_expr,
src,
err
);

let pat = match branch {
Some(pat) => pat,
None => match cie {
ConstIntExpr::U(n) => mk().lit_pat(mk().int_unsuffixed_lit(n)),
ConstIntExpr::I(n) => mk().lit_pat(mk().int_unsuffixed_lit(n)),
},
};
match cie {
ConstIntExpr::U(n) => mk().lit_pat(mk().int_unsuffixed_lit(n)),
ConstIntExpr::I(n) => mk().lit_pat(mk().int_unsuffixed_lit(n)),
}
});

self.switch_expr_cases
.last_mut()
Expand Down Expand Up @@ -2415,3 +2411,173 @@ impl Cfg<Label, StmtOrDecl> {
Ok(())
}
}

fn expr_to_pat(expr: Expr) -> Result<Pat, String> {
use syn::{
ExprArray, ExprCall, ExprLit, ExprParen, ExprPath, ExprReference, ExprStruct, ExprTuple,
ExprUnary, FieldPat, FieldValue, LitInt, PatLit, PatParen, PatPath, PatReference, PatSlice,
PatStruct, PatTuple, PatTupleStruct, UnOp,
};

match expr {
Expr::Array(ExprArray {
attrs,
bracket_token,
elems,
}) => {
let elems = punctuated_expr_to_pat(elems)?;

Ok(Pat::Slice(PatSlice {
attrs,
bracket_token,
elems,
}))
}

Expr::Call(ExprCall {
attrs,
func,
paren_token,
args,
}) => {
let (qself, path) = match *func {
Expr::Path(ExprPath { qself, path, .. }) => (qself, path),
_ => return Err("`ExprCall::func` is not an `ExprPath`".into()),
};
let elems = punctuated_expr_to_pat(args)?;

Ok(Pat::TupleStruct(PatTupleStruct {
attrs,
qself,
path,
paren_token,
elems,
}))
}

Expr::Lit(ExprLit { attrs, lit }) => Ok(Pat::Lit(PatLit { attrs, lit })),

Expr::Paren(ExprParen {
attrs,
paren_token,
expr,
}) => {
let pat = Box::new(expr_to_pat(*expr)?);
Ok(Pat::Paren(PatParen {
attrs,
paren_token,
pat,
}))
}

Expr::Path(ExprPath { attrs, qself, path }) => {
Ok(Pat::Path(PatPath { attrs, qself, path }))
}

Expr::Range(range) => Ok(Pat::Range(range)),

Expr::Reference(ExprReference {
attrs,
and_token,
mutability,
expr,
}) => {
let pat = Box::new(expr_to_pat(*expr)?);

Ok(Pat::Reference(PatReference {
attrs,
and_token,
mutability,
pat,
}))
}

Expr::Struct(ExprStruct {
attrs,
qself,
path,
brace_token,
fields,
dot2_token: None,
rest: None,
}) => {
let fields = fields
.into_iter()
.map(|field| {
let FieldValue {
attrs,
member,
colon_token,
expr,
} = field;
let pat = Box::new(expr_to_pat(expr)?);

Ok(FieldPat {
attrs,
member,
colon_token,
pat,
})
})
.collect::<Result<_, String>>()?;

Ok(Pat::Struct(PatStruct {
attrs,
qself,
path,
brace_token,
fields,
rest: None,
}))
}

Expr::Tuple(ExprTuple {
attrs,
paren_token,
elems,
}) => {
let elems = punctuated_expr_to_pat(elems)?;

Ok(Pat::Tuple(PatTuple {
attrs,
paren_token,
elems,
}))
}

// There is no equivalent `PatUnary`, but the negative sign can be folded into the literal.
Expr::Unary(ExprUnary {
attrs,
op: UnOp::Neg(_),
expr,
}) => {
let Expr::Lit(ExprLit {
attrs: _,
lit: Lit::Int(lit_int),
}) = *expr else {
return Err("`ExprUnary::expr` is not an `ExprLit` with `lit: Lit::Int`".into());
};

let repr = format!("-{}{}", lit_int.base10_digits(), lit_int.suffix());
let lit = Lit::Int(LitInt::new(&repr, lit_int.span()));
Ok(Pat::Lit(PatLit { attrs, lit }))
}

_ => Err("`Expr` with no equivalent `Pat`".into()),
}
}

fn punctuated_expr_to_pat(
elems: Punctuated<Expr, syn::Token![,]>,
) -> Result<Punctuated<Pat, syn::Token![,]>, String> {
use syn::punctuated::Pair;

elems
.into_pairs()
.map(|pair| {
let (expr, token) = pair.into_tuple();
let pat = expr_to_pat(expr)?;
Ok(Pair::new(pat, token))
})
.collect()
}
30 changes: 23 additions & 7 deletions c2rust-transpile/src/translator/literals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ impl<'c> Translation<'c> {
/// Generate an integer literal corresponding to the given type, value, and base.
pub fn mk_int_lit(
&self,
ctx: ExprContext,
ty: CQualTypeId,
val: u64,
base: IntBase,
Expand All @@ -36,7 +37,7 @@ impl<'c> Translation<'c> {
expr = neg_expr(expr);
}

Ok(if is_suffix {
Ok(if is_suffix || ctx.is_pattern {
expr
} else {
mk().cast_expr(expr, target_ty)
Expand Down Expand Up @@ -68,13 +69,18 @@ impl<'c> Translation<'c> {
lit: &CLiteral,
) -> TranslationResult<WithStmts<Box<Expr>>> {
match *lit {
CLiteral::Integer(val, base) => {
Ok(WithStmts::new_val(self.mk_int_lit(ty, val, base, false)?))
}
CLiteral::Integer(val, base) => Ok(WithStmts::new_val(
self.mk_int_lit(ctx, ty, val, base, false)?,
)),

CLiteral::Character(val) => {
let val = val as u32;
let expr = match char::from_u32(val) {
let mut expr = match char::from_u32(val).filter(|_| {
// Always convert character literals as integers in patterns.
// Character literals have problems with typing that need to be resolved. See
// https://github.com/immunant/c2rust/issues/648
!ctx.is_pattern
}) {
Some(c) => mk().lit_expr(c),
None => {
// Fallback for characters outside of the valid Unicode range
Expand All @@ -88,8 +94,12 @@ impl<'c> Translation<'c> {
}
};

let type_rs = self.convert_type(ty.ctype)?;
Ok(WithStmts::new_val(mk().cast_expr(expr, type_rs)))
if !ctx.is_pattern {
let type_rs = self.convert_type(ty.ctype)?;
expr = mk().cast_expr(expr, type_rs);
}

Ok(WithStmts::new_val(expr))
}

CLiteral::Floating(val, ref c_str) => {
Expand Down Expand Up @@ -123,6 +133,12 @@ impl<'c> Translation<'c> {
}

CLiteral::String(ref bytes, element_size) => {
if ctx.is_pattern {
return Err(TranslationError::generic(
"CLiteral::String is not supported in patterns",
));
}

let bytes_padded = self.string_literal_bytes(ty.ctype, bytes, element_size);
let len = bytes_padded.len();
let val = mk().lit_expr(bytes_padded);
Expand Down
22 changes: 16 additions & 6 deletions c2rust-transpile/src/translator/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ impl<'c> Translation<'c> {

// We haven't tried to expand it yet.
None => {
self.convert_decl(ctx, *macro_id)?;
self.convert_decl(ctx.not_pattern(), *macro_id)?;
if let Some(Some(expansion)) = self.macro_expansions.borrow().get(macro_id) {
expansion.ty
} else {
Expand All @@ -204,7 +204,7 @@ impl<'c> Translation<'c> {

self.add_import(*macro_id, &rust_name);

let val = WithStmts::new_val(mk().path_expr(vec![rust_name]));
let mut val = WithStmts::new_val(mk().path_expr(vec![rust_name]));

let expr_kind = &self.ast_context.index_unwrap_parens(expr_id).kind;
// TODO We'd like to get rid of this cast eventually (see #1321).
Expand All @@ -214,14 +214,24 @@ impl<'c> Translation<'c> {
// so we need to cast it to the `override_ty` here.
let expr_ty = override_ty.or_else(|| expr_kind.get_qual_type());
if let Some(expr_ty) = expr_ty {
self.make_cast(ctx, CQualTypeId::new(macro_ty), expr_ty, val)
.map(Some)
} else {
Ok(Some(val))
match self.make_cast(ctx, CQualTypeId::new(macro_ty), expr_ty, val) {
Ok(new_val) => val = new_val,
Err(err) => {
info!(
"Could not convert cast of macro {} for {:?}: {}",
self.renamer.borrow_mut().get(macro_id).unwrap(),
expr_id,
err
);
return Ok(None);
}
}
}

// TODO: May need to handle volatile reads here.
// See `DeclRef` below.

Ok(Some(val))
}

/// Convert the expansion of a function-like macro.
Expand Down
Loading
Loading