Skip to content

Commit a7de658

Browse files
authored
Translate enums that have integer type instead of enumerator type (#54)
In C, as opposed to C++, enums have integral type rather than enumerator type.
1 parent 4cb7e30 commit a7de658

25 files changed

Lines changed: 1439 additions & 49 deletions

cpp2rust/converter/converter.cpp

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1731,6 +1731,16 @@ bool Converter::VisitCXXBoolLiteralExpr(clang::CXXBoolLiteralExpr *expr) {
17311731
return false;
17321732
}
17331733

1734+
void Converter::ConvertIntegerToEnumeralCast(clang::Expr *to,
1735+
clang::Expr *from) {
1736+
StrCat(GetUnsafeTypeAsString(to->getType()), "::from");
1737+
PushParen paren(*this);
1738+
Convert(from);
1739+
if (!from->getType()->isSpecificBuiltinType(clang::BuiltinType::Int)) {
1740+
StrCat(keyword::kAs, "i32");
1741+
}
1742+
}
1743+
17341744
bool Converter::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) {
17351745
auto *sub_expr = expr->getSubExpr();
17361746
auto type = expr->getType();
@@ -1846,6 +1856,10 @@ bool Converter::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) {
18461856
Convert(sub_expr);
18471857
break;
18481858
}
1859+
if (type->isEnumeralType() && !sub_expr->getType()->isEnumeralType()) {
1860+
ConvertIntegerToEnumeralCast(expr, sub_expr);
1861+
break;
1862+
}
18491863
{
18501864
PushParen outer(*this);
18511865
if (clang::isa<clang::BinaryOperator>(sub_expr)) {
@@ -1890,6 +1904,10 @@ bool Converter::VisitExplicitCastExpr(clang::ExplicitCastExpr *expr) {
18901904
StrCat(')');
18911905
return false;
18921906
}
1907+
if (type->isEnumeralType() && !sub_expr->getType()->isEnumeralType()) {
1908+
ConvertIntegerToEnumeralCast(expr, sub_expr);
1909+
return false;
1910+
}
18931911
{
18941912
PushParen paren(*this);
18951913
Convert(sub_expr);
@@ -2200,10 +2218,14 @@ std::string Converter::ConvertDeclRefExpr(clang::DeclRefExpr *expr) {
22002218
}
22012219

22022220
if (auto enum_constant = clang::dyn_cast<clang::EnumConstantDecl>(decl)) {
2203-
return std::format("{}::{}",
2204-
GetRecordName(clang::dyn_cast<clang::EnumDecl>(
2205-
enum_constant->getDeclContext())),
2206-
std::string_view(enum_constant->getName()));
2221+
auto qualified = std::format("{}::{}",
2222+
GetRecordName(clang::dyn_cast<clang::EnumDecl>(
2223+
enum_constant->getDeclContext())),
2224+
std::string_view(enum_constant->getName()));
2225+
if (!expr->getType()->isEnumeralType()) {
2226+
return std::format("({} as i32)", qualified);
2227+
}
2228+
return qualified;
22072229
}
22082230

22092231
if (IsGlobalVar(expr)) {
@@ -2704,9 +2726,29 @@ bool Converter::VisitEnumDecl(clang::EnumDecl *decl) {
27042726
std::string_view(init.data(), init.size())));
27052727
}
27062728
StrCat("}");
2729+
2730+
AddFromImpl(decl);
27072731
return false;
27082732
}
27092733

2734+
void Converter::AddFromImpl(clang::EnumDecl *decl) {
2735+
auto name = GetRecordName(decl);
2736+
StrCat(std::format("impl From<i32> for {}", name));
2737+
PushBrace impl(*this);
2738+
StrCat(std::format("fn from(n: i32) -> {}", name));
2739+
PushBrace fn(*this);
2740+
StrCat("match n");
2741+
PushBrace match(*this);
2742+
for (auto e : decl->enumerators()) {
2743+
llvm::SmallVector<char, 32> init;
2744+
e->getInitVal().toString(init, 10);
2745+
StrCat(std::format("{} => {}::{},",
2746+
std::string_view(init.data(), init.size()), name,
2747+
std::string_view(e->getName())));
2748+
}
2749+
StrCat(std::format("_ => panic!(\"invalid {} value: {{}}\", n),", name));
2750+
}
2751+
27102752
bool Converter::VisitCXXDefaultArgExpr(clang::CXXDefaultArgExpr *expr) {
27112753
if (expr->getType()->isPointerType()) {
27122754
StrCat(keyword_default_);

cpp2rust/converter/converter.h

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

242242
virtual bool VisitCXXBoolLiteralExpr(clang::CXXBoolLiteralExpr *expr);
243243

244+
void ConvertIntegerToEnumeralCast(clang::Expr *to, clang::Expr *from);
245+
244246
virtual bool VisitImplicitCastExpr(clang::ImplicitCastExpr *expr);
245247

246248
virtual bool VisitExplicitCastExpr(clang::ExplicitCastExpr *expr);
@@ -293,6 +295,8 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
293295

294296
virtual bool VisitEnumDecl(clang::EnumDecl *decl);
295297

298+
virtual void AddFromImpl(clang::EnumDecl *decl);
299+
296300
virtual bool VisitCXXDefaultArgExpr(clang::CXXDefaultArgExpr *expr);
297301

298302
virtual bool VisitLambdaExpr(clang::LambdaExpr *expr);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// panic
2+
3+
enum Color { RED, GREEN, BLUE };
4+
5+
int main() {
6+
int n = 3;
7+
Color c = (Color)n;
8+
return c == BLUE ? 0 : 1;
9+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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(Clone, Copy, PartialEq, Debug, Default)]
10+
enum Color {
11+
#[default]
12+
RED = 0,
13+
GREEN = 1,
14+
BLUE = 2,
15+
}
16+
impl From<i32> for Color {
17+
fn from(n: i32) -> Color {
18+
match n {
19+
0 => Color::RED,
20+
1 => Color::GREEN,
21+
2 => Color::BLUE,
22+
_ => panic!("invalid Color value: {}", n),
23+
}
24+
}
25+
}
26+
pub fn main() {
27+
std::process::exit(main_0());
28+
}
29+
fn main_0() -> i32 {
30+
let n: Value<i32> = Rc::new(RefCell::new(3));
31+
let c: Value<Color> = Rc::new(RefCell::new(Color::from((*n.borrow()))));
32+
return if (((*c.borrow()) as i32) == (Color::BLUE as i32)) {
33+
0
34+
} else {
35+
1
36+
};
37+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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+
#[derive(Clone, Copy, PartialEq, Debug, Default)]
10+
enum Color {
11+
#[default]
12+
RED = 0,
13+
GREEN = 1,
14+
BLUE = 2,
15+
}
16+
impl From<i32> for Color {
17+
fn from(n: i32) -> Color {
18+
match n {
19+
0 => Color::RED,
20+
1 => Color::GREEN,
21+
2 => Color::BLUE,
22+
_ => panic!("invalid Color value: {}", n),
23+
}
24+
}
25+
}
26+
pub fn main() {
27+
unsafe {
28+
std::process::exit(main_0() as i32);
29+
}
30+
}
31+
unsafe fn main_0() -> i32 {
32+
let mut n: i32 = 3;
33+
let mut c: Color = Color::from(n);
34+
return if ((c as i32) == (Color::BLUE as i32)) {
35+
0
36+
} else {
37+
1
38+
};
39+
}

tests/unit/enum_int_interop.cpp

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#include <assert.h>
2+
3+
enum Color { RED, GREEN, BLUE };
4+
5+
enum Option {
6+
OPT_NONE = 0,
7+
OPT_A = 10,
8+
OPT_B = 20,
9+
OPT_C = 30,
10+
};
11+
12+
typedef enum {
13+
TAG_ZERO = 0,
14+
TAG_ONE = 1,
15+
TAG_TWO = 2,
16+
} Tag;
17+
18+
int as_int(Color c) { return c; }
19+
20+
int classify_option(int option) {
21+
switch (option) {
22+
case OPT_NONE:
23+
return -1;
24+
case OPT_A:
25+
return 1;
26+
case OPT_B:
27+
return 2;
28+
case OPT_C:
29+
return 3;
30+
default:
31+
return 0;
32+
}
33+
}
34+
35+
Color make_color(int n) { return (Color)n; }
36+
37+
int main() {
38+
Color c = RED;
39+
40+
assert(c == RED);
41+
assert(c == 0);
42+
assert(c != 1);
43+
44+
if (c == GREEN) {
45+
return 1;
46+
}
47+
48+
switch (c) {
49+
case 0:
50+
break;
51+
case 1:
52+
return 1;
53+
case 2:
54+
return 2;
55+
default:
56+
return 99;
57+
}
58+
59+
int x = c;
60+
assert(x == 0);
61+
62+
int y = c + 1;
63+
assert(y == 1);
64+
65+
c = (Color)2;
66+
assert(c == BLUE);
67+
assert(c == 2);
68+
69+
c = make_color(1);
70+
assert(c == GREEN);
71+
72+
Color cmp = (Color)(c + 1);
73+
assert(cmp == BLUE);
74+
75+
Option o = OPT_A;
76+
assert(o == OPT_A);
77+
assert(o == 10);
78+
79+
int oi = o;
80+
assert(oi == 10);
81+
82+
o = (Option)20;
83+
assert(o == OPT_B);
84+
85+
int rc = classify_option(o);
86+
assert(rc == 2);
87+
88+
rc = classify_option(20);
89+
assert(rc == 2);
90+
91+
rc = classify_option(OPT_C);
92+
assert(rc == 3);
93+
94+
Tag t = TAG_ONE;
95+
assert(t == 1);
96+
assert(t == TAG_ONE);
97+
98+
int ti = t;
99+
assert(ti == 1);
100+
101+
t = (Tag)2;
102+
assert(t == TAG_TWO);
103+
104+
switch (t) {
105+
case TAG_ZERO:
106+
return 90;
107+
case 1:
108+
return 91;
109+
case 2:
110+
break;
111+
}
112+
113+
int extra = (int)RED + (int)GREEN + (int)BLUE;
114+
assert(extra == 0 + 1 + 2);
115+
116+
return 0;
117+
}

0 commit comments

Comments
 (0)