From d5522de00a8a99902ff31c6c12650224aefa4041 Mon Sep 17 00:00:00 2001 From: Rua Date: Sat, 16 May 2026 23:15:15 +0200 Subject: [PATCH 1/4] refactor: Don't remove raw borrows in `remove_unnecessary_refs` --- c2rust-refactor/src/transform/canonicalize_refs.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/c2rust-refactor/src/transform/canonicalize_refs.rs b/c2rust-refactor/src/transform/canonicalize_refs.rs index c40656e98f..34ce8aa7be 100644 --- a/c2rust-refactor/src/transform/canonicalize_refs.rs +++ b/c2rust-refactor/src/transform/canonicalize_refs.rs @@ -1,5 +1,5 @@ use rustc_ast::ptr::P; -use rustc_ast::{Crate, Expr, ExprKind, Mutability, UnOp}; +use rustc_ast::{BorrowKind, Crate, Expr, ExprKind, Mutability, UnOp}; use rustc_middle::ty::adjustment::{Adjust, AutoBorrow, AutoBorrowMutability}; use rustc_type_ir::sty; @@ -85,7 +85,7 @@ impl Transform for RemoveUnnecessaryRefs { fn remove_ref(expr: &mut P) { match &expr.kind { - ExprKind::AddrOf(_, _, inner) => *expr = inner.clone(), + ExprKind::AddrOf(BorrowKind::Ref, _, inner) => *expr = inner.clone(), _ => {} } } @@ -106,7 +106,7 @@ fn remove_all_derefs(expr: &mut P, cx: &RefactorCtxt) { } fn remove_reborrow(expr: &mut P, cx: &RefactorCtxt) { - if let ExprKind::AddrOf(_, _, ref subexpr) = expr.kind { + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref subexpr) = expr.kind { if let ExprKind::Unary(UnOp::Deref, ref subexpr) = subexpr.kind { if is_pointer(subexpr, cx) { // &* on a pointer produces a reference, so it's not a no-op From 36789dddc419667ab7e9b48018efa42693e09330 Mon Sep 17 00:00:00 2001 From: Rua Date: Sat, 16 May 2026 19:18:30 +0200 Subject: [PATCH 2/4] transpile: Add `CQualTypeId::mutability` helper --- c2rust-transpile/src/c_ast/mod.rs | 12 ++++++++++++ c2rust-transpile/src/convert_type.rs | 10 ++-------- c2rust-transpile/src/translator/functions.rs | 6 +----- c2rust-transpile/src/translator/literals.rs | 7 +------ c2rust-transpile/src/translator/mod.rs | 6 +----- c2rust-transpile/src/translator/pointers.rs | 6 ++---- c2rust-transpile/src/translator/variadic.rs | 7 +------ 7 files changed, 20 insertions(+), 34 deletions(-) diff --git a/c2rust-transpile/src/c_ast/mod.rs b/c2rust-transpile/src/c_ast/mod.rs index 3011f16632..399dc0d5dc 100644 --- a/c2rust-transpile/src/c_ast/mod.rs +++ b/c2rust-transpile/src/c_ast/mod.rs @@ -1,5 +1,6 @@ use crate::c_ast::iterators::{immediate_children_all_types, NodeVisitor}; use crate::iterators::{DFNodes, SomeId}; +use c2rust_ast_builder::properties::Mutability; use c2rust_ast_exporter::clang_ast::LRValue; use indexmap::{IndexMap, IndexSet}; use itertools::Itertools; @@ -2433,6 +2434,13 @@ impl Qualifiers { is_volatile: self.is_volatile || other.is_volatile, } } + + pub fn mutability(self) -> Mutability { + match self.is_const { + true => Mutability::Immutable, + false => Mutability::Mutable, + } + } } /// Qualified type @@ -2449,6 +2457,10 @@ impl CQualTypeId { ctype, } } + + pub fn mutability(self) -> Mutability { + self.qualifiers.mutability() + } } // TODO: these may be interesting, but I'm not sure if they fit here: diff --git a/c2rust-transpile/src/convert_type.rs b/c2rust-transpile/src/convert_type.rs index 05fd52b148..d05e281b6f 100644 --- a/c2rust-transpile/src/convert_type.rs +++ b/c2rust-transpile/src/convert_type.rs @@ -5,7 +5,7 @@ use crate::renamer::*; use crate::translator::variadic::mk_va_list_ty; use crate::TranspilerConfig; use crate::{CrateSet, ExternCrate}; -use c2rust_ast_builder::{mk, properties::*}; +use c2rust_ast_builder::mk; use c2rust_rust_tools::RustEdition; use failure::format_err; use indexmap::IndexSet; @@ -170,13 +170,7 @@ impl TypeConverter { let param = mk().angle_bracketed_args(vec![pointee_ty]); Ok(mk().path_ty(vec![mk().path_segment_with_args("Option", param)])) } else { - let mutbl = if qtype.qualifiers.is_const { - Mutability::Immutable - } else { - Mutability::Mutable - }; - - Ok(mk().set_mutbl(mutbl).ptr_ty(pointee_ty)) + Ok(mk().set_mutbl(qtype.mutability()).ptr_ty(pointee_ty)) } } diff --git a/c2rust-transpile/src/translator/functions.rs b/c2rust-transpile/src/translator/functions.rs index 7071bff675..6e7875e883 100644 --- a/c2rust-transpile/src/translator/functions.rs +++ b/c2rust-transpile/src/translator/functions.rs @@ -341,11 +341,7 @@ impl<'c> Translation<'c> { typ: CQualTypeId, ) -> TranslationResult { if self.ast_context.is_va_list(typ.ctype) { - let mutbl = if typ.qualifiers.is_const { - Mutability::Immutable - } else { - Mutability::Mutable - }; + let mutbl = typ.mutability(); let ty = mk().abs_path_ty(vec!["core", "ffi", "VaList"]); return Ok(ConvertedFunctionParam { mutbl, ty }); } diff --git a/c2rust-transpile/src/translator/literals.rs b/c2rust-transpile/src/translator/literals.rs index e1bfeca9f3..263e5947f6 100644 --- a/c2rust-transpile/src/translator/literals.rs +++ b/c2rust-transpile/src/translator/literals.rs @@ -193,13 +193,8 @@ impl<'c> Translation<'c> { })) } else { Ok(val.and_then(|val| { - let mutbl = if qty.qualifiers.is_const { - Mutability::Immutable - } else { - Mutability::Mutable - }; let local = mk().local( - mk().set_mutbl(mutbl).ident_pat(&fresh_name), + mk().set_mutbl(qty.mutability()).ident_pat(&fresh_name), Some(fresh_ty), Some(val), ); diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index a86e745bdd..e2850d9bb2 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -2630,11 +2630,7 @@ impl<'c> Translation<'c> { self.convert_type(typ.ctype)? }; - let mutbl = if typ.qualifiers.is_const { - Mutability::Immutable - } else { - Mutability::Mutable - }; + let mutbl = typ.mutability(); Ok(ConvertedVariable { ty, mutbl, init }) } diff --git a/c2rust-transpile/src/translator/pointers.rs b/c2rust-transpile/src/translator/pointers.rs index a15fef518c..3961fbe18e 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -110,16 +110,14 @@ impl<'c> Translation<'c> { let mut needs_cast = false; let mut ref_cast_pointee_ty = None; - let mutbl = if pointee_cty.qualifiers.is_const { - Mutability::Immutable - } else if ctx.is_const { + let mutbl = if ctx.is_const && !pointee_cty.qualifiers.is_const { // const contexts aren't able to use &mut, so we work around that // by using & and an extra cast through & to *const to *mut // TODO: Rust 1.83: Allowed, so this can be removed. needs_cast = true; Mutability::Immutable } else { - Mutability::Mutable + pointee_cty.mutability() }; // Narrow string literals are translated directly as `[u8; N]` literals when their address diff --git a/c2rust-transpile/src/translator/variadic.rs b/c2rust-transpile/src/translator/variadic.rs index 0357085340..e5c74d4ca7 100644 --- a/c2rust-transpile/src/translator/variadic.rs +++ b/c2rust-transpile/src/translator/variadic.rs @@ -199,12 +199,7 @@ impl<'c> Translation<'c> { is_variadic, )?; - let m = if p.qualifiers.is_const { - Mutability::Immutable - } else { - Mutability::Mutable - }; - Some(mk().set_mutbl(m).ptr_ty(fn_ty)) + Some(mk().set_mutbl(p.mutability()).ptr_ty(fn_ty)) } else { None } From fbb9dc922866c04d782b7128b2102bfe6446eea3 Mon Sep 17 00:00:00 2001 From: Rua Date: Sat, 16 May 2026 19:18:41 +0200 Subject: [PATCH 3/4] transpile: Add panicking variant of `TypedAstContext::type_for_kind` --- c2rust-transpile/src/c_ast/conversion.rs | 10 +++------- c2rust-transpile/src/c_ast/mod.rs | 18 +++++++++--------- c2rust-transpile/src/translator/mod.rs | 10 +++------- c2rust-transpile/src/translator/operators.rs | 6 +----- 4 files changed, 16 insertions(+), 28 deletions(-) diff --git a/c2rust-transpile/src/c_ast/conversion.rs b/c2rust-transpile/src/c_ast/conversion.rs index 30108e6238..0c823b6009 100644 --- a/c2rust-transpile/src/c_ast/conversion.rs +++ b/c2rust-transpile/src/c_ast/conversion.rs @@ -488,7 +488,7 @@ impl ConversionContext { for (_decl_id, located_kind) in self.typed_context.c_decls.iter() { if let kind @ CDeclKind::Function { .. } = &located_kind.kind { let new_kind = self.typed_context.fn_decl_ty_with_declared_args(kind); - if self.typed_context.type_for_kind(&new_kind).is_none() { + if self.typed_context.try_type_for_kind(&new_kind).is_none() { // Create and insert fn type let new_id = CTypeId(self.id_mapper.fresh_id()); self.typed_context @@ -2126,9 +2126,7 @@ impl ConversionContext { } }; log::trace!("Selected kind {kind} for typedef {name}"); - Some(CQualTypeId::new( - self.typed_context.type_for_kind(&kind).unwrap(), - )) + Some(CQualTypeId::new(self.typed_context.type_for_kind(&kind))) }) .unwrap_or(typ); @@ -2171,9 +2169,7 @@ impl ConversionContext { } }; log::trace!("Selected kind {kind} for typedef {name}"); - Some(CQualTypeId::new( - self.typed_context.type_for_kind(&kind).unwrap(), - )) + Some(CQualTypeId::new(self.typed_context.type_for_kind(&kind))) }; let file = self .typed_context diff --git a/c2rust-transpile/src/c_ast/mod.rs b/c2rust-transpile/src/c_ast/mod.rs index 399dc0d5dc..f542fd2a12 100644 --- a/c2rust-transpile/src/c_ast/mod.rs +++ b/c2rust-transpile/src/c_ast/mod.rs @@ -743,12 +743,17 @@ impl TypedAstContext { ty.map(|ty| (expr_id, ty)) } - pub fn type_for_kind(&self, kind: &CTypeKind) -> Option { + pub fn try_type_for_kind(&self, kind: &CTypeKind) -> Option { self.c_types .iter() .find_map(|(id, k)| if kind == &k.kind { Some(*id) } else { None }) } + pub fn type_for_kind(&self, kind: &CTypeKind) -> CTypeId { + self.try_type_for_kind(kind) + .expect("could not find type for CTypeKind::{kind:?}") + } + pub fn resolve_type_id(&self, typ: CTypeId) -> CTypeId { use CTypeKind::*; let ty = match self.index(typ).kind { @@ -846,9 +851,7 @@ impl TypedAstContext { pub fn fn_declref_ty_with_declared_args(&self, func_expr: CExprId) -> Option { if let Some(func_decl @ CDeclKind::Function { .. }) = self.fn_declref_decl(func_expr) { let kind_with_declared_args = self.fn_decl_ty_with_declared_args(func_decl); - let specific_typ = self - .type_for_kind(&kind_with_declared_args) - .unwrap_or_else(|| panic!("no type for kind {kind_with_declared_args:?}")); + let specific_typ = self.type_for_kind(&kind_with_declared_args); return Some(CQualTypeId::new(specific_typ)); } None @@ -1298,10 +1301,7 @@ impl TypedAstContext { CUnTypeOp::AlignOf => CTypeKind::Size, CUnTypeOp::PreferredAlignOf => CTypeKind::Size, }; - let ty = self - .ast_context - .type_for_kind(&kind) - .expect("CTypeKind::Size should be size_t"); + let ty = self.ast_context.type_for_kind(&kind); Some(CQualTypeId::new(ty)) } _ => return, @@ -2021,7 +2021,7 @@ impl CUnOp { } Not => { return ast_context - .type_for_kind(&CTypeKind::Int) + .try_type_for_kind(&CTypeKind::Int) .map(CQualTypeId::new) } Real | Imag => { diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index e2850d9bb2..ca1cef39f6 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -3081,7 +3081,7 @@ impl<'c> Translation<'c> { if let Some(ty) = self .ast_context - .type_for_kind(&kind_with_declared_args) + .try_type_for_kind(&kind_with_declared_args) .map(CQualTypeId::new) { let ty = self.convert_type(ty.ctype)?; @@ -3266,14 +3266,10 @@ impl<'c> Translation<'c> { // but the function's declaration will. let kind_with_declared_args = self.ast_context.fn_decl_ty_with_declared_args(func_decl); - let func_ty = self - .ast_context - .type_for_kind(&kind_with_declared_args) - .unwrap_or_else(|| panic!("no type for kind {kind_with_declared_args:?}")); + let func_ty = self.ast_context.type_for_kind(&kind_with_declared_args); let func_ptr_ty = self .ast_context - .type_for_kind(&CTypeKind::Pointer(CQualTypeId::new(func_ty))) - .unwrap_or_else(|| panic!("no type for kind {kind_with_declared_args:?}")); + .type_for_kind(&CTypeKind::Pointer(CQualTypeId::new(func_ty))); CQualTypeId::new(func_ptr_ty) } else { diff --git a/c2rust-transpile/src/translator/operators.rs b/c2rust-transpile/src/translator/operators.rs index a03f240ffe..49a8bc4498 100644 --- a/c2rust-transpile/src/translator/operators.rs +++ b/c2rust-transpile/src/translator/operators.rs @@ -640,11 +640,7 @@ impl<'c> Translation<'c> { let one_type_id = if let CTypeKind::Pointer(..) = self.ast_context.resolve_type(arg_type.ctype).kind { - CQualTypeId::new( - self.ast_context - .type_for_kind(&CTypeKind::Int) - .ok_or_else(|| format_err!("couldn't find type for CTypeKind::Int"))?, - ) + CQualTypeId::new(self.ast_context.type_for_kind(&CTypeKind::Int)) } else { arg_type }; From dcaf8fe9c3159ec6b4ceb75c8238065a6c3f1605 Mon Sep 17 00:00:00 2001 From: Rua Date: Sat, 16 May 2026 20:47:04 +0200 Subject: [PATCH 4/4] transpile: Use strict provenance APIs for pointer casts --- c2rust-transpile/src/translator/pointers.rs | 111 ++++++++++++++++---- tests/unit/pointers/src/test_pointers.rs | 2 +- tests/unit/statics/src/test_sections.rs | 2 +- 3 files changed, 94 insertions(+), 21 deletions(-) diff --git a/c2rust-transpile/src/translator/pointers.rs b/c2rust-transpile/src/translator/pointers.rs index 3961fbe18e..80fe5e8fd7 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -1,8 +1,8 @@ -use std::ops::Index; - use c2rust_ast_builder::{mk, properties::Mutability}; use c2rust_ast_exporter::clang_ast::LRValue; +use c2rust_rust_tools::RustEdition; use failure::{err_msg, format_err}; +use std::ops::Index; use syn::{BinOp, Expr, Type, UnOp}; use crate::c_ast::CUnOp; @@ -483,17 +483,68 @@ impl<'c> Translation<'c> { WithStmts::new_val(transmute_expr(intptr_t, target_ty, val)).set_unsafe() })) - } else if source_ty_kind.is_bool() { - self.use_crate(ExternCrate::Libc); - Ok(val.map(|mut val| { - // First cast the boolean to pointer size - val = mk().cast_expr(val, mk().abs_path_ty(vec!["libc", "size_t"])); - mk().cast_expr(val, target_ty) - })) - } else if let &CTypeKind::Enum(..) = source_ty_kind { - val.try_map(|val| self.convert_cast_from_enum(target_cty, val)) + } else + // Rust 1.90: `const_strict_provenance` feature added + // Rust 1.91: stabilized + if ctx.is_const && self.tcfg.edition < RustEdition::Edition2024 { + if source_ty_kind.is_bool() { + self.use_crate(ExternCrate::Libc); + Ok(val.map(|mut val| { + // First cast the boolean to pointer size + val = mk().cast_expr(val, mk().abs_path_ty(vec!["libc", "size_t"])); + mk().cast_expr(val, target_ty) + })) + } else if let &CTypeKind::Enum(..) = source_ty_kind { + val.try_map(|val| self.convert_cast_from_enum(target_cty, val)) + } else { + Ok(val.map(|val| mk().cast_expr(val, target_ty))) + } } else { - Ok(val.map(|val| mk().cast_expr(val, target_ty))) + // First cast the value to `usize`. + let source_type_kind = &self.ast_context.resolve_type(source_cty).kind; + let size_type_id = self.ast_context.type_for_kind(&CTypeKind::Size); + + let val = if let &CTypeKind::Enum(..) = source_type_kind { + val.try_map(|val| self.convert_cast_from_enum(size_type_id, val))? + } else { + let size_type_rs = self.convert_type(size_type_id)?; + val.map(|val| mk().cast_expr(val, size_type_rs)) + }; + + // Then convert the `usize` into a pointer. + let pointee_type_id = self + .ast_context + .get_pointee_qual_type(target_cty) + .expect("target type must be a pointer"); + let mutability = pointee_type_id.mutability(); + + let fn_name = match self.tcfg.edition { + RustEdition::Edition2021 => { + // Rust 1.76: feature name changed to `exposed_provenance[_mut]` + // Rust 1.84: stabilized + self.use_feature("strict_provenance"); + + // Rust 1.79: method name changed to `with_exposed_provenance[_mut]` + match mutability { + Mutability::Immutable => "from_exposed_addr", + Mutability::Mutable => "from_exposed_addr_mut", + } + } + RustEdition::Edition2024 => match mutability { + Mutability::Immutable => "with_exposed_provenance", + Mutability::Mutable => "with_exposed_provenance_mut", + }, + }; + let pointee_type_rs = self.convert_pointee_type(pointee_type_id.ctype)?; + let type_args = mk().angle_bracketed_args(vec![pointee_type_rs]); + let fn_expr = mk().abs_path_expr(vec![ + mk().path_segment("core"), + mk().path_segment("ptr"), + mk().path_segment_with_args(fn_name, type_args), + ]); + let val = val.map(|val| mk().call_expr(fn_expr, vec![val])); + + Ok(val) } } @@ -512,18 +563,40 @@ impl<'c> Translation<'c> { )); } - let target_ty = self.convert_type(target_cty)?; - let source_ty = self.convert_type(source_cty)?; - let target_ty_kind = &self.ast_context.resolve_type(target_cty).kind; + let target_type_rs = self.convert_type(target_cty)?; if self.ast_context.is_function_pointer(source_cty) { + let source_ty = self.convert_type(source_cty)?; + Ok(val.and_then(|val| { - WithStmts::new_val(transmute_expr(source_ty, target_ty, val)).set_unsafe() + WithStmts::new_val(transmute_expr(source_ty, target_type_rs, val)).set_unsafe() })) - } else if let &CTypeKind::Enum(enum_decl_id) = target_ty_kind { - val.try_map(|val| self.convert_cast_to_enum(ctx, target_cty, enum_decl_id, expr, val)) } else { - Ok(val.map(|val| mk().cast_expr(val, target_ty))) + // First convert the pointer to `usize`. + let method_name = match self.tcfg.edition { + RustEdition::Edition2021 => { + // Rust 1.76: feature name changed to `exposed_provenance` + // Rust 1.84: stabilized + self.use_feature("strict_provenance"); + + // Rust 1.79: method name changed to `expose_provenance` + "expose_addr" + } + RustEdition::Edition2024 => "expose_provenance", + }; + + let val = val.map(|val| mk().method_call_expr(val, method_name, vec![])); + + // Then cast the `usize` to the target type. + let target_ty_kind = &self.ast_context.resolve_type(target_cty).kind; + + if let &CTypeKind::Enum(enum_decl_id) = target_ty_kind { + val.try_map(|val| { + self.convert_cast_to_enum(ctx, target_cty, enum_decl_id, expr, val) + }) + } else { + Ok(val.map(|val| mk().cast_expr(val, target_type_rs))) + } } } diff --git a/tests/unit/pointers/src/test_pointers.rs b/tests/unit/pointers/src/test_pointers.rs index 6a95f04988..beb74e85c6 100644 --- a/tests/unit/pointers/src/test_pointers.rs +++ b/tests/unit/pointers/src/test_pointers.rs @@ -1,4 +1,4 @@ -//! feature_c_variadic, feature_raw_ref_op +//! feature_c_variadic, feature_raw_ref_op, feature_strict_provenance use crate::function_pointers::rust_entry3; use crate::pointer_arith::rust_entry2; diff --git a/tests/unit/statics/src/test_sections.rs b/tests/unit/statics/src/test_sections.rs index 964183f83e..f4516fe32f 100644 --- a/tests/unit/statics/src/test_sections.rs +++ b/tests/unit/statics/src/test_sections.rs @@ -1,4 +1,4 @@ -//! feature_raw_ref_op +//! feature_raw_ref_op, feature_strict_provenance #[cfg(not(target_os = "macos"))] use crate::attributes::{rust_no_attrs, rust_used_static, rust_used_static2, rust_used_static3};