Skip to content

use a repeat expression for arrays with a single initializer #1218

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
120 changes: 67 additions & 53 deletions c2rust-transpile/src/translator/literals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<TranslationResult<WithStmts<_>>>()?
.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)?
Copy link
Contributor

Choose a reason for hiding this comment

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

We specifically want zero initialization, not necessarily default initialization, although they are usually the same, and seems to be for all cases in fn implicit_default_expr. It seems like it doesn't handle structs/unions/enums, though, which fn zero_initializer does, so we probably want to use both and clarify the naming/docs to ensure it's specifically zero.

.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::<TranslationResult<WithStmts<_>>>()?
.map(|vals| mk().array_expr(vals)))
}
}
}
CTypeKind::Struct(struct_id) => {
Expand Down
10 changes: 10 additions & 0 deletions tests/arrays/src/arrays.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Comment on lines +17 to +25
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you add to test_arrays.rs that these arrays translate as expected?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What actually runs the logic in test_arrays.rs? just a cargo test does not seem to do it, the functions in that file appear to never be actually called, and they are not marked #[test].


int i = 0;

char abc[] = "abc";
Expand Down
Loading