Skip to content

Commit 0d01b4f

Browse files
authored
Avoid implicit autoderef on dereferences coming from rules (#107)
Adds the `is_index_base` field in the IR for indexed arguments, for example in `a0[a1]`, `a0` is decorated with `is_index_base = true`. Inside Converter, when a deref is needed and `is_index_base == true`, then explicit autoref is triggered.
1 parent ad9215a commit 0d01b4f

14 files changed

Lines changed: 95 additions & 33 deletions

File tree

cpp2rust/converter/converter.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3704,6 +3704,11 @@ std::string Converter::ConvertPlaceholder(clang::Expr *expr, clang::Expr *arg,
37043704

37053705
if (ph_ctx.needs_object_receiver()) {
37063706
Buffer buf(*this);
3707+
PushExplicitAutoref autoref(
3708+
*this,
3709+
ph_ctx.is_index_base
3710+
? std::optional(ph_ctx.access == TranslationRule::Access::kWrite)
3711+
: std::nullopt);
37073712
ConvertDeref(arg);
37083713
return std::move(buf).str();
37093714
}
@@ -3778,6 +3783,7 @@ std::string Converter::ConvertIRFragment(
37783783
.maps_to_rust_ptr = Mapper::MapsToPointer(arg->getType()),
37793784
.declared_in_rule_as_rust_ptr =
37803785
Mapper::ParamIsPointer(GetCalleeOrExpr(expr), arg_idx),
3786+
.is_index_base = ph->is_index_base,
37813787
};
37823788
result += ConvertPlaceholder(expr, arg, ph_ctx);
37833789
} else if (auto *mc =

cpp2rust/converter/converter.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
185185
bool is_cpp_ptr;
186186
bool maps_to_rust_ptr;
187187
bool declared_in_rule_as_rust_ptr;
188+
bool is_index_base;
188189

189190
bool needs_materialization() const {
190191
return materialize_ctx && materialize_idx >= 0 &&

cpp2rust/converter/translation_rule.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,11 @@ Access ParseAccessJSON(llvm::StringRef value) {
4545
PlaceholderFragment
4646
ParsePlaceholderFragmentJSON(const llvm::json::Object &obj) {
4747
auto access = obj.getString("access");
48-
return {(unsigned)*obj.getInteger("arg"), ParseAccessJSON(*access)};
48+
return {
49+
(unsigned)*obj.getInteger("arg"),
50+
ParseAccessJSON(*access),
51+
obj.getBoolean("is_index_base").value_or(false),
52+
};
4953
}
5054

5155
std::vector<BodyFragment> ParseBodyFragmentsJSON(const llvm::json::Array &arr);

cpp2rust/converter/translation_rule.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ enum class Access { kRead, kWrite, kMove };
2626
struct PlaceholderFragment {
2727
unsigned n; // "a0", "a1", ...
2828
Access access;
29+
bool is_index_base = false;
2930

3031
void dump() const;
3132
};

rule-preprocessor/src/ir.rs

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,17 @@ pub struct FnIr {
6363

6464
impl FnIr {
6565
/// Find the next unvisited placeholder for `param`, mark it visited,
66-
/// and if it was "unknown", patch it with the given access.
67-
/// Searches inside MethodCall bodies recursively.
66+
/// and apply `patch` to it. Searches inside MethodCall bodies
67+
/// recursively.
6868
pub fn resolve_next_param(
6969
&mut self,
7070
param: &str,
71-
access: Access,
7271
visited: &mut HashMap<String, usize>,
72+
patch: impl Fn(&mut PlaceholderInner),
7373
) {
7474
let n = visited.entry(param.to_string()).or_insert(0);
7575
let nth = std::mem::replace(n, *n + 1);
76-
resolve_nth_unknown(&mut self.body, param, access, nth);
76+
resolve_nth_unknown(&mut self.body, param, nth, patch);
7777
}
7878

7979
pub fn has_unknowns(&self) -> bool {
@@ -139,6 +139,8 @@ pub enum Access {
139139
pub struct PlaceholderInner {
140140
pub arg: i32,
141141
pub access: Access,
142+
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
143+
pub is_index_base: bool,
142144
}
143145

144146
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -157,34 +159,38 @@ impl BodyFragment {
157159
}
158160
}
159161

160-
/// Resolve the nth Unknown placeholder for `param` in a body fragment list.
161-
fn resolve_nth_unknown(body: &mut [BodyFragment], param: &str, access: Access, nth: usize) {
162+
/// Find the nth occurrence of `param` in a body fragment list and apply
163+
/// `patch` to it.
164+
fn resolve_nth_unknown(
165+
body: &mut [BodyFragment],
166+
param: &str,
167+
nth: usize,
168+
patch: impl Fn(&mut PlaceholderInner),
169+
) {
162170
let mut count = 0;
163171
fn resolve(
164172
body: &mut [BodyFragment],
165173
param: &str,
166-
access: Access,
167174
nth: usize,
168175
count: &mut usize,
176+
patch: &impl Fn(&mut PlaceholderInner),
169177
) -> bool {
170178
for frag in body {
171179
match frag {
172180
BodyFragment::Placeholder { placeholder }
173181
if placeholder.arg == param[1..].parse().unwrap_or(0) =>
174182
{
175183
if *count == nth {
176-
if placeholder.access == Access::Unknown {
177-
placeholder.access = access;
178-
}
184+
patch(placeholder);
179185
return true;
180186
}
181187
*count += 1;
182188
}
183189
BodyFragment::MethodCall { method_call } => {
184-
if resolve(&mut method_call.receiver, param, access, nth, count) {
190+
if resolve(&mut method_call.receiver, param, nth, count, patch) {
185191
return true;
186192
}
187-
if resolve(&mut method_call.body, param, access, nth, count) {
193+
if resolve(&mut method_call.body, param, nth, count, patch) {
188194
return true;
189195
}
190196
}
@@ -193,7 +199,7 @@ fn resolve_nth_unknown(body: &mut [BodyFragment], param: &str, access: Access, n
193199
}
194200
false
195201
}
196-
resolve(body, param, access, nth, &mut count);
202+
resolve(body, param, nth, &mut count, &patch);
197203
}
198204

199205
// A rule file's IR: mix of function rules (f1, f2, ...) and type rules (t1, t2, ...)
@@ -211,7 +217,6 @@ pub type FileIr = BTreeMap<String, RuleIr>;
211217
/// All IR for all rule files.
212218
pub struct RulesIR {
213219
pub all_ir: HashMap<String, FileIr>,
214-
pub has_unknowns: bool,
215220
pub crate_root: PathBuf,
216221
}
217222

rule-preprocessor/src/semantic.rs

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@ pub struct SemanticAnalysis;
1010

1111
impl SemanticAnalysis {
1212
pub fn run(ir: RulesIR) -> RulesIR {
13-
if !ir.has_unknowns {
14-
return ir;
15-
}
16-
1713
let args = build_rustc_args(&ir.crate_root);
1814
let mut resolver = MethodResolver { ir };
1915

@@ -131,7 +127,6 @@ impl MethodResolver {
131127
fn resolve_fn_decl<'tcx>(&mut self, tcx: rustc_middle::ty::TyCtxt<'tcx>, f: &FnDecl<'tcx>) {
132128
if let Some(file_ir) = self.ir.all_ir.get_mut(&f.source_file)
133129
&& let Some(RuleIr::Fn(fn_ir)) = file_ir.get_mut(&f.name)
134-
&& fn_ir.has_unknowns()
135130
{
136131
f.resolve_unknowns(tcx, fn_ir);
137132
}
@@ -218,11 +213,29 @@ struct AstVisitor<'a, 'tcx> {
218213
}
219214

220215
impl<'a, 'tcx> AstVisitor<'a, 'tcx> {
216+
fn visit_expr_as_index_base(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>, context: Access) {
217+
if let Some(param) = self.expr_as_decl_ref(expr) {
218+
self.fn_ir
219+
.resolve_next_param(&param, &mut self.visited, |p| {
220+
if p.access == Access::Unknown {
221+
p.access = context;
222+
}
223+
p.is_index_base = true;
224+
});
225+
return;
226+
}
227+
self.visit_expr(expr, context);
228+
}
229+
221230
fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>, context: Access) {
222231
// Reached an argument used inside the rule body
223232
if let Some(param) = self.expr_as_decl_ref(expr) {
224233
self.fn_ir
225-
.resolve_next_param(&param, context, &mut self.visited);
234+
.resolve_next_param(&param, &mut self.visited, |p| {
235+
if p.access == Access::Unknown {
236+
p.access = context;
237+
}
238+
});
226239
return;
227240
}
228241

@@ -311,7 +324,11 @@ impl<'a, 'tcx> AstVisitor<'a, 'tcx> {
311324
| rustc_hir::ExprKind::Repeat(e, _) => {
312325
self.visit_expr(e, context);
313326
}
314-
rustc_hir::ExprKind::Index(a, b, _) | rustc_hir::ExprKind::Binary(_, a, b) => {
327+
rustc_hir::ExprKind::Index(base, idx, _) => {
328+
self.visit_expr_as_index_base(base, context);
329+
self.visit_expr(idx, context);
330+
}
331+
rustc_hir::ExprKind::Binary(_, a, b) => {
315332
self.visit_expr(a, context);
316333
self.visit_expr(b, context);
317334
}

rule-preprocessor/src/syntactic.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ impl SyntacticAnalysis {
4444
pub fn run(crate_root: &Path) -> RulesIR {
4545
let rule_files = Self::collect_rule_files(crate_root);
4646
let mut all_ir = HashMap::new();
47-
let mut has_unknowns = false;
4847

4948
for rule_file in &rule_files {
5049
let source = std::fs::read_to_string(rule_file).unwrap();
@@ -56,10 +55,6 @@ impl SyntacticAnalysis {
5655
rule_file.display()
5756
);
5857

59-
has_unknowns |= file_ir.values().any(|r| match r {
60-
RuleIr::Fn(f) => f.has_unknowns(),
61-
RuleIr::Type(_) => false,
62-
});
6358
let canonical = rule_file
6459
.canonicalize()
6560
.unwrap_or_else(|_| rule_file.clone())
@@ -70,7 +65,6 @@ impl SyntacticAnalysis {
7065

7166
RulesIR {
7267
all_ir,
73-
has_unknowns,
7468
crate_root: crate_root.to_path_buf(),
7569
}
7670
}
@@ -196,6 +190,7 @@ impl<'a> FragmentCtx<'a> {
196190
placeholder: PlaceholderInner {
197191
arg: token.text()[1..].parse().unwrap_or(0),
198192
access,
193+
is_index_base: false,
199194
},
200195
});
201196
return;

rules/string/ir_refcount.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
{
1111
"placeholder": {
1212
"arg": 0,
13-
"access": "read"
13+
"access": "read",
14+
"is_index_base": true
1415
}
1516
},
1617
{

rules/string/ir_unsafe.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
{
1111
"placeholder": {
1212
"arg": 0,
13-
"access": "read"
13+
"access": "read",
14+
"is_index_base": true
1415
}
1516
},
1617
{
@@ -1194,7 +1195,8 @@
11941195
{
11951196
"placeholder": {
11961197
"arg": 0,
1197-
"access": "write"
1198+
"access": "write",
1199+
"is_index_base": true
11981200
}
11991201
},
12001202
{

rules/vector/ir_refcount.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2011,7 +2011,8 @@
20112011
{
20122012
"placeholder": {
20132013
"arg": 0,
2014-
"access": "read"
2014+
"access": "read",
2015+
"is_index_base": true
20152016
}
20162017
},
20172018
{

0 commit comments

Comments
 (0)