From 5fda0dfc4b044323550797ee1ad88c1a7856ead1 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Thu, 3 Sep 2026 11:09:41 +0100 Subject: [PATCH 1/8] Translate concept specialization expr --- cpp2rust/converter/converter.cpp | 8 ++++++++ cpp2rust/converter/converter.h | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 86440281f..777bf4cef 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -3703,6 +3703,14 @@ bool Converter::VisitUnaryExprOrTypeTraitExpr( return false; } +bool Converter::VisitConceptSpecializationExpr( + clang::ConceptSpecializationExpr *expr) { + assert(!expr->isValueDependent()); + StrCat(expr->isSatisfied() ? keyword::kTrue : keyword::kFalse); + computed_expr_type_ = ComputedExprType::FreshValue; + return false; +} + bool Converter::VisitTypeTraitExpr(clang::TypeTraitExpr *expr) { clang::Expr::EvalResult result; ENSURE(expr->EvaluateAsInt(result, ctx_)); diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 5e8465c70..26847cd1f 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -424,8 +424,12 @@ class Converter : public clang::RecursiveASTVisitor { VisitUnaryExprOrTypeTraitExpr(clang::UnaryExprOrTypeTraitExpr *expr); virtual bool VisitTypeTraitExpr(clang::TypeTraitExpr *expr); + virtual bool VisitSizeOfPackExpr(clang::SizeOfPackExpr *expr); + virtual bool + VisitConceptSpecializationExpr(clang::ConceptSpecializationExpr *expr); + virtual bool VisitOffsetOfExpr(clang::OffsetOfExpr *expr); virtual bool VisitEnumDecl(clang::EnumDecl *decl); From d2881b5d2bdeaf9714c75a0da6843392ac8a6b15 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 9 Sep 2026 22:37:52 +0100 Subject: [PATCH 2/8] Ignore static_asserts and concepts --- cpp2rust/converter/converter.cpp | 6 ++++++ cpp2rust/converter/converter.h | 3 +++ 2 files changed, 9 insertions(+) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 777bf4cef..184521d8b 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1268,6 +1268,12 @@ bool Converter::VisitTypeAliasTemplateDecl(clang::TypeAliasTemplateDecl *) { return false; } +bool Converter::VisitStaticAssertDecl(clang::StaticAssertDecl *) { + return false; +} + +bool Converter::VisitConceptDecl(clang::ConceptDecl *) { return false; } + static bool IsaSemiColonStmt(const clang::Stmt *stmt) { switch (stmt->getStmtClass()) { case clang::Stmt::IfStmtClass: diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 26847cd1f..ed206bcb4 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -164,6 +164,9 @@ class Converter : public clang::RecursiveASTVisitor { virtual bool VisitTypeAliasDecl(clang::TypeAliasDecl *decl); virtual bool VisitTypeAliasTemplateDecl(clang::TypeAliasTemplateDecl *decl); + bool VisitStaticAssertDecl(clang::StaticAssertDecl *decl); + bool VisitConceptDecl(clang::ConceptDecl *decl); + virtual bool VisitCompoundStmt(clang::CompoundStmt *stmt); virtual bool VisitDeclStmt(clang::DeclStmt *stmt); From 4edadd870bf62a4533327d9247dd0e3c6ffffae0 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 9 Sep 2026 22:40:44 +0100 Subject: [PATCH 3/8] Update tests --- tests/unit/concepts.cpp | 27 +++++++++++++++++ tests/unit/out/refcount/concepts.rs | 46 +++++++++++++++++++++++++++++ tests/unit/out/unsafe/concepts.rs | 45 ++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 tests/unit/concepts.cpp create mode 100644 tests/unit/out/refcount/concepts.rs create mode 100644 tests/unit/out/unsafe/concepts.rs diff --git a/tests/unit/concepts.cpp b/tests/unit/concepts.cpp new file mode 100644 index 000000000..469b53df2 --- /dev/null +++ b/tests/unit/concepts.cpp @@ -0,0 +1,27 @@ +// ADDITIONAL_COMPILE_FLAGS: -std=c++20 +#include +#include + +template +concept Small = sizeof(T) <= 4; + +static_assert(Small); + +template bool is_small() { return Small; } + +template int pick(T x) { + if (std::integral && Small) { + return 1; + } + return 2; +} + +int main() { + static_assert(!Small); + assert(is_small()); + assert(!is_small()); + assert(pick(1) == 1); + assert(pick(1L) == 2); + assert(pick(1.0f) == 2); + return 0; +} diff --git a/tests/unit/out/refcount/concepts.rs b/tests/unit/out/refcount/concepts.rs new file mode 100644 index 000000000..7c4be8709 --- /dev/null +++ b/tests/unit/out/refcount/concepts.rs @@ -0,0 +1,46 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::{Read, Seek, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub fn is_small_0() -> bool { + return true; +} +pub fn is_small_1() -> bool { + return false; +} +pub fn pick_2(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + if (true) && (true) { + return 1; + } + return 2; +} +pub fn pick_3(x: i64) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + if (true) && (false) { + return 1; + } + return 2; +} +pub fn pick_4(x: f32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + if (false) && (true) { + return 1; + } + return 2; +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + assert!(({ is_small_0() })); + assert!(!({ is_small_1() })); + assert!((({ pick_2(1,) }) == 1)); + assert!((({ pick_3(1_i64,) }) == 2)); + assert!((({ pick_4(1.0E+0,) }) == 2)); + return 0; +} diff --git a/tests/unit/out/unsafe/concepts.rs b/tests/unit/out/unsafe/concepts.rs new file mode 100644 index 000000000..05cff6b40 --- /dev/null +++ b/tests/unit/out/unsafe/concepts.rs @@ -0,0 +1,45 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::{Read, Seek, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub unsafe fn is_small_0() -> bool { + return true; +} +pub unsafe fn is_small_1() -> bool { + return false; +} +pub unsafe fn pick_2(mut x: i32) -> i32 { + if (true) && (true) { + return 1; + } + return 2; +} +pub unsafe fn pick_3(mut x: i64) -> i32 { + if (true) && (false) { + return 1; + } + return 2; +} +pub unsafe fn pick_4(mut x: f32) -> i32 { + if (false) && (true) { + return 1; + } + return 2; +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + assert!((unsafe { is_small_0() })); + assert!(!(unsafe { is_small_1() })); + assert!(((unsafe { pick_2(1,) }) == (1))); + assert!(((unsafe { pick_3(1_i64,) }) == (2))); + assert!(((unsafe { pick_4(1.0E+0,) }) == (2))); + return 0; +} From 82ea91e6f6d3ce6da6aa57feda4f2698e30b633e Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 9 Sep 2026 22:41:43 +0100 Subject: [PATCH 4/8] Add visitor for requires expr --- cpp2rust/converter/converter.cpp | 7 +++++++ cpp2rust/converter/converter.h | 1 + 2 files changed, 8 insertions(+) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 184521d8b..bafd8786b 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -3717,6 +3717,13 @@ bool Converter::VisitConceptSpecializationExpr( return false; } +bool Converter::VisitRequiresExpr(clang::RequiresExpr *expr) { + assert(!expr->isValueDependent()); + StrCat(expr->isSatisfied() ? keyword::kTrue : keyword::kFalse); + computed_expr_type_ = ComputedExprType::FreshValue; + return false; +} + bool Converter::VisitTypeTraitExpr(clang::TypeTraitExpr *expr) { clang::Expr::EvalResult result; ENSURE(expr->EvaluateAsInt(result, ctx_)); diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index ed206bcb4..3d19c309d 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -432,6 +432,7 @@ class Converter : public clang::RecursiveASTVisitor { virtual bool VisitConceptSpecializationExpr(clang::ConceptSpecializationExpr *expr); + virtual bool VisitRequiresExpr(clang::RequiresExpr *expr); virtual bool VisitOffsetOfExpr(clang::OffsetOfExpr *expr); From fe2940494d2f22ecf8f061a9eb2ae4d2e322f034 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 9 Sep 2026 22:43:00 +0100 Subject: [PATCH 5/8] Update tests --- tests/unit/concepts.cpp | 15 +++++++++ tests/unit/out/refcount/concepts.rs | 48 +++++++++++++++++++++++++---- tests/unit/out/unsafe/concepts.rs | 30 ++++++++++++++---- 3 files changed, 81 insertions(+), 12 deletions(-) diff --git a/tests/unit/concepts.cpp b/tests/unit/concepts.cpp index 469b53df2..2dcb0517b 100644 --- a/tests/unit/concepts.cpp +++ b/tests/unit/concepts.cpp @@ -7,8 +7,19 @@ concept Small = sizeof(T) <= 4; static_assert(Small); +template +concept HasSize = requires(T t) { + { t.size() } -> std::same_as; +}; + +struct Sized { + int size() { return 4; } +}; + template bool is_small() { return Small; } +template bool has_size() { return requires(T t) { t.size(); }; } + template int pick(T x) { if (std::integral && Small) { return 1; @@ -20,6 +31,10 @@ int main() { static_assert(!Small); assert(is_small()); assert(!is_small()); + assert(HasSize); + assert(!HasSize); + assert(has_size()); + assert(!has_size()); assert(pick(1) == 1); assert(pick(1L) == 2); assert(pick(1.0f) == 2); diff --git a/tests/unit/out/refcount/concepts.rs b/tests/unit/out/refcount/concepts.rs index 7c4be8709..d75da0d9d 100644 --- a/tests/unit/out/refcount/concepts.rs +++ b/tests/unit/out/refcount/concepts.rs @@ -6,27 +6,51 @@ use std::io::prelude::*; use std::io::{Read, Seek, Write}; use std::os::fd::AsFd; use std::rc::{Rc, Weak}; +#[derive(Default)] +pub struct Sized {} +impl Clone for Sized { + fn clone(&self) -> Self { + let __this: Value = Rc::new(RefCell::new(Self {})); + let this: Ptr = __this.as_pointer(); + Rc::try_unwrap(__this).ok().unwrap().into_inner() + } +} +impl ByteRepr for Sized { + fn byte_size() -> usize { + 1 + } + fn to_bytes(&self, buf: &mut [u8]) {} + fn from_bytes(buf: &[u8]) -> Self { + Self {} + } +} pub fn is_small_0() -> bool { return true; } pub fn is_small_1() -> bool { return false; } -pub fn pick_2(x: i32) -> i32 { +pub fn has_size_2() -> bool { + return true; +} +pub fn has_size_3() -> bool { + return false; +} +pub fn pick_4(x: i32) -> i32 { let x: Value = Rc::new(RefCell::new(x)); if (true) && (true) { return 1; } return 2; } -pub fn pick_3(x: i64) -> i32 { +pub fn pick_5(x: i64) -> i32 { let x: Value = Rc::new(RefCell::new(x)); if (true) && (false) { return 1; } return 2; } -pub fn pick_4(x: f32) -> i32 { +pub fn pick_6(x: f32) -> i32 { let x: Value = Rc::new(RefCell::new(x)); if (false) && (true) { return 1; @@ -39,8 +63,20 @@ pub fn main() { fn main_0() -> i32 { assert!(({ is_small_0() })); assert!(!({ is_small_1() })); - assert!((({ pick_2(1,) }) == 1)); - assert!((({ pick_3(1_i64,) }) == 2)); - assert!((({ pick_4(1.0E+0,) }) == 2)); + assert!(true); + assert!(!false); + assert!(({ has_size_2() })); + assert!(!({ has_size_3() })); + assert!((({ pick_4(1,) }) == 1)); + assert!((({ pick_5(1_i64,) }) == 2)); + assert!((({ pick_6(1.0E+0,) }) == 2)); return 0; } +pub trait SizedImpl { + fn size(&self) -> i32; +} +impl SizedImpl for Ptr { + fn size(&self) -> i32 { + return 4; + } +} diff --git a/tests/unit/out/unsafe/concepts.rs b/tests/unit/out/unsafe/concepts.rs index 05cff6b40..65dc17012 100644 --- a/tests/unit/out/unsafe/concepts.rs +++ b/tests/unit/out/unsafe/concepts.rs @@ -6,25 +6,39 @@ use std::collections::BTreeMap; use std::io::{Read, Seek, Write}; use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; use std::rc::Rc; +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct Sized {} +impl Sized { + pub unsafe fn size(&mut self) -> i32 { + return 4; + } +} pub unsafe fn is_small_0() -> bool { return true; } pub unsafe fn is_small_1() -> bool { return false; } -pub unsafe fn pick_2(mut x: i32) -> i32 { +pub unsafe fn has_size_2() -> bool { + return true; +} +pub unsafe fn has_size_3() -> bool { + return false; +} +pub unsafe fn pick_4(mut x: i32) -> i32 { if (true) && (true) { return 1; } return 2; } -pub unsafe fn pick_3(mut x: i64) -> i32 { +pub unsafe fn pick_5(mut x: i64) -> i32 { if (true) && (false) { return 1; } return 2; } -pub unsafe fn pick_4(mut x: f32) -> i32 { +pub unsafe fn pick_6(mut x: f32) -> i32 { if (false) && (true) { return 1; } @@ -38,8 +52,12 @@ pub fn main() { unsafe fn main_0() -> i32 { assert!((unsafe { is_small_0() })); assert!(!(unsafe { is_small_1() })); - assert!(((unsafe { pick_2(1,) }) == (1))); - assert!(((unsafe { pick_3(1_i64,) }) == (2))); - assert!(((unsafe { pick_4(1.0E+0,) }) == (2))); + assert!(true); + assert!(!false); + assert!((unsafe { has_size_2() })); + assert!(!(unsafe { has_size_3() })); + assert!(((unsafe { pick_4(1,) }) == (1))); + assert!(((unsafe { pick_5(1_i64,) }) == (2))); + assert!(((unsafe { pick_6(1.0E+0,) }) == (2))); return 0; } From d295ff96dd97d7087076923a0e70bab36e16118a Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 9 Sep 2026 22:45:51 +0100 Subject: [PATCH 6/8] clang-format --- tests/unit/concepts.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/unit/concepts.cpp b/tests/unit/concepts.cpp index 2dcb0517b..46dc77537 100644 --- a/tests/unit/concepts.cpp +++ b/tests/unit/concepts.cpp @@ -18,7 +18,9 @@ struct Sized { template bool is_small() { return Small; } -template bool has_size() { return requires(T t) { t.size(); }; } +template bool has_size() { + return requires(T t) { t.size(); }; +} template int pick(T x) { if (std::integral && Small) { From 5a471beeeeebcee50ef0dd5c9029fd6e84c29744 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 22 Sep 2026 15:54:27 +0100 Subject: [PATCH 7/8] Update tests --- tests/unit/out/refcount/concepts.rs | 22 ++++------------------ tests/unit/out/unsafe/concepts.rs | 4 +++- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/tests/unit/out/refcount/concepts.rs b/tests/unit/out/refcount/concepts.rs index d75da0d9d..6fbb4bc3f 100644 --- a/tests/unit/out/refcount/concepts.rs +++ b/tests/unit/out/refcount/concepts.rs @@ -6,24 +6,8 @@ use std::io::prelude::*; use std::io::{Read, Seek, Write}; use std::os::fd::AsFd; use std::rc::{Rc, Weak}; -#[derive(Default)] +#[derive(Clone, ByteRepr, Default)] pub struct Sized {} -impl Clone for Sized { - fn clone(&self) -> Self { - let __this: Value = Rc::new(RefCell::new(Self {})); - let this: Ptr = __this.as_pointer(); - Rc::try_unwrap(__this).ok().unwrap().into_inner() - } -} -impl ByteRepr for Sized { - fn byte_size() -> usize { - 1 - } - fn to_bytes(&self, buf: &mut [u8]) {} - fn from_bytes(buf: &[u8]) -> Self { - Self {} - } -} pub fn is_small_0() -> bool { return true; } @@ -58,13 +42,14 @@ pub fn pick_6(x: f32) -> i32 { return 2; } pub fn main() { + __cpp2rust_init_globals(); std::process::exit(main_0()); } fn main_0() -> i32 { assert!(({ is_small_0() })); assert!(!({ is_small_1() })); assert!(true); - assert!(!false); + assert!(!(false)); assert!(({ has_size_2() })); assert!(!({ has_size_3() })); assert!((({ pick_4(1,) }) == 1)); @@ -80,3 +65,4 @@ impl SizedImpl for Ptr { return 4; } } +pub fn __cpp2rust_init_globals() {} diff --git a/tests/unit/out/unsafe/concepts.rs b/tests/unit/out/unsafe/concepts.rs index 65dc17012..932c13fe1 100644 --- a/tests/unit/out/unsafe/concepts.rs +++ b/tests/unit/out/unsafe/concepts.rs @@ -46,6 +46,7 @@ pub unsafe fn pick_6(mut x: f32) -> i32 { } pub fn main() { unsafe { + __cpp2rust_init_globals(); std::process::exit(main_0() as i32); } } @@ -53,7 +54,7 @@ unsafe fn main_0() -> i32 { assert!((unsafe { is_small_0() })); assert!(!(unsafe { is_small_1() })); assert!(true); - assert!(!false); + assert!(!(false)); assert!((unsafe { has_size_2() })); assert!(!(unsafe { has_size_3() })); assert!(((unsafe { pick_4(1,) }) == (1))); @@ -61,3 +62,4 @@ unsafe fn main_0() -> i32 { assert!(((unsafe { pick_6(1.0E+0,) }) == (2))); return 0; } +pub unsafe fn __cpp2rust_init_globals() {} From c08d280c77f519d102ae13200361e386dead08cc Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 22 Sep 2026 16:33:50 +0100 Subject: [PATCH 8/8] Translate static assert to rust const assert --- cpp2rust/converter/converter.cpp | 41 +++++++----------- cpp2rust/converter/converter.h | 1 - cpp2rust/converter/converter_lib.cpp | 63 ++++++++++++++++++++++++++++ cpp2rust/converter/converter_lib.h | 7 ++++ tests/unit/concepts.cpp | 1 + tests/unit/out/refcount/concepts.rs | 6 +++ tests/unit/out/unsafe/concepts.rs | 6 +++ 7 files changed, 99 insertions(+), 26 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index bafd8786b..c4088c017 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1268,7 +1268,22 @@ bool Converter::VisitTypeAliasTemplateDecl(clang::TypeAliasTemplateDecl *) { return false; } -bool Converter::VisitStaticAssertDecl(clang::StaticAssertDecl *) { +bool Converter::VisitStaticAssertDecl(clang::StaticAssertDecl *decl) { + auto *assert_expr = decl->getAssertExpr(); + if (assert_expr->isValueDependent()) { + return false; + } + std::string condition; + if (assert_expr->getType()->isBooleanType() && + IsRustConstEvaluableExpr(assert_expr)) { + condition = ToString(assert_expr); + } else { + bool value = false; + ENSURE(assert_expr->EvaluateAsBooleanCondition(value, ctx_)); + condition = value ? keyword::kTrue : keyword::kFalse; + } + StrCat(std::format("const _: () = assert!({}{});", condition, + GetAssertMessageAsString(assert_expr, ctx_))); return false; } @@ -2273,30 +2288,6 @@ bool Converter::VisitCharacterLiteral(clang::CharacterLiteral *expr) { return false; } -std::string Converter::GetEscapedCharLiteral(char character) const { - switch (character) { - case '"': - return "\\\""; - case '\'': - return "\\'"; - case '\\': - return "\\\\"; - case '\n': - return "\\n"; - case '\r': - return "\\r"; - case '\t': - return "\\t"; - case '\0': - return "\\0"; - } - auto uc = static_cast(character); - if (uc < 0x20 || uc >= 0x7F) { - return std::format("\\x{:02x}", uc); - } - return std::string(1, character); -} - std::string Converter::GetEscapedUTF8CharLiteral(clang::Expr *expr) const { auto char_expr = clang::dyn_cast(expr->IgnoreCasts()); diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 3d19c309d..87281dba3 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -354,7 +354,6 @@ class Converter : public clang::RecursiveASTVisitor { virtual bool VisitCharacterLiteral(clang::CharacterLiteral *expr); - std::string GetEscapedCharLiteral(char character) const; std::string GetCodeUnitArrayLiteral(const clang::StringLiteral *expr); bool IsArrayInitContext() const; diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index 82acf2fd0..7b5c08282 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -5,9 +5,11 @@ #include #include +#include #include #include #include +#include #include #include @@ -108,6 +110,67 @@ bool IsBuiltinConstantP(const clang::Expr *expr) { return false; } +bool IsRustConstEvaluableExpr(const clang::Expr *expr) { + expr = expr->IgnoreParenImpCasts(); + if (clang::isa( + expr)) { + return true; + } + if (auto *trait = clang::dyn_cast(expr)) { + return trait->getKind() == clang::UnaryExprOrTypeTrait::UETT_SizeOf; + } + if (auto *unary = clang::dyn_cast(expr)) { + return unary->getOpcode() == clang::UO_LNot && + IsRustConstEvaluableExpr(unary->getSubExpr()); + } + if (auto *binary = clang::dyn_cast(expr)) { + return (binary->isEqualityOp() || binary->isRelationalOp()) && + IsRustConstEvaluableExpr(binary->getLHS()) && + IsRustConstEvaluableExpr(binary->getRHS()); + } + return false; +} + +std::string GetEscapedCharLiteral(char character) { + switch (character) { + case '"': + return "\\\""; + case '\'': + return "\\'"; + case '\\': + return "\\\\"; + case '\n': + return "\\n"; + case '\r': + return "\\r"; + case '\t': + return "\\t"; + case '\0': + return "\\0"; + } + auto uc = static_cast(character); + if (uc < 0x20 || uc >= 0x7F) { + return std::format("\\x{:02x}", uc); + } + return std::string(1, character); +} + +std::string GetAssertMessageAsString(const clang::Expr *expr, + const clang::ASTContext &ctx) { + auto text = clang::Lexer::getSourceText( + clang::CharSourceRange::getTokenRange(expr->getSourceRange()), + ctx.getSourceManager(), ctx.getLangOpts()); + std::string message = R"(, ")"; + for (char c : text) { + // Doubled so that assert! does not read them as a format placeholder. + if (c == '{' || c == '}') { + message += c; + } + message += GetEscapedCharLiteral(c); + } + return message + '"'; +} + bool IsComparisonWithNullOp(const clang::BinaryOperator *expr) { if (!expr->isComparisonOp()) { return false; diff --git a/cpp2rust/converter/converter_lib.h b/cpp2rust/converter/converter_lib.h index cec83b2b2..5f09e0d2f 100644 --- a/cpp2rust/converter/converter_lib.h +++ b/cpp2rust/converter/converter_lib.h @@ -38,6 +38,13 @@ bool IsGlobalVar(const clang::VarDecl *decl); bool IsGlobalVar(const clang::Expr *expr); +bool IsRustConstEvaluableExpr(const clang::Expr *expr); + +std::string GetEscapedCharLiteral(char character); + +std::string GetAssertMessageAsString(const clang::Expr *expr, + const clang::ASTContext &ctx); + bool IsComparisonWithNullOp(const clang::BinaryOperator *expr); bool IsInMainFile(const clang::Decl *decl); diff --git a/tests/unit/concepts.cpp b/tests/unit/concepts.cpp index 46dc77537..aeb57b20c 100644 --- a/tests/unit/concepts.cpp +++ b/tests/unit/concepts.cpp @@ -6,6 +6,7 @@ template concept Small = sizeof(T) <= 4; static_assert(Small); +static_assert(sizeof(int) == 4); template concept HasSize = requires(T t) { diff --git a/tests/unit/out/refcount/concepts.rs b/tests/unit/out/refcount/concepts.rs index 6fbb4bc3f..42486c596 100644 --- a/tests/unit/out/refcount/concepts.rs +++ b/tests/unit/out/refcount/concepts.rs @@ -6,6 +6,11 @@ use std::io::prelude::*; use std::io::{Read, Seek, Write}; use std::os::fd::AsFd; use std::rc::{Rc, Weak}; +const _: () = assert!(true, "Small"); +const _: () = assert!( + (::std::mem::size_of::() == 4_usize), + "sizeof(int) == 4" +); #[derive(Clone, ByteRepr, Default)] pub struct Sized {} pub fn is_small_0() -> bool { @@ -46,6 +51,7 @@ pub fn main() { std::process::exit(main_0()); } fn main_0() -> i32 { + const _: () = assert!(!(false), "!Small");; assert!(({ is_small_0() })); assert!(!({ is_small_1() })); assert!(true); diff --git a/tests/unit/out/unsafe/concepts.rs b/tests/unit/out/unsafe/concepts.rs index 932c13fe1..bfb3757e7 100644 --- a/tests/unit/out/unsafe/concepts.rs +++ b/tests/unit/out/unsafe/concepts.rs @@ -6,6 +6,11 @@ use std::collections::BTreeMap; use std::io::{Read, Seek, Write}; use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; use std::rc::Rc; +const _: () = assert!(true, "Small"); +const _: () = assert!( + ((::std::mem::size_of::()) == (4_usize)), + "sizeof(int) == 4" +); #[repr(C)] #[derive(Copy, Clone, Default)] pub struct Sized {} @@ -51,6 +56,7 @@ pub fn main() { } } unsafe fn main_0() -> i32 { + const _: () = assert!(!(false), "!Small");; assert!((unsafe { is_small_0() })); assert!(!(unsafe { is_small_1() })); assert!(true);