Skip to content
Merged
13 changes: 10 additions & 3 deletions cpp2rust/converter/converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,10 +219,15 @@ std::string Converter::ConvertFreshPointer(
return str;
}

std::string Converter::ConvertFreshObject(clang::Expr *expr, std::string_view) {
std::string Converter::ConvertFreshObject(clang::Expr *expr) {
return ConvertFreshPointer(expr);
}

std::string Converter::ConvertPointerReceiver(clang::Expr *arg,
std::string_view param_type) {
return std::format("({} as {})", ConvertFreshObject(arg), param_type);
}

std::string Converter::ConvertTemporaryPointer(
clang::Expr *expr, std::optional<clang::QualType> implicit_convert_to) {
return std::format("&mut {}", ConvertFreshRValue(expr, implicit_convert_to));
Expand Down Expand Up @@ -5141,8 +5146,10 @@ std::string Converter::ConvertPlaceholder(clang::Expr *expr, clang::Expr *arg,

if (ph_ctx.needs_pointer_receiver()) {
auto param_type = Mapper::GetParamType(*this, expr, ph_ctx.arg_idx);
return std::format("({} as {})", ConvertFreshObject(arg, param_type),
param_type);
bool borrowed = ph_ctx.access == TranslationRule::Access::kBorrow ||
ph_ctx.access == TranslationRule::Access::kBorrowMut;
PushExprKind push(*this, borrowed ? ExprKind::Object : ExprKind::XValue);
return ConvertPointerReceiver(arg, param_type);
}

if (ph_ctx.needs_object_receiver()) {
Expand Down
10 changes: 6 additions & 4 deletions cpp2rust/converter/converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -1076,15 +1076,17 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
virtual std::string
ConvertFreshPointer(clang::Expr *expr,
std::optional<clang::QualType> implicit_convert_to = {});
// target_ptr_type, when known (e.g. a translation rule's parameter type),
// is the Rust pointer type the result will be used as.
virtual std::string ConvertFreshObject(clang::Expr *expr,
std::string_view target_ptr_type = {});
virtual std::string ConvertFreshObject(clang::Expr *expr);

virtual std::string ConvertPointerReceiver(clang::Expr *arg,
std::string_view param_type);

// A pointer to a new temporary with the value of the prvalue expr, which
// lives until the end of the statement.
virtual std::string
ConvertTemporaryPointer(clang::Expr *expr,
std::optional<clang::QualType> implicit_convert_to);

std::string
ConvertPointer(clang::Expr *expr,
std::optional<clang::QualType> implicit_convert_to = {},
Expand Down
76 changes: 29 additions & 47 deletions cpp2rust/converter/models/converter_refcount.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -392,27 +392,20 @@ std::string ConverterRefCount::ConvertObject(clang::Expr *expr,
return str;
}

std::string
ConverterRefCount::ConvertFreshObject(clang::Expr *expr,
std::string_view target_ptr_type) {
auto shape = ObjectShape::Whole;
if (!target_ptr_type.empty()) {
auto type = expr->getType().getNonReferenceType();
auto pointee = type->isPointerType() ? type->getPointeeType() : type;
if (IsBoxedType(ctx_, pointee) || pointee->isArrayType()) {
auto normalize = [](std::string s) {
std::erase(s, ' ');
for (size_t pos; (pos = s.find("::<")) != std::string::npos;)
s.erase(pos, 2);
return s;
};
if (normalize(std::string(target_ptr_type)) ==
normalize(ConvertPtrType(pointee))) {
shape = ObjectShape::Element;
}
}
}
auto str = ConvertObject(expr, shape);
std::string ConverterRefCount::AsPointerMethod(clang::QualType type) const {
return isObject() && WantsElementPtr() &&
type.getNonReferenceType()->isRecordType()
? ".as_pointer().decay()"
: ".as_pointer()";
}

std::string ConverterRefCount::ConvertPointerReceiver(clang::Expr *arg,
std::string_view) {
return isObject() ? ConvertObject(arg) : ConvertFreshObject(arg);
}

std::string ConverterRefCount::ConvertFreshObject(clang::Expr *expr) {
auto str = ConvertObject(expr);
if (isFresh()) {
return str;
}
Expand Down Expand Up @@ -1072,11 +1065,12 @@ void ConverterRefCount::ConvertDeclRefValue(clang::Expr *expr,

if (isAddrOf()) {
if (is_global_value) {
StrCat(std::format("{}.with(|v| v.as_pointer())", std::move(str)));
StrCat(std::format("{}.with(|v| v{})", std::move(str),
AsPointerMethod(decl_t)));
} else if (is_global_ptr) {
StrCat(str);
} else {
StrCat(str, ".as_pointer()");
StrCat(str, AsPointerMethod(decl_t));
}
computed_expr_type_ = ComputedExprType::FreshPointer;
return;
Expand Down Expand Up @@ -1353,7 +1347,7 @@ bool ConverterRefCount::VisitCallExpr(clang::CallExpr *expr) {

if (isAddrOf() && !ty->isReferenceType() && !IsPointerType(ctx_, ty)) {
PushConversionKind push(*this, ConversionKind::FullRefCount);
StrCat(BoxValue(std::move(str)), ".as_pointer()");
StrCat(BoxValue(std::move(str)), AsPointerMethod(ty));
return false;
}

Expand Down Expand Up @@ -2090,7 +2084,7 @@ bool ConverterRefCount::VisitMemberExpr(clang::MemberExpr *expr) {
if (member->getType()->isReferenceType()) {
computed_expr_type_ = ComputedExprType::Pointer;
} else {
StrCat(".as_pointer()");
StrCat(AsPointerMethod(member->getType()));
computed_expr_type_ = ComputedExprType::FreshPointer;
}
return false;
Expand Down Expand Up @@ -2125,8 +2119,8 @@ void ConverterRefCount::ConvertInlineField(clang::MemberExpr *expr) {
StrCat(std::format("array_field_ptr!({}, {})", ConvertRecordPtr(expr),
name));
} else if (IsBoxedType(ctx_, type)) {
StrCat(std::format("field_ref!({}, {}).as_pointer()",
ConvertRecordPtr(expr), name));
StrCat(std::format("field_ref!({}, {}){}", ConvertRecordPtr(expr), name,
AsPointerMethod(type)));
} else {
StrCat(std::format("field_ptr!({}, {})", ConvertRecordPtr(expr), name));
}
Expand Down Expand Up @@ -2838,28 +2832,17 @@ bool ConverterRefCount::ConvertCXXOperatorCallExpr(
StrCat(GetPointerDerefPrefix(expr->getType()));
}

if (is_inner_boxed && !isObject()) {
StrCat('(');
}

PushConversionKind push(*this, ConversionKind::Unboxed);
if (deref && !is_inner_boxed) {
StrCat(ConvertElemPlace(expr->getArg(0), expr->getArg(1)));
} else {
StrCat(std::format("({} as {}).offset({})",
StrCat(std::format("{}.offset({})",
ConvertObject(expr->getArg(0), ObjectShape::Element),
ConvertPtrType(expr->getArg(0)->getType()),
ConvertSubscriptIndex(expr->getArg(1))));
}

// The pointer to the element is already a pointer to the whole inner
// container, which as_pointer can't tell from a pointer to its first
// element without a type.
if (is_inner_boxed && (!isObject() || WantsElementPtr())) {
StrCat(".as_pointer()");
if (!isObject()) {
StrCat(std::format("as Ptr<{}>)", ToString(expr->getType())));
}
if (is_inner_boxed && isObject() && WantsElementPtr()) {
StrCat(".decay()");
}

if (isAddrOf()) {
Expand Down Expand Up @@ -2918,10 +2901,9 @@ std::string ConverterRefCount::ConvertElemPlace(clang::Expr *container,
return std::format("index!({}, {})", place, ConvertSubscriptIndex(idx));
}
}
auto ptr =
std::format("({} as {})", ConvertObject(container, ObjectShape::Element),
ConvertPtrType(container->getType()));
return std::format("elem!({}, {})", ptr, ConvertSubscriptIndex(idx));
return std::format("elem!({}, {})",
ConvertObject(container, ObjectShape::Element),
ConvertSubscriptIndex(idx));
}

void ConverterRefCount::ConvertFunctionParameters(clang::FunctionDecl *decl) {
Expand Down Expand Up @@ -3420,8 +3402,8 @@ void ConverterRefCount::SetUFCSReceiver(clang::Expr *base, bool is_arrow,
if (IsTemporaryObject(base) && base->getType()->isRecordType() &&
!IsReferenceType(base->IgnoreImplicit())) {
PushConversionKind push(*this, ConversionKind::FullRefCount);
ufcs_receiver_ =
token::kRef + BoxValue(ConvertRValue(base)) + ".as_pointer()";
ufcs_receiver_ = token::kRef + BoxValue(ConvertRValue(base)) +
AsPointerMethod(base->getType());
return;
}
ufcs_receiver_ = token::kRef + (base_is_pointer ? ConvertRValue(base)
Expand Down
11 changes: 8 additions & 3 deletions cpp2rust/converter/models/converter_refcount.h
Original file line number Diff line number Diff line change
Expand Up @@ -393,12 +393,17 @@ class ConverterRefCount final : public Converter {
enum class ObjectShape { Whole, Element };
std::string ConvertObject(clang::Expr *expr,
ObjectShape shape = ObjectShape::Whole);
std::string
ConvertFreshObject(clang::Expr *expr,
std::string_view target_ptr_type = {}) override;
std::string ConvertFreshObject(clang::Expr *expr) override;

std::string ConvertPointerReceiver(clang::Expr *arg,
std::string_view param_type) override;

std::string AsPointerMethod(clang::QualType type) const;

std::string ConvertTemporaryPointer(
clang::Expr *expr,
std::optional<clang::QualType> implicit_convert_to) override;

bool WantsElementPtr() const { return object_shape_ == ObjectShape::Element; }
std::string
ConvertFresh(clang::Expr *expr,
Expand Down
2 changes: 1 addition & 1 deletion libcc2rs/src/cstr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ mod tests {
#[test]
fn stops_at_first_nul_in_vec() {
let v = std::rc::Rc::new(RefCell::new(EMBEDDED_NUL.to_vec()));
check(&v.as_pointer());
check(&v.as_pointer().decay());
}

#[test]
Expand Down
32 changes: 5 additions & 27 deletions libcc2rs/src/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -442,27 +442,6 @@ where
}
}

impl<S, T, G, M> AsPointer<T> for FieldRef<S, Vec<T>, G, M>
where
S: Record,
T: ByteRepr,
G: Fn(&S) -> &Vec<T> + Copy + 'static,
M: Fn(&mut S) -> &mut Vec<T> + Copy + 'static,
{
#[inline]
fn as_pointer(&self) -> Ptr<T> {
match &self.base.kind {
PtrKind::Field(_, _, FieldShape::Single) => self
.base
.field_ptr(self.get, self.get_mut, self.offset)
.decay(),
_ => self
.base
.elems_of(self.get, self.get_mut, (self.offset)(&S::OFFSETS), false),
}
}
}

// The sequence that `get` gives in the struct that `base` points to, which is
// reached through `base`: a field, as a sequence of one element, or the
// elements of an array or vector field. It is the parent of the pointers into
Expand Down Expand Up @@ -589,7 +568,7 @@ macro_rules! array_field_ptr {

// Field `field` of the struct that a Ptr or a Value holds, to make a pointer
// to with as_pointer(), e.g., `field_ref!(p, v).as_pointer()` for a vector
// field `v`, which gives a pointer to the vector or to its first element.
// field `v`, which gives a pointer to the vector.
#[macro_export]
macro_rules! field_ref {
($base:expr, $field:ident) => {{
Expand Down Expand Up @@ -1027,7 +1006,8 @@ impl<S: Record, T: ByteRepr> VecElem<'_, S, T> {
let elems: Ptr<T> = self
.base
.field_ref(self.get, self.get_mut, self.offset)
.as_pointer();
.as_pointer()
.decay();
elems.offset(self.idx).upgrade()
}
}
Expand Down Expand Up @@ -1585,7 +1565,7 @@ mod tests {
#[should_panic(expected = "too large for a pointer to its elements")]
fn far_vector_field_ptr() {
let far: Ptr<Far> = field_ptr!(holds_far(), far);
let _: Ptr<i32> = field_ref!(far, v).as_pointer();
let _: Ptr<i32> = field_ref!(far, v).as_pointer().decay();
}

#[test]
Expand All @@ -1595,8 +1575,6 @@ mod tests {
let s: Value<Far> = Rc::new(RefCell::new(holds_far().borrow().far.clone()));
let arr: Ptr<i32> = array_field_ptr!(s, arr);
assert_eq!(arr.offset(2).read(), 3);
let v: Ptr<i32> = field_ref!(s, v).as_pointer();
assert_eq!(v.offset(1).read(), 6);
}

#[derive(Clone, Default, Record, ByteRepr)]
Expand Down Expand Up @@ -1644,7 +1622,7 @@ mod tests {
assert_ne!(a.offset(1), b);
assert_eq!(b.offset(-2), a);
// The elements of a vector are not in the struct.
let v: Ptr<i32> = field_ref!(s, v).as_pointer();
let v: Ptr<i32> = field_ref!(s, v).as_pointer().decay();
assert_ne!(v.offset(6), field_ptr!(s, c));
}

Expand Down
19 changes: 1 addition & 18 deletions libcc2rs/src/rc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1445,30 +1445,13 @@ impl<T> AsPointer<T> for Option<Rc<RefCell<Box<[T]>>>> {
}
}

impl<T> AsPointer<T> for Rc<RefCell<Vec<T>>> {
#[inline]
fn as_pointer(&self) -> Ptr<T> {
Ptr {
offset: 0,
kind: PtrKind::StackVec(Rc::downgrade(self)),
}
}
}

impl<T> AsPointer<Vec<T>> for Ptr<Vec<T>> {
#[inline]
fn as_pointer(&self) -> Ptr<Vec<T>> {
self.clone()
}
}

impl<T: 'static> AsPointer<T> for Ptr<Vec<T>> {
#[inline]
fn as_pointer(&self) -> Ptr<T> {
self.decay()
}
}

impl<T: 'static> Ptr<Vec<T>> {
#[inline]
pub fn decay(&self) -> Ptr<T> {
Expand Down Expand Up @@ -1714,7 +1697,7 @@ mod tests {
assert_eq!(pp.borrow().read(), 3);
pp_ptr.read().write(7);
assert_eq!(a.borrow()[2], 7);
pp_ptr.write(v.as_pointer().offset(1));
pp_ptr.write(v.as_pointer().decay().offset(1));
pp_ptr.read().write(8);
assert_eq!(*v.borrow(), vec![4, 8]);
}
Expand Down
13 changes: 8 additions & 5 deletions rule-preprocessor/src/syntactic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,9 @@ impl<'a> FragmentCtx<'a> {
});
return;
}
let mut access = self.builder.classify_access(token);
let mut access = self
.builder
.classify_access(token, param.is_refcount_pointer);
if param.is_mut_ref && self.text_buf.ends_with('*') {
self.text_buf.pop();
}
Expand Down Expand Up @@ -392,7 +394,7 @@ impl<'a> FnIrBuilder<'a> {
generics
}

fn classify_rw_from_name_ref(&self, name_ref: &ast::NameRef) -> Access {
fn classify_rw_from_name_ref(&self, name_ref: &ast::NameRef, is_ptr: bool) -> Access {
name_ref
.syntax()
.ancestors()
Expand Down Expand Up @@ -425,7 +427,8 @@ impl<'a> FnIrBuilder<'a> {
{
return None;
}
Some(Access::Unknown)
// Every Ptr method takes &self.
Some(if is_ptr { Access::Borrow } else { Access::Unknown })
},
ast::ReturnExpr(_) => {
panic!("return statements are not supported in rule bodies");
Expand Down Expand Up @@ -461,12 +464,12 @@ impl<'a> FnIrBuilder<'a> {
.is_some_and(|ty| matches!(ty, ast::Type::RefType(r) if r.mut_token().is_some()))
}

fn classify_access(&self, token: &ra_ap_syntax::SyntaxToken) -> Access {
fn classify_access(&self, token: &ra_ap_syntax::SyntaxToken, is_ptr: bool) -> Access {
let Some(name_ref) = token.parent().and_then(ast::NameRef::cast) else {
// Inside a macro invocation. Phase 2 will resolve this.
return Access::Unknown;
};
self.classify_rw_from_name_ref(&name_ref)
self.classify_rw_from_name_ref(&name_ref, is_ptr)
}

fn trim_whitespaces(fragments: &mut Vec<BodyFragment>) {
Expand Down
Loading
Loading