diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index cb5c814b..8701a9b0 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -3421,8 +3422,42 @@ void Converter::ConvertPointerOffset(clang::Expr *base, clang::Expr *idx, computed_expr_type_ = ComputedExprType::FreshPointer; } +static bool IsFlexibleArrayMemberAccess(clang::ASTContext &ctx, + clang::Expr *array) { + return array->isFlexibleArrayMemberLike( + ctx, clang::LangOptions::StrictFlexArraysLevelKind::OneZeroOrIncomplete, + /*IgnoreTemplateOrMacroSubstitution=*/true); +} + +void Converter::EmitFlexibleArrayElementPtr(clang::Expr *array, + clang::Expr *idx, bool is_mut) { + { + PushExplicitAutoref no_autoref(*this, std::nullopt); + Convert(array); + } + StrCat(is_mut ? ".as_mut_ptr()" : ".as_ptr()", ".add"); + { + PushParen call(*this); + { + PushParen paren(*this); + Convert(idx); + } + StrCat(keyword::kAs, "usize"); + } +} + void Converter::ConvertArraySubscript(clang::Expr *base, clang::Expr *idx, clang::QualType type) { + if (auto inner = base->IgnoreImplicit()) { + if (inner->getType()->isArrayType() && + IsFlexibleArrayMemberAccess(ctx_, inner)) { + PushParen outer(*this); + StrCat(token::kStar); + EmitFlexibleArrayElementPtr(inner, idx, + !inner->getType().isConstQualified()); + return; + } + } if (IsUniquePtr(base->getType())) { PushExplicitAutoref no_autoref(*this, std::nullopt); Convert(base->IgnoreImplicit()); @@ -3725,6 +3760,19 @@ void Converter::ConvertUnsignedArithBinaryOperator(clang::BinaryOperator *op, void Converter::ConvertAddrOf(clang::Expr *expr, clang::QualType pointer_type) { assert(pointer_type->isPointerType()); + if (auto ase = + clang::dyn_cast(expr->IgnoreParens())) { + auto base = ase->getBase(); + auto inner = base->IgnoreImplicit(); + if (base->IgnoreCasts()->getType()->isArrayType() && + IsFlexibleArrayMemberAccess(ctx_, inner)) { + EmitFlexibleArrayElementPtr( + inner, ase->getIdx(), + !pointer_type->getPointeeType().isConstQualified()); + computed_expr_type_ = ComputedExprType::FreshPointer; + return; + } + } if (IsReferenceType(expr) || pointer_type->isFunctionPointerType()) { PushExprKind push(*this, ExprKind::AddrOf); Convert(expr); diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 0e56f8a8..1f0a113a 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -443,6 +443,9 @@ class Converter : public clang::RecursiveASTVisitor { virtual void ConvertArraySubscript(clang::Expr *base, clang::Expr *idx, clang::QualType type); + void EmitFlexibleArrayElementPtr(clang::Expr *array, clang::Expr *idx, + bool is_mut); + virtual void ConvertAssignment(clang::Expr *lhs, clang::Expr *rhs, std::string_view assign_operator); diff --git a/tests/unit/out/unsafe/union_field_alignment.rs b/tests/unit/out/unsafe/union_field_alignment.rs index f30c73da..15b8df18 100644 --- a/tests/unit/out/unsafe/union_field_alignment.rs +++ b/tests/unit/out/unsafe/union_field_alignment.rs @@ -31,7 +31,7 @@ pub fn main() { unsafe fn main_0() -> i32 { let mut n: node = ::default(); n.next = std::ptr::null_mut(); - n.x.bytes[(0) as usize] = 171_u8; - assert!(((((n.x.bytes[(0) as usize] as i32) == (171)) as i32) != 0)); + (*n.x.bytes.as_mut_ptr().add((0) as usize)) = 171_u8; + assert!((((((*n.x.bytes.as_mut_ptr().add((0) as usize)) as i32) == (171)) as i32) != 0)); return 0; } diff --git a/tests/unit/out/unsafe/union_flex_array_member.rs b/tests/unit/out/unsafe/union_flex_array_member.rs new file mode 100644 index 00000000..4ad51e20 --- /dev/null +++ b/tests/unit/out/unsafe/union_flex_array_member.rs @@ -0,0 +1,63 @@ +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; +#[repr(C)] +#[derive(Copy, Clone)] +pub union anon_0 { + pub bytes: [u8; 1], + pub aligner: *mut ::libc::c_void, +} +impl Default for anon_0 { + fn default() -> Self { + unsafe { std::mem::zeroed() } + } +} +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct node { + pub len: u64, + pub pos: u64, + pub x: anon_0, +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut tail_size: u64 = 32_u64; + let mut n: *mut node = (libcc2rs::malloc_unsafe( + (::std::mem::size_of::() as u64 as u64).wrapping_add(tail_size), + ) as *mut node); + (*n).len = tail_size; + let mut i: u64 = 0_u64; + 'loop_: while ((((i) < (tail_size)) as i32) != 0) { + (*(*n).x.bytes.as_mut_ptr().add((i) as usize)) = (((i) & (255_u64)) as u8); + i.postfix_inc(); + } + let mut i: u64 = 0_u64; + 'loop_: while ((((i) < (tail_size)) as i32) != 0) { + assert!( + (((((*(*n).x.bytes.as_mut_ptr().add((i) as usize)) as i32) + == ((((i) & (255_u64)) as u8) as i32)) as i32) + != 0) + ); + i.postfix_inc(); + } + let mut p: *mut u8 = ((*n).x.bytes.as_mut_ptr().add((10) as usize)); + assert!((((((*p) as i32) == (10)) as i32) != 0)); + (*p) = 170_u8; + assert!((((((*(*n).x.bytes.as_mut_ptr().add((10) as usize)) as i32) == (170)) as i32) != 0)); + (*n).pos = 20_u64; + let mut q: *mut u8 = ((*n).x.bytes.as_mut_ptr().add(((*n).pos) as usize)); + assert!((((((*q) as i32) == (20)) as i32) != 0)); + (*q) = 187_u8; + assert!((((((*q) as i32) == (187)) as i32) != 0)); + libcc2rs::free_unsafe((n as *mut node as *mut ::libc::c_void)); + return 0; +} diff --git a/tests/unit/union_flex_array_member.c b/tests/unit/union_flex_array_member.c index c9d65425..7a41d456 100644 --- a/tests/unit/union_flex_array_member.c +++ b/tests/unit/union_flex_array_member.c @@ -1,4 +1,4 @@ -// no-compile +// no-compile: refcount #include #include #include @@ -7,6 +7,7 @@ struct node { size_t len; + size_t pos; union { uint8_t bytes[1]; void *aligner; @@ -30,6 +31,12 @@ int main(void) { *p = 0xAA; assert(n->x.bytes[10] == 0xAA); + n->pos = 20; + uint8_t *q = &n->x.bytes[n->pos]; + assert(*q == 20); + *q = 0xBB; + assert(*q == 0xBB); + free(n); return 0; }