From d4401a3369557ee21194897fb992fe9804427728 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Thu, 21 May 2026 16:48:02 +0100 Subject: [PATCH 1/3] Use ConvertPointeeType instead of Convert(t->getPointeeType()) --- cpp2rust/converter/converter.cpp | 23 +++++++++++---- cpp2rust/converter/converter.h | 2 ++ .../converter/models/converter_refcount.cpp | 11 +++++-- .../converter/models/converter_refcount.h | 2 +- .../out/refcount/ptr_to_incomplete_struct.rs | 29 +++++++++++++++++++ .../out/unsafe/ptr_to_incomplete_struct.rs | 20 +++++++++++++ tests/unit/ptr_to_incomplete_struct.c | 10 +++++++ 7 files changed, 88 insertions(+), 9 deletions(-) create mode 100644 tests/unit/out/refcount/ptr_to_incomplete_struct.rs create mode 100644 tests/unit/out/unsafe/ptr_to_incomplete_struct.rs create mode 100644 tests/unit/ptr_to_incomplete_struct.c diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index cab732b0..976255ab 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -86,6 +86,19 @@ bool Converter::ConvertMappedType(clang::QualType qual_type) { return true; } +std::string Converter::ConvertPointeeType(clang::QualType ptr_type) { + assert(!ptr_type.isNull() && ptr_type->isPointerType()); + auto pointee = ptr_type->getPointeeType(); + if (!pointee->isRecordType()) { + return ToString(pointee); + } + + auto str = ToString(ptr_type); + Unwrap(str, "*mut ", ""); + Unwrap(str, "*const ", ""); + return str; +} + bool Converter::VisitBuiltinType(clang::BuiltinType *type) { switch (type->getKind()) { case clang::BuiltinType::Bool: @@ -216,9 +229,8 @@ bool Converter::VisitIncompleteArrayType(clang::IncompleteArrayType *type) { } bool Converter::VisitLValueReferenceType(clang::LValueReferenceType *type) { - StrCat(token::kStar); auto pointee_type = type->getPointeeType(); - StrCat(pointee_type.isConstQualified() ? keyword::kConst : keyword_mut_); + StrCat(pointee_type.isConstQualified() ? "*const" : "*mut"); return Convert(pointee_type); } @@ -250,9 +262,8 @@ bool Converter::VisitPointerType(clang::PointerType *type) { return false; } - StrCat(token::kStar); auto pointee_type = type->getPointeeType(); - StrCat(pointee_type.isConstQualified() ? keyword::kConst : keyword_mut_); + StrCat(pointee_type.isConstQualified() ? "*const" : "*mut"); if (pointee_type->isRecordType() && abstract_structs_.contains(GetID(pointee_type->getAsRecordDecl()))) { StrCat(keyword::kDyn); @@ -1850,7 +1861,7 @@ bool Converter::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) { if (type->isVoidPointerType()) { StrCat(keyword::kAs, type->getPointeeType().isConstQualified() ? "*const" : "*mut"); - Convert(sub_expr->getType()->getPointeeType()); + StrCat(ConvertPointeeType(sub_expr->getType())); } ConvertCast(type); break; @@ -2109,7 +2120,7 @@ void Converter::ConvertBinaryOperator(clang::BinaryOperator *expr) { StrCat(keyword::kAs, "usize"); } StrCat(token::kDiv); - auto pointee_type_as_string = ToString(lhs_type->getPointeeType()); + auto pointee_type_as_string = ConvertPointeeType(lhs_type); auto size_of_as_string = std::format("::std::mem::size_of::<{}>()", pointee_type_as_string); StrCat(size_of_as_string); diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index fa81c6a2..07105383 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -398,6 +398,8 @@ class Converter : public clang::RecursiveASTVisitor { virtual bool Convert(clang::QualType qual_type); virtual bool ConvertMappedType(clang::QualType qual_type); + virtual std::string ConvertPointeeType(clang::QualType ptr_type); + virtual bool Convert(clang::Decl *decl); virtual bool Convert(clang::Stmt *stmt); virtual bool Convert(clang::Expr *expr); diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index accbc2d7..199c5005 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -1080,9 +1080,9 @@ bool ConverterRefCount::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) { if (pointee_type && abstract_structs_.contains(GetID(pointee_type))) { PushConversionKind push(*this, ConversionKind::Unboxed); - StrCat(std::format("({}.to_strong() as Value).as_pointer_dyn()", + StrCat(std::format("({}.to_strong() as Value<{}>).as_pointer_dyn()", ToString(sub_expr->IgnoreCasts()), - ToString(expr->getType()->getPointeeType()))); + ConvertPointeeType(expr->getType()))); computed_expr_type_ = ComputedExprType::FreshPointer; return false; } @@ -1223,6 +1223,12 @@ bool ConverterRefCount::VisitExplicitCastExpr(clang::ExplicitCastExpr *expr) { StrCat(std::format(".cast::<{}>().expect(\"ub:wrong type\")", ConvertPointeeType(expr->getType()))); return false; + } else if (expr->getType()->isVoidPointerType() && + expr->getSubExpr()->getType()->isPointerType()) { + StrCat( + std::format("{}.to_any()", ConvertFreshPointer(expr->getSubExpr()))); + computed_expr_type_ = ComputedExprType::FreshPointer; + return false; } else if (expr->getSubExpr()->getType()->isPointerType() && !expr->getSubExpr()->isNullPointerConstant( ctx_, clang::Expr::NPC_ValueDependentIsNull)) { @@ -2321,6 +2327,7 @@ std::string ConverterRefCount::ConvertPointeeType(clang::QualType ptr_type) { // not have a translation rule. Hence ToString(ptr_type->getPointeeType()) is // not enough auto str = ToString(ptr_type); + Unwrap(str, "PtrDyn<", ">"); Unwrap(str, "Ptr<", ">"); return str; } diff --git a/cpp2rust/converter/models/converter_refcount.h b/cpp2rust/converter/models/converter_refcount.h index c5eafadf..23505c39 100644 --- a/cpp2rust/converter/models/converter_refcount.h +++ b/cpp2rust/converter/models/converter_refcount.h @@ -216,7 +216,7 @@ class ConverterRefCount final : public Converter { std::string ConvertFreshPointer(clang::Expr *expr) override; std::string ConvertPtrType(clang::QualType type); - std::string ConvertPointeeType(clang::QualType ptr_type); + std::string ConvertPointeeType(clang::QualType ptr_type) override; /// The kind of conversion that should be performed. enum class ConversionKind : uint8_t { diff --git a/tests/unit/out/refcount/ptr_to_incomplete_struct.rs b/tests/unit/out/refcount/ptr_to_incomplete_struct.rs new file mode 100644 index 00000000..2c4cf29e --- /dev/null +++ b/tests/unit/out/refcount/ptr_to_incomplete_struct.rs @@ -0,0 +1,29 @@ +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 main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let fp: Value> = Rc::new(RefCell::new((libcc2rs::cout()).clone())); + let p: Value = Rc::new(RefCell::new((*fp.borrow()).clone().to_any())); + let fp2: Value> = Rc::new(RefCell::new( + ((*p.borrow()) + .cast::<::std::fs::File>() + .expect("ub:wrong type")) + .clone(), + )); + assert!( + ((({ + let _lhs = (*fp.borrow()).clone(); + _lhs == (*fp2.borrow()).clone() + }) as i32) + != 0) + ); + return 0; +} diff --git a/tests/unit/out/unsafe/ptr_to_incomplete_struct.rs b/tests/unit/out/unsafe/ptr_to_incomplete_struct.rs new file mode 100644 index 00000000..7c4d37cb --- /dev/null +++ b/tests/unit/out/unsafe/ptr_to_incomplete_struct.rs @@ -0,0 +1,20 @@ +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 fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut fp: *mut ::libc::FILE = libcc2rs::stdout_unsafe(); + let mut p: *mut ::libc::c_void = (fp as *mut ::libc::c_void); + let mut fp2: *mut ::libc::FILE = (p as *mut ::libc::FILE); + assert!(((((fp) == (fp2)) as i32) != 0)); + return 0; +} diff --git a/tests/unit/ptr_to_incomplete_struct.c b/tests/unit/ptr_to_incomplete_struct.c new file mode 100644 index 00000000..3de9f1fe --- /dev/null +++ b/tests/unit/ptr_to_incomplete_struct.c @@ -0,0 +1,10 @@ +#include +#include + +int main(void) { + FILE *fp = stdout; + void *p = (void *)fp; + FILE *fp2 = (FILE *)p; + assert(fp == fp2); + return 0; +} From 1f65fe22133729345668360271df8384484c632b Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Thu, 21 May 2026 17:44:59 +0100 Subject: [PATCH 2/3] Update tests --- tests/unit/out/unsafe/ptr_to_incomplete_struct.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/out/unsafe/ptr_to_incomplete_struct.rs b/tests/unit/out/unsafe/ptr_to_incomplete_struct.rs index 7c4d37cb..db2c4937 100644 --- a/tests/unit/out/unsafe/ptr_to_incomplete_struct.rs +++ b/tests/unit/out/unsafe/ptr_to_incomplete_struct.rs @@ -12,9 +12,9 @@ pub fn main() { } } unsafe fn main_0() -> i32 { - let mut fp: *mut ::libc::FILE = libcc2rs::stdout_unsafe(); + let mut fp: *mut ::std::fs::File = libcc2rs::cout_unsafe(); let mut p: *mut ::libc::c_void = (fp as *mut ::libc::c_void); - let mut fp2: *mut ::libc::FILE = (p as *mut ::libc::FILE); + let mut fp2: *mut ::std::fs::File = (p as *mut ::std::fs::File); assert!(((((fp) == (fp2)) as i32) != 0)); return 0; } From 4ee1cd6d32648980812f6b53e34f76813e7bbcc7 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Thu, 21 May 2026 17:46:46 +0100 Subject: [PATCH 3/3] Update tests --- .../out/unsafe/ptr_to_incomplete_struct.rs | 4 +- tests/unit/out/unsafe/sys_stat.rs | 124 +++--------------- 2 files changed, 21 insertions(+), 107 deletions(-) diff --git a/tests/unit/out/unsafe/ptr_to_incomplete_struct.rs b/tests/unit/out/unsafe/ptr_to_incomplete_struct.rs index db2c4937..7c4d37cb 100644 --- a/tests/unit/out/unsafe/ptr_to_incomplete_struct.rs +++ b/tests/unit/out/unsafe/ptr_to_incomplete_struct.rs @@ -12,9 +12,9 @@ pub fn main() { } } unsafe fn main_0() -> i32 { - let mut fp: *mut ::std::fs::File = libcc2rs::cout_unsafe(); + let mut fp: *mut ::libc::FILE = libcc2rs::stdout_unsafe(); let mut p: *mut ::libc::c_void = (fp as *mut ::libc::c_void); - let mut fp2: *mut ::std::fs::File = (p as *mut ::std::fs::File); + let mut fp2: *mut ::libc::FILE = (p as *mut ::libc::FILE); assert!(((((fp) == (fp2)) as i32) != 0)); return 0; } diff --git a/tests/unit/out/unsafe/sys_stat.rs b/tests/unit/out/unsafe/sys_stat.rs index a90489cd..4bfa9ef2 100644 --- a/tests/unit/out/unsafe/sys_stat.rs +++ b/tests/unit/out/unsafe/sys_stat.rs @@ -8,50 +8,16 @@ use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; use std::rc::Rc; pub unsafe fn test_stat_0() { let mut path: *const u8 = (b"/tmp/cpp2rust_stat_test.tmp\0".as_ptr().cast_mut()).cast_const(); - let mut fp: *mut ::std::fs::File = - match std::ffi::CStr::from_ptr((b"wb\0".as_ptr().cast_mut()).cast_const() as *const i8) - .to_str() - .expect("invalid c-string") - { - v if v == "rb" => std::fs::OpenOptions::new() - .read(true) - .open( - std::ffi::CStr::from_ptr(path as *const i8) - .to_str() - .expect("invalid c-string"), - ) - .ok() - .map_or(std::ptr::null_mut(), |f| Box::into_raw(Box::new(f))), - v if v == "wb" => std::fs::OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open( - std::ffi::CStr::from_ptr(path as *const i8) - .to_str() - .expect("invalid c-string"), - ) - .ok() - .map_or(std::ptr::null_mut(), |f| Box::into_raw(Box::new(f))), - _ => panic!("unsupported mode"), - }; + let mut fp: *mut ::libc::FILE = libc::fopen( + path as *const i8, + (b"wb\0".as_ptr().cast_mut()).cast_const() as *const i8, + ); assert!((((!((fp).is_null())) as i32) != 0)); - { - let bytes = - std::ffi::CStr::from_ptr((b"hello\0".as_ptr().cast_mut()).cast_const() as *const i8) - .to_bytes(); - match (*fp).write_all(bytes) { - Ok(()) => 0, - Err(_) => -1, - } - }; - assert!( - (((({ - Box::from_raw(fp); - 0 - }) == (0)) as i32) - != 0) + libc::fputs( + (b"hello\0".as_ptr().cast_mut()).cast_const() as *const i8, + fp, ); + assert!(((((libc::fclose(fp)) == (0)) as i32) != 0)); let mut st: stat = std::mem::zeroed::(); assert!(((((libc::stat(path as *const i8, (&mut st as *mut stat))) == (0)) as i32) != 0)); assert!(((((st.st_size) == (5_i64)) as i32) != 0)); @@ -59,73 +25,21 @@ pub unsafe fn test_stat_0() { } pub unsafe fn test_fstat_1() { let mut path: *const u8 = (b"/tmp/cpp2rust_fstat_test.tmp\0".as_ptr().cast_mut()).cast_const(); - let mut fp: *mut ::std::fs::File = - match std::ffi::CStr::from_ptr((b"wb\0".as_ptr().cast_mut()).cast_const() as *const i8) - .to_str() - .expect("invalid c-string") - { - v if v == "rb" => std::fs::OpenOptions::new() - .read(true) - .open( - std::ffi::CStr::from_ptr(path as *const i8) - .to_str() - .expect("invalid c-string"), - ) - .ok() - .map_or(std::ptr::null_mut(), |f| Box::into_raw(Box::new(f))), - v if v == "wb" => std::fs::OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open( - std::ffi::CStr::from_ptr(path as *const i8) - .to_str() - .expect("invalid c-string"), - ) - .ok() - .map_or(std::ptr::null_mut(), |f| Box::into_raw(Box::new(f))), - _ => panic!("unsupported mode"), - }; + let mut fp: *mut ::libc::FILE = libc::fopen( + path as *const i8, + (b"wb\0".as_ptr().cast_mut()).cast_const() as *const i8, + ); assert!((((!((fp).is_null())) as i32) != 0)); - { - let bytes = std::ffi::CStr::from_ptr( - (b"hello world\0".as_ptr().cast_mut()).cast_const() as *const i8 - ) - .to_bytes(); - match (*fp).write_all(bytes) { - Ok(()) => 0, - Err(_) => -1, - } - }; - if !(fp).is_null() { - match (*fp).sync_all() { - Ok(_) => 0, - Err(_) => -1, - } - } else { - ::std::io::stdout().flush().unwrap(); - ::std::io::stderr().flush().unwrap(); - 0 - }; - let mut fd: i32 = if fp == libcc2rs::cin_unsafe() { - 0 - } else if fp == libcc2rs::cout_unsafe() { - 1 - } else if fp == libcc2rs::cerr_unsafe() { - 2 - } else { - ::std::os::fd::AsRawFd::as_raw_fd(&*fp) - }; + libc::fputs( + (b"hello world\0".as_ptr().cast_mut()).cast_const() as *const i8, + fp, + ); + libc::fflush(fp); + let mut fd: i32 = libc::fileno(fp); let mut st: stat = std::mem::zeroed::(); assert!(((((libc::fstat(fd, (&mut st as *mut stat))) == (0)) as i32) != 0)); assert!(((((st.st_size) == (11_i64)) as i32) != 0)); - assert!( - (((({ - Box::from_raw(fp); - 0 - }) == (0)) as i32) - != 0) - ); + assert!(((((libc::fclose(fp)) == (0)) as i32) != 0)); libc::unlink(path as *const i8); } pub fn main() {