Skip to content

Commit d424cd1

Browse files
lucic71nunoplopes
andauthored
Avoid collision between tag and typedef identifier (#166)
C allows name collision between the following 2 declarations: ```c struct X {}; typedef enum {} X; ``` The 2 declarations live in different name spaces: 6.2.3 Name spaces of identifiers [...] there are separate namespaces for various categories of identifiers, as follows: 1. the tags of structures, unions, and enumerations (disambiguated by following any) of the keywords struct, union, or enum); 2. all other identifiers, called ordinary identifiers (declared in ordinary declarators or as enumeration constants). `struct X {}` lives in namespace 1 and `typedef enum {} X` lives in namespace 2. We cannot translate both to `X` in the Rust code. We need to disambiguate between the 2 names. I chose to translate them as: ``` struct X {} -> X typedef enum {} X -> X_enum ``` C++ does not have the name space rule for identifiers. I added the `tag->getASTContext().getLangOpts().CPlusPlus` check in DisambiguateAnonymousTag to avoid polluting the C++ generated files. --------- Co-authored-by: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
1 parent db0d23f commit d424cd1

18 files changed

Lines changed: 496 additions & 111 deletions

cpp2rust/converter/converter_lib.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,31 @@ std::string GetID(const clang::Decl *decl) {
358358
return GetLocationID(decl) + GetParamSignature(decl);
359359
}
360360

361+
std::string DisambiguateAnonymousTag(const clang::TagDecl *tag) {
362+
if (!tag) {
363+
return "";
364+
}
365+
// C++ does not allow collision between tags and typedef identifiers.
366+
if (tag->getASTContext().getLangOpts().CPlusPlus) {
367+
return "";
368+
}
369+
// Tag has an identifier, nothing to disambiguate.
370+
if (tag->getIdentifier()) {
371+
return "";
372+
}
373+
// The anonymous decl is named through a typedef; guards getName() below.
374+
auto typedef_decl = tag->getTypedefNameForAnonDecl();
375+
if (!typedef_decl || !typedef_decl->getDeclName().isIdentifier()) {
376+
return "";
377+
}
378+
// Only disambiguate user-defined types.
379+
if (tag->getASTContext().getSourceManager().isInSystemHeader(
380+
tag->getLocation())) {
381+
return "";
382+
}
383+
return typedef_decl->getName().str() + '_' + tag->getKindName().str();
384+
}
385+
361386
static std::unordered_map<std::string, size_t> type_mapping;
362387

