Skip to content

Commit 4b05f90

Browse files
authored
Translate C++ enums as Rust integers (#325)
The previous translation, C++ enums -> Rust enums had two weak points: * it did not allow enums that have equal variants, for example `enum E { A = 1, B = A }` * operations on the enum could not be used in const context, for example: `int global = E::A != E::B`. `!=` for enums is not const so it cannot be used in const expressions like initializing a global variable The new translation does C++ enums -> Rust integers and the name of the values becomes `E_A` instead of `E::A`.
1 parent 5c99fd1 commit 4b05f90

61 files changed

Lines changed: 806 additions & 2125 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.

‎cpp2rust/converter/converter.cpp‎

Lines changed: 23 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2070,25 +2070,24 @@ bool Converter::VisitCXXBoolLiteralExpr(clang::CXXBoolLiteralExpr *expr) {
20702070

20712071
void Converter::ConvertIntegerToEnumeralCast(clang::Expr *to,
20722072
clang::Expr *from) {
2073-
// Short circuit `Enum::from(X as i32)` to `X`
2073+
// Short circuit `(X as i32) as Enum` to `X`
20742074
if (auto ref =
20752075
clang::dyn_cast<clang::DeclRefExpr>(from->IgnoreParenImpCasts())) {
20762076
if (auto ec = clang::dyn_cast<clang::EnumConstantDecl>(ref->getDecl())) {
20772077
auto src_enum = clang::dyn_cast<clang::EnumDecl>(ec->getDeclContext());
20782078
auto dst_enum = to->getType()->getAs<clang::EnumType>();
20792079
if (src_enum && dst_enum && dst_enum->getDecl() == src_enum) {
2080-
StrCat(std::format("{}::{}", GetRecordName(src_enum),
2081-
std::string_view(ec->getName())));
2080+
StrCat(EnumeratorName(ec));
20822081
return;
20832082
}
20842083
}
20852084
}
2086-
StrCat(GetUnsafeTypeAsString(to->getType()), "::from");
20872085
PushParen paren(*this);
2088-
Convert(from);
2089-
if (!from->getType()->isSpecificBuiltinType(clang::BuiltinType::Int)) {
2090-
StrCat(keyword::kAs, "i32");
2086+
{
2087+
PushParen inner(*this);
2088+
Convert(from);
20912089
}
2090+
StrCat(keyword::kAs, GetUnsafeTypeAsString(to->getType()));
20922091
}
20932092

20942093
void Converter::ConvertIntegralToBooleanCast(clang::ImplicitCastExpr *expr) {
@@ -2107,11 +2106,7 @@ void Converter::ConvertIntegralToBooleanCast(clang::ImplicitCastExpr *expr) {
21072106
PushParen paren(*this);
21082107
Convert(sub_expr);
21092108
StrCat(token::kDiff);
2110-
if (sub_expr->getType()->isEnumeralType()) {
2111-
StrCat(GetUnsafeTypeAsString(sub_expr->getType()), "::from(0)");
2112-
} else /* sub_expr->getType()->isIntegerType() */ {
2113-
StrCat(token::kZero);
2114-
}
2109+
StrCat(token::kZero);
21152110
}
21162111

21172112
bool Converter::IsCastRedundantInRust(clang::Expr *expr,
@@ -2657,14 +2652,11 @@ std::string Converter::ConvertDeclRefExpr(clang::DeclRefExpr *expr) {
26572652
}
26582653

26592654
if (auto enum_constant = clang::dyn_cast<clang::EnumConstantDecl>(decl)) {
2660-
auto qualified = std::format("{}::{}",
2661-
GetRecordName(clang::dyn_cast<clang::EnumDecl>(
2662-
enum_constant->getDeclContext())),
2663-
std::string_view(enum_constant->getName()));
2655+
auto name = EnumeratorName(enum_constant);
26642656
if (!expr->getType()->isEnumeralType()) {
2665-
return std::format("({} as i32)", qualified);
2657+
return std::format("({} as i32)", name);
26662658
}
2667-
return qualified;
2659+
return name;
26682660
}
26692661

26702662
if (IsGlobalVar(expr)) {
@@ -3237,50 +3229,23 @@ bool Converter::VisitEnumDecl(clang::EnumDecl *decl) {
32373229
return false;
32383230
}
32393231
Mapper::AddRuleForUserDefinedType(decl);
3240-
Mapper::SetDerives(ctx_.getCanonicalTagType(decl),
3241-
{"Clone", "Copy", "PartialEq", "Debug", "Default"});
3242-
StrCat("#[derive(Clone, Copy, PartialEq, Debug, Default)]");
3243-
StrCat(std::format("enum {}", GetRecordName(decl)));
3244-
StrCat('{');
3245-
bool first_enumerator = true;
3232+
auto name = GetRecordName(decl);
3233+
StrCat(std::format("pub type {} = {};", name,
3234+
GetUnsafeTypeAsString(decl->getIntegerType())));
32463235
for (auto e : decl->enumerators()) {
32473236
llvm::SmallVector<char, 32> init;
32483237
e->getInitVal().toString(init, 10);
3249-
if (first_enumerator) {
3250-
StrCat("#[default]");
3251-
first_enumerator = false;
3252-
}
3253-
StrCat(std::format("{} = {},", std::string_view(e->getName()),
3238+
StrCat(std::format("pub const {}: {} = {};", EnumeratorName(e), name,
32543239
std::string_view(init.data(), init.size())));
32553240
}
3256-
StrCat('}');
3257-
3258-
AddFromImpl(decl);
3259-
AddIncDecImpls(decl);
3260-
AddByteReprTrait(decl);
32613241
return false;
32623242
}
32633243

3264-
void Converter::AddFromImpl(clang::EnumDecl *decl) {
3265-
auto name = GetRecordName(decl);
3266-
StrCat(std::format("impl From<i32> for {}", name));
3267-
PushBrace impl(*this);
3268-
StrCat(std::format("fn from(n: i32) -> {}", name));
3269-
PushBrace fn(*this);
3270-
StrCat("match n");
3271-
PushBrace match(*this);
3272-
for (auto e : decl->enumerators()) {
3273-
llvm::SmallVector<char, 32> init;
3274-
e->getInitVal().toString(init, 10);
3275-
StrCat(std::format("{} => {}::{},",
3276-
std::string_view(init.data(), init.size()), name,
3277-
std::string_view(e->getName())));
3278-
}
3279-
StrCat(std::format("_ => panic!(\"invalid {} value: {{}}\", n),", name));
3280-
}
3281-
3282-
void Converter::AddIncDecImpls(clang::EnumDecl *decl) {
3283-
StrCat(std::format("libcc2rs::impl_enum_inc_dec!({});", GetRecordName(decl)));
3244+
std::string
3245+
Converter::EnumeratorName(const clang::EnumConstantDecl *decl) const {
3246+
auto *enum_decl = clang::cast<clang::EnumDecl>(decl->getDeclContext());
3247+
return std::format("{}_{}", GetRecordName(enum_decl),
3248+
std::string_view(decl->getName()));
32843249
}
32853250

32863251
bool Converter::VisitCXXDefaultArgExpr(clang::CXXDefaultArgExpr *expr) {
@@ -3570,9 +3535,10 @@ std::string Converter::GetDefaultAsStringFallback(clang::QualType qual_type) {
35703535

35713536
if (qual_type->isEnumeralType()) {
35723537
auto enum_decl = qual_type->castAs<clang::EnumType>()->getDecl();
3573-
return std::format(
3574-
"{}::{}", GetRecordName(enum_decl),
3575-
std::string_view(enum_decl->enumerator_begin()->getName()));
3538+
if (enum_decl->enumerators().empty()) {
3539+
return std::string(1, token::kZero);
3540+
}
3541+
return EnumeratorName(*enum_decl->enumerator_begin());
35763542
}
35773543

35783544
return std::format("<{}>::default()", ToString(qual_type));
@@ -4049,8 +4015,6 @@ void Converter::EmitDefaultStructLiteral(const clang::RecordDecl *decl) {
40494015

40504016
void Converter::AddByteReprTrait(const clang::RecordDecl *decl) {}
40514017

4052-
void Converter::AddByteReprTrait(const clang::EnumDecl *decl) {}
4053-
40544018
void Converter::ConvertUnsignedArithBinaryOperator(clang::BinaryOperator *op,
40554019
clang::Expr *expr) {
40564020
StrCat(token::kDot);

‎cpp2rust/converter/converter.h‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -374,9 +374,7 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
374374

375375
virtual bool VisitEnumDecl(clang::EnumDecl *decl);
376376

377-
virtual void AddFromImpl(clang::EnumDecl *decl);
378-
379-
virtual void AddIncDecImpls(clang::EnumDecl *decl);
377+
virtual std::string EnumeratorName(const clang::EnumConstantDecl *decl) const;
380378

381379
virtual bool VisitCXXDefaultArgExpr(clang::CXXDefaultArgExpr *expr);
382380

@@ -560,8 +558,6 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
560558

561559
virtual void AddByteReprTrait(const clang::RecordDecl *decl);
562560

563-
virtual void AddByteReprTrait(const clang::EnumDecl *decl);
564-
565561
virtual void
566562
ConvertUnsignedArithBinaryOperator(clang::BinaryOperator *binary_operator,
567563
clang::Expr *expr);

‎cpp2rust/converter/models/converter_refcount.cpp‎

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -654,17 +654,6 @@ void ConverterRefCount::AddByteReprTrait(const clang::RecordDecl *decl) {
654654
}
655655
}
656656

657-
void ConverterRefCount::AddByteReprTrait(const clang::EnumDecl *decl) {
658-
auto name = GetRecordName(decl);
659-
StrCat(std::format("impl ByteRepr for {}", name));
660-
PushBrace impl_brace(*this);
661-
StrCat(
662-
"fn to_bytes(&self, buf: &mut [u8]) { (*self as i32).to_bytes(buf); }");
663-
StrCat(std::format("fn from_bytes(buf: &[u8]) -> Self {{ "
664-
"<{}>::from(i32::from_bytes(buf)) }}",
665-
name));
666-
}
667-
668657
std::string
669658
ConverterRefCount::GetSelfMaybeWithMut(const clang::CXXMethodDecl *decl) {
670659
return "&self";

‎cpp2rust/converter/models/converter_refcount.h‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@ class ConverterRefCount final : public Converter {
4545

4646
void AddByteReprTrait(const clang::RecordDecl *decl) override;
4747

48-
void AddByteReprTrait(const clang::EnumDecl *decl) override;
49-
5048
bool
5149
VisitUnaryExprOrTypeTraitExpr(clang::UnaryExprOrTypeTraitExpr *expr) override;
5250

‎libcc2rs/src/inc.rs‎

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -124,38 +124,6 @@ impl<T> UnsafePrefixInc for *mut T {
124124
}
125125
}
126126

127-
#[macro_export]
128-
macro_rules! impl_enum_inc_dec {
129-
($t:ty) => {
130-
impl PostfixInc for $t {
131-
fn postfix_inc(&mut self) -> Self {
132-
let copy = *self;
133-
*self = <$t>::from((*self as i32).wrapping_add(1));
134-
copy
135-
}
136-
}
137-
impl PrefixInc for $t {
138-
fn prefix_inc(&mut self) -> Self {
139-
*self = <$t>::from((*self as i32).wrapping_add(1));
140-
*self
141-
}
142-
}
143-
impl PostfixDec for $t {
144-
fn postfix_dec(&mut self) -> Self {
145-
let copy = *self;
146-
*self = <$t>::from((*self as i32).wrapping_sub(1));
147-
copy
148-
}
149-
}
150-
impl PrefixDec for $t {
151-
fn prefix_dec(&mut self) -> Self {
152-
*self = <$t>::from((*self as i32).wrapping_sub(1));
153-
*self
154-
}
155-
}
156-
};
157-
}
158-
159127
#[cfg(test)]
160128
mod tests {
161129
use super::*;

‎tests/multi-file/cross_tu_anon_enum_collision/out/refcount/cross_tu_anon_enum_collision.rs‎

Lines changed: 6 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,11 @@ use std::io::prelude::*;
66
use std::io::{Read, Seek, Write};
77
use std::os::fd::AsFd;
88
use std::rc::{Rc, Weak};
9-
#[derive(Clone, Copy, PartialEq, Debug, Default)]
10-
enum anon_0 {
11-
#[default]
12-
ALPHA = 7,
13-
}
14-
impl From<i32> for anon_0 {
15-
fn from(n: i32) -> anon_0 {
16-
match n {
17-
7 => anon_0::ALPHA,
18-
_ => panic!("invalid anon_0 value: {}", n),
19-
}
20-
}
21-
}
22-
libcc2rs::impl_enum_inc_dec!(anon_0);
23-
impl ByteRepr for anon_0 {
24-
fn to_bytes(&self, buf: &mut [u8]) {
25-
(*self as i32).to_bytes(buf);
26-
}
27-
fn from_bytes(buf: &[u8]) -> Self {
28-
<anon_0>::from(i32::from_bytes(buf))
29-
}
30-
}
9+
pub type anon_0 = u32;
10+
pub const anon_0_ALPHA: anon_0 = 7;
3111
pub fn a_value_1() -> i32 {
3212
let x: Value<i32> = Rc::new(RefCell::new(0));
33-
(*x.borrow_mut()) |= (anon_0::ALPHA as i32);
13+
(*x.borrow_mut()) |= (anon_0_ALPHA as i32);
3414
return (*x.borrow());
3515
}
3616
pub fn main() {
@@ -41,30 +21,10 @@ fn main_0() -> i32 {
4121
assert!((((({ b_value_2() }) == 9) as i32) != 0));
4222
return 0;
4323
}
44-
#[derive(Clone, Copy, PartialEq, Debug, Default)]
45-
enum anon_3 {
46-
#[default]
47-
BETA = 9,
48-
}
49-
impl From<i32> for anon_3 {
50-
fn from(n: i32) -> anon_3 {
51-
match n {
52-
9 => anon_3::BETA,
53-
_ => panic!("invalid anon_3 value: {}", n),
54-
}
55-
}
56-
}
57-
libcc2rs::impl_enum_inc_dec!(anon_3);
58-
impl ByteRepr for anon_3 {
59-
fn to_bytes(&self, buf: &mut [u8]) {
60-
(*self as i32).to_bytes(buf);
61-
}
62-
fn from_bytes(buf: &[u8]) -> Self {
63-
<anon_3>::from(i32::from_bytes(buf))
64-
}
65-
}
24+
pub type anon_3 = u32;
25+
pub const anon_3_BETA: anon_3 = 9;
6626
pub fn b_value_2() -> i32 {
6727
let x: Value<i32> = Rc::new(RefCell::new(0));
68-
(*x.borrow_mut()) |= (anon_3::BETA as i32);
28+
(*x.borrow_mut()) |= (anon_3_BETA as i32);
6929
return (*x.borrow());
7030
}

‎tests/multi-file/cross_tu_anon_enum_collision/out/unsafe/cross_tu_anon_enum_collision.rs‎

Lines changed: 6 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,11 @@ use std::collections::BTreeMap;
66
use std::io::{Read, Seek, Write};
77
use std::os::fd::{AsFd, FromRawFd, IntoRawFd};
88
use std::rc::Rc;
9-
#[derive(Clone, Copy, PartialEq, Debug, Default)]
10-
enum anon_0 {
11-
#[default]
12-
ALPHA = 7,
13-
}
14-
impl From<i32> for anon_0 {
15-
fn from(n: i32) -> anon_0 {
16-
match n {
17-
7 => anon_0::ALPHA,
18-
_ => panic!("invalid anon_0 value: {}", n),
19-
}
20-
}
21-
}
22-
libcc2rs::impl_enum_inc_dec!(anon_0);
9+
pub type anon_0 = u32;
10+
pub const anon_0_ALPHA: anon_0 = 7;
2311
pub unsafe fn a_value_1() -> i32 {
2412
let mut x: i32 = 0;
25-
x |= (anon_0::ALPHA as i32);
13+
x |= (anon_0_ALPHA as i32);
2614
return x;
2715
}
2816
pub fn main() {
@@ -35,22 +23,10 @@ unsafe fn main_0() -> i32 {
3523
assert!(((((unsafe { b_value_2() }) == (9)) as i32) != 0));
3624
return 0;
3725
}
38-
#[derive(Clone, Copy, PartialEq, Debug, Default)]
39-
enum anon_3 {
40-
#[default]
41-
BETA = 9,
42-
}
43-
impl From<i32> for anon_3 {
44-
fn from(n: i32) -> anon_3 {
45-
match n {
46-
9 => anon_3::BETA,
47-
_ => panic!("invalid anon_3 value: {}", n),
48-
}
49-
}
50-
}
51-
libcc2rs::impl_enum_inc_dec!(anon_3);
26+
pub type anon_3 = u32;
27+
pub const anon_3_BETA: anon_3 = 9;
5228
pub unsafe fn b_value_2() -> i32 {
5329
let mut x: i32 = 0;
54-
x |= (anon_3::BETA as i32);
30+
x |= (anon_3_BETA as i32);
5531
return x;
5632
}

‎tests/multi-file/cross_tu_tag_collision/out/refcount/cross_tu_tag_collision.rs‎

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -43,33 +43,11 @@ fn main_0() -> i32 {
4343
assert!((((({ b_value_1() }) == 2) as i32) != 0));
4444
return 0;
4545
}
46-
#[derive(Clone, Copy, PartialEq, Debug, Default)]
47-
enum widget_enum {
48-
#[default]
49-
WIDGET_A = 0,
50-
WIDGET_B = 1,
51-
WIDGET_C = 2,
52-
}
53-
impl From<i32> for widget_enum {
54-
fn from(n: i32) -> widget_enum {
55-
match n {
56-
0 => widget_enum::WIDGET_A,
57-
1 => widget_enum::WIDGET_B,
58-
2 => widget_enum::WIDGET_C,
59-
_ => panic!("invalid widget_enum value: {}", n),
60-
}
61-
}
62-
}
63-
libcc2rs::impl_enum_inc_dec!(widget_enum);
64-
impl ByteRepr for widget_enum {
65-
fn to_bytes(&self, buf: &mut [u8]) {
66-
(*self as i32).to_bytes(buf);
67-
}
68-
fn from_bytes(buf: &[u8]) -> Self {
69-
<widget_enum>::from(i32::from_bytes(buf))
70-
}
71-
}
46+
pub type widget_enum = u32;
47+
pub const widget_enum_WIDGET_A: widget_enum = 0;
48+
pub const widget_enum_WIDGET_B: widget_enum = 1;
49+
pub const widget_enum_WIDGET_C: widget_enum = 2;
7250
pub fn b_value_1() -> i32 {
73-
let w: Value<widget_enum> = Rc::new(RefCell::new(widget_enum::WIDGET_C));
51+
let w: Value<widget_enum> = Rc::new(RefCell::new(widget_enum_WIDGET_C));
7452
return ((*w.borrow()) as i32);
7553
}

0 commit comments

Comments
 (0)