Skip to content

Commit 69be664

Browse files
committed
Fix opaque then defined pattern
1 parent f46eae8 commit 69be664

8 files changed

Lines changed: 138 additions & 20 deletions

File tree

cpp2rust/converter/converter.cpp

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,10 @@
1919

2020
namespace cpp2rust {
2121
std::unordered_map<std::string, std::string> Converter::inner_structs_;
22-
std::unordered_set<std::string> Converter::record_decls_;
2322
std::unordered_set<std::string> Converter::decl_ids_;
2423
std::unordered_set<std::string> Converter::globals_;
2524
std::unordered_set<std::string> Converter::abstract_structs_;
26-
std::unordered_map<std::string, std::string> Converter::referenced_records_;
25+
Converter::RecordIndex Converter::record_decls_;
2726

2827
void Converter::ConvertUniquePtrDeref(clang::CXXOperatorCallExpr *expr) {
2928
bool is_star = expr->getOperator() == clang::OverloadedOperatorKind::OO_Star;
@@ -56,14 +55,11 @@ use std::rc::Rc;
5655

5756
std::string Converter::EmitOpaqueRecordMarkers() {
5857
std::string out;
59-
for (const auto &[id, name] : referenced_records_) {
60-
if (record_decls_.contains(id)) {
61-
continue;
62-
}
58+
record_decls_.ForEachUndefined([&](const std::string &name) {
6359
out += "#[repr(C)] pub struct ";
6460
out += name;
6561
out += " { _opaque: [u8; 0] }\n";
66-
}
62+
});
6763
return out;
6864
}
6965

@@ -177,7 +173,7 @@ bool Converter::VisitRecordType(clang::RecordType *type) {
177173
auto name = GetRecordName(decl);
178174
StrCat(name);
179175
if (!ctx_.getSourceManager().isInSystemHeader(decl->getLocation())) {
180-
referenced_records_.try_emplace(GetID(decl), std::move(name));
176+
record_decls_.MarkReferenced(std::move(name));
181177
}
182178
Mapper::AddRuleForUserDefinedType(decl);
183179
return false;
@@ -635,7 +631,7 @@ bool Converter::VisitRecordDecl(clang::RecordDecl *decl) {
635631
return false;
636632
}
637633

638-
if (!record_decls_.insert(GetID(decl)).second) {
634+
if (!record_decls_.MarkDefined(GetRecordName(decl))) {
639635
return false;
640636
}
641637

@@ -752,7 +748,7 @@ bool Converter::VisitCXXRecordDecl(clang::CXXRecordDecl *decl) {
752748
}
753749
}
754750

755-
if (!record_decls_.insert(GetID(decl)).second) {
751+
if (!record_decls_.MarkDefined(GetRecordName(decl))) {
756752
return false;
757753
}
758754

cpp2rust/converter/converter.h

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
5151

5252
virtual void EmitFilePreamble();
5353

54-
// Returns Rust text emitting `pub struct N { _opaque: [u8; 0] }` for each
55-
// record name referenced by some TU but never defined by any.
5654
static std::string EmitOpaqueRecordMarkers();
5755

5856
virtual bool VisitBuiltinType(clang::BuiltinType *type);
@@ -648,9 +646,37 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
648646
~ScopedMapIterDecl() { c.map_iter_decls_.erase(decl); }
649647
};
650648
static std::unordered_set<std::string> decl_ids_;
651-
static std::unordered_set<std::string> record_decls_;
652649
static std::unordered_set<std::string> abstract_structs_;
653-
static std::unordered_map<std::string, std::string> referenced_records_;
650+
651+
class RecordIndex {
652+
public:
653+
void MarkReferenced(std::string name) {
654+
entries_.try_emplace(std::move(name), false);
655+
}
656+
// Returns false if `name` is already defined; otherwise marks it and
657+
// returns true.
658+
bool MarkDefined(const std::string &name) {
659+
bool &defined = entries_[name];
660+
if (defined) {
661+
return false;
662+
}
663+
defined = true;
664+
return true;
665+
}
666+
template <typename F> void ForEachUndefined(F &&f) const {
667+
for (const auto &[name, defined] : entries_) {
668+
if (!defined) {
669+
f(name);
670+
}
671+
}
672+
}
673+
674+
private:
675+
// record name -> true if a definition has been emitted, false if only
676+
// referenced.
677+
std::unordered_map<std::string, bool> entries_;
678+
};
679+
static RecordIndex record_decls_;
654680

655681
enum class ExprKind : uint8_t {
656682
Callee,
Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
11
#include "header.h"
22

3-
void touch(struct container *c) {
4-
/* read the opaque pointer field without dereferencing the opaque type */
5-
(void)c->p;
6-
}
3+
void touch(struct container *c) { (void)c->p; }

tests/multi-file/opaque_forward_decl/header.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#pragma once
22

3-
/* Forward declaration only; no full definition exists anywhere in the build. */
43
struct opaque;
54

65
struct container {

tests/unit/opaque_forward_decl.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#include <stddef.h>
22

3-
/* Forward declaration only; no body for `opaque` anywhere in this TU. */
43
struct opaque;
54

65
struct container {

tests/unit/opaque_then_defined.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#include <assert.h>
2+
3+
struct node;
4+
5+
struct list {
6+
struct node *head;
7+
int size;
8+
};
9+
10+
struct node {
11+
int value;
12+
struct node *next;
13+
};
14+
15+
int main(void) {
16+
struct node n = {42, 0};
17+
struct list l = {&n, 1};
18+
assert(l.head->value == 42);
19+
assert(l.size == 1);
20+
return 0;
21+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
extern crate libcc2rs;
2+
use libcc2rs::*;
3+
use std::cell::RefCell;
4+
use std::collections::BTreeMap;
5+
use std::io::prelude::*;
6+
use std::io::{Read, Seek, Write};
7+
use std::os::fd::AsFd;
8+
use std::rc::{Rc, Weak};
9+
#[derive(Default)]
10+
pub struct list {
11+
pub head: Value<Ptr<node>>,
12+
pub size: Value<i32>,
13+
}
14+
impl ByteRepr for list {}
15+
#[derive(Default)]
16+
pub struct node {
17+
pub value: Value<i32>,
18+
pub next: Value<Ptr<node>>,
19+
}
20+
impl ByteRepr for node {}
21+
pub fn main() {
22+
std::process::exit(main_0());
23+
}
24+
fn main_0() -> i32 {
25+
let n: Value<node> = Rc::new(RefCell::new(node {
26+
value: Rc::new(RefCell::new(42)),
27+
next: Rc::new(RefCell::new(Ptr::<node>::null())),
28+
}));
29+
let l: Value<list> = Rc::new(RefCell::new(list {
30+
head: Rc::new(RefCell::new((n.as_pointer()))),
31+
size: Rc::new(RefCell::new(1)),
32+
}));
33+
assert!(
34+
((((*(*(*(*l.borrow()).head.borrow()).upgrade().deref())
35+
.value
36+
.borrow())
37+
== 42) as i32)
38+
!= 0)
39+
);
40+
assert!(((((*(*l.borrow()).size.borrow()) == 1) as i32) != 0));
41+
return 0;
42+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
extern crate libc;
2+
use libc::*;
3+
extern crate libcc2rs;
4+
use libcc2rs::*;
5+
use std::collections::BTreeMap;
6+
use std::io::{Read, Seek, Write};
7+
use std::os::fd::{AsFd, FromRawFd, IntoRawFd};
8+
use std::rc::Rc;
9+
#[repr(C)]
10+
#[derive(Copy, Clone, Default)]
11+
pub struct list {
12+
pub head: *mut node,
13+
pub size: i32,
14+
}
15+
#[repr(C)]
16+
#[derive(Copy, Clone, Default)]
17+
pub struct node {
18+
pub value: i32,
19+
pub next: *mut node,
20+
}
21+
pub fn main() {
22+
unsafe {
23+
std::process::exit(main_0() as i32);
24+
}
25+
}
26+
unsafe fn main_0() -> i32 {
27+
let mut n: node = node {
28+
value: 42,
29+
next: std::ptr::null_mut(),
30+
};
31+
let mut l: list = list {
32+
head: (&mut n as *mut node),
33+
size: 1,
34+
};
35+
assert!((((((*l.head).value) == (42)) as i32) != 0));
36+
assert!(((((l.size) == (1)) as i32) != 0));
37+
return 0;
38+
}

0 commit comments

Comments
 (0)