diff --git a/c2rust-transpile/src/translator/literals.rs b/c2rust-transpile/src/translator/literals.rs index 5c8c02824..ac6bb3b24 100644 --- a/c2rust-transpile/src/translator/literals.rs +++ b/c2rust-transpile/src/translator/literals.rs @@ -173,61 +173,75 @@ impl<'c> Translation<'c> { CTypeKind::ConstantArray(ty, n) => { // Convert all of the provided initializer values - // Need to check to see if the next item is a string literal, - // if it is need to treat it as a declaration, rather than - // an init list. https://github.com/GaloisInc/C2Rust/issues/40 - let mut is_string = false; + let to_array_element = |id: &CExprId| -> TranslationResult<_> { + self.convert_expr(ctx.used(), *id)?.result_map(|x| { + // Array literals require all of their elements to be + // the correct type; they will not use implicit casts to + // change mut to const. This becomes a problem when an + // array literal is used in a position where there is no + // type information available to force its type to the + // correct const or mut variation. To avoid this issue + // we manually insert the otherwise elided casts in this + // particular context. + if let CExprKind::ImplicitCast(ty, _, CastKind::ConstCast, _, _) = + self.ast_context[*id].kind + { + let t = self.convert_type(ty.ctype)?; + Ok(mk().cast_expr(x, t)) + } else { + Ok(x) + } + }) + }; - if ids.len() == 1 { - let v = ids.first().unwrap(); - if let CExprKind::Literal(_, CLiteral::String { .. }) = - self.ast_context.index(*v).kind - { - is_string = true; - } - } + let is_string_literal = |id: CExprId| { + matches!( + self.ast_context.index(id).kind, + CExprKind::Literal(_, CLiteral::String { .. }) + ) + }; - if is_string { - let v = ids.first().unwrap(); - self.convert_expr(ctx.used(), *v) - } else if ids.is_empty() { - // this was likely a C array of the form `int x[16] = {}`, - // we'll emit that as [0; 16]. - let len = mk().lit_expr(mk().int_unsuffixed_lit(n as u128)); - self.implicit_default_expr(ty, ctx.is_static)? - .and_then(|default_value| { - Ok(WithStmts::new_val(mk().repeat_expr(default_value, len))) - }) - } else { - Ok(ids - .iter() - .map(|id| { - self.convert_expr(ctx.used(), *id)?.result_map(|x| { - // Array literals require all of their elements to be - // the correct type; they will not use implicit casts to - // change mut to const. This becomes a problem when an - // array literal is used in a position where there is no - // type information available to force its type to the - // correct const or mut variation. To avoid this issue - // we manually insert the otherwise elided casts in this - // particular context. - if let CExprKind::ImplicitCast(ty, _, CastKind::ConstCast, _, _) = - self.ast_context[*id].kind - { - let t = self.convert_type(ty.ctype)?; - Ok(mk().cast_expr(x, t)) - } else { - Ok(x) - } - }) - }) - .chain( - // Pad out the array literal with default values to the desired size - iter::repeat(self.implicit_default_expr(ty, ctx.is_static)) - .take(n - ids.len()), - ) - .collect::>>()? - .map(|vals| mk().array_expr(vals))) + let is_zero_literal = |id: CExprId| { + matches!( + self.ast_context.index(id).kind, + CExprKind::Literal(_, CLiteral::Integer(0, _base)) + ) + }; + + match ids { + [] => { + // this was likely a C array of the form `int x[16] = {}`, + // we'll emit that as [0; 16]. + let len = mk().lit_expr(mk().int_unsuffixed_lit(n as u128)); + Ok(self + .implicit_default_expr(ty, ctx.is_static)? + .map(|default_value| mk().repeat_expr(default_value, len))) + } + [single] if is_string_literal(*single) => { + // Need to check to see if the next item is a string literal, + // if it is need to treat it as a declaration, rather than + // an init list. https://github.com/GaloisInc/C2Rust/issues/40 + self.convert_expr(ctx.used(), *single) + } + [single] if is_zero_literal(*single) && n > 1 => { + // this was likely a C array of the form `int x[16] = { 0 }`, + // we'll emit that as [0; 16]. + let len = mk().lit_expr(mk().int_unsuffixed_lit(n as u128)); + Ok(to_array_element(single)? + .map(|default_value| mk().repeat_expr(default_value, len))) + } + [..] => { + Ok(ids + .iter() + .map(to_array_element) + .chain( + // Pad out the array literal with default values to the desired size + iter::repeat(self.implicit_default_expr(ty, ctx.is_static)) + .take(n - ids.len()), + ) + .collect::>>()? + .map(|vals| mk().array_expr(vals))) + } } } CTypeKind::Struct(struct_id) => { diff --git a/tests/arrays/src/arrays.c b/tests/arrays/src/arrays.c index 1e75c3fcf..283950e65 100644 --- a/tests/arrays/src/arrays.c +++ b/tests/arrays/src/arrays.c @@ -14,6 +14,16 @@ void entry(const unsigned buffer_size, int buffer[const]) struct {char* x; int y;} arr3[1] = {}; arr3[0].y += 9; + int arr4[16] = {0}; + arr4[15] += 9; + + struct {short; int y;} arr5[1] = { { 1, 2 } }; + arr5[0].y += 9; + + // excess elements + int arr6[2] = { 1, 2, 3 }; + int arr7[0] = { 1234 }; + int i = 0; char abc[] = "abc";