diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 976255ab..c34d0d96 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -19,10 +19,10 @@ namespace cpp2rust { std::unordered_map Converter::inner_structs_; -std::unordered_set Converter::record_decls_; std::unordered_set Converter::decl_ids_; std::unordered_set Converter::globals_; std::unordered_set Converter::abstract_structs_; +Converter::RecordIndex Converter::record_decls_; void Converter::ConvertUniquePtrDeref(clang::CXXOperatorCallExpr *expr) { bool is_star = expr->getOperator() == clang::OverloadedOperatorKind::OO_Star; @@ -53,6 +53,16 @@ use std::rc::Rc; )"); } +std::string Converter::EmitOpaqueRecords() { + std::string out; + record_decls_.ForEachUndefined([&](const std::string &name) { + out += "pub struct "; + out += name; + out += ";\n"; + }); + return out; +} + bool Converter::VisitRecoveryExpr(clang::RecoveryExpr *expr) { llvm::errs() << "RecoveryExpr: "; expr->dump(); @@ -160,7 +170,11 @@ bool Converter::VisitRecordType(clang::RecordType *type) { } } - StrCat(GetRecordName(decl)); + auto name = GetRecordName(decl); + StrCat(name); + if (!ctx_.getSourceManager().isInSystemHeader(decl->getLocation())) { + record_decls_.MarkReferenced(std::move(name)); + } Mapper::AddRuleForUserDefinedType(decl); return false; } @@ -617,7 +631,7 @@ bool Converter::VisitRecordDecl(clang::RecordDecl *decl) { return false; } - if (!record_decls_.insert(GetID(decl)).second) { + if (!record_decls_.MarkDefined(GetRecordName(decl))) { return false; } @@ -734,7 +748,7 @@ bool Converter::VisitCXXRecordDecl(clang::CXXRecordDecl *decl) { } } - if (!record_decls_.insert(GetID(decl)).second) { + if (!record_decls_.MarkDefined(GetRecordName(decl))) { return false; } diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 07105383..aa1ad22b 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -51,6 +51,8 @@ class Converter : public clang::RecursiveASTVisitor { virtual void EmitFilePreamble(); + static std::string EmitOpaqueRecords(); + virtual bool VisitBuiltinType(clang::BuiltinType *type); virtual bool VisitRecordType(clang::RecordType *type); @@ -644,9 +646,38 @@ class Converter : public clang::RecursiveASTVisitor { ~ScopedMapIterDecl() { c.map_iter_decls_.erase(decl); } }; static std::unordered_set decl_ids_; - static std::unordered_set record_decls_; static std::unordered_set abstract_structs_; + class RecordIndex { + public: + void MarkReferenced(std::string name) { + entries_.try_emplace(std::move(name), false); + } + // Returns false if `name` is already defined; otherwise marks it and + // returns true. + bool MarkDefined(const std::string &name) { + bool &defined = entries_[name]; + if (defined) { + return false; + } + defined = true; + return true; + } + template void ForEachUndefined(F &&f) const { + for (const auto &[name, defined] : entries_) { + if (!defined) { + f(name); + } + } + } + + private: + // record name -> true if a definition has been emitted, false if only + // referenced. + std::unordered_map entries_; + }; + static RecordIndex record_decls_; + enum class ExprKind : uint8_t { Callee, LValue, diff --git a/cpp2rust/cpp2rust_lib.cpp b/cpp2rust/cpp2rust_lib.cpp index 75b2a2b7..100d6d93 100644 --- a/cpp2rust/cpp2rust_lib.cpp +++ b/cpp2rust/cpp2rust_lib.cpp @@ -10,6 +10,7 @@ #include #include "compat/platform_flags.h" +#include "converter/converter.h" #include "frontend_action.h" namespace cpp2rust { @@ -29,6 +30,7 @@ std::string TranspileSrc(std::string_view cc_code, Model model, rules_dir), cc_code, tool_args, std::filesystem::path(filename).filename().string(), filename.ends_with(".c") ? CLANG_C_COMPILER : CLANG_CXX_COMPILER); + rs_code += Converter::EmitOpaqueRecords(); return rs_code; } @@ -68,6 +70,7 @@ std::string TranspileDir(std::string_view build_dir, Model model, std::string rs_code; FrontendActionFactory factory(rs_code, model, rules_dir); Tool.run(&factory); + rs_code += Converter::EmitOpaqueRecords(); return rs_code; } } // namespace cpp2rust diff --git a/tests/multi-file/opaque_forward_decl/CMakeLists.txt b/tests/multi-file/opaque_forward_decl/CMakeLists.txt new file mode 100644 index 00000000..c9192a90 --- /dev/null +++ b/tests/multi-file/opaque_forward_decl/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.16) +project(opaque_forward_decl LANGUAGES C) +add_executable(app a.c b.c) diff --git a/tests/multi-file/opaque_forward_decl/a.c b/tests/multi-file/opaque_forward_decl/a.c new file mode 100644 index 00000000..18277f6f --- /dev/null +++ b/tests/multi-file/opaque_forward_decl/a.c @@ -0,0 +1,12 @@ +#include +#include + +#include "header.h" + +int main(void) { + struct container c = {NULL, 42}; + touch(&c); + assert(c.x == 42); + assert(c.p == NULL); + return 0; +} diff --git a/tests/multi-file/opaque_forward_decl/b.c b/tests/multi-file/opaque_forward_decl/b.c new file mode 100644 index 00000000..722d2675 --- /dev/null +++ b/tests/multi-file/opaque_forward_decl/b.c @@ -0,0 +1,3 @@ +#include "header.h" + +void touch(struct container *c) { (void)c->p; } diff --git a/tests/multi-file/opaque_forward_decl/header.h b/tests/multi-file/opaque_forward_decl/header.h new file mode 100644 index 00000000..5d6fb56e --- /dev/null +++ b/tests/multi-file/opaque_forward_decl/header.h @@ -0,0 +1,10 @@ +#pragma once + +struct opaque; + +struct container { + struct opaque *p; + int x; +}; + +void touch(struct container *c); diff --git a/tests/multi-file/opaque_forward_decl/out/refcount/opaque_forward_decl.rs b/tests/multi-file/opaque_forward_decl/out/refcount/opaque_forward_decl.rs new file mode 100644 index 00000000..db3db41a --- /dev/null +++ b/tests/multi-file/opaque_forward_decl/out/refcount/opaque_forward_decl.rs @@ -0,0 +1,35 @@ +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 container { + pub p: Value>, + pub x: Value, +} +impl ByteRepr for container {} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let c: Value = Rc::new(RefCell::new(container { + p: Rc::new(RefCell::new(Ptr::::null())), + x: Rc::new(RefCell::new(42)), + })); + ({ + let _c: Ptr = (c.as_pointer()); + touch_0(_c) + }); + assert!(((((*(*c.borrow()).x.borrow()) == 42) as i32) != 0)); + assert!(((((*(*c.borrow()).p.borrow()).is_null()) as i32) != 0)); + return 0; +} +pub fn touch_0(c: Ptr) { + let c: Value> = Rc::new(RefCell::new(c)); + (*(*(*c.borrow()).upgrade().deref()).p.borrow()).clone(); +} +pub struct opaque; diff --git a/tests/multi-file/opaque_forward_decl/out/unsafe/opaque_forward_decl.rs b/tests/multi-file/opaque_forward_decl/out/unsafe/opaque_forward_decl.rs new file mode 100644 index 00000000..e708f3f2 --- /dev/null +++ b/tests/multi-file/opaque_forward_decl/out/unsafe/opaque_forward_decl.rs @@ -0,0 +1,36 @@ +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; +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct container { + pub p: *mut opaque, + pub x: i32, +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut c: container = container { + p: std::ptr::null_mut(), + x: 42, + }; + (unsafe { + let _c: *mut container = (&mut c as *mut container); + touch_0(_c) + }); + assert!(((((c.x) == (42)) as i32) != 0)); + assert!(((((c.p).is_null()) as i32) != 0)); + return 0; +} +pub unsafe fn touch_0(mut c: *mut container) { + &((*c).p); +} +pub struct opaque; diff --git a/tests/unit/opaque_forward_decl.c b/tests/unit/opaque_forward_decl.c new file mode 100644 index 00000000..d08cbfe2 --- /dev/null +++ b/tests/unit/opaque_forward_decl.c @@ -0,0 +1,14 @@ +#include + +struct opaque; + +struct container { + struct opaque *p; + int x; +}; + +int main(void) { + struct container c = {NULL, 42}; + (void)c.p; + return c.x - 42; +} diff --git a/tests/unit/opaque_then_defined.c b/tests/unit/opaque_then_defined.c new file mode 100644 index 00000000..659dbaaa --- /dev/null +++ b/tests/unit/opaque_then_defined.c @@ -0,0 +1,21 @@ +#include + +struct node; + +struct list { + struct node *head; + int size; +}; + +struct node { + int value; + struct node *next; +}; + +int main(void) { + struct node n = {42, 0}; + struct list l = {&n, 1}; + assert(l.head->value == 42); + assert(l.size == 1); + return 0; +} diff --git a/tests/unit/out/refcount/opaque_forward_decl.rs b/tests/unit/out/refcount/opaque_forward_decl.rs new file mode 100644 index 00000000..206844e8 --- /dev/null +++ b/tests/unit/out/refcount/opaque_forward_decl.rs @@ -0,0 +1,26 @@ +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 container { + pub p: Value>, + pub x: Value, +} +impl ByteRepr for container {} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let c: Value = Rc::new(RefCell::new(container { + p: Rc::new(RefCell::new(Ptr::::null())), + x: Rc::new(RefCell::new(42)), + })); + (*(*c.borrow()).p.borrow()).clone(); + return ((*(*c.borrow()).x.borrow()) - 42); +} +pub struct opaque; diff --git a/tests/unit/out/refcount/opaque_then_defined.rs b/tests/unit/out/refcount/opaque_then_defined.rs new file mode 100644 index 00000000..3ce92afb --- /dev/null +++ b/tests/unit/out/refcount/opaque_then_defined.rs @@ -0,0 +1,42 @@ +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 list { + pub head: Value>, + pub size: Value, +} +impl ByteRepr for list {} +#[derive(Default)] +pub struct node { + pub value: Value, + pub next: Value>, +} +impl ByteRepr for node {} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let n: Value = Rc::new(RefCell::new(node { + value: Rc::new(RefCell::new(42)), + next: Rc::new(RefCell::new(Ptr::::null())), + })); + let l: Value = Rc::new(RefCell::new(list { + head: Rc::new(RefCell::new((n.as_pointer()))), + size: Rc::new(RefCell::new(1)), + })); + assert!( + ((((*(*(*(*l.borrow()).head.borrow()).upgrade().deref()) + .value + .borrow()) + == 42) as i32) + != 0) + ); + assert!(((((*(*l.borrow()).size.borrow()) == 1) as i32) != 0)); + return 0; +} diff --git a/tests/unit/out/unsafe/opaque_forward_decl.rs b/tests/unit/out/unsafe/opaque_forward_decl.rs new file mode 100644 index 00000000..966d4ee6 --- /dev/null +++ b/tests/unit/out/unsafe/opaque_forward_decl.rs @@ -0,0 +1,28 @@ +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; +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct container { + pub p: *mut opaque, + pub x: i32, +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut c: container = container { + p: std::ptr::null_mut(), + x: 42, + }; + &(c.p); + return ((c.x) - (42)); +} +pub struct opaque; diff --git a/tests/unit/out/unsafe/opaque_then_defined.rs b/tests/unit/out/unsafe/opaque_then_defined.rs new file mode 100644 index 00000000..bdd386e0 --- /dev/null +++ b/tests/unit/out/unsafe/opaque_then_defined.rs @@ -0,0 +1,38 @@ +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; +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct list { + pub head: *mut node, + pub size: i32, +} +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct node { + pub value: i32, + pub next: *mut node, +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut n: node = node { + value: 42, + next: std::ptr::null_mut(), + }; + let mut l: list = list { + head: (&mut n as *mut node), + size: 1, + }; + assert!((((((*l.head).value) == (42)) as i32) != 0)); + assert!(((((l.size) == (1)) as i32) != 0)); + return 0; +}