Skip to content

Commit f99ddd3

Browse files
Copilotnunoplopes
andauthored
Fix #319: Fix implicit pointer cast between types with different Rust representations (#336)
When size_t and unsigned long (or similar typedef-aliased types) are used interchangeably as pointer types, Clang emits a CK_NoOp ImplicitCastExpr in the AST. The translator was silently dropping this, producing a type mismatch in the generated Rust code (`Ptr<usize>` passed where `Ptr<u64>` is expected). --------- Co-authored-by: Nuno Lopes <nuno.lopes@tecnico.ulisboa.pt>
1 parent a6eac06 commit f99ddd3

7 files changed

Lines changed: 128 additions & 9 deletions

File tree

‎cpp2rust/converter/converter.cpp‎

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -119,13 +119,13 @@ std::string Converter::ConvertPointeeType(clang::QualType ptr_type) {
119119
assert(!ptr_type.isNull() && ptr_type->isPointerType());
120120
auto pointee = ptr_type->getPointeeType();
121121
if (!pointee->isRecordType()) {
122-
return ToString(pointee);
122+
return std::string(Trim(ToString(pointee)));
123123
}
124124

125125
auto str = ToString(ptr_type);
126126
Unwrap(str, "*mut ", "");
127127
Unwrap(str, "*const ", "");
128-
return str;
128+
return std::string(Trim(str));
129129
}
130130

131131
bool Converter::VisitBuiltinType(clang::BuiltinType *type) {
@@ -1875,6 +1875,29 @@ void Converter::ConvertParamTy(clang::QualType param_type, clang::Expr *expr) {
18751875
} else {
18761876
ConvertVarInit(param_type, expr);
18771877
}
1878+
ConvertParamTyPointerCastIfNeeded(param_type, expr);
1879+
}
1880+
1881+
void Converter::ConvertParamTyPointerCastIfNeeded(clang::QualType param_type,
1882+
clang::Expr *expr) {
1883+
if (!param_type->isPointerType() || !expr->getType()->isPointerType() ||
1884+
IsVaListType(param_type) || IsVaListType(expr->getType())) {
1885+
return;
1886+
}
1887+
switch (GetConstCastType(param_type->getPointeeType(),
1888+
expr->getType()->getPointeeType())) {
1889+
case ConstCastType::MutableToConst:
1890+
StrCat(".cast_const()");
1891+
return;
1892+
case ConstCastType::ConstToMutable:
1893+
StrCat(".cast_mut()");
1894+
return;
1895+
default:
1896+
break;
1897+
}
1898+
if (!IsCastRedundantInRust(expr, param_type)) {
1899+
ConvertCast(param_type);
1900+
}
18781901
}
18791902

18801903
void Converter::EmitHoistedArgs(CallInfo &info) {
@@ -2257,6 +2280,7 @@ bool Converter::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) {
22572280
}
22582281
case clang::CastKind::CK_NoOp: {
22592282
const char *suffix = nullptr;
2283+
bool type_changed = false;
22602284
if (expr->getType()->isPointerType() &&
22612285
sub_expr->getType()->isPointerType() &&
22622286
!clang::isa<clang::CXXThisExpr>(expr->IgnoreImplicit())) {
@@ -2269,15 +2293,22 @@ bool Converter::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) {
22692293
suffix = ".cast_mut()";
22702294
break;
22712295
default:
2296+
type_changed = !IsCastRedundantInRust(sub_expr, type);
22722297
break;
22732298
}
22742299
}
2275-
{
2276-
PushParen paren(*this, suffix);
2300+
if (type_changed) {
2301+
PushParen paren(*this);
22772302
Convert(sub_expr);
2278-
}
2279-
if (suffix) {
2280-
StrCat(suffix);
2303+
ConvertCast(type);
2304+
} else {
2305+
{
2306+
PushParen paren(*this, suffix);
2307+
Convert(sub_expr);
2308+
}
2309+
if (suffix) {
2310+
StrCat(suffix);
2311+
}
22812312
}
22822313
break;
22832314
}

‎cpp2rust/converter/converter.h‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,15 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
282282

283283
void ConvertParamTy(clang::QualType param_type, clang::Expr *expr);
284284

285+
// Emits a pointer-type adjustment (const/mut fixup or reinterpret cast)
286+
// after `expr` has been converted, for cases where the argument's Rust
287+
// pointee type differs from the parameter's Rust pointee type even though
288+
// Clang did not insert an implicit cast node for the call argument (e.g.
289+
// when two C types are canonically identical, such as `size_t` and
290+
// `unsigned long`, but map to different Rust types).
291+
virtual void ConvertParamTyPointerCastIfNeeded(clang::QualType param_type,
292+
clang::Expr *expr);
293+
285294
void EmitHoistedArgs(CallInfo &info);
286295