363388
static size_t GetDeclId(const clang::NamedDecl *decl, bool internal) {

cpp2rust/converter/converter_lib.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ std::string GetID(const clang::Decl *decl);
8989

9090
std::string GetNamedDeclAsString(const clang::NamedDecl *decl);
9191

92+
std::string DisambiguateAnonymousTag(const clang::TagDecl *tag);
93+
9294
const char *AccessSpecifierAsString(clang::AccessSpecifier spec);
9395

9496
template <class T> llvm::SmallString<16> GetNumAsString(const T &num) {

cpp2rust/converter/mapper.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,11 @@ std::string ToString(clang::QualType qual_type) {
736736
return ToString(clang::cast<clang::NamedDecl>(tag));
737737
}
738738

739+
if (auto renamed = DisambiguateAnonymousTag(qual_type->getAsTagDecl());
740+
!renamed.empty()) {
741+
return renamed;
742+
}
743+
739744
std::string type;
740745
llvm::raw_string_ostream os(type);
741746
normalizeQualType(qual_type).print(os, getPrintPolicy());
@@ -745,16 +750,23 @@ std::string ToString(clang::QualType qual_type) {
745750
std::string ToString(const clang::NamedDecl *decl) {
746751
if (auto *record = clang::dyn_cast<clang::RecordDecl>(decl);
747752
record && !record->getIdentifier()) {
753+
if (auto renamed = DisambiguateAnonymousTag(record); !renamed.empty()) {
754+
return renamed;
755+
}
748756
if (auto *typedef_decl = record->getTypedefNameForAnonDecl()) {
749757
return ToString(clang::cast<clang::NamedDecl>(typedef_decl));
750758
}
751759
return GetNamedDeclAsString(record);
752760
}
753761

754-
if (auto *enum_decl = clang::dyn_cast<clang::EnumDecl>(decl);
755-
enum_decl && !enum_decl->getIdentifier() &&
756-
!enum_decl->getTypedefNameForAnonDecl()) {
757-
return std::format("anon_enum_{}", GetLineNumber(enum_decl));
762+
if (auto *enum_decl = clang::dyn_cast<clang::EnumDecl>(decl)) {
763+
if (auto renamed = DisambiguateAnonymousTag(enum_decl); !renamed.empty()) {
764+
return renamed;
765+
}
766+
if (!enum_decl->getIdentifier() &&
767+
!enum_decl->getTypedefNameForAnonDecl()) {
768+
return std::format("anon_enum_{}", GetLineNumber(enum_decl));
769+
}
758770
}
759771

760772
std::string out;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
cmake_minimum_required(VERSION 3.16)
2+
project(cross_tu_tag_collision LANGUAGES C)
3+
add_executable(app a.c b.c)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#include <assert.h>
2+
3+
struct widget {
4+
int id;
5+
};
6+
7+
int b_value(void);
8+
9+
int a_value(void) {
10+
struct widget w;
11+
w.id = 11;
12+
return w.id;
13+
}
14+
15+
int main(void) {
16+
assert(a_value() == 11);
17+
assert(b_value() == 2);
18+
return 0;
19+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
typedef enum { WIDGET_A, WIDGET_B, WIDGET_C } widget;
2+
3+
int b_value(void) {
4+
widget w = WIDGET_C;
5+
return (int)w;
6+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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 widget {
11+
pub id: Value<i32>,
12+
}
13+
impl ByteRepr for widget {
14+
fn to_bytes(&self, buf: &mut [u8]) {
15+
(*self.id.borrow()).to_bytes(&mut buf[0..4]);
16+
}
17+
fn from_bytes(buf: &[u8]) -> Self {
18+
Self {
19+
id: Rc::new(RefCell::new(<i32>::from_bytes(&buf[0..4]))),
20+
}
21+
}
22+
}
23+
pub fn a_value_0() -> i32 {
24+
let w: Value<widget> = <Value<widget>>::default();
25+
(*(*w.borrow()).id.borrow_mut()) = 11;
26+
return (*(*w.borrow()).id.borrow());
27+
}
28+
pub fn main() {
29+
std::process::exit(main_0());
30+
}
31+
fn main_0() -> i32 {
32+
assert!((((({ a_value_0() }) == 11) as i32) != 0));
33+
assert!((((({ b_value_1() }) == 2) as i32) != 0));
34+
return 0;
35+
}
36+
#[derive(Clone, Copy, PartialEq, Debug, Default)]
37+
enum widget_enum {
38+
#[default]
39+
WIDGET_A = 0,
40+
WIDGET_B = 1,
41+
WIDGET_C = 2,
42+
}
43+
impl From<i32> for widget_enum {
44+
fn from(n: i32) -> widget_enum {
45+
match n {
46+
0 => widget_enum::WIDGET_A,
47+
1 => widget_enum::WIDGET_B,
48+
2 => widget_enum::WIDGET_C,
49+
_ => panic!("invalid widget_enum value: {}", n),
50+
}
51+
}
52+
}
53+
libcc2rs::impl_enum_inc_dec!(widget_enum);
54+
pub fn b_value_1() -> i32 {
55+
let w: Value<widget_enum> = Rc::new(RefCell::new(widget_enum::WIDGET_C));
56+
return ((*w.borrow()) as i32).clone();
57+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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 widget {
12+
pub id: i32,
13+
}
14+
pub unsafe fn a_value_0() -> i32 {
15+
let mut w: widget = <widget>::default();
16+
w.id = 11;
17+
return w.id;
18+
}
19+
pub fn main() {
20+
unsafe {
21+
std::process::exit(main_0() as i32);
22+
}
23+
}
24+
unsafe fn main_0() -> i32 {
25+
assert!(((((unsafe { a_value_0() }) == (11)) as i32) != 0));
26+
assert!(((((unsafe { b_value_1() }) == (2)) as i32) != 0));
27+
return 0;
28+
}
29+
#[derive(Clone, Copy, PartialEq, Debug, Default)]
30+
enum widget_enum {
31+
#[default]
32+
WIDGET_A = 0,
33+
WIDGET_B = 1,
34+
WIDGET_C = 2,
35+
}
36+
impl From<i32> for widget_enum {
37+
fn from(n: i32) -> widget_enum {
38+
match n {
39+
0 => widget_enum::WIDGET_A,
40+
1 => widget_enum::WIDGET_B,
41+
2 => widget_enum::WIDGET_C,
42+
_ => panic!("invalid widget_enum value: {}", n),
43+
}
44+
}
45+
}
46+
libcc2rs::impl_enum_inc_dec!(widget_enum);
47+
pub unsafe fn b_value_1() -> i32 {
48+
let mut w: widget_enum = widget_enum::WIDGET_C;
49+
return (w as i32);
50+
}

tests/unit/out/refcount/anonymous_enum_c.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,21 +53,21 @@ impl ByteRepr for S {
5353
}
5454
}
5555
#[derive(Clone, Copy, PartialEq, Debug, Default)]
56-
enum TdEnum {
56+
enum TdEnum_enum {
5757
#[default]
5858
TD_A = 0,
5959
TD_B = 1,
6060
}
61-
impl From<i32> for TdEnum {
62-
fn from(n: i32) -> TdEnum {
61+
impl From<i32> for TdEnum_enum {
62+
fn from(n: i32) -> TdEnum_enum {
6363
match n {
64-
0 => TdEnum::TD_A,
65-
1 => TdEnum::TD_B,
66-
_ => panic!("invalid TdEnum value: {}", n),
64+
0 => TdEnum_enum::TD_A,
65+
1 => TdEnum_enum::TD_B,
66+
_ => panic!("invalid TdEnum_enum value: {}", n),
6767
}
6868
}
6969
}
70-
libcc2rs::impl_enum_inc_dec!(TdEnum);
70+
libcc2rs::impl_enum_inc_dec!(TdEnum_enum);
7171
#[derive(Clone, Copy, PartialEq, Debug, Default)]
7272
enum anon_enum_24 {
7373
#[default]
@@ -113,10 +113,10 @@ fn main_0() -> i32 {
113113
assert!(((((anon_enum_3::FIRST_A as i32) != (anon_enum_3::FIRST_B as i32)) as i32) != 0));
114114
assert!(((((anon_enum_11::SECOND_A as i32) != (anon_enum_11::SECOND_B as i32)) as i32) != 0));
115115
assert!(((((anon_enum_31::THIRD_A as i32) != (anon_enum_31::THIRD_B as i32)) as i32) != 0));
116-
let td: Value<TdEnum> = Rc::new(RefCell::new(TdEnum::TD_A));
117-
assert!((((((*td.borrow()) as u32) == ((TdEnum::TD_A as i32) as u32)) as i32) != 0));
118-
(*td.borrow_mut()) = TdEnum::TD_B;
119-
assert!((((((*td.borrow()) as u32) == ((TdEnum::TD_B as i32) as u32)) as i32) != 0));
116+
let td: Value<TdEnum_enum> = Rc::new(RefCell::new(TdEnum_enum::TD_A));
117+
assert!((((((*td.borrow()) as u32) == ((TdEnum_enum::TD_A as i32) as u32)) as i32) != 0));
118+
(*td.borrow_mut()) = TdEnum_enum::TD_B;
119+
assert!((((((*td.borrow()) as u32) == ((TdEnum_enum::TD_B as i32) as u32)) as i32) != 0));
120120
let w: Value<WithAnonField> = <Value<WithAnonField>>::default();
121121
(*(*w.borrow()).field.borrow_mut()) = anon_enum_24::FIELD_A;
122122
assert!(

tests/unit/out/refcount/enum_int_interop_c.rs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -45,23 +45,23 @@ impl From<i32> for Option {
4545
}
4646
libcc2rs::impl_enum_inc_dec!(Option);
4747
#[derive(Clone, Copy, PartialEq, Debug, Default)]
48-
enum Tag {
48+
enum Tag_enum {
4949
#[default]
5050
TAG_ZERO = 0,
5151
TAG_ONE = 1,
5252
TAG_TWO = 2,
5353
}
54-
impl From<i32> for Tag {
55-
fn from(n: i32) -> Tag {
54+
impl From<i32> for Tag_enum {
55+
fn from(n: i32) -> Tag_enum {
5656
match n {
57-
0 => Tag::TAG_ZERO,
58-
1 => Tag::TAG_ONE,
59-
2 => Tag::TAG_TWO,
60-
_ => panic!("invalid Tag value: {}", n),
57+
0 => Tag_enum::TAG_ZERO,
58+
1 => Tag_enum::TAG_ONE,
59+
2 => Tag_enum::TAG_TWO,
60+
_ => panic!("invalid Tag_enum value: {}", n),
6161
}
6262
}
6363
}
64-
libcc2rs::impl_enum_inc_dec!(Tag);
64+
libcc2rs::impl_enum_inc_dec!(Tag_enum);
6565
#[derive(Default)]
6666
pub struct Entry {
6767
pub name: Value<Ptr<u8>>,
@@ -76,7 +76,7 @@ thread_local!(
7676
pub static global_opt_1: Value<Option> = Rc::new(RefCell::new(Option::OPT_B));
7777
);
7878
thread_local!(
79-
pub static global_tag_2: Value<Tag> = Rc::new(RefCell::new(Tag::TAG_TWO));
79+
pub static global_tag_2: Value<Tag_enum> = Rc::new(RefCell::new(Tag_enum::TAG_TWO));
8080
);
8181
thread_local!(
8282
pub static entries_3: Value<Box<[Entry]>> = Rc::new(RefCell::new(Box::new([
@@ -199,17 +199,17 @@ fn main_0() -> i32 {
199199
classify_option_5(_option)
200200
});
201201
assert!(((((*rc.borrow()) == 3) as i32) != 0));
202-
let t: Value<Tag> = Rc::new(RefCell::new(Tag::TAG_ONE));
202+
let t: Value<Tag_enum> = Rc::new(RefCell::new(Tag_enum::TAG_ONE));
203203
assert!((((((*t.borrow()) as u32) == 1_u32) as i32) != 0));
204-
assert!((((((*t.borrow()) as u32) == ((Tag::TAG_ONE as i32) as u32)) as i32) != 0));
204+
assert!((((((*t.borrow()) as u32) == ((Tag_enum::TAG_ONE as i32) as u32)) as i32) != 0));
205205
let ti: Value<i32> = Rc::new(RefCell::new(((*t.borrow()) as i32).clone()));
206206
assert!(((((*ti.borrow()) == 1) as i32) != 0));
207-
(*t.borrow_mut()) = Tag::from(2);
208-
assert!((((((*t.borrow()) as u32) == ((Tag::TAG_TWO as i32) as u32)) as i32) != 0));
207+
(*t.borrow_mut()) = Tag_enum::from(2);
208+
assert!((((((*t.borrow()) as u32) == ((Tag_enum::TAG_TWO as i32) as u32)) as i32) != 0));
209209
'switch: {
210210
let __match_cond = ((*t.borrow()) as u32);
211211
match __match_cond {
212-
v if v == ((Tag::TAG_ZERO as i32) as u32) => {
212+
v if v == ((Tag_enum::TAG_ZERO as i32) as u32) => {
213213
return 90;
214214
}
215215
v if v == (1 as u32) => {
@@ -236,8 +236,8 @@ fn main_0() -> i32 {
236236
!= 0)
237237
);
238238
assert!(
239-
(((((*global_tag_2.with(Value::clone).borrow()) as u32) == ((Tag::TAG_TWO as i32) as u32))
240-
as i32)
239+
(((((*global_tag_2.with(Value::clone).borrow()) as u32)
240+
== ((Tag_enum::TAG_TWO as i32) as u32)) as i32)
241241
!= 0)
242242
);
243243
assert!(

0 commit comments

Comments
 (0)