Skip to content

Commit ce0af71

Browse files
authored
Translate access to flexible array member (#165)
Changed the access pattern of flexible arrays from ```rs let bytes: [u8; 1] = ...; bytes[10] = ... ``` to ```rs let bytes: [u8; 1] = ...; *bytes.as_mut_ptr().add(10) = ...; ``` The original access panics because the bounds are violated, i.e. accessing the 10th element in an array declared with 1 element. The new access does not panic, it preserves the C/C++ behavior.
1 parent c4b9533 commit ce0af71

5 files changed

Lines changed: 124 additions & 3 deletions

File tree

cpp2rust/converter/converter.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include <clang/AST/APValue.h>
77
#include <clang/AST/ParentMapContext.h>
8+
#include <clang/Basic/LangOptions.h>
89
#include <clang/Basic/SourceManager.h>
910
#include <llvm/ADT/DenseMap.h>
1011
#include <llvm/Support/ConvertUTF.h>
@@ -3421,8 +3422,42 @@ void Converter::ConvertPointerOffset(clang::Expr *base, clang::Expr *idx,
34213422
computed_expr_type_ = ComputedExprType::FreshPointer;
34223423
}
34233424

3425+
static bool IsFlexibleArrayMemberAccess(clang::ASTContext &ctx,
3426+
clang::Expr *array) {
3427+
return array->isFlexibleArrayMemberLike(
3428+
ctx, clang::LangOptions::StrictFlexArraysLevelKind::OneZeroOrIncomplete,
3429+
/*IgnoreTemplateOrMacroSubstitution=*/true);
3430+
}
3431+
3432+
void Converter::EmitFlexibleArrayElementPtr(clang::Expr *array,
3433+
clang::Expr *idx, bool is_mut) {
3434+
{
3435+
PushExplicitAutoref no_autoref(*this, std::nullopt);
3436+
Convert(array);
3437+
}
3438+
StrCat(is_mut ? ".as_mut_ptr()" : ".as_ptr()", ".add");
3439+
{
3440+
PushParen call(*this);
3441+
{
3442+
PushParen paren(*this);
3443+
Convert(idx);
3444+
}
3445+
StrCat(keyword::kAs, "usize");
3446+
}
3447+
}
3448+
34243449
void Converter::ConvertArraySubscript(clang::Expr *base, clang::Expr *idx,
34253450
clang::QualType type) {
3451+
if (auto inner = base->IgnoreImplicit()) {
3452+
if (inner->getType()->isArrayType() &&
3453+
IsFlexibleArrayMemberAccess(ctx_, inner)) {
3454+
PushParen outer(*this);
3455+
StrCat(token::kStar);
3456+
EmitFlexibleArrayElementPtr(inner, idx,
3457+
!inner->getType().isConstQualified());
3458+
return;
3459+
}
3460+
}
34263461
if (IsUniquePtr(base->getType())) {
34273462
PushExplicitAutoref no_autoref(*this, std::nullopt);
34283463
Convert(base->IgnoreImplicit());
@@ -3725,6 +3760,19 @@ void Converter::ConvertUnsignedArithBinaryOperator(clang::BinaryOperator *op,
37253760

37263761
void Converter::ConvertAddrOf(clang::Expr *expr, clang::QualType pointer_type) {
37273762
assert(pointer_type->isPointerType());
3763+
if (auto ase =
3764+
clang::dyn_cast<clang::ArraySubscriptExpr>(expr->IgnoreParens())) {
3765+
auto base = ase->getBase();
3766+
auto inner = base->IgnoreImplicit();
3767+
if (base->IgnoreCasts()->getType()->isArrayType() &&
3768+
IsFlexibleArrayMemberAccess(ctx_, inner)) {
3769+
EmitFlexibleArrayElementPtr(
3770+
inner, ase->getIdx(),
3771+
!pointer_type->getPointeeType().isConstQualified());
3772+
computed_expr_type_ = ComputedExprType::FreshPointer;
3773+
return;
3774+
}
3775+
}
37283776
if (IsReferenceType(expr) || pointer_type->isFunctionPointerType()) {
37293777
PushExprKind push(*this, ExprKind::AddrOf);
37303778
Convert(expr);

cpp2rust/converter/converter.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,9 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
443443
virtual void ConvertArraySubscript(clang::Expr *base, clang::Expr *idx,
444444
clang::QualType type);
445445

446+
void EmitFlexibleArrayElementPtr(clang::Expr *array, clang::Expr *idx,
447+
bool is_mut);
448+
446449
virtual void ConvertAssignment(clang::Expr *lhs, clang::Expr *rhs,
447450
std::string_view assign_operator);
448451

tests/unit/out/unsafe/union_field_alignment.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ pub fn main() {
3131
unsafe fn main_0() -> i32 {
3232
let mut n: node = <node>::default();
3333
n.next = std::ptr::null_mut();
34-
n.x.bytes[(0) as usize] = 171_u8;
35-
assert!(((((n.x.bytes[(0) as usize] as i32) == (171)) as i32) != 0));
34+
(*n.x.bytes.as_mut_ptr().add((0) as usize)) = 171_u8;
35+
assert!((((((*n.x.bytes.as_mut_ptr().add((0) as usize)) as i32) == (171)) as i32) != 0));
3636
return 0;
3737
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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+
#[repr(C)]
10+
#[derive(Copy, Clone)]
11+
pub union anon_0 {
12+
pub bytes: [u8; 1],
13+
pub aligner: *mut ::libc::c_void,
14+
}
15+
impl Default for anon_0 {
16+
fn default() -> Self {
17+
unsafe { std::mem::zeroed() }
18+
}
19+
}
20+
#[repr(C)]
21+
#[derive(Copy, Clone, Default)]
22+
pub struct node {
23+
pub len: u64,
24+
pub pos: u64,
25+
pub x: anon_0,
26+
}
27+
pub fn main() {
28+
unsafe {
29+
std::process::exit(main_0() as i32);
30+
}
31+
}
32+
unsafe fn main_0() -> i32 {
33+
let mut tail_size: u64 = 32_u64;
34+
let mut n: *mut node = (libcc2rs::malloc_unsafe(
35+
(::std::mem::size_of::<node>() as u64 as u64).wrapping_add(tail_size),
36+
) as *mut node);
37+
(*n).len = tail_size;
38+
let mut i: u64 = 0_u64;
39+
'loop_: while ((((i) < (tail_size)) as i32) != 0) {
40+
(*(*n).x.bytes.as_mut_ptr().add((i) as usize)) = (((i) & (255_u64)) as u8);
41+
i.postfix_inc();
42+
}
43+
let mut i: u64 = 0_u64;
44+
'loop_: while ((((i) < (tail_size)) as i32) != 0) {
45+
assert!(
46+
(((((*(*n).x.bytes.as_mut_ptr().add((i) as usize)) as i32)
47+
== ((((i) & (255_u64)) as u8) as i32)) as i32)
48+
!= 0)
49+
);
50+
i.postfix_inc();
51+
}
52+
let mut p: *mut u8 = ((*n).x.bytes.as_mut_ptr().add((10) as usize));
53+
assert!((((((*p) as i32) == (10)) as i32) != 0));
54+
(*p) = 170_u8;
55+
assert!((((((*(*n).x.bytes.as_mut_ptr().add((10) as usize)) as i32) == (170)) as i32) != 0));
56+
(*n).pos = 20_u64;
57+
let mut q: *mut u8 = ((*n).x.bytes.as_mut_ptr().add(((*n).pos) as usize));
58+
assert!((((((*q) as i32) == (20)) as i32) != 0));
59+
(*q) = 187_u8;
60+
assert!((((((*q) as i32) == (187)) as i32) != 0));
61+
libcc2rs::free_unsafe((n as *mut node as *mut ::libc::c_void));
62+
return 0;
63+
}

tests/unit/union_flex_array_member.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// no-compile
1+
// no-compile: refcount
22
#include <assert.h>
33
#include <stddef.h>
44
#include <stdint.h>
@@ -7,6 +7,7 @@
77

88
struct node {
99
size_t len;
10+
size_t pos;
1011
union {
1112
uint8_t bytes[1];
1213
void *aligner;
@@ -30,6 +31,12 @@ int main(void) {
3031
*p = 0xAA;
3132
assert(n->x.bytes[10] == 0xAA);
3233

34+
n->pos = 20;
35+
uint8_t *q = &n->x.bytes[n->pos];
36+
assert(*q == 20);
37+
*q = 0xBB;
38+
assert(*q == 0xBB);
39+
3340
free(n);
3441
return 0;
3542
}

0 commit comments

Comments
 (0)