Skip to content

Commit 4abb1bb

Browse files
authored
Avoid implicit autoref in the unsafe model (#60)
1 parent 39b1133 commit 4abb1bb

10 files changed

Lines changed: 195 additions & 21 deletions

File tree

cpp2rust/converter/converter.cpp

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <llvm/Support/ConvertUTF.h>
1010

1111
#include <format>
12+
#include <utility>
1213

1314
#include "compiler.h"
1415
#include "converter/converter_lib.h"
@@ -1059,7 +1060,10 @@ void Converter::ConvertLoopVariable(clang::VarDecl *decl,
10591060
StrCat(std::format(".as_mut_ptr().add({})", loop_var_name));
10601061
}
10611062
} else {
1062-
Convert(range_init);
1063+
{
1064+
PushExplicitAutoref autoref(*this, /*is_mut=*/false);
1065+
Convert(range_init);
1066+
}
10631067
StrCat(std::format("[{}]", loop_var_name));
10641068
StrCat(".clone()");
10651069
}
@@ -2218,19 +2222,25 @@ void Converter::EmitStmtExprTail(clang::Expr *tail) { Convert(tail); }
22182222
bool Converter::VisitConditionalOperator(clang::ConditionalOperator *expr) {
22192223
StrCat(keyword::kIf);
22202224
ConvertCondition(expr->getCond());
2225+
bool branch_is_addr =
2226+
expr->isLValue() && !isRValue() && !expr->getType()->isFunctionType();
22212227
{
22222228
PushBrace then_brace(*this);
2223-
if (expr->isLValue() && !isRValue() && !expr->getType()->isFunctionType()) {
2229+
if (branch_is_addr) {
22242230
StrCat(token::kRef, keyword_mut_);
22252231
}
2232+
PushExplicitAutoref no_autoref(*this, branch_is_addr ? std::nullopt
2233+
: autoref_mut_);
22262234
Convert(expr->getTrueExpr());
22272235
}
22282236
StrCat(keyword::kElse);
22292237
{
22302238
PushBrace else_brace(*this);
2231-
if (expr->isLValue() && !isRValue() && !expr->getType()->isFunctionType()) {
2239+
if (branch_is_addr) {
22322240
StrCat(token::kRef, keyword_mut_);
22332241
}
2242+
PushExplicitAutoref no_autoref(*this, branch_is_addr ? std::nullopt
2243+
: autoref_mut_);
22342244
Convert(expr->getFalseExpr());
22352245
}
22362246
return false;
@@ -2285,11 +2295,7 @@ bool Converter::VisitDeclRefExpr(clang::DeclRefExpr *expr) {
22852295

22862296
if (decl->getType()->getAs<clang::ReferenceType>() && !isAddrOf() &&
22872297
!map_iter_decls_.contains(clang::dyn_cast<clang::VarDecl>(decl))) {
2288-
{
2289-
PushParen paren(*this);
2290-
StrCat(GetPointerDerefPrefix(decl->getType().getNonReferenceType()),
2291-
std::move(str));
2292-
}
2298+
EmitDeref(std::move(str), decl->getType().getNonReferenceType());
22932299
SetValueFreshness(expr->getType());
22942300
return false;
22952301
}
@@ -2362,9 +2368,11 @@ bool Converter::ConvertCXXOperatorCallExpr(clang::CXXOperatorCallExpr *expr) {
23622368
Convert(expr->getArg(0));
23632369
}
23642370
break;
2365-
case clang::OverloadedOperatorKind::OO_Subscript:
2371+
case clang::OverloadedOperatorKind::OO_Subscript: {
2372+
PushExplicitAutoref autoref(*this, IsMutatingCall(expr));
23662373
ConvertArraySubscript(expr->getArg(0), expr->getArg(1), expr->getType());
23672374
break;
2375+
}
23682376
case clang::OverloadedOperatorKind::OO_LessLess:
23692377
if (IsCallToOstream(expr)) {
23702378
ConvertCallToOstream(expr);
@@ -2430,8 +2438,7 @@ bool Converter::VisitMemberExpr(clang::MemberExpr *expr) {
24302438
}
24312439

24322440
if (!isAddrOf() && member->getType()->isReferenceType()) {
2433-
PushParen paren(*this);
2434-
StrCat(GetPointerDerefPrefix(member->getType().getNonReferenceType()), str);
2441+
EmitDeref(std::move(str), member->getType().getNonReferenceType());
24352442
return false;
24362443
}
24372444

@@ -3200,10 +3207,14 @@ void Converter::ConvertPointerOffset(clang::Expr *base, clang::Expr *idx,
32003207

32013208
void Converter::ConvertArraySubscript(clang::Expr *base, clang::Expr *idx,
32023209
clang::QualType type) {
3203-
Convert(base->IgnoreImplicit());
32043210
if (IsUniquePtr(base->getType())) {
3211+
PushExplicitAutoref no_autoref(*this, std::nullopt);
3212+
Convert(base->IgnoreImplicit());
32053213
StrCat(".as_mut().unwrap()");
3214+
} else {
3215+
Convert(base->IgnoreImplicit());
32063216
}
3217+
PushExplicitAutoref no_autoref(*this, std::nullopt);
32073218
PushBracket bracket(*this);
32083219
{
32093220
PushParen paren(*this);
@@ -3509,11 +3520,19 @@ void Converter::ConvertAddrOf(clang::Expr *expr, clang::QualType pointer_type) {
35093520
}
35103521
}
35113522

3523+
void Converter::EmitDeref(std::string inner, clang::QualType pointee_type) {
3524+
auto wrap = std::exchange(autoref_mut_, std::nullopt);
3525+
PushParen outer(*this, wrap.has_value());
3526+
if (wrap) {
3527+
StrCat(*wrap ? "&mut" : "&");
3528+
}
3529+
PushParen paren(*this);
3530+
StrCat(GetPointerDerefPrefix(pointee_type), std::move(inner));
3531+
}
3532+
35123533
void Converter::ConvertDeref(clang::Expr *expr) {
35133534
if (!isAddrOf()) {
3514-
PushParen paren(*this);
3515-
StrCat(GetPointerDerefPrefix(expr->getType()->getPointeeType()),
3516-
ToString(expr));
3535+
EmitDeref(ToString(expr), expr->getType()->getPointeeType());
35173536
} else {
35183537
Convert(expr);
35193538
}

cpp2rust/converter/converter.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,8 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
474474

475475
virtual void ConvertDeref(clang::Expr *expr);
476476

477+
void EmitDeref(std::string inner, clang::QualType pointee_type);
478+
477479
virtual void ConvertArrow(clang::Expr *expr);
478480

479481
virtual void ConvertCast(clang::QualType qual_type);
@@ -515,6 +517,17 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
515517
clang::ASTContext &ctx_;
516518
clang::FunctionDecl *curr_function_ = nullptr;
517519
bool in_function_formals_ = false;
520+
std::optional<bool> autoref_mut_;
521+
522+
struct PushExplicitAutoref {
523+
Converter &c;
524+
std::optional<bool> prev;
525+
PushExplicitAutoref(Converter &c, std::optional<bool> v)
526+
: c(c), prev(c.autoref_mut_) {
527+
c.autoref_mut_ = v;
528+
}
529+
~PushExplicitAutoref() { c.autoref_mut_ = prev; }
530+
};
518531
std::stack<clang::Expr *> curr_for_inc_;
519532
std::stack<clang::QualType> curr_init_type_;
520533

cpp2rust/converter/converter_lib.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,15 @@ bool IsMut(clang::QualType qual_type) {
156156
qual_type->getPointeeType().isConstQualified());
157157
}
158158

159+
bool IsMutatingCall(const clang::CallExpr *expr) {
160+
if (auto *callee = expr->getDirectCallee()) {
161+
if (auto *method = clang::dyn_cast<clang::CXXMethodDecl>(callee)) {
162+
return !method->isConst();
163+
}
164+
}
165+
return true;
166+
}
167+
159168
bool IsOverloadedFunction(const clang::FunctionDecl *decl) {
160169
const auto *ctx = decl->getDeclContext();
161170
const auto decl_name = decl->getDeclName();

cpp2rust/converter/converter_lib.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ bool IsUnsignedArithOp(const clang::BinaryOperator *expr);
4545

4646
bool IsMut(clang::QualType qual_type);
4747

48+
bool IsMutatingCall(const clang::CallExpr *expr);
49+
4850
bool IsOverloadedFunction(const clang::FunctionDecl *decl);
4951

5052
bool IsOverloadedMethod(const clang::CXXMethodDecl *decl);

tests/lit/lit/formats/Cpp2RustTest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def __init__(self):
2929
self.regex_translation_fail = re.compile(r"//\s*translation-fail\s*(?::\s*(.*))?$", re.MULTILINE)
3030
self.regex_nondet_result = re.compile(r"//\s*nondet-result\s*(?::\s*(.*))?$", re.MULTILINE)
3131
self.rust_version = read_rust_version()
32-
os.environ['RUSTFLAGS'] = '-Awarnings -A dangerous-implicit-autorefs'
32+
os.environ['RUSTFLAGS'] = '-Awarnings'
3333

3434
def updateExpected(self, generated, expected_path):
3535
os.makedirs(os.path.dirname(expected_path), exist_ok=True)

tests/unit/implicit_autoref.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#include <cassert>
2+
#include <vector>
3+
4+
struct Holder {
5+
std::vector<int> v;
6+
};
7+
8+
int main() {
9+
std::vector<int> v;
10+
v.push_back(10);
11+
v.push_back(20);
12+
13+
std::vector<int> *p = &v;
14+
int a = (*p)[0];
15+
(*p)[1] = 30;
16+
17+
Holder h;
18+
h.v.push_back(40);
19+
h.v.push_back(50);
20+
Holder *hp = &h;
21+
int b = (*hp).v[0];
22+
(*hp).v[1] = 60;
23+
24+
assert(a == 10);
25+
assert((*p)[1] == 30);
26+
assert(b == 40);
27+
assert((*hp).v[1] == 60);
28+
return 0;
29+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
extern crate libcc2rs;
2+
use libcc2rs::*;
3+
use std::cell::RefCell;
4+
use std::collections::BTreeMap;
5+
use std::io::prelude::*;
6+
use std::io::{Read, Seek, Write};
7+
use std::os::fd::AsFd;
8+
use std::rc::{Rc, Weak};
9+
#[derive(Default)]
10+
pub struct Holder {
11+
pub v: Value<Vec<i32>>,
12+
}
13+
impl Clone for Holder {
14+
fn clone(&self) -> Self {
15+
let mut this = Self {
16+
v: Rc::new(RefCell::new((*self.v.borrow()).clone())),
17+
};
18+
this
19+
}
20+
}
21+
impl ByteRepr for Holder {}
22+
pub fn main() {
23+
std::process::exit(main_0());
24+
}
25+
fn main_0() -> i32 {
26+
let v: Value<Vec<i32>> = Rc::new(RefCell::new(Vec::new()));
27+
(*v.borrow_mut()).push(10);
28+
(*v.borrow_mut()).push(20);
29+
let p: Value<Ptr<Vec<i32>>> = Rc::new(RefCell::new((v.as_pointer())));
30+
let a: Value<i32> = Rc::new(RefCell::new(
31+
((((*p.borrow()).to_strong().as_pointer()) as Ptr<i32>)
32+
.offset(0_u64 as isize)
33+
.read()),
34+
));
35+
(((*p.borrow()).to_strong().as_pointer()) as Ptr<i32>)
36+
.offset(1_u64 as isize)
37+
.write(30);
38+
let h: Value<Holder> = Rc::new(RefCell::new(<Holder>::default()));
39+
(*(*h.borrow()).v.borrow_mut()).push(40);
40+
(*(*h.borrow()).v.borrow_mut()).push(50);
41+
let hp: Value<Ptr<Holder>> = Rc::new(RefCell::new((h.as_pointer())));
42+
let b: Value<i32> = Rc::new(RefCell::new(
43+
(((*(*hp.borrow()).upgrade().deref()).v.as_pointer() as Ptr<i32>)
44+
.offset(0_u64 as isize)
45+
.read()),
46+
));
47+
((*(*hp.borrow()).upgrade().deref()).v.as_pointer() as Ptr<i32>)
48+
.offset(1_u64 as isize)
49+
.write(60);
50+
assert!(((*a.borrow()) == 10));
51+
assert!(
52+
(((((*p.borrow()).to_strong().as_pointer()) as Ptr<i32>)
53+
.offset(1_u64 as isize)
54+
.read())
55+
== 30)
56+
);
57+
assert!(((*b.borrow()) == 40));
58+
assert!(
59+
((((*(*hp.borrow()).upgrade().deref()).v.as_pointer() as Ptr<i32>)
60+
.offset(1_u64 as isize)
61+
.read())
62+
== 60)
63+
);
64+
return 0;
65+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
extern crate libc;
2+
use libc::*;
3+
extern crate libcc2rs;
4+
use libcc2rs::*;
5+
use std::collections::BTreeMap;
6+
use std::io::{Read, Seek, Write};
7+
use std::os::fd::{AsFd, FromRawFd, IntoRawFd};
8+
use std::rc::Rc;
9+
#[repr(C)]
10+
#[derive(Clone, Default)]
11+
pub struct Holder {
12+
pub v: Vec<i32>,
13+
}
14+
pub fn main() {
15+
unsafe {
16+
std::process::exit(main_0() as i32);
17+
}
18+
}
19+
unsafe fn main_0() -> i32 {
20+
let mut v: Vec<i32> = Vec::new();
21+
v.push(10);
22+
v.push(20);
23+
let mut p: *mut Vec<i32> = (&mut v as *mut Vec<i32>);
24+
let mut a: i32 = (&mut (*p))[(0_u64) as usize];
25+
(&mut (*p))[(1_u64) as usize] = 30;
26+
let mut h: Holder = <Holder>::default();
27+
h.v.push(40);
28+
h.v.push(50);
29+
let mut hp: *mut Holder = (&mut h as *mut Holder);
30+
let mut b: i32 = (&mut (*hp)).v[(0_u64) as usize];
31+
(&mut (*hp)).v[(1_u64) as usize] = 60;
32+
assert!(((a) == (10)));
33+
assert!((((&mut (*p))[(1_u64) as usize]) == (30)));
34+
assert!(((b) == (40)));
35+
assert!((((&mut (*hp)).v[(1_u64) as usize]) == (60)));
36+
return 0;
37+
}

tests/unit/out/unsafe/vector2.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ pub unsafe fn fn_0(v: *mut Vec<i32>, mut v3: Vec<i32>) {
1414
v2.push(0);
1515
v2.push(1);
1616
v2.push(3);
17-
x = (*v)[(2_u64) as usize];
17+
x = (&mut (*v))[(2_u64) as usize];
1818
v2[(0_u64) as usize] = 1;
1919
(if true { &mut v3 } else { &mut (*v) })[(0_u64) as usize] = 7;
2020
v2 = (*v).clone();
21-
(*v4)[(1_u64) as usize] = 13;
21+
(&mut (*v4))[(1_u64) as usize] = 13;
2222
assert!(((x) == (6)));
2323
assert!(((*((*v).first_mut().unwrap())) == (4)));
24-
assert!((((*v)[(1_u64) as usize]) == (5)));
25-
assert!((((*v)[(2_u64) as usize]) == (6)));
24+
assert!((((&mut (*v))[(1_u64) as usize]) == (5)));
25+
assert!((((&mut (*v))[(2_u64) as usize]) == (6)));
2626
assert!(((*((*v).last_mut().unwrap())) == (20)));
2727
assert!(((v2[(0_u64) as usize]) == (4)));
2828
assert!(((v2[(1_u64) as usize]) == (5)));

tests/unit/out/unsafe/vector3.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ unsafe fn main_0() -> i32 {
3535
'loop_: for v2 in 0..(v.len()) {
3636
let mut v2 = v.as_mut_ptr().add(v2);
3737
'loop_: for i in 0..((*v2).len()) {
38-
let mut i = (*v2)[i].clone();
38+
let mut i = (&(*v2))[i].clone();
3939
printf(b"%d\n\0".as_ptr() as *const i8, ((i) + (3)));
4040
}
4141
}

0 commit comments

Comments
 (0)