diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index d1e6eb00..359210aa 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -519,9 +519,11 @@ bool IsPointerType(clang::QualType qual_type) { ->getCanonicalTypeInternal())); } -bool Converter::RecordDerivesDefault(const clang::CXXRecordDecl *decl) { - if (GetUserDefinedDefaultConstructor(decl)) { - return false; +bool Converter::RecordDerivesDefault(const clang::RecordDecl *decl) { + if (auto cxx_decl = clang::dyn_cast(decl)) { + if (GetUserDefinedDefaultConstructor(cxx_decl)) { + return false; + } } for (auto f : decl->fields()) { @@ -546,7 +548,7 @@ bool Converter::RecordDerivesDefault(const clang::CXXRecordDecl *decl) { return true; } -static bool recordDerivesCopy(const clang::CXXRecordDecl *decl) { +static bool recordDerivesCopy(const clang::RecordDecl *decl) { for (auto f : decl->fields()) { // Records that contain std::vector, std::array, std::string or anything // that is translated to Vec<>, do not derive Copy @@ -569,8 +571,8 @@ static bool recordDerivesCopy(const clang::CXXRecordDecl *decl) { } } - // Look recursively into fields that are CXXRecordDecl - if (auto field_record = f->getType()->getAsCXXRecordDecl()) { + // Look recursively into fields that are RecordDecl + if (auto field_record = f->getType()->getAsRecordDecl()) { if (!recordDerivesCopy(field_record)) { return false; } @@ -580,6 +582,109 @@ static bool recordDerivesCopy(const clang::CXXRecordDecl *decl) { return true; } +bool Converter::VisitRecordDecl(clang::RecordDecl *decl) { + decl->dumpColor(); + + // VisitCXXRecordDecl already visited the record + if (clang::isa(decl)) { + return true; + } + + if (!decl->isCompleteDefinition()) { + return false; + } + + if (!record_decls_.insert(GetID(decl)).second) { + return false; + } + + Mapper::AddRuleForUserDefinedType(decl); + EmitRustStruct(decl); + + return false; +} + +void Converter::EmitRustStruct(clang::RecordDecl *decl) { + // Enums and static variables. In rust they live outside the record + for (auto *d : decl->decls()) { + if (auto *enum_decl = llvm::dyn_cast(d)) { + VisitEnumDecl(enum_decl); + } + if (auto *var_decl = clang::dyn_cast(d)) { + VisitVarDecl(var_decl); + } + } + + // Inner records. In rust they live outside the record + for (auto *d : decl->decls()) { + if (auto *nested = clang::dyn_cast(d)) { + if (!nested->isImplicit()) { + inner_structs_[GetID(nested)] = GetRecordName(nested); + if (auto *cxx = clang::dyn_cast(nested)) { + VisitCXXRecordDecl(cxx); + } else { + VisitRecordDecl(nested); + } + } + } + } + + // Derived traits + StrCat("#[derive("); + for (auto *attr : GetStructAttributes(decl)) { + StrCat(attr, ","); + } + StrCat(")]"); + + // Fields + auto access = clang::dyn_cast(decl) + ? AccessSpecifierAsString(decl->getAccess()) + : keyword::kPub; + StrCat(access, keyword::kStruct, GetRecordName(decl), + token::kOpenCurlyBracket); + for (auto *field : decl->fields()) { + VisitFieldDecl(field); + } + StrCat(token::kCloseCurlyBracket); + + // C++ method decls + if (auto *cxx = clang::dyn_cast(decl)) { + auto struct_name = GetRecordName(cxx); + + ConvertCXXMethodDecls( + cxx, std::string(keyword::kImpl) + ' ' + struct_name, + [](const auto *method) { + return !method->isImplicit() && + !(method->getDefinition() && + method->getDefinition()->isDefaulted()) && + (method->isThisDeclarationADefinition() || + clang::isa(method)) && + !method->isVirtual() && + !clang::isa(method); + }); + + if (cxx->bases_begin() != cxx->bases_end()) { + ConvertCXXMethodDecls( + cxx, + std::format("{} impl {} for {}", keyword_unsafe_, + GetUnsafeTypeAsString(cxx->bases_begin()->getType()), + struct_name), + [](const auto *method) { + return !method->isImplicit() && method->isVirtual(); + }); + } + } + + // Traits + if (auto *cxx = clang::dyn_cast(decl)) { + AddOrdTrait(cxx); + AddCloneTrait(cxx); + AddDropTrait(cxx); + AddDefaultTrait(cxx); + } + AddByteReprTrait(decl); +} + bool Converter::VisitCXXRecordDecl(clang::CXXRecordDecl *decl) { if (clang::isa(decl)) { materializeTemplateSpecialization(decl); @@ -623,74 +728,7 @@ bool Converter::VisitCXXRecordDecl(clang::CXXRecordDecl *decl) { } } - auto struct_name = GetRecordName(decl); - - // First visit the nested enums - for (auto d : decl->decls()) { - if (auto enum_decl = llvm::dyn_cast(d)) { - VisitEnumDecl(enum_decl); - } - } - - for (auto *decl : decl->decls()) { - if (auto var_decl = clang::dyn_cast(decl)) { - VisitVarDecl(var_decl); - } - } - - auto nested = GetNestedStructs(decl); - for (auto *record_decl : nested) { - auto ID = GetID(record_decl); - inner_structs_[ID] = GetRecordName(record_decl); - VisitCXXRecordDecl(record_decl); - } - - StrCat(token::kHash, token::kOpenBracket, "derive", token::kOpenParen); - bool derives_default = RecordDerivesDefault(decl); - - for (auto *struct_attr : GetStructAttributes(decl, derives_default)) { - StrCat(struct_attr, token::kComma); - } - StrCat(token::kCloseParen, token::kCloseBracket); - - auto access_specifier = decl->getAccess(); - StrCat(AccessSpecifierAsString(access_specifier), keyword::kStruct, - struct_name, token::kOpenCurlyBracket); - for (auto *field : decl->fields()) { - VisitFieldDecl(field); - } - StrCat(token::kCloseCurlyBracket); - - ConvertCXXMethodDecls( - decl, std::string(keyword::kImpl) + ' ' + struct_name, - [](const auto *method) { - return !method->isImplicit() && - !(method->getDefinition() && - method->getDefinition()->isDefaulted()) && - (method->isThisDeclarationADefinition() || - clang::isa(method)) && - !method->isVirtual() && - !clang::isa(method); - }); - - AddOrdTrait(decl); - AddCloneTrait(decl); - AddDropTrait(decl); - if (!derives_default) { - AddDefaultTrait(decl); - } - AddByteReprTrait(decl); - - if (decl->bases_begin() != decl->bases_end()) { - ConvertCXXMethodDecls( - decl, - std::format("{} impl {} for {}", keyword_unsafe_, - GetUnsafeTypeAsString(decl->bases_begin()->getType()), - struct_name), - [](const auto *method) { - return !method->isImplicit() && method->isVirtual(); - }); - } + EmitRustStruct(decl); } else { // FIXME: improve error handling assert(0 && "unsupported union"); @@ -2797,15 +2835,18 @@ std::string Converter::GetRecordName(const clang::NamedDecl *decl) const { } std::vector -Converter::GetStructAttributes(const clang::CXXRecordDecl *decl, - bool &out_impl_default) { +Converter::GetStructAttributes(const clang::RecordDecl *decl) { std::vector struct_attrs = {}; if (recordDerivesCopy(decl)) { struct_attrs.emplace_back("Copy"); } - if (!decl->defaultedCopyConstructorIsDeleted()) { + if (auto cxx_decl = clang::dyn_cast(decl)) { + if (!cxx_decl->defaultedCopyConstructorIsDeleted()) { + struct_attrs.emplace_back("Clone"); + } + } else /* RecordDecl */ { struct_attrs.emplace_back("Clone"); } @@ -3106,11 +3147,14 @@ void Converter::AddCloneTrait(const clang::CXXRecordDecl *decl) {} void Converter::AddDropTrait(const clang::CXXRecordDecl *decl) {} void Converter::AddDefaultTrait(const clang::CXXRecordDecl *decl) { + if (RecordDerivesDefault(decl)) { + return; + } auto struct_name = GetRecordName(decl); StrCat(std::format("impl Default for {}", struct_name), token::kOpenCurlyBracket, "fn default() -> Self", token::kOpenCurlyBracket); - if (auto default_ctor = GetUserDefinedDefaultConstructor(decl)) { + if (auto *default_ctor = GetUserDefinedDefaultConstructor(decl)) { StrCat(keyword_unsafe_, token::kOpenCurlyBracket); Convert(clang::CXXConstructExpr::Create( ctx_, ctx_.getCanonicalTagType(decl), clang::SourceLocation(), @@ -3133,7 +3177,7 @@ void Converter::AddDefaultTrait(const clang::CXXRecordDecl *decl) { StrCat(token::kCloseCurlyBracket, token::kCloseCurlyBracket); } -void Converter::AddByteReprTrait(const clang::CXXRecordDecl *decl) {} +void Converter::AddByteReprTrait(const clang::RecordDecl *decl) {} void Converter::ConvertUnsignedArithBinaryOperator(clang::BinaryOperator *op, clang::Expr *expr) { diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 6f2c3719..74e8e750 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -95,8 +95,12 @@ class Converter : public clang::RecursiveASTVisitor { virtual bool ConvertLambdaVarDecl(clang::VarDecl *decl); + bool VisitRecordDecl(clang::RecordDecl *decl); + virtual bool VisitCXXRecordDecl(clang::CXXRecordDecl *decl); + void EmitRustStruct(clang::RecordDecl *decl); + virtual bool VisitCXXMethodDecl(clang::CXXMethodDecl *decl); virtual std::string GetSelfMaybeWithMut(const clang::CXXMethodDecl *decl); @@ -355,7 +359,7 @@ class Converter : public clang::RecursiveASTVisitor { virtual std::string GetRecordName(const clang::NamedDecl *decl) const; virtual std::vector - GetStructAttributes(const clang::CXXRecordDecl *decl, bool &out_impl_default); + GetStructAttributes(const clang::RecordDecl *decl); virtual std::string GetUnsafeTypeAsString(clang::QualType qual_type) const; @@ -410,7 +414,7 @@ class Converter : public clang::RecursiveASTVisitor { virtual void AddDefaultTrait(const clang::CXXRecordDecl *decl); - virtual void AddByteReprTrait(const clang::CXXRecordDecl *decl); + virtual void AddByteReprTrait(const clang::RecordDecl *decl); virtual void ConvertUnsignedArithBinaryOperator(clang::BinaryOperator *binary_operator, @@ -453,7 +457,7 @@ class Converter : public clang::RecursiveASTVisitor { virtual bool IsReferenceType(const clang::Expr *expr) const; - virtual bool RecordDerivesDefault(const clang::CXXRecordDecl *decl); + virtual bool RecordDerivesDefault(const clang::RecordDecl *decl); std::string *rs_code_; clang::ASTContext &ctx_; diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 223bd51a..97c3d84f 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -494,7 +494,7 @@ void ConverterRefCount::AddDropTrait(const clang::CXXRecordDecl *decl) { StrCat("}"); } -void ConverterRefCount::AddByteReprTrait(const clang::CXXRecordDecl *decl) { +void ConverterRefCount::AddByteReprTrait(const clang::RecordDecl *decl) { auto struct_name = GetRecordName(decl); StrCat(std::format("impl ByteRepr for {}", struct_name), token::kOpenCurlyBracket, token::kCloseCurlyBracket); @@ -1604,11 +1604,10 @@ ConverterRefCount::ConvertVarDefaultInit(clang::QualType qual_type) { } std::vector -ConverterRefCount::GetStructAttributes(const clang::CXXRecordDecl *decl, - bool &out_impl_default) { +ConverterRefCount::GetStructAttributes(const clang::RecordDecl *decl) { std::vector attrs = {}; - if (out_impl_default) { + if (RecordDerivesDefault(decl)) { attrs.emplace_back("Default"); } return attrs; diff --git a/cpp2rust/converter/models/converter_refcount.h b/cpp2rust/converter/models/converter_refcount.h index a55aad58..26ba88a3 100644 --- a/cpp2rust/converter/models/converter_refcount.h +++ b/cpp2rust/converter/models/converter_refcount.h @@ -35,7 +35,7 @@ class ConverterRefCount final : public Converter { void AddDropTrait(const clang::CXXRecordDecl *decl) override; - void AddByteReprTrait(const clang::CXXRecordDecl *decl) override; + void AddByteReprTrait(const clang::RecordDecl *decl) override; void AddDefaultTrait(const clang::CXXRecordDecl *decl) override; @@ -121,8 +121,7 @@ class ConverterRefCount final : public Converter { std::string ConvertVarDefaultInit(clang::QualType qual_type) override; std::vector - GetStructAttributes(const clang::CXXRecordDecl *decl, - bool &out_impl_default) override; + GetStructAttributes(const clang::RecordDecl *decl) override; bool MayCauseBorrowMutError(const clang::Expr *lhs, const clang::Expr *rhs); diff --git a/tests/unit/c_struct.c b/tests/unit/c_struct.c new file mode 100644 index 00000000..9e79b582 --- /dev/null +++ b/tests/unit/c_struct.c @@ -0,0 +1,51 @@ +#include + +struct Point { + int x; + int y; +}; + +struct Line { + struct Point start; + struct Point end; +}; + +struct Node { + int value; + struct Node *next; +}; + +struct Container { + struct Inner { + int a; + int b; + } inner; + enum Color { RED, GREEN, BLUE } color; + int count; +}; + +int main() { + struct Point p = {10, 20}; + assert(p.x == 10); + assert(p.y == 20); + + struct Line l = {{1, 2}, {3, 4}}; + assert(l.start.x == 1); + assert(l.end.y == 4); + + struct Node a = {1, 0}; + struct Node b = {2, &a}; + assert(b.next->value == 1); + + struct Container c = {{5, 6}, GREEN, 42}; + assert(c.inner.a == 5); + assert(c.inner.b == 6); + assert(c.color == GREEN); + assert(c.count == 42); + + struct Container c2; + c2.color = BLUE; + assert(c2.color == 2); + + return 0; +} diff --git a/tests/unit/out/refcount/c_struct.rs b/tests/unit/out/refcount/c_struct.rs new file mode 100644 index 00000000..2343bb35 --- /dev/null +++ b/tests/unit/out/refcount/c_struct.rs @@ -0,0 +1,99 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::{Read, Seek, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +#[derive(Default)] +pub struct Point { + pub x: Value, + pub y: Value, +} +impl ByteRepr for Point {} +#[derive(Default)] +pub struct Line { + pub start: Value, + pub end: Value, +} +impl ByteRepr for Line {} +#[derive(Default)] +pub struct Node { + pub value: Value, + pub next: Value>, +} +impl ByteRepr for Node {} +#[derive(Clone, Copy, PartialEq, Debug, Default)] +enum Color { + #[default] + RED = 0, + GREEN = 1, + BLUE = 2, +} +#[derive(Default)] +pub struct Inner { + pub a: Value, + pub b: Value, +} +impl ByteRepr for Inner {} +#[derive(Default)] +pub struct Container { + pub inner: Value, + pub color: Value, + pub count: Value, +} +impl ByteRepr for Container {} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let p: Value = Rc::new(RefCell::new(Point { + x: Rc::new(RefCell::new(10)), + y: Rc::new(RefCell::new(20)), + })); + assert!(((*(*p.borrow()).x.borrow()) == 10)); + assert!(((*(*p.borrow()).y.borrow()) == 20)); + let l: Value = Rc::new(RefCell::new(Line { + start: Rc::new(RefCell::new(Point { + x: Rc::new(RefCell::new(1)), + y: Rc::new(RefCell::new(2)), + })), + end: Rc::new(RefCell::new(Point { + x: Rc::new(RefCell::new(3)), + y: Rc::new(RefCell::new(4)), + })), + })); + assert!(((*(*(*l.borrow()).start.borrow()).x.borrow()) == 1)); + assert!(((*(*(*l.borrow()).end.borrow()).y.borrow()) == 4)); + let a: Value = Rc::new(RefCell::new(Node { + value: Rc::new(RefCell::new(1)), + next: Rc::new(RefCell::new(Default::default())), + })); + let b: Value = Rc::new(RefCell::new(Node { + value: Rc::new(RefCell::new(2)), + next: Rc::new(RefCell::new((a.as_pointer()))), + })); + assert!( + ((*(*(*(*b.borrow()).next.borrow()).upgrade().deref()) + .value + .borrow()) + == 1) + ); + let c: Value = Rc::new(RefCell::new(Container { + inner: Rc::new(RefCell::new(Inner { + a: Rc::new(RefCell::new(5)), + b: Rc::new(RefCell::new(6)), + })), + color: Rc::new(RefCell::new((Color::GREEN as Color))), + count: Rc::new(RefCell::new(42)), + })); + assert!(((*(*(*c.borrow()).inner.borrow()).a.borrow()) == 5)); + assert!(((*(*(*c.borrow()).inner.borrow()).b.borrow()) == 6)); + assert!((((*(*c.borrow()).color.borrow()) as u32) == (Color::GREEN as u32))); + assert!(((*(*c.borrow()).count.borrow()) == 42)); + let c2: Value = >::default(); + (*(*c2.borrow()).color.borrow_mut()) = (Color::BLUE as Color); + assert!((((*(*c2.borrow()).color.borrow()) as u32) == 2_u32)); + return 0; +} diff --git a/tests/unit/out/refcount/polymorphism.rs b/tests/unit/out/refcount/polymorphism.rs index d86e1977..8add81ba 100644 --- a/tests/unit/out/refcount/polymorphism.rs +++ b/tests/unit/out/refcount/polymorphism.rs @@ -11,6 +11,11 @@ pub trait Animal { } #[derive(Default)] pub struct Dog {} +impl Animal for Dog { + fn bark(&self) -> bool { + return true; + } +} impl Clone for Dog { fn clone(&self) -> Self { let mut this = Self {}; @@ -18,11 +23,6 @@ impl Clone for Dog { } } impl ByteRepr for Dog {} -impl Animal for Dog { - fn bark(&self) -> bool { - return true; - } -} #[derive(Default)] pub struct Cat {} impl Cat { @@ -30,6 +30,11 @@ impl Cat { return true; } } +impl Animal for Cat { + fn bark(&self) -> bool { + return false; + } +} impl Clone for Cat { fn clone(&self) -> Self { let mut this = Self {}; @@ -37,11 +42,6 @@ impl Clone for Cat { } } impl ByteRepr for Cat {} -impl Animal for Cat { - fn bark(&self) -> bool { - return false; - } -} pub fn main() { std::process::exit(main_0()); } diff --git a/tests/unit/out/refcount/va_arg_struct_ctx.rs b/tests/unit/out/refcount/va_arg_struct_ctx.rs index 3d3f74c7..3076b601 100644 --- a/tests/unit/out/refcount/va_arg_struct_ctx.rs +++ b/tests/unit/out/refcount/va_arg_struct_ctx.rs @@ -11,15 +11,6 @@ pub struct context { pub verbose: Value, pub last_error: Value, } -impl Clone for context { - fn clone(&self) -> Self { - let mut this = Self { - verbose: Rc::new(RefCell::new((*self.verbose.borrow()))), - last_error: Rc::new(RefCell::new((*self.last_error.borrow()))), - }; - this - } -} impl ByteRepr for context {} pub fn set_error_0(ctx: Ptr, fmt: Ptr, args: &[VaArg]) { let ctx: Value> = Rc::new(RefCell::new(ctx)); @@ -35,7 +26,7 @@ pub fn main() { std::process::exit(main_0()); } fn main_0() -> i32 { - let ctx: Value = Rc::new(RefCell::new(::default())); + let ctx: Value = >::default(); (*(*ctx.borrow()).verbose.borrow_mut()) = 1; (*(*ctx.borrow()).last_error.borrow_mut()) = 0; ({ diff --git a/tests/unit/out/unsafe/c_struct.rs b/tests/unit/out/unsafe/c_struct.rs new file mode 100644 index 00000000..3d12a8fd --- /dev/null +++ b/tests/unit/out/unsafe/c_struct.rs @@ -0,0 +1,79 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::{Read, Seek, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +#[derive(Copy, Clone, Default)] +pub struct Point { + pub x: i32, + pub y: i32, +} +#[derive(Copy, Clone, Default)] +pub struct Line { + pub start: Point, + pub end: Point, +} +#[derive(Copy, Clone, Default)] +pub struct Node { + pub value: i32, + pub next: *mut Node, +} +#[derive(Clone, Copy, PartialEq, Debug, Default)] +enum Color { + #[default] + RED = 0, + GREEN = 1, + BLUE = 2, +} +#[derive(Copy, Clone, Default)] +pub struct Inner { + pub a: i32, + pub b: i32, +} +#[derive(Copy, Clone, Default)] +pub struct Container { + pub inner: Inner, + pub color: Color, + pub count: i32, +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut p: Point = Point { x: 10, y: 20 }; + assert!(((p.x) == (10))); + assert!(((p.y) == (20))); + let mut l: Line = Line { + start: Point { x: 1, y: 2 }, + end: Point { x: 3, y: 4 }, + }; + assert!(((l.start.x) == (1))); + assert!(((l.end.y) == (4))); + let mut a: Node = Node { + value: 1, + next: Default::default(), + }; + let mut b: Node = Node { + value: 2, + next: (&mut a as *mut Node), + }; + assert!((((*b.next).value) == (1))); + let mut c: Container = Container { + inner: Inner { a: 5, b: 6 }, + color: (Color::GREEN as Color), + count: 42, + }; + assert!(((c.inner.a) == (5))); + assert!(((c.inner.b) == (6))); + assert!(((c.color as u32) == (Color::GREEN as u32))); + assert!(((c.count) == (42))); + let mut c2: Container = ::default(); + c2.color = (Color::BLUE as Color); + assert!(((c2.color as u32) == (2_u32))); + return 0; +} diff --git a/tests/unit/va_arg_struct_ctx.cpp b/tests/unit/va_arg_struct_ctx.c similarity index 100% rename from tests/unit/va_arg_struct_ctx.cpp rename to tests/unit/va_arg_struct_ctx.c