From fb9c8c9642bccf5981d61ec770c079233910cc2d Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Thu, 3 Sep 2026 11:09:41 +0100 Subject: [PATCH 1/6] 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 038c2cb9..a2c12d17 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -3407,6 +3407,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 a06830fe..52624555 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -405,8 +405,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 ca66b00050fd20261a6eb30ca875233e13d6c60c Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 9 Sep 2026 22:37:52 +0100 Subject: [PATCH 2/6] 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 a2c12d17..292c19de 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1172,6 +1172,12 @@ bool Converter::VisitTypedefDecl([[maybe_unused]] clang::TypedefDecl *decl) { 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 52624555..633ccd05 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -157,6 +157,9 @@ class Converter : public clang::RecursiveASTVisitor { virtual bool VisitTypedefDecl(clang::TypedefDecl *decl); + bool VisitStaticAssertDecl(clang::StaticAssertDecl *decl); + bool VisitConceptDecl(clang::ConceptDecl *decl); + virtual bool VisitCompoundStmt(clang::CompoundStmt *stmt); virtual bool VisitDeclStmt(clang::DeclStmt *stmt); From 05008e308affa513f723c72e63989c7ad53f455e Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 9 Sep 2026 22:40:44 +0100 Subject: [PATCH 3/6] 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 00000000..469b53df --- /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 00000000..7c4be870 --- /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 00000000..05cff6b4 --- /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 c6138857a97c12e4eb9c896074a6cc5327f1cc06 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 9 Sep 2026 22:41:43 +0100 Subject: [PATCH 4/6] 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 292c19de..c9918c59 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -3421,6 +3421,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 633ccd05..e4cb99fe 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -413,6 +413,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 0d6852addbadd2c09d4cff8937b89a9d730b1040 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 9 Sep 2026 22:43:00 +0100 Subject: [PATCH 5/6] 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 469b53df..2dcb0517 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 7c4be870..d75da0d9 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 05cff6b4..65dc1701 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 b99dcc82d95215b9103b0e58967a3395cfbfc3a1 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 9 Sep 2026 22:45:51 +0100 Subject: [PATCH 6/6] 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 2dcb0517..46dc7753 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) {