Skip to content

Commit 88338be

Browse files
committed
Merge branch 'master' into mismatched-pointers
2 parents 4e55bc6 + 0db209a commit 88338be

154 files changed

Lines changed: 2140 additions & 2601 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎cmake/rust-toolchain.cmake‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
set(RUST_STABLE_VERSION "1.98.0")
2-
set(RUST_NIGHTLY_VERSION "nightly-2026-08-27")
1+
set(RUST_STABLE_VERSION "1.99.0")
2+
set(RUST_NIGHTLY_VERSION "nightly-2026-10-01")

‎cpp2rust/converter/converter.cpp‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -595,7 +595,8 @@ void Converter::ConvertGlobalVarDecl(clang::VarDecl *decl) {
595595
}
596596

597597
bool Converter::VisitVarDecl(clang::VarDecl *decl) {
598-
if (clang::isa<clang::VarTemplatePartialSpecializationDecl>(decl)) {
598+
if (clang::isa<clang::VarTemplatePartialSpecializationDecl>(decl) ||
599+
decl->getDeclContext()->isDependentContext()) {
599600
return false;
600601
}
601602
if (ConvertLambdaVarDecl(decl)) {
@@ -760,6 +761,11 @@ void Converter::EmitRustStructOrUnion(clang::RecordDecl *decl) {
760761
VisitEnumDecl(enum_decl);
761762
}
762763
if (auto *var_decl = clang::dyn_cast<clang::VarDecl>(d)) {
764+
if (auto *def = var_decl->getDefinition();
765+
def && var_decl->getInstantiatedFromStaticDataMember() &&
766+
!var_decl->hasInit()) {
767+
var_decl = def;
768+
}
763769
VisitVarDecl(var_decl);
764770
}
765771
if (auto *friend_decl = clang::dyn_cast<clang::FriendDecl>(d)) {
@@ -3430,7 +3436,7 @@ bool Converter::VisitInitListExpr(clang::InitListExpr *expr) {
34303436
}
34313437
PushBracket bracket(*this);
34323438
for (auto *init : expr->inits()) {
3433-
ConvertVarInit(init->getType(), init);
3439+
ConvertVarInit(ctx_.getAsArrayType(qual_type)->getElementType(), init);
34343440
StrCat(token::kComma);
34353441
}
34363442
if (expr->hasArrayFiller()) {

‎cpp2rust/converter/converter_lib.cpp‎

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,6 +1338,16 @@ GetAllVars(const clang::Stmt *stmt) {
13381338
return vars;
13391339
}
13401340

1341+
bool ReadsMemory(const clang::Stmt *stmt) {
1342+
if (auto *cast = clang::dyn_cast<clang::ImplicitCastExpr>(stmt);
1343+
cast && cast->getCastKind() == clang::CK_LValueToRValue) {
1344+
return true;
1345+
}
1346+
return std::ranges::any_of(stmt->children(), [](const clang::Stmt *child) {
1347+
return child && ReadsMemory(child);
1348+
});
1349+
}
1350+
13411351
bool ReferencesThis(const clang::Stmt *stmt) {
13421352
if (!stmt) {
13431353
return false;
@@ -1450,19 +1460,20 @@ bool HasReceiver(clang::Expr *expr) {
14501460

14511461
std::optional<clang::QualType> GetParamImplicitConvertTarget(clang::Expr *expr,
14521462
unsigned arg_idx) {
1453-
auto *call = clang::dyn_cast<clang::CallExpr>(expr);
1454-
if (!call) {
1455-
return std::nullopt;
1463+
const clang::FunctionDecl *fn = nullptr;
1464+
if (auto *call = clang::dyn_cast<clang::CallExpr>(expr)) {
1465+
fn = call->getDirectCallee();
1466+
} else if (auto *ctor = clang::dyn_cast<clang::CXXConstructExpr>(expr)) {
1467+
fn = ctor->getConstructor();
14561468
}
1457-
auto *fn = call->getDirectCallee();
14581469
if (!fn) {
14591470
return std::nullopt;
14601471
}
14611472
unsigned param_idx = arg_idx - HasReceiver(expr);
14621473
if (param_idx >= fn->getNumParams()) {
14631474
return std::nullopt;
14641475
}
1465-
return fn->getParamDecl(param_idx)->getType();
1476+
return fn->getParamDecl(param_idx)->getType().getNonReferenceType();
14661477
}
14671478

14681479
std::optional<IteratorCategory>

‎cpp2rust/converter/converter_lib.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,9 @@ GetAllVars(const clang::Stmt *stmt);
242242

243243
bool ReferencesThis(const clang::Stmt *stmt);
244244

245+
// Whether evaluating `stmt` reads a value from memory, e.g., a pointer.
246+
bool ReadsMemory(const clang::Stmt *stmt);
247+
245248
bool MayCauseBorrowMutError(const clang::Expr *lhs, const clang::Expr *rhs);
246249

247250
bool ArgsMayAlias(const clang::Expr *a, const clang::Expr *b);

‎cpp2rust/converter/models/converter_refcount.cpp‎

Lines changed: 48 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -561,45 +561,28 @@ bool ConverterRefCount::RecordDerivesClone(const clang::RecordDecl *decl) {
561561
});
562562
}
563563

564-
void ConverterRefCount::AddCloneTrait(const clang::RecordDecl *decl) {
565-
auto record_name = GetRecordName(decl);
566-
564+
bool ConverterRefCount::RecordDerivesDeepClone(const clang::RecordDecl *decl) {
567565
if (decl->isUnion()) {
568-
StrCat("impl Clone for", record_name);
569-
PushBrace impl_brace(*this);
570-
StrCat("fn clone(&self) -> Self");
571-
PushBrace fn_brace(*this);
572-
StrCat(record_name,
573-
"{ __bytes: Rc::new(RefCell::new(self.__bytes.borrow().clone())) }");
574-
return;
566+
return true;
575567
}
576-
577568
if (RecordDerivesClone(decl) || !RecordImplementsClone(decl)) {
578-
return;
569+
return false;
579570
}
580571
// Without a user-defined copy constructor, each field is copied with its
581572
// own clone(), and Values are copied deeply.
582573
auto *cxx = clang::dyn_cast<clang::CXXRecordDecl>(decl);
583-
if (!cxx ||
584-
(!GetUserDefinedCopyConstructor(cxx) && cxx->getNumBases() == 0)) {
585-
StrCat(keyword::kImpl, "Clone for", record_name);
586-
PushBrace impl_brace(*this);
587-
StrCat("fn clone(&self) -> Self");
588-
PushBrace fn_brace(*this);
589-
StrCat("Self");
590-
PushBrace init_brace(*this);
591-
for (auto *field : decl->fields()) {
592-
auto name = GetNamedDeclAsString(field);
593-
auto value = std::format("self.{}.clone()", name);
594-
if (IsValueField(ctx_, field)) {
595-
value = std::format(
596-
"Rc::new(RefCell::new((*self.{}.borrow()).clone()))", name);
597-
}
598-
StrCat(std::format("{}: {},", name, value));
599-
}
574+
return !cxx ||
575+
(!GetUserDefinedCopyConstructor(cxx) && cxx->getNumBases() == 0);
576+
}
577+
578+
void ConverterRefCount::AddCloneTrait(const clang::RecordDecl *decl) {
579+
if (RecordDerivesClone(decl) || RecordDerivesDeepClone(decl) ||
580+
!RecordImplementsClone(decl)) {
600581
return;
601582
}
602583

584+
auto record_name = GetRecordName(decl);
585+
auto *cxx = clang::cast<clang::CXXRecordDecl>(decl);
603586
StrCat(keyword::kImpl, "Clone for", record_name, '{');
604587
StrCat("fn clone(&self) -> Self {");
605588

@@ -648,10 +631,10 @@ void ConverterRefCount::EmitRustUnion(clang::RecordDecl *decl) {
648631
std::vector<std::string>(attrs.begin(), attrs.end()));
649632

650633
auto size = ctx_.getTypeSizeInChars(ctx_.getCanonicalTagType(decl));
651-
StrCat(std::format("#[derive(ByteRepr)] #[byte_size({0})] pub struct {1} {{ "
652-
"#[offset(0)] #[byte_size({0})] __bytes: Value<Box<[u8]>> "
653-
"}}",
654-
size.getQuantity(), name));
634+
StrCat(std::format(
635+
"#[derive(ByteRepr, DeepClone)] #[byte_size({0})] pub struct {1} {{ "
636+
"#[offset(0)] #[byte_size({0})] __bytes: Value<Box<[u8]>> }}",
637+
size.getQuantity(), name));
655638

656639
StrCat("impl", name);
657640
{
@@ -1833,12 +1816,16 @@ bool ConverterRefCount::VisitMemberExpr(clang::MemberExpr *expr) {
18331816
return false;
18341817
}
18351818

1819+
// A pointer to a Value field is made without copying the Value out.
1820+
bool value_field_ptr = isAddrOf() && !known &&
1821+
clang::isa<clang::FieldDecl>(member) &&
1822+
!member->getType()->isReferenceType();
18361823
std::string str;
18371824
if (known) {
18381825
str = GetMappedAsString(expr);
18391826
} else if (clang::isa<clang::FieldDecl>(member)) {
18401827
// The Value or the pointer that the field holds.
1841-
str = ReadField(expr);
1828+
str = ReadField(expr, value_field_ptr ? ".as_pointer()" : ".clone()");
18421829
} else {
18431830
Buffer buf(*this);
18441831
PushExprKind push(*this, ExprKind::RValue);
@@ -1851,7 +1838,7 @@ bool ConverterRefCount::VisitMemberExpr(clang::MemberExpr *expr) {
18511838
if (member->getType()->isReferenceType()) {
18521839
computed_expr_type_ = ComputedExprType::Pointer;
18531840
} else {
1854-
StrCat(".as_pointer()");
1841+
StrCat(value_field_ptr ? "" : ".as_pointer()");
18551842
computed_expr_type_ = ComputedExprType::FreshPointer;
18561843
}
18571844
return false;
@@ -1924,7 +1911,8 @@ std::string ConverterRefCount::ConvertRecordPtr(clang::MemberExpr *expr) {
19241911
return std::move(buf).str();
19251912
}
19261913

1927-
std::string ConverterRefCount::ReadField(clang::MemberExpr *expr) {
1914+
std::string ConverterRefCount::ReadField(clang::MemberExpr *expr,
1915+
std::string_view copy) {
19281916
// Unless the struct is reached through a pointer, in which case the field
19291917
// is read in a closure, the borrow of the struct ends at the end of the
19301918
// block.
@@ -1939,7 +1927,7 @@ std::string ConverterRefCount::ReadField(clang::MemberExpr *expr) {
19391927
str = std::move(buf).str();
19401928
}
19411929
if (!TypeIsCopyable(expr->getMemberDecl()->getType())) {
1942-
str += ".clone()";
1930+
str += copy;
19431931
}
19441932
return ptr.empty() ? std::format("{{ {} }}", str)
19451933
: std::format("{}.with(|__s| {})", ptr, str);
@@ -2298,6 +2286,8 @@ ConverterRefCount::GetStructAttributes(const clang::RecordDecl *decl) {
22982286

22992287
if (RecordDerivesClone(decl)) {
23002288
attrs.emplace_back("Clone");
2289+
} else if (RecordDerivesDeepClone(decl)) {
2290+
attrs.emplace_back("DeepClone");
23012291
}
23022292

23032293
// Gives access to the fields through pointers to them.
@@ -2426,7 +2416,10 @@ void ConverterRefCount::ConvertAssignment(clang::Expr *lhs, clang::Expr *rhs,
24262416

24272417
PushBrace brace(*this, isRValue());
24282418

2429-
if (MayCauseBorrowMutError(lhs, rhs)) {
2419+
// C++ evaluates the assigned value first, which may modify what the place
2420+
// is computed from, like a pointer it is reached through.
2421+
if (MayCauseBorrowMutError(lhs, rhs) ||
2422+
(rhs->HasSideEffects(ctx_) && ReadsMemory(lhs))) {
24302423
if (CanBraceAssignedValue(lhs, rhs, assign_operator)) {
24312424
rhs_as_string = std::format("{{ {} }}", rhs_as_string);
24322425
} else {
@@ -2589,7 +2582,7 @@ bool ConverterRefCount::ConvertCXXOperatorCallExpr(
25892582
if (isLValue()) {
25902583
PushConversionKind push_ck(*this, ConversionKind::Unboxed);
25912584
pending_deref_.set(
2592-
std::format("({} as {}).offset({})",
2585+
std::format("elem!(({} as {}), {})",
25932586
ConvertObject(expr->getArg(0), ObjectShape::Element),
25942587
ConvertPtrType(expr->getArg(0)->getType()),
25952588
ConvertSubscriptIndex(expr->getArg(1))),
@@ -2609,10 +2602,12 @@ bool ConverterRefCount::ConvertCXXOperatorCallExpr(
26092602
}
26102603

26112604
PushConversionKind push(*this, ConversionKind::Unboxed);
2612-
StrCat(std::format("({} as {}).offset({})",
2613-
ConvertObject(expr->getArg(0), ObjectShape::Element),
2614-
ConvertPtrType(expr->getArg(0)->getType()),
2615-
ConvertSubscriptIndex(expr->getArg(1))));
2605+
auto ptr = std::format(
2606+
"({} as {})", ConvertObject(expr->getArg(0), ObjectShape::Element),
2607+
ConvertPtrType(expr->getArg(0)->getType()));
2608+
auto idx = ConvertSubscriptIndex(expr->getArg(1));
2609+
StrCat(deref && !is_inner_boxed ? std::format("elem!({}, {})", ptr, idx)
2610+
: std::format("{}.offset({})", ptr, idx));
26162611

26172612
if (is_inner_boxed) {
26182613
StrCat(GetPointerDerefSuffix(expr->getType()), ".as_pointer()");
@@ -2739,6 +2734,12 @@ void ConverterRefCount::ConvertArraySubscript(clang::Expr *base,
27392734
}
27402735
}
27412736

2737+
void ConverterRefCount::ConvertPointerElem(clang::Expr *base,
2738+
clang::Expr *idx) {
2739+
StrCat(std::format("elem!({}, {})", ToString(base), ConvertRValue(idx)));
2740+
computed_expr_type_ = ComputedExprType::FreshPointer;
2741+
}
2742+
27422743
void ConverterRefCount::ConvertPointerSubscript(
27432744
clang::ArraySubscriptExpr *expr) {
27442745
auto *base = expr->getBase();
@@ -2747,7 +2748,7 @@ void ConverterRefCount::ConvertPointerSubscript(
27472748
if (isLValue()) {
27482749
pending_deref_.assert_consumed();
27492750
Buffer buf(*this);
2750-
ConvertPointerOffset(base, idx);
2751+
ConvertPointerElem(base, idx);
27512752
pending_deref_.set_unchecked(std::move(buf).str(), isFresh(), expr);
27522753
return;
27532754
}
@@ -2756,8 +2757,10 @@ void ConverterRefCount::ConvertPointerSubscript(
27562757
PushParen paren(*this, deref);
27572758
if (deref) {
27582759
StrCat(GetPointerDerefPrefix(expr->getType()));
2760+
ConvertPointerElem(base, idx);
2761+
} else {
2762+
ConvertPointerOffset(base, idx);
27592763
}
2760-
ConvertPointerOffset(base, idx);
27612764
if (deref) {
27622765
StrCat(GetPointerDerefSuffix(expr->getType()));
27632766
SetValueFreshness(expr->getType());

‎cpp2rust/converter/models/converter_refcount.h‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ class ConverterRefCount final : public Converter {
5353

5454
bool RecordDerivesClone(const clang::RecordDecl *decl);
5555

56+
bool RecordDerivesDeepClone(const clang::RecordDecl *decl);
57+
5658
void EmitByteSizeAttr(const clang::RecordDecl *decl) override;
5759

5860
bool
@@ -153,7 +155,8 @@ class ConverterRefCount final : public Converter {
153155

154156
// Copies the value of the field `expr` out of its struct, such that the
155157
// struct doesn't stay borrowed.
156-
std::string ReadField(clang::MemberExpr *expr);
158+
std::string ReadField(clang::MemberExpr *expr,
159+
std::string_view copy = ".clone()");
157160

158161
// A field that is read through a pointer to its struct is read in a
159162
// closure, `p.with(|__s| __s.x)`. While converting the struct whose field
@@ -382,6 +385,9 @@ class ConverterRefCount final : public Converter {
382385

383386
std::string ConvertSubscriptIndex(clang::Expr *idx);
384387

388+
// Element `idx` of pointer `base`, accessed without creating a pointer to it.
389+
void ConvertPointerElem(clang::Expr *base, clang::Expr *idx);
390+
385391
std::string GetSafeTypeAsString(clang::QualType qual_type) const;
386392

387393
bool NeedsMut(const clang::VarDecl *decl, clang::QualType type,

‎libcc2rs-macros/src/deep_clone.rs‎

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright (c) 2022-present INESC-ID.
2+
// Distributed under the MIT license that can be found in the LICENSE file.
3+
4+
use proc_macro::TokenStream;
5+
use proc_macro2::TokenStream as TokenStream2;
6+
use quote::quote;
7+
use syn::{Data, DeriveInput, Fields, Index, Type, parse_macro_input};
8+
9+
// Whether `ty` is a Value<_>, which is copied with DeepClone.
10+
fn is_value(ty: &Type) -> bool {
11+
match ty {
12+
Type::Path(path) => path
13+
.path
14+
.segments
15+
.last()
16+
.is_some_and(|segment| segment.ident == "Value"),
17+
_ => false,
18+
}
19+
}
20+
21+
fn clone_field(ty: &Type, field: TokenStream2) -> TokenStream2 {
22+
if is_value(ty) {
23+
quote! { ::libcc2rs::DeepClone::deep_clone(&self.#field) }
24+
} else {
25+
quote! { ::core::clone::Clone::clone(&self.#field) }
26+
}
27+
}
28+
29+
pub fn expand(input: TokenStream) -> TokenStream {
30+
let input = parse_macro_input!(input as DeriveInput);
31+
let name = &input.ident;
32+
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
33+
let Data::Struct(data) = &input.data else {
34+
panic!("derive(DeepClone) is only supported on structs, `{name}` is not one");
35+
};
36+
let body = match &data.fields {
37+
Fields::Named(fields) => {
38+
let fields = fields.named.iter().map(|field| {
39+
let ident = field.ident.as_ref().unwrap();
40+
let value = clone_field(&field.ty, quote! { #ident });
41+
quote! { #ident: #value }
42+
});
43+
quote! { Self { #(#fields,)* } }
44+
}
45+
Fields::Unnamed(fields) => {
46+
let fields = fields.unnamed.iter().enumerate().map(|(i, field)| {
47+
let index = Index::from(i);
48+
clone_field(&field.ty, quote! { #index })
49+
});
50+
quote! { Self(#(#fields,)*) }
51+
}
52+
Fields::Unit => quote! { Self },
53+
};
54+
quote! {
55+
impl #impl_generics ::core::clone::Clone for #name #ty_generics #where_clause {
56+
fn clone(&self) -> Self {
57+
#body
58+
}
59+
}
60+
}
61+
.into()
62+
}

0 commit comments

Comments
 (0)