287296
void EmitArgList(const CallInfo &info);

‎cpp2rust/converter/models/converter_refcount.cpp‎

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,6 +1241,16 @@ bool ConverterRefCount::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) {
12411241

12421242
if (expr->getCastKind() == clang::CastKind::CK_NoOp) {
12431243
Convert(sub_expr);
1244+
1245+
if (expr->getType()->isPointerType() &&
1246+
sub_expr->getType()->isPointerType()) {
1247+
auto dest_type = ConvertPointeeType(expr->getType());
1248+
if (dest_type != ConvertPointeeType(sub_expr->getType())) {
1249+
StrCat(std::format(".reinterpret_cast::<{}>()", dest_type));
1250+
computed_expr_type_ = ComputedExprType::FreshPointer;
1251+
return false;
1252+
}
1253+
}
12441254
return false;
12451255
}
12461256

@@ -2574,7 +2584,7 @@ std::string ConverterRefCount::ConvertPointeeType(clang::QualType ptr_type) {
25742584
PushConversionKind push(*this, ConversionKind::Unboxed);
25752585
auto pointee = ptr_type->getPointeeType();
25762586
if (!pointee->isRecordType()) {
2577-
return ToString(pointee);
2587+
return std::string(Trim(ToString(pointee)));
25782588
}
25792589

25802590
// Pointee of a pointer to incomplete type is an incomplete type that does
@@ -2583,7 +2593,19 @@ std::string ConverterRefCount::ConvertPointeeType(clang::QualType ptr_type) {
25832593
auto str = ToString(ptr_type);
25842594
Unwrap(str, "PtrDyn<", ">");
25852595
Unwrap(str, "Ptr<", ">");
2586-
return str;
2596+
return std::string(Trim(str));
2597+
}
2598+
2599+
void ConverterRefCount::ConvertParamTyPointerCastIfNeeded(
2600+
clang::QualType param_type, clang::Expr *expr) {
2601+
if (!param_type->isPointerType() || !expr->getType()->isPointerType() ||
2602+
IsVaListType(param_type) || IsVaListType(expr->getType())) {
2603+
return;
2604+
}
2605+
auto dest_type = ConvertPointeeType(param_type);
2606+
if (dest_type != ConvertPointeeType(expr->getType())) {
2607+
StrCat(std::format(".reinterpret_cast::<{}>()", dest_type));
2608+
}
25872609
}
25882610

25892611
bool ConverterRefCount::ShouldConvertMethod(const clang::CXXMethodDecl *decl) {

‎cpp2rust/converter/models/converter_refcount.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,9 @@ class ConverterRefCount final : public Converter {
263263
std::string ConvertPtrType(clang::QualType type);
264264
std::string ConvertPointeeType(clang::QualType ptr_type) override;
265265

266+
void ConvertParamTyPointerCastIfNeeded(clang::QualType param_type,
267+
clang::Expr *expr) override;
268+
266269
std::string ConvertSubscriptIndex(clang::Expr *idx);
267270

268271
std::string GetSafeTypeAsString(clang::QualType qual_type) const;

‎tests/unit/implicit_ptr_cast.c‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#include <assert.h>
2+
#include <stddef.h>
3+
4+
static void write_ulong(unsigned long *p) { *p = 42; }
5+
6+
int main(void) {
7+
size_t x = 0;
8+
write_ulong(&x);
9+
assert(x == 42);
10+
return 0;
11+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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 write_ulong_0(p: Ptr<u64>) {
10+
let p: Value<Ptr<u64>> = Rc::new(RefCell::new(p));
11+
(*p.borrow()).write(42_u64);
12+
}
13+
pub fn main() {
14+
std::process::exit(main_0());
15+
}
16+
fn main_0() -> i32 {
17+
let x: Value<usize> = Rc::new(RefCell::new(0_usize));
18+
({ write_ulong_0((x.as_pointer()).reinterpret_cast::<u64>()) });
19+
assert!(((((*x.borrow()) == 42_usize) as i32) != 0));
20+
return 0;
21+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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+
pub unsafe fn write_ulong_0(mut p: *mut u64) {
10+
(*p) = 42_u64;
11+
}
12+
pub fn main() {
13+
unsafe {
14+
std::process::exit(main_0() as i32);
15+
}
16+
}
17+
unsafe fn main_0() -> i32 {
18+
let mut x: usize = 0_usize;
19+
(unsafe { write_ulong_0((&mut x as *mut usize) as *mut u64) });
20+
assert!(((((x) == (42_usize)) as i32) != 0));
21+
return 0;
22+
}

0 commit comments

Comments
 (0)