Skip to content

Commit 18cf966

Browse files
committed
Translate string literals in C and C++ (Cpp2Rust#36)
This PR adds support for declaring and using const/non-const pointers and arrays containing string literals. VisitStringLiteral now always generates a string literal (`[u8; N]` in unsafe, `Box<[u8]>` in refcount). It's the job of `VisitImplicitCast::CK_ArrayToPointerDecay` to convert the string literal to poiner if it's necessary. In C string literals are `char[]` and in C++ they are `const char[]`. `VisitImplicitCast::CK_NoOp` handles the conversion between const and non-const char pointers. Besides always generating a string literal instead of pointer, VisitStringLiteral also pad with null bytes the following code: `char array_bigger_than_string_literal[10] = "1"`, effectively becoming `"1\0\0\0..."`.
1 parent 061de0b commit 18cf966

51 files changed

Lines changed: 1259 additions & 68 deletions

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: 54 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1689,7 +1689,7 @@ std::string Converter::GetEscapedUTF8CharLiteral(clang::Expr *expr) const {
16891689
}
16901690

16911691
std::string Converter::GetEscapedStringLiteral(clang::Expr *expr,
1692-
bool add_null_char) const {
1692+
uint64_t pad_nulls) const {
16931693
auto str_expr = clang::dyn_cast<clang::StringLiteral>(expr->IgnoreCasts());
16941694
assert(str_expr);
16951695
auto raw = str_expr->getString();
@@ -1698,15 +1698,31 @@ std::string Converter::GetEscapedStringLiteral(clang::Expr *expr,
16981698
for (unsigned char c : raw) {
16991699
out += GetEscapedCharLiteral(static_cast<char>(c));
17001700
}
1701-
if (add_null_char) {
1701+
for (uint64_t i = 0; i < pad_nulls; ++i) {
17021702
out += "\\0";
17031703
}
17041704
out.push_back('"');
17051705
return out;
17061706
}
17071707

17081708
bool Converter::VisitStringLiteral(clang::StringLiteral *expr) {
1709-
StrCat(std::format("b{}.as_ptr()", GetEscapedStringLiteral(expr, true)));
1709+
if (!curr_init_type_.empty() && curr_init_type_.top()->isArrayType()) {
1710+
if (auto *arr_ty = ctx_.getAsConstantArrayType(curr_init_type_.top())) {
1711+
uint64_t arr_size = arr_ty->getSize().getZExtValue();
1712+
if (expr->getString().empty()) {
1713+
StrCat(std::format("[0u8; {}]", arr_size));
1714+
return false;
1715+
}
1716+
uint64_t pad = arr_size > expr->getString().size()
1717+
? arr_size - expr->getString().size()
1718+
: 0;
1719+
StrCat(token::kStar,
1720+
std::format("b{}", GetEscapedStringLiteral(expr, pad)));
1721+
return false;
1722+
}
1723+
StrCat(token::kStar);
1724+
}
1725+
StrCat(std::format("b{}", GetEscapedStringLiteral(expr, 1)));
17101726
return false;
17111727
}
17121728

@@ -1726,23 +1742,26 @@ bool Converter::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) {
17261742
SetValueFreshness(type);
17271743
break;
17281744
}
1729-
case clang::CastKind::CK_ArrayToPointerDecay:
1730-
if (clang::isa<clang::StringLiteral>(sub_expr) ||
1731-
clang::isa<clang::PredefinedExpr>(sub_expr)) {
1732-
return Convert(sub_expr);
1733-
}
1745+
case clang::CastKind::CK_ArrayToPointerDecay: {
17341746
// __va_list_tag [1] decays to __va_list_tag *. Just pass through by value
17351747
if (IsVaListType(sub_expr->getType())) {
17361748
Convert(sub_expr);
17371749
break;
17381750
}
17391751
Convert(sub_expr);
1740-
if (sub_expr->getType().isConstQualified()) {
1741-
StrCat(keyword_ptr_decay_const_);
1752+
bool dest_pointee_const =
1753+
expr->getType()->getPointeeType().isConstQualified();
1754+
if (clang::isa<clang::StringLiteral>(sub_expr) ||
1755+
clang::isa<clang::PredefinedExpr>(sub_expr)) {
1756+
StrCat(".as_ptr()");
1757+
if (!dest_pointee_const) {
1758+
StrCat(".cast_mut()");
1759+
}
17421760
} else {
1743-
StrCat(keyword_ptr_decay_);
1761+
StrCat(dest_pointee_const ? ".as_ptr()" : ".as_mut_ptr()");
17441762
}
17451763
break;
1764+
}
17461765
case clang::CastKind::CK_BitCast: {
17471766
PushParen paren(*this);
17481767
Convert(sub_expr);
@@ -1756,6 +1775,21 @@ bool Converter::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) {
17561775
}
17571776
case clang::CastKind::CK_NoOp: {
17581777
Convert(sub_expr);
1778+
if (expr->getType()->isPointerType() &&
1779+
sub_expr->getType()->isPointerType() &&
1780+
!clang::isa<clang::CXXThisExpr>(expr->IgnoreImplicit())) {
1781+
switch (GetConstCastType(expr->getType()->getPointeeType(),
1782+
sub_expr->getType()->getPointeeType())) {
1783+
case ConstCastType::MutableToConst:
1784+
StrCat(".cast_const()");
1785+
break;
1786+
case ConstCastType::ConstToMutable:
1787+
StrCat(".cast_mut()");
1788+
break;
1789+
default:
1790+
break;
1791+
}
1792+
}
17591793
break;
17601794
}
17611795
case clang::CastKind::CK_FunctionToPointerDecay:
@@ -2973,9 +3007,8 @@ void Converter::ConvertVarInit(clang::QualType qual_type, clang::Expr *expr) {
29733007
if (auto *lambda = clang::dyn_cast<clang::LambdaExpr>(
29743008
expr->IgnoreUnlessSpelledInSource())) {
29753009
PushExprKind push(*this, ExprKind::AddrOf);
2976-
curr_init_type_.push(qual_type);
3010+
PushInitType init_type(*this, qual_type);
29773011
VisitLambdaExpr(lambda);
2978-
curr_init_type_.pop();
29793012
return;
29803013
}
29813014
}
@@ -2992,21 +3025,18 @@ void Converter::ConvertVarInit(clang::QualType qual_type, clang::Expr *expr) {
29923025
{
29933026
PushParen paren(*this);
29943027
StrCat(token::kStar);
2995-
curr_init_type_.push(qual_type);
3028+
PushInitType init_type(*this, qual_type);
29963029
Convert(expr);
2997-
curr_init_type_.pop();
29983030
}
29993031
StrCat(".clone()");
30003032
} else if (IsReferenceType(expr) || qual_type->isFunctionPointerType()) {
30013033
PushExprKind push(*this, ExprKind::AddrOf);
3002-
curr_init_type_.push(qual_type);
3034+
PushInitType init_type(*this, qual_type);
30033035
Convert(expr);
3004-
curr_init_type_.pop();
30053036
} else {
30063037
PushExprKind push(*this, ExprKind::RValue);
3007-
curr_init_type_.push(qual_type);
3038+
PushInitType init_type(*this, qual_type);
30083039
Convert(expr);
3009-
curr_init_type_.pop();
30103040
}
30113041
if (qual_type->isReferenceType() && !IsReferenceType(expr)) {
30123042
StrCat(keyword::kAs);
@@ -3080,9 +3110,11 @@ void Converter::ConvertArraySubscript(clang::Expr *base, clang::Expr *idx,
30803110

30813111
void Converter::ConvertAssignment(clang::Expr *lhs, clang::Expr *rhs,
30823112
std::string_view assign_operator) {
3083-
curr_init_type_.push(lhs->getType());
3084-
auto lhs_as_string = ConvertLValue(lhs);
3085-
curr_init_type_.pop();
3113+
std::string lhs_as_string;
3114+
{
3115+
PushInitType init_type(*this, lhs->getType());
3116+
lhs_as_string = ConvertLValue(lhs);
3117+
}
30863118
auto rhs_as_string = ConvertFreshRValue(rhs);
30873119

30883120
PushBrace brace(*this, !isVoid());

cpp2rust/converter/converter.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
233233
std::string GetEscapedUTF8CharLiteral(clang::Expr *expr) const;
234234

235235
std::string GetEscapedStringLiteral(clang::Expr *expr,
236-
bool add_null_char = false) const;
236+
uint64_t pad_nulls = 0) const;
237237
virtual bool VisitStringLiteral(clang::StringLiteral *expr);
238238

239239
virtual bool VisitCXXBoolLiteralExpr(clang::CXXBoolLiteralExpr *expr);
@@ -526,6 +526,19 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
526526
std::stack<BreakTarget> &stack_;
527527
};
528528

529+
class PushInitType {
530+
public:
531+
PushInitType(Converter &c, clang::QualType type) : c_(c) {
532+
c_.curr_init_type_.push(type);
533+
}
534+
~PushInitType() { c_.curr_init_type_.pop(); }
535+
PushInitType(const PushInitType &) = delete;
536+
PushInitType &operator=(const PushInitType &) = delete;
537+
538+
private:
539+
Converter &c_;
540+
};
541+
529542
std::unordered_set<const clang::VarDecl *> map_iter_decls_;
530543

531544
struct ScopedMapIterDecl {

cpp2rust/converter/converter_lib.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,4 +802,16 @@ std::string ReplaceAll(std::string str, std::string_view from,
802802
return str;
803803
}
804804

805+
ConstCastType GetConstCastType(clang::QualType to, clang::QualType from) {
806+
if (to.isConstQualified() && from.isConstQualified()) {
807+
return ConstCastType::ConstToConst;
808+
} else if (!to.isConstQualified() && from.isConstQualified()) {
809+
return ConstCastType::ConstToMutable;
810+
} else if (to.isConstQualified() && !from.isConstQualified()) {
811+
return ConstCastType::MutableToConst;
812+
} else {
813+
return ConstCastType::MutableToMutable;
814+
}
815+
}
816+
805817
} // namespace cpp2rust

cpp2rust/converter/converter_lib.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,4 +171,13 @@ void Unwrap(std::string &s, std::string_view prefix, std::string_view suffix);
171171
std::string ReplaceAll(std::string str, std::string_view from,
172172
std::string_view to);
173173

174+
enum class ConstCastType {
175+
ConstToConst,
176+
ConstToMutable,
177+
MutableToConst,
178+
MutableToMutable,
179+
};
180+
181+
ConstCastType GetConstCastType(clang::QualType to, clang::QualType from);
182+
174183
} // namespace cpp2rust

cpp2rust/converter/models/converter_refcount.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,22 @@ bool ConverterRefCount::VisitCallExpr(clang::CallExpr *expr) {
946946
}
947947

948948
bool ConverterRefCount::VisitStringLiteral(clang::StringLiteral *expr) {
949+
if (!curr_init_type_.empty() && curr_init_type_.top()->isArrayType()) {
950+
uint64_t pad = 1;
951+
if (auto *arr_ty = ctx_.getAsConstantArrayType(curr_init_type_.top())) {
952+
uint64_t arr_size = arr_ty->getSize().getZExtValue();
953+
if (expr->getString().empty()) {
954+
StrCat(std::format("vec![0u8; {}].into_boxed_slice()", arr_size));
955+
return false;
956+
}
957+
pad = arr_size > expr->getString().size()
958+
? arr_size - expr->getString().size()
959+
: 0;
960+
}
961+
StrCat(std::format("Box::<[u8]>::from(b{}.as_slice())",
962+
GetEscapedStringLiteral(expr, pad)));
963+
return false;
964+
}
949965
StrCat(GetEscapedStringLiteral(expr));
950966
return false;
951967
}
@@ -1035,6 +1051,11 @@ bool ConverterRefCount::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) {
10351051
return false;
10361052
}
10371053

1054+
if (expr->getCastKind() == clang::CastKind::CK_NoOp) {
1055+
Convert(sub_expr);
1056+
return false;
1057+
}
1058+
10381059
return Converter::VisitImplicitCastExpr(expr);
10391060
}
10401061

@@ -1667,6 +1688,7 @@ void ConverterRefCount::ConvertVarInit(clang::QualType qual_type,
16671688

16681689
bool is_ref = qual_type->isReferenceType();
16691690
PushConversionKind push(*this, ConversionKind::Unboxed, is_ref);
1691+
PushInitType init_type(*this, qual_type);
16701692
StrCat(BoxValue((is_ref || qual_type->isFunctionPointerType())
16711693
? ConvertFreshPointer(expr)
16721694
: ConvertFreshRValue(expr)));

rules/string/ir_unsafe.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1548,21 +1548,21 @@
15481548
{
15491549
"placeholder": {
15501550
"arg": "a0",
1551-
"access": "read"
1551+
"access": "write"
15521552
}
15531553
}
15541554
],
15551555
"body": [
15561556
{
1557-
"text": ".as_ptr()"
1557+
"text": ".as_mut_ptr()"
15581558
}
15591559
]
15601560
}
15611561
}
15621562
],
15631563
"params": {
15641564
"a0": {
1565-
"type": "Vec<u8>"
1565+
"type": "&mut Vec<u8>"
15661566
}
15671567
},
15681568
"return_type": {

rules/string/tgt_unsafe.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ unsafe fn f4(a0: &mut Vec<u8>, a1: *mut u8, a2: usize) {
3535
unsafe fn f5(a0: Vec<u8>) -> *const u8 {
3636
a0.as_ptr()
3737
}
38-
unsafe fn f6(a0: Vec<u8>) -> *const u8 {
39-
a0.as_ptr()
38+
unsafe fn f6(a0: &mut Vec<u8>) -> *const u8 {
39+
a0.as_mut_ptr()
4040
}
4141
unsafe fn f7(a0: *const u8, a1: usize) -> Vec<u8> {
4242
std::slice::from_raw_parts(a0, a1 as usize)
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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+
pub fn foo_mut_0(str: Ptr<u8>) {
10+
let str: Value<Ptr<u8>> = Rc::new(RefCell::new(str));
11+
}
12+
pub fn foo_const_1(str: Ptr<u8>) {
13+
let str: Value<Ptr<u8>> = Rc::new(RefCell::new(str));
14+
}
15+
pub fn main() {
16+
std::process::exit(main_0());
17+
}
18+
fn main_0() -> i32 {
19+
let immutable_strings: Value<Box<[Ptr<u8>]>> = Rc::new(RefCell::new(Box::new([
20+
Ptr::from_string_literal("a"),
21+
Ptr::from_string_literal("b"),
22+
Ptr::from_string_literal("c"),
23+
])));
24+
let immutable_string: Value<Ptr<u8>> = Rc::new(RefCell::new(Ptr::from_string_literal("hello")));
25+
let mutable_string_arr: Value<Box<[u8]>> =
26+
Rc::new(RefCell::new(Box::<[u8]>::from(b"papanasi\0".as_slice())));
27+
let immutable_string_arr: Value<Box<[u8]>> =
28+
Rc::new(RefCell::new(Box::<[u8]>::from(b"papanasi\0".as_slice())));
29+
let immutable_empty: Value<Ptr<u8>> = Rc::new(RefCell::new(Ptr::from_string_literal("")));
30+
let mutable_empty_arr: Value<Box<[u8]>> =
31+
Rc::new(RefCell::new(vec![0u8; 1].into_boxed_slice()));
32+
let immutable_empty_arr: Value<Box<[u8]>> =
33+
Rc::new(RefCell::new(vec![0u8; 1].into_boxed_slice()));
34+
({
35+
let _str: Ptr<u8> = (mutable_string_arr.as_pointer() as Ptr<u8>);
36+
foo_mut_0(_str)
37+
});
38+
({
39+
let _str: Ptr<u8> = Ptr::from_string_literal("world");
40+
foo_const_1(_str)
41+
});
42+
({
43+
let _str: Ptr<u8> = (*immutable_string.borrow()).clone();
44+
foo_const_1(_str)
45+
});
46+
({
47+
let _str: Ptr<u8> = (immutable_string_arr.as_pointer() as Ptr<u8>);
48+
foo_const_1(_str)
49+
});
50+
({
51+
let _str: Ptr<u8> = Ptr::from_string_literal("");
52+
foo_const_1(_str)
53+
});
54+
({
55+
let _str: Ptr<u8> = (*immutable_empty.borrow()).clone();
56+
foo_const_1(_str)
57+
});
58+
({
59+
let _str: Ptr<u8> = (immutable_empty_arr.as_pointer() as Ptr<u8>);
60+
foo_const_1(_str)
61+
});
62+
return 0;
63+
}

0 commit comments

Comments
 (0)