Skip to content

Commit 11cea86

Browse files
committed
transpile: In is_simple_lvalue, also check index expr for side effects
1 parent b1ed8e2 commit 11cea86

3 files changed

Lines changed: 105 additions & 10 deletions

File tree

c2rust-transpile/src/translator/named_references.rs

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
//! corresponding to a single Rust expression.
44
55
use super::*;
6+
use syn::{
7+
Arm, ExprArray, ExprConst, ExprGroup, ExprIf, ExprMatch, ExprRange, ExprRawAddr, ExprReference,
8+
ExprRepeat, ExprStruct, ExprTuple, ExprUnsafe, FieldValue,
9+
};
610

711
/// Check if something is a valid Rust lvalue. Inspired by `librustc::ty::expr_is_lval`.
812
fn is_lvalue(e: &Expr) -> bool {
@@ -28,12 +32,95 @@ fn is_simple_lvalue(e: &Expr) -> bool {
2832
ref expr,
2933
..
3034
})
31-
| Field(ExprField { base: ref expr, .. })
32-
| Index(ExprIndex { ref expr, .. }) => is_simple_lvalue(expr),
35+
| Field(ExprField { base: ref expr, .. }) => is_simple_lvalue(expr),
36+
Index(ExprIndex {
37+
ref expr,
38+
ref index,
39+
..
40+
}) => is_simple_lvalue(expr) && !expr_has_side_effects(index),
3341
_ => false,
3442
}
3543
}
3644

45+
/// Returns whether the expression has (or could have) any side effects.
46+
/// This is pessimistic, and does not cover all expression types currently.
47+
fn expr_has_side_effects(expr: &Expr) -> bool {
48+
use Expr::*;
49+
50+
match unparen(expr) {
51+
Array(ExprArray { elems, .. }) => elems.iter().any(|elem| expr_has_side_effects(elem)),
52+
Cast(ExprCast { expr, .. }) => expr_has_side_effects(expr),
53+
Binary(ExprBinary { left, right, .. }) => {
54+
expr_has_side_effects(left) || expr_has_side_effects(right)
55+
}
56+
Block(ExprBlock { block, .. }) => block_has_side_effects(block),
57+
Const(ExprConst { block, .. }) => block_has_side_effects(block),
58+
Field(ExprField { base, .. }) => expr_has_side_effects(base),
59+
Group(ExprGroup { expr, .. }) => expr_has_side_effects(expr),
60+
Index(ExprIndex { expr, index, .. }) => {
61+
expr_has_side_effects(expr) || expr_has_side_effects(index)
62+
}
63+
Infer(_) => false,
64+
If(ExprIf {
65+
cond,
66+
then_branch,
67+
else_branch,
68+
..
69+
}) => {
70+
expr_has_side_effects(cond)
71+
|| block_has_side_effects(then_branch)
72+
|| else_branch
73+
.as_ref()
74+
.map_or(false, |(_, expr)| expr_has_side_effects(expr))
75+
}
76+
Lit(_) => false,
77+
Match(ExprMatch { expr, arms, .. }) => {
78+
expr_has_side_effects(expr)
79+
|| arms.iter().any(|Arm { guard, body, .. }| {
80+
guard
81+
.as_ref()
82+
.map_or(false, |(_, expr)| expr_has_side_effects(expr))
83+
|| expr_has_side_effects(body)
84+
})
85+
}
86+
Paren(ExprParen { expr, .. }) => expr_has_side_effects(expr),
87+
Path(_) => false,
88+
Range(ExprRange { start, end, .. }) => {
89+
start
90+
.as_ref()
91+
.map_or(false, |expr| expr_has_side_effects(expr))
92+
|| end
93+
.as_ref()
94+
.map_or(false, |expr| expr_has_side_effects(expr))
95+
}
96+
RawAddr(ExprRawAddr { expr, .. }) => expr_has_side_effects(expr),
97+
Reference(ExprReference { expr, .. }) => expr_has_side_effects(expr),
98+
Repeat(ExprRepeat { expr, len, .. }) => {
99+
expr_has_side_effects(expr) || expr_has_side_effects(len)
100+
}
101+
Struct(ExprStruct { fields, rest, .. }) => {
102+
fields
103+
.iter()
104+
.any(|FieldValue { expr, .. }| expr_has_side_effects(expr))
105+
|| rest
106+
.as_ref()
107+
.map_or(false, |rest| expr_has_side_effects(rest))
108+
}
109+
Tuple(ExprTuple { elems, .. }) => elems.iter().any(|elem| expr_has_side_effects(elem)),
110+
Unary(ExprUnary { expr, .. }) => expr_has_side_effects(expr),
111+
Unsafe(ExprUnsafe { block, .. }) => block_has_side_effects(block),
112+
_ => true,
113+
}
114+
}
115+
116+
fn block_has_side_effects(block: &Block) -> bool {
117+
use Stmt::*;
118+
block.stmts.iter().any(|stmt| match stmt {
119+
Expr(expr, _) => expr_has_side_effects(expr),
120+
_ => true,
121+
})
122+
}
123+
37124
pub struct NamedReference<R> {
38125
pub lvalue: Box<Expr>,
39126
pub rvalue: R,

c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2021.snap

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,14 @@ pub unsafe extern "C" fn unary_with_side_effect() {
4949
(b"\0".as_ptr() as *const ::core::ffi::c_char).offset(side_effect() as isize)
5050
as *const ::core::ffi::c_char;
5151
*arr[side_effect() as usize];
52-
arr[side_effect() as usize] = arr[side_effect() as usize].offset(1);
53-
arr[side_effect() as usize] = arr[side_effect() as usize].offset(-1);
54-
arr[side_effect() as usize] = arr[side_effect() as usize].offset(1);
55-
arr[side_effect() as usize] = arr[side_effect() as usize].offset(-1);
52+
let ref mut c2rust_fresh0 = arr[side_effect() as usize];
53+
*c2rust_fresh0 = (*c2rust_fresh0).offset(1);
54+
let ref mut c2rust_fresh1 = arr[side_effect() as usize];
55+
*c2rust_fresh1 = (*c2rust_fresh1).offset(-1);
56+
let ref mut c2rust_fresh2 = arr[side_effect() as usize];
57+
*c2rust_fresh2 = (*c2rust_fresh2).offset(1);
58+
let ref mut c2rust_fresh3 = arr[side_effect() as usize];
59+
*c2rust_fresh3 = (*c2rust_fresh3).offset(-1);
5660
}
5761
#[no_mangle]
5862
pub unsafe extern "C" fn unsigned_compound_desugaring() {

c2rust-transpile/tests/snapshots/snapshots__transpile@exprs.c.2024.snap

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,14 @@ pub unsafe extern "C" fn unary_with_side_effect() {
4949
(b"\0".as_ptr() as *const ::core::ffi::c_char).offset(side_effect() as isize)
5050
as *const ::core::ffi::c_char;
5151
*arr[side_effect() as usize];
52-
arr[side_effect() as usize] = arr[side_effect() as usize].offset(1);
53-
arr[side_effect() as usize] = arr[side_effect() as usize].offset(-1);
54-
arr[side_effect() as usize] = arr[side_effect() as usize].offset(1);
55-
arr[side_effect() as usize] = arr[side_effect() as usize].offset(-1);
52+
let ref mut c2rust_fresh0 = arr[side_effect() as usize];
53+
*c2rust_fresh0 = (*c2rust_fresh0).offset(1);
54+
let ref mut c2rust_fresh1 = arr[side_effect() as usize];
55+
*c2rust_fresh1 = (*c2rust_fresh1).offset(-1);
56+
let ref mut c2rust_fresh2 = arr[side_effect() as usize];
57+
*c2rust_fresh2 = (*c2rust_fresh2).offset(1);
58+
let ref mut c2rust_fresh3 = arr[side_effect() as usize];
59+
*c2rust_fresh3 = (*c2rust_fresh3).offset(-1);
5660
}
5761
#[unsafe(no_mangle)]
5862
pub unsafe extern "C" fn unsigned_compound_desugaring() {

0 commit comments

Comments
 (0)