Skip to content
Merged
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
34 changes: 21 additions & 13 deletions cpp2rust/converter/converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1765,6 +1765,7 @@ void Converter::ConvertPrintf(clang::CallExpr *expr) {
}

void Converter::ConvertVariadicArg(clang::Expr *arg) {
PushExprKind push(*this, ExprKind::RValue);
if (arg->getType()->isFunctionPointerType()) {
Convert(arg);
StrCat(".map_or(::std::ptr::null_mut(), |f| f as *mut ::libc::c_void)");
Expand Down Expand Up @@ -1832,19 +1833,16 @@ bool Converter::VisitCallExpr(clang::CallExpr *expr) {
return false;
}

bool deref_ref = (IsReferenceType(expr) ||
GetReturnTypeOfFunction(expr)->isReferenceType()) &&
!isAddrOf() && !isVoid();

auto **args = expr->getArgs();
auto num_args = expr->getNumArgs();
auto ctx = CollectRefBindingTempArgs(expr);
std::string str;
{
PushExprKind push(*this, ExprKind::RValue);
str = GetMappedAsString(expr, args, num_args, &ctx);
};
auto str = GetMappedAsString(expr, args, num_args, &ctx);

bool deref_ref = (IsReferenceType(expr) ||
GetReturnTypeOfFunction(expr)->isReferenceType()) &&
!isAddrOf() && !isVoid();
if (deref_ref) {
if (deref_ref && !Mapper::IsRefExpr(ctx_, expr)) {
str = "( * " + std::move(str) + " )";
}

Expand Down Expand Up @@ -3152,7 +3150,8 @@ std::string Converter::ConvertDeclRef(clang::Expr *expr,

if (ShouldReplaceWithMappedBody(decl)) {
if (auto str = GetMappedAsString(expr); !str.empty()) {
if (RuleRegistry::ReturnsReference(expr) && !isAddrOf()) {
if (RuleRegistry::ReturnsReference(expr) && !isAddrOf() &&
!Mapper::IsRefExpr(ctx_, expr)) {
return std::format("({}{})", GetPointerDerefPrefix(expr->getType()),
str);
}
Expand Down Expand Up @@ -3409,7 +3408,7 @@ bool Converter::VisitMemberExpr(clang::MemberExpr *expr) {
is_reference_type |= method->getReturnType()->isReferenceType();
}

if (is_reference_type) {
if (is_reference_type || RuleRegistry::ReturnsPointer(ctx_, expr)) {
computed_expr_type_ = ComputedExprType::Pointer;
} else {
StrCat(token::kRef);
Expand Down Expand Up @@ -3462,7 +3461,8 @@ void Converter::SetUFCSReceiver(clang::Expr *base, bool is_arrow,

void Converter::ConvertMemberExpr(clang::MemberExpr *expr) {
if (auto mapped = GetMappedAsString(expr); !mapped.empty()) {
if (RuleRegistry::ReturnsPointer(ctx_, expr)) {
if (RuleRegistry::ReturnsPointer(ctx_, expr) && !isAddrOf() &&
!Mapper::IsRefExpr(ctx_, expr)) {
PushParen paren(*this);
StrCat(token::kStar, mapped);
} else {
Expand Down Expand Up @@ -5203,7 +5203,10 @@ std::string Converter::GetMappedAsString(clang::Expr *expr, clang::Expr **args,
return {};

auto saved_borrows = std::exchange(mut_borrows_, {});
auto result = ConvertIRFragment(tgt_ir->body, expr, args, num_args, ctx);
auto *operand = tgt_ir->refExprOperand();
bool lvalue = operand && !isAddrOf();
auto result = ConvertIRFragment(lvalue ? *operand : tgt_ir->body, expr, args,
num_args, ctx);
if (tgt_ir->multi_statement) {
result = '{' + result + '}';
}
Expand Down Expand Up @@ -5257,6 +5260,10 @@ std::string Converter::ConvertIRFragment(
} else if (auto *mc =
std::get_if<std::unique_ptr<MethodCallFragment>>(&frag)) {
result += ConvertMappedMethodCall(expr, **mc, args, num_args, ctx);
} else if (auto *re =
std::get_if<std::unique_ptr<RefExprFragment>>(&frag)) {
result += (*re)->prefix + ' ' +
ConvertIRFragment((*re)->inner, expr, args, num_args, ctx);
}
}

Expand Down Expand Up @@ -5292,6 +5299,7 @@ Converter::ConvertInitFragment(clang::Expr *expr,
auto type = RuleRegistry::GetInitType(expr);

Buffer buf(*this);
PushExprKind push(*this, ExprKind::RValue);
ConvertConstructFromArgs(
type, llvm::ArrayRef(all_args).drop_front(tgt_ir->params.size()),
expr->getExprLoc());
Expand Down
5 changes: 5 additions & 0 deletions cpp2rust/converter/mapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ bool Contains(clang::ASTContext &ctx, const clang::Expr *expr) {
return RuleRegistry::Search(ctx, expr).first != nullptr;
}

bool IsRefExpr(clang::ASTContext &ctx, const clang::Expr *expr) {
const auto *rule = RuleRegistry::GetExprRule(ctx, expr);
return rule && rule->refExprOperand();
}

std::string MapFunctionName(clang::ASTContext &ctx,
const clang::FunctionDecl *decl) {
assert(decl);
Expand Down
1 change: 1 addition & 0 deletions cpp2rust/converter/mapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class Converter;
namespace cpp2rust::Mapper {
bool Contains(clang::ASTContext &ctx, clang::QualType qual_type);
bool Contains(clang::ASTContext &ctx, const clang::Expr *expr);
bool IsRefExpr(clang::ASTContext &ctx, const clang::Expr *expr);

std::string Map(clang::ASTContext &ctx, clang::QualType qual_type);
std::string Map(Converter &converter, clang::QualType qual_type);
Expand Down
4 changes: 4 additions & 0 deletions cpp2rust/converter/rules/registry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ void collectGenerics(const std::vector<TranslationRule::BodyFragment> &body,
&fragment)) {
collectGenerics((*call)->receiver, used);
collectGenerics((*call)->body, used);
} else if (auto ref_expr = std::get_if<
std::unique_ptr<TranslationRule::RefExprFragment>>(
&fragment)) {
collectGenerics((*ref_expr)->inner, used);
}
}
}
Expand Down
32 changes: 32 additions & 0 deletions cpp2rust/converter/translation_rule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,15 @@ MethodCallFragment ParseMethodCallFragmentJSON(const llvm::json::Object &obj) {
return mc;
}

RefExprFragment ParseRefExprFragmentJSON(const llvm::json::Object &obj) {
RefExprFragment re;
re.prefix = obj.getString("prefix").value_or("").str();
if (auto *inner = obj.getArray("inner")) {
re.inner = ParseBodyFragmentsJSON(*inner);
}
return re;
}

std::vector<BodyFragment> ParseBodyFragmentsJSON(const llvm::json::Array &arr) {
std::vector<BodyFragment> result;
for (auto &frag : arr) {
Expand All @@ -87,6 +96,9 @@ std::vector<BodyFragment> ParseBodyFragmentsJSON(const llvm::json::Array &arr) {
} else if (auto *mc = frag_obj->getObject("method_call")) {
result.push_back(std::make_unique<MethodCallFragment>(
ParseMethodCallFragmentJSON(*mc)));
} else if (auto *re = frag_obj->getObject("ref_expr")) {
result.push_back(
std::make_unique<RefExprFragment>(ParseRefExprFragmentJSON(*re)));
} else if (frag_obj->get("va_args")) {
result.push_back(VaArgsFragment{});
} else if (frag_obj->get("init")) {
Expand All @@ -110,6 +122,8 @@ void BodyFragmentDump(const BodyFragment &frag) {
} else if (auto *mc =
std::get_if<std::unique_ptr<MethodCallFragment>>(&frag)) {
(*mc)->dump();
} else if (auto *re = std::get_if<std::unique_ptr<RefExprFragment>>(&frag)) {
(*re)->dump();
}
}

Expand All @@ -118,6 +132,9 @@ bool HasInitFragment(const std::vector<BodyFragment> &body) {
if (auto *mc = std::get_if<std::unique_ptr<MethodCallFragment>>(&frag)) {
return HasInitFragment((*mc)->receiver) || HasInitFragment((*mc)->body);
}
if (auto *re = std::get_if<std::unique_ptr<RefExprFragment>>(&frag)) {
return HasInitFragment((*re)->inner);
}
return std::holds_alternative<InitFragment>(frag);
});
}
Expand Down Expand Up @@ -196,6 +213,21 @@ void ExprRule::dump() const {

bool ExprRule::usesInit() const { return HasInitFragment(body); }

const std::vector<BodyFragment> *ExprRule::refExprOperand() const {
if (body.size() != 1) {
return nullptr;
}
auto *re = std::get_if<std::unique_ptr<RefExprFragment>>(&body[0]);
return re ? &(*re)->inner : nullptr;
}

void RefExprFragment::dump() const {
log() << " ref_expr: " << prefix << '\n';
for (auto &frag : inner) {
BodyFragmentDump(frag);
}
}

void GenericFragment::dump() const { log() << " generic: " << n << '\n'; }

void TypeInfo::dump() const {
Expand Down
12 changes: 11 additions & 1 deletion cpp2rust/converter/translation_rule.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,12 @@ struct InitFragment {
};

struct MethodCallFragment; // forward declaration
struct RefExprFragment;

using BodyFragment = std::variant<TextFragment, PlaceholderFragment,
GenericFragment, VaArgsFragment, InitFragment,
std::unique_ptr<MethodCallFragment>>;
std::unique_ptr<MethodCallFragment>,
std::unique_ptr<RefExprFragment>>;

struct MethodCallFragment {
std::vector<BodyFragment> receiver;
Expand All @@ -62,6 +64,13 @@ struct MethodCallFragment {
void dump() const;
};

struct RefExprFragment {
std::string prefix;
std::vector<BodyFragment> inner;

void dump() const;
};

struct TypeInfo {
std::vector<std::string> derives;
std::string type;
Expand All @@ -83,6 +92,7 @@ struct ExprRule {

void dump() const;
bool usesInit() const;
const std::vector<BodyFragment> *refExprOperand() const;
};

struct TypeRule {
Expand Down
5 changes: 5 additions & 0 deletions docs/src/rules/ir.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ The fragment kinds are:
- `method_call`: a method call split into `receiver` and `body` fragment lists,
so the code generator can rewrite the pair (see
[Rule Rewriting](./rewriting.md)).
- `ref_expr`: a body that takes the address of an lvalue, `&mut a0.0`, split
into the `prefix` (`&`, `&mut`, `&raw const`, `&raw mut`) and the `inner`
fragments of the lvalue. Where the converter would dereference the rule's
result it emits the lvalue alone, so `p.first = 1.5` is `p.0 = 1.5` rather
than `(*&mut p.0) = 1.5`.
- `va_args`: the expansion point for a variadic tail.
- `init`: the value built from the call's trailing arguments.

Expand Down
2 changes: 2 additions & 0 deletions docs/src/rules/loading.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ When a rule matches, the converter walks its body fragments and emits:
converter dereferences it; this only happens for receivers, not for ordinary
arguments.
- `generic` fragments as the Rust mapping of the bound C++ type,
- `ref_expr` fragments as the prefix followed by the lvalue, or as the lvalue
alone where the result is dereferenced,
- `va_args` fragments as the converted variadic tail,
- `init` fragments as the value built from the call's trailing arguments,
- `method_call` fragments as receiver followed by body, possibly rewritten (see
Expand Down
15 changes: 15 additions & 0 deletions rule-preprocessor/src/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ impl FnIr {
BodyFragment::MethodCall { method_call } => {
check(&method_call.receiver) || check(&method_call.body)
}
BodyFragment::RefExpr { ref_expr } => check(&ref_expr.inner),
_ => false,
})
}
Expand Down Expand Up @@ -161,6 +162,9 @@ pub enum BodyFragment {
MethodCall {
method_call: MethodCallInner,
},
RefExpr {
ref_expr: RefExprInner,
},
VaArgs {
va_args: std::marker::PhantomData<()>,
},
Expand Down Expand Up @@ -193,6 +197,12 @@ pub struct MethodCallInner {
pub body: Vec<BodyFragment>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RefExprInner {
pub prefix: String,
pub inner: Vec<BodyFragment>,
}

// For convenience: match on fragment kind
impl BodyFragment {
pub fn as_text_mut(&mut self) -> Option<&mut String> {
Expand Down Expand Up @@ -238,6 +248,11 @@ fn resolve_nth_unknown(
return true;
}
}
BodyFragment::RefExpr { ref_expr } => {
if resolve(&mut ref_expr.inner, param, nth, count, patch) {
return true;
}
}
_ => {}
}
}
Expand Down
28 changes: 26 additions & 2 deletions rule-preprocessor/src/syntactic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use std::collections::BTreeMap;
use std::path::{Path, PathBuf};

use crate::ir::{
Access, BodyFragment, FileIr, FnIr, MethodCallInner, Model, PlaceholderInner, RuleIr, RulesIR,
TypeInfo, TypeIr,
Access, BodyFragment, FileIr, FnIr, MethodCallInner, Model, PlaceholderInner, RefExprInner,
RuleIr, RulesIR, TypeInfo, TypeIr,
};

/// Classify a type AST node as refcount pointer (`Ptr<T>`), unsafe pointer
Expand Down Expand Up @@ -490,6 +490,30 @@ impl<'a> FnIrBuilder<'a> {
};
let stmt_list = body.stmt_list().unwrap();

if stmt_list.statements().next().is_none()
&& let Some(ast::Expr::RefExpr(ref_expr)) = stmt_list.tail_expr()
{
let mut prefix = String::from("&");
if ref_expr.raw_token().is_some() {
prefix.push_str("raw ");
}
if ref_expr.mut_token().is_some() {
prefix.push_str("mut");
} else if ref_expr.const_token().is_some() {
prefix.push_str("const");
}
let mut inner_ctx = FragmentCtx::new(self, params, generic_names);
inner_ctx.visit(ra_ap_syntax::NodeOrToken::Node(
ref_expr.expr().unwrap().syntax().clone(),
));
return vec![BodyFragment::RefExpr {
ref_expr: RefExprInner {
prefix: prefix.trim_end().to_string(),
inner: inner_ctx.finish(),
},
}];
}

let mut ctx = FragmentCtx::new(self, params, generic_names);
for child in stmt_list.syntax().children_with_tokens() {
if let ra_ap_syntax::NodeOrToken::Token(ref t) = child
Expand Down
13 changes: 7 additions & 6 deletions tests/unit/out/unsafe/clone_vs_move.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,22 +169,23 @@ unsafe fn main_0() -> i32 {
let mut map1: UnsafeMap<i32, (i32, i32)> = UnsafeMap::new();
let mut i: i32 = 0;
'loop_: while ((i) < (N)) {
(*&mut (*map1.get_or_insert_with(i.clone(), || (i, <i32>::default()))).1) = i;
(*map1.get_or_insert_with(i.clone(), || (i, <i32>::default()))).1 = i;
i.prefix_inc();
}
let mut map2: UnsafeMap<i32, (i32, i32)> = map1.clone();
let mut i: i32 = 0;
'loop_: while ((i) < (N)) {
assert!(((*&mut (*map2.get_or_insert_with(i.clone(), || (i, <i32>::default()))).1) == (i)));
(*&mut (*map2.get_or_insert_with(i.clone(), || (i, <i32>::default()))).1).prefix_inc();
assert!((((*map2.get_or_insert_with(i.clone(), || (i, <i32>::default()))).1) == (i)));
(*map2.get_or_insert_with(i.clone(), || (i, <i32>::default())))
.1
.prefix_inc();
i.prefix_inc();
}
let mut i: i32 = 0;
'loop_: while ((i) < (N)) {
assert!(((*&mut (*map1.get_or_insert_with(i.clone(), || (i, <i32>::default()))).1) == (i)));
assert!((((*map1.get_or_insert_with(i.clone(), || (i, <i32>::default()))).1) == (i)));
assert!(
((*&mut (*map2.get_or_insert_with(i.clone(), || (i, <i32>::default()))).1)
== ((i) + (1)))
(((*map2.get_or_insert_with(i.clone(), || (i, <i32>::default()))).1) == ((i) + (1)))
);
i.prefix_inc();
}
Expand Down
6 changes: 3 additions & 3 deletions tests/unit/out/unsafe/find.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ unsafe fn main_0() -> i32 {
};
let mut v_result_true: bool = it != v.end();
let mut m: UnsafeMap<i32, (i32, f64)> = UnsafeMap::new();
(*&mut (*m.get_or_insert_with(1.clone(), || (1, <f64>::default()))).1) = 1_f64;
(*&mut (*m.get_or_insert_with(2.clone(), || (2, <f64>::default()))).1) = 2_f64;
(*&mut (*m.get_or_insert_with(3.clone(), || (3, <f64>::default()))).1) = 3_f64;
(*m.get_or_insert_with(1.clone(), || (1, <f64>::default()))).1 = 1_f64;
(*m.get_or_insert_with(2.clone(), || (2, <f64>::default()))).1 = 2_f64;
(*m.get_or_insert_with(3.clone(), || (3, <f64>::default()))).1 = 3_f64;
let mut m_begin: UnsafeBidirectionalIterator<(i32, f64)> = m.begin();
let mut m_end: UnsafeBidirectionalIterator<(i32, f64)> = m.end();
let mut m_result_true: bool = m_begin != m_end;
Expand Down
3 changes: 1 addition & 2 deletions tests/unit/out/unsafe/foreach_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ unsafe fn main_0() -> i32 {
let mut i: i32 = 0;
let mut k: i32 = 100;
'loop_: while ((i) < (100)) {
(*&mut (*m.get_or_insert_with(i.clone(), || (i, <f64>::default()))).1) =
((k as f64) / (2_f64));
(*m.get_or_insert_with(i.clone(), || (i, <f64>::default()))).1 = ((k as f64) / (2_f64));
{
i.prefix_inc();
k.prefix_dec()
Expand Down
Loading
Loading