Skip to content

Commit 6e5fd7d

Browse files
committed
shorter output for arrays initialized with { 0 }
1 parent d28087d commit 6e5fd7d

File tree

2 files changed

+77
-53
lines changed

2 files changed

+77
-53
lines changed

c2rust-transpile/src/translator/literals.rs

+67-53
Original file line numberDiff line numberDiff line change
@@ -173,61 +173,75 @@ impl<'c> Translation<'c> {
173173
CTypeKind::ConstantArray(ty, n) => {
174174
// Convert all of the provided initializer values
175175

176-
// Need to check to see if the next item is a string literal,
177-
// if it is need to treat it as a declaration, rather than
178-
// an init list. https://github.com/GaloisInc/C2Rust/issues/40
179-
let mut is_string = false;
176+
let to_array_element = |id: &CExprId| -> TranslationResult<_> {
177+
self.convert_expr(ctx.used(), *id)?.result_map(|x| {
178+
// Array literals require all of their elements to be
179+
// the correct type; they will not use implicit casts to
180+
// change mut to const. This becomes a problem when an
181+
// array literal is used in a position where there is no
182+
// type information available to force its type to the
183+
// correct const or mut variation. To avoid this issue
184+
// we manually insert the otherwise elided casts in this
185+
// particular context.
186+
if let CExprKind::ImplicitCast(ty, _, CastKind::ConstCast, _, _) =
187+
self.ast_context[*id].kind
188+
{
189+
let t = self.convert_type(ty.ctype)?;
190+
Ok(mk().cast_expr(x, t))
191+
} else {
192+
Ok(x)
193+
}
194+
})
195+
};
180196

181-
if ids.len() == 1 {
182-
let v = ids.first().unwrap();
183-
if let CExprKind::Literal(_, CLiteral::String { .. }) =
184-
self.ast_context.index(*v).kind
185-
{
186-
is_string = true;
187-
}
188-
}
197+
let is_string_literal = |id: CExprId| {
198+
matches!(
199+
self.ast_context.index(id).kind,
200+
CExprKind::Literal(_, CLiteral::String { .. })
201+
)
202+
};
189203

190-
if is_string {
191-
let v = ids.first().unwrap();
192-
self.convert_expr(ctx.used(), *v)
193-
} else if ids.is_empty() {
194-
// this was likely a C array of the form `int x[16] = {}`,
195-
// we'll emit that as [0; 16].
196-
let len = mk().lit_expr(mk().int_unsuffixed_lit(n as u128));
197-
self.implicit_default_expr(ty, ctx.is_static)?
198-
.and_then(|default_value| {
199-
Ok(WithStmts::new_val(mk().repeat_expr(default_value, len)))
200-
})
201-
} else {
202-
Ok(ids
203-
.iter()
204-
.map(|id| {
205-
self.convert_expr(ctx.used(), *id)?.result_map(|x| {
206-
// Array literals require all of their elements to be
207-
// the correct type; they will not use implicit casts to
208-
// change mut to const. This becomes a problem when an
209-
// array literal is used in a position where there is no
210-
// type information available to force its type to the
211-
// correct const or mut variation. To avoid this issue
212-
// we manually insert the otherwise elided casts in this
213-
// particular context.
214-
if let CExprKind::ImplicitCast(ty, _, CastKind::ConstCast, _, _) =
215-
self.ast_context[*id].kind
216-
{
217-
let t = self.convert_type(ty.ctype)?;
218-
Ok(mk().cast_expr(x, t))
219-
} else {
220-
Ok(x)
221-
}
222-
})
223-
})
224-
.chain(
225-
// Pad out the array literal with default values to the desired size
226-
iter::repeat(self.implicit_default_expr(ty, ctx.is_static))
227-
.take(n - ids.len()),
228-
)
229-
.collect::<TranslationResult<WithStmts<_>>>()?
230-
.map(|vals| mk().array_expr(vals)))
204+
let is_zero_literal = |id: CExprId| {
205+
matches!(
206+
self.ast_context.index(id).kind,
207+
CExprKind::Literal(_, CLiteral::Integer(0, _base))
208+
)
209+
};
210+
211+
match ids {
212+
[] => {
213+
// this was likely a C array of the form `int x[16] = {}`,
214+
// we'll emit that as [0; 16].
215+
let len = mk().lit_expr(mk().int_unsuffixed_lit(n as u128));
216+
Ok(self
217+
.implicit_default_expr(ty, ctx.is_static)?
218+
.map(|default_value| mk().repeat_expr(default_value, len)))
219+
}
220+
[single] if is_string_literal(*single) => {
221+
// Need to check to see if the next item is a string literal,
222+
// if it is need to treat it as a declaration, rather than
223+
// an init list. https://github.com/GaloisInc/C2Rust/issues/40
224+
self.convert_expr(ctx.used(), *single)
225+
}
226+
[single] if is_zero_literal(*single) && n > 1 => {
227+
// this was likely a C array of the form `int x[16] = { 0 }`,
228+
// we'll emit that as [0; 16].
229+
let len = mk().lit_expr(mk().int_unsuffixed_lit(n as u128));
230+
Ok(to_array_element(single)?
231+
.map(|default_value| mk().repeat_expr(default_value, len)))
232+
}
233+
[..] => {
234+
Ok(ids
235+
.iter()
236+
.map(to_array_element)
237+
.chain(
238+
// Pad out the array literal with default values to the desired size
239+
iter::repeat(self.implicit_default_expr(ty, ctx.is_static))
240+
.take(n - ids.len()),
241+
)
242+
.collect::<TranslationResult<WithStmts<_>>>()?
243+
.map(|vals| mk().array_expr(vals)))
244+
}
231245
}
232246
}
233247
CTypeKind::Struct(struct_id) => {

tests/arrays/src/arrays.c

+10
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@ void entry(const unsigned buffer_size, int buffer[const])
1414
struct {char* x; int y;} arr3[1] = {};
1515
arr3[0].y += 9;
1616

17+
int arr4[16] = {0};
18+
arr4[15] += 9;
19+
20+
struct {short; int y;} arr5[1] = { { 1, 2 } };
21+
arr5[0].y += 9;
22+
23+
// excess elements
24+
int arr6[2] = { 1, 2, 3 };
25+
int arr7[0] = { 1234 };
26+
1727
int i = 0;
1828

1929
char abc[] = "abc";

0 commit comments

Comments
 (0)