Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 81 additions & 37 deletions c2rust-transpile/src/c_ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,13 @@ impl<T> Located<T> {
pub fn end_loc(&self) -> Option<SrcLoc> {
self.loc.map(|loc| loc.end())
}

pub fn with_kind<U>(&self, kind: U) -> Located<U> {
Located {
loc: self.loc,
kind,
}
}
}

/// This holds a [`SrcLoc`] and its [`include_path`](TypedAstContext::include_path)
Expand Down Expand Up @@ -1075,51 +1082,84 @@ impl TypedAstContext {
}
}

/// Identifies typedefs that name unnamed types.
/// Later, the two declarations can be collapsed into a single name and declaration,
/// eliminating the typedef altogether.
pub fn set_prenamed_decls(&mut self) {
/// Eliminates typedefs in various ways. Currently does the following:
/// - Typedefs that resolve to one of `PULLBACK_KINDS` will point directly to the target type,
/// without intermediate (compiler-internal) typedefs if there are any.
/// - Typedefs that point to structs, unions or enums that have no name, or the same name as
/// the typedef, are stored in `prenamed_decls` so they can be skipped later.
pub fn bypass_typedefs(&mut self) {
let mut replacements: HashMap<CDeclId, CDecl> = HashMap::new();
let mut prenamed_decls: IndexMap<CDeclId, CDeclId> = IndexMap::new();

for (&decl_id, decl) in self.iter_decls() {
if let CDeclKind::Typedef { ref name, typ, .. } = decl.kind {
if let Some(subdecl_id) = self.resolve_type(typ.ctype).kind.as_underlying_decl() {
use CDeclKind::*;
let is_unnamed = match self[subdecl_id].kind {
Struct { name: None, .. }
| Union { name: None, .. }
| Enum { name: None, .. } => true,

// Detect case where typedef and struct share the same name.
// In this case the purpose of the typedef was simply to eliminate
// the need for the 'struct' tag when referring to the type name.
Struct {
name: Some(ref target_name),
..
}
| Union {
name: Some(ref target_name),
..
}
| Enum {
name: Some(ref target_name),
..
} => name == target_name,

_ => false,
};

if is_unnamed
&& !prenamed_decls
.values()
.any(|decl_id| *decl_id == subdecl_id)
{
prenamed_decls.insert(decl_id, subdecl_id);
let CDeclKind::Typedef {
ref name,
typ,
is_implicit,
ref target_dependent_macro,
} = decl.kind else {
continue
};
let resolved_type_id = self.resolve_type_id(typ.ctype);
let resolved_type_kind = &self[resolved_type_id].kind;

if CTypeKind::PULLBACK_KINDS.contains(resolved_type_kind)
&& name == resolved_type_kind.as_str()
{
// If the typedef resolves to a portable type, and its name matches the
// expected name, then replace its definition to directly target the type,
// bypassing any intermediate typedefs.
let kind = CDeclKind::Typedef {
name: name.clone(),
typ: typ.with_ctype(resolved_type_id),
is_implicit,
target_dependent_macro: target_dependent_macro.clone(),
};
replacements.insert(decl_id, decl.with_kind(kind));
} else if let Some(subdecl_id) = resolved_type_kind.as_underlying_decl() {
use CDeclKind::*;

// Identifies typedefs that name unnamed types.
// Later, the two declarations can be collapsed into a single name and declaration,
// eliminating the typedef altogether.
let is_unnamed = match self[subdecl_id].kind {
Struct { name: None, .. }
| Union { name: None, .. }
| Enum { name: None, .. } => true,

// Detect case where typedef and struct share the same name.
// In this case the purpose of the typedef was simply to eliminate
// the need for the 'struct' tag when referring to the type name.
Struct {
name: Some(ref target_name),
..
}
| Union {
name: Some(ref target_name),
..
}
| Enum {
name: Some(ref target_name),
..
} => name == target_name,

_ => false,
};

if is_unnamed
&& !prenamed_decls
.values()
.any(|decl_id| *decl_id == subdecl_id)
{
prenamed_decls.insert(decl_id, subdecl_id);
}
}
}

for (decl_id, decl) in replacements {
self.c_decls[&decl_id] = decl;
}

self.prenamed_decls = prenamed_decls;
}

Expand Down Expand Up @@ -2492,6 +2532,10 @@ impl CQualTypeId {
ctype,
}
}

pub fn with_ctype(self, ctype: CTypeId) -> Self {
Self { ctype, ..self }
}
}

// TODO: these may be interesting, but I'm not sure if they fit here:
Expand Down
4 changes: 1 addition & 3 deletions c2rust-transpile/src/translator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -709,9 +709,7 @@ pub fn translate(
None
};

// Identify typedefs that name unnamed types and collapse the two declarations
// into a single name and declaration, eliminating the typedef altogether.
t.ast_context.set_prenamed_decls();
t.ast_context.bypass_typedefs();

// Headers often pull in declarations that are unused;
// we simplify the translator output by omitting those.
Expand Down
20 changes: 10 additions & 10 deletions c2rust-transpile/tests/snapshots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,11 @@ fn test_main_fn() {
transpile("main_fn.c").run();
}

#[test]
fn test_out_of_range_lit() {
transpile("out_of_range_lit.c").run();
}

#[test]
fn test_predefined() {
transpile("predefined.c").run();
Expand Down Expand Up @@ -472,6 +477,11 @@ fn test_str_init() {
transpile("str_init.c").run();
}

#[test]
fn test_typedefidx() {
transpile("typedefidx.c").run();
}

#[test]
fn test_types_compatible() {
transpile("types_compatible.c").run();
Expand Down Expand Up @@ -526,11 +536,6 @@ fn test_macros_os_specific() {
transpile("macros.c").os_specific(true).run();
}

#[test]
fn test_out_of_range_lit() {
transpile("out_of_range_lit.c").os_specific(true).run();
}

#[test]
fn test_rnd() {
transpile("rnd.c").os_specific(true).run();
Expand All @@ -549,11 +554,6 @@ fn test_sigign() {
.run();
}

#[test]
fn test_typedefidx() {
transpile("typedefidx.c").os_specific(true).run();
}

#[test]
fn test_types() {
transpile("types.c")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ extern "C" {
__n: size_t,
) -> *mut ::core::ffi::c_void;
}
pub type __darwin_size_t = usize;
pub type size_t = __darwin_size_t;
pub type size_t = usize;
#[no_mangle]
pub unsafe extern "C" fn errno_is_error() -> bool {
return *__error() != 0 as ::core::ffi::c_int;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ unsafe extern "C" {
__n: size_t,
) -> *mut ::core::ffi::c_void;
}
pub type __darwin_size_t = usize;
pub type size_t = __darwin_size_t;
pub type size_t = usize;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn errno_is_error() -> bool {
return *__error() != 0 as ::core::ffi::c_int;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
source: c2rust-transpile/tests/snapshots.rs
expression: cat tests/snapshots/os-specific/out_of_range_lit.2021.macos.rs
expression: cat tests/snapshots/out_of_range_lit.2021.clang15.rs
---
#![allow(
clippy::missing_safety_doc,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
source: c2rust-transpile/tests/snapshots.rs
expression: cat tests/snapshots/os-specific/out_of_range_lit.2024.macos.rs
expression: cat tests/snapshots/out_of_range_lit.2024.clang15.rs
---
#![allow(
clippy::missing_safety_doc,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,8 @@ expression: cat tests/snapshots/os-specific/rnd.2021.linux.clang15.rs
extern "C" {
fn abs(__x: ::core::ffi::c_int) -> ::core::ffi::c_int;
}
pub type __int32_t = i32;
pub type __uint32_t = u32;
pub type int32_t = __int32_t;
pub type uint32_t = __uint32_t;
pub type int32_t = i32;
pub type uint32_t = u32;
#[no_mangle]
pub static mut cur_rand_seed: uint32_t = 0 as uint32_t;
#[no_mangle]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@ expression: cat tests/snapshots/os-specific/rnd.2024.linux.clang15.rs
unsafe extern "C" {
unsafe fn abs(__x: ::core::ffi::c_int) -> ::core::ffi::c_int;
}
pub type __int32_t = i32;
pub type __uint32_t = u32;
pub type int32_t = __int32_t;
pub type uint32_t = __uint32_t;
pub type int32_t = i32;
pub type uint32_t = u32;
#[unsafe(no_mangle)]
pub static mut cur_rand_seed: uint32_t = 0 as uint32_t;
#[unsafe(no_mangle)]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
source: c2rust-transpile/tests/snapshots.rs
expression: cat tests/snapshots/os-specific/typedefidx.2021.linux.clang15.rs
expression: cat tests/snapshots/typedefidx.2021.clang15.rs
---
#![allow(
clippy::missing_safety_doc,
Expand All @@ -12,8 +12,7 @@ expression: cat tests/snapshots/os-specific/typedefidx.2021.linux.clang15.rs
unused_mut
)]
pub type size_t = usize;
pub type __uint64_t = u64;
pub type uint64_t = __uint64_t;
pub type uint64_t = u64;
#[no_mangle]
pub unsafe extern "C" fn index_typedef(mut arr: *mut uint64_t) {
let mut foo3: size_t = *arr.offset(25 as ::core::ffi::c_int as isize) as size_t;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
source: c2rust-transpile/tests/snapshots.rs
expression: cat tests/snapshots/os-specific/typedefidx.2024.linux.clang15.rs
expression: cat tests/snapshots/typedefidx.2024.clang15.rs
---
#![allow(
clippy::missing_safety_doc,
Expand All @@ -13,8 +13,7 @@ expression: cat tests/snapshots/os-specific/typedefidx.2024.linux.clang15.rs
unused_mut
)]
pub type size_t = usize;
pub type __uint64_t = u64;
pub type uint64_t = __uint64_t;
pub type uint64_t = u64;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn index_typedef(mut arr: *mut uint64_t) {
let mut foo3: size_t = *arr.offset(25 as ::core::ffi::c_int as isize) as size_t;
Expand Down

This file was deleted.

Loading
Loading