1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#![deny(missing_docs)]
//! This module provides helper functions for generating rvalue and lvalues
//! corresponding to a single Rust expression.
use super::*;
/// Check if something is a valid Rust lvalue. Inspired by `librustc::ty::expr_is_lval`.
fn is_lvalue(e: &Expr) -> bool {
use Expr::*;
matches!(
unparen(e),
Unary(ExprUnary {
op: syn::UnOp::Deref(_),
..
}) | Path(..)
| Field(..)
| Index(..)
)
}
/// Check if something is a side-effect free Rust lvalue.
fn is_simple_lvalue(e: &Expr) -> bool {
use Expr::*;
match *unparen(e) {
Path(..) => true,
Unary(ExprUnary {
op: syn::UnOp::Deref(_),
ref expr,
..
})
| Field(ExprField { base: ref expr, .. })
| Index(ExprIndex { ref expr, .. }) => is_simple_lvalue(expr),
_ => false,
}
}
pub struct NamedReference<R> {
pub lvalue: Box<Expr>,
pub rvalue: R,
}
impl<R> NamedReference<R> {
pub fn map_rvalue<S, F: Fn(R) -> S>(self, f: F) -> NamedReference<S> {
let Self { lvalue, rvalue } = self;
NamedReference {
lvalue,
rvalue: f(rvalue),
}
}
}
impl<'c> Translation<'c> {
/// Get back a Rust lvalue corresponding to the expression passed in.
///
/// Do not use the output lvalue expression more than once.
pub fn name_reference_write(
&self,
ctx: ExprContext,
reference: CExprId,
) -> TranslationResult<WithStmts<NamedReference<()>>> {
self.name_reference(ctx, reference, false)
.map(|ws| ws.map(|named_ref| named_ref.map_rvalue(|_| ())))
}
/// Get back a Rust (lvalue, rvalue) pair corresponding to the expression passed in.
///
/// You may reuse either of these expressions.
pub fn name_reference_write_read(
&self,
ctx: ExprContext,
reference: CExprId,
) -> TranslationResult<WithStmts<NamedReference<Box<Expr>>>> {
let msg: &str = "When called with `uses_read = true`, `name_reference` should always \
return an rvalue (something from which to read the memory location)";
self.name_reference(ctx, reference, true)
.map(|ws| ws.map(|named_ref| named_ref.map_rvalue(|rvalue| rvalue.expect(msg))))
}
/// Given the LHS access to a variable, produce the RHS one
fn read(&self, reference_ty: CQualTypeId, write: Box<Expr>) -> TranslationResult<Box<Expr>> {
if reference_ty.qualifiers.is_volatile {
self.volatile_read(write, reference_ty)
} else {
Ok(write)
}
}
/// This function transforms an expression that should refer to a memory location (a C lvalue)
/// into a Rust lvalue for writing to that location.
///
/// When called with `uses_read`, this function returns an rvalue too. The rvalue can be used to
/// read multiple times without duplicating side-effects.
///
/// NOTE: Use `name_reference_write` or `name_reference_write_read` instead of calling this
/// directly.
fn name_reference(
&self,
ctx: ExprContext,
reference: CExprId,
uses_read: bool,
) -> TranslationResult<WithStmts<NamedReference<Option<Box<Expr>>>>> {
let reference_ty = self
.ast_context
.index(reference)
.kind
.get_qual_type()
.ok_or_else(|| format_err!("bad reference type"))?;
let read = |write| self.read(reference_ty, write);
let reference = self.convert_expr(ctx.used(), reference, Some(reference_ty))?;
reference.and_then(|reference| {
if !uses_read && is_lvalue(&reference) {
Ok(WithStmts::new_val(NamedReference {
lvalue: reference,
rvalue: None,
}))
} else if is_simple_lvalue(&reference) {
Ok(WithStmts::new_val(NamedReference {
lvalue: reference.clone(),
rvalue: Some(read(reference)?),
}))
} else {
// This is the case where we explicitly need to factor out possible side-effects.
let ptr_name = self.renamer.borrow_mut().fresh();
// let ref mut p = lhs;
let compute_ref = mk().local_stmt(Box::new(mk().local(
mk().mutbl().ident_ref_pat(&ptr_name),
None,
Some(reference),
)));
let write =
mk().unary_expr(UnOp::Deref(Default::default()), mk().ident_expr(&ptr_name));
Ok(WithStmts::new(
vec![compute_ref],
NamedReference {
lvalue: write.clone(),
rvalue: Some(read(write)?),
},
))
}
})
}
}