Skip to content

Commit 8434b34

Browse files
committed
analyze: add test case for C null pointer projection special cases
1 parent 10033f4 commit 8434b34

File tree

1 file changed

+43
-0
lines changed

1 file changed

+43
-0
lines changed

c2rust-analyze/tests/filecheck/non_null_rewrites.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,46 @@ unsafe fn local_field_projection(cond: bool) -> i32 {
146146
}
147147
*p
148148
}
149+
150+
// CHECK-LABEL: unsafe fn null_ptr_special_cases{{[<(]}}
151+
unsafe fn null_ptr_special_cases(cond: bool, p: *const i32, i: isize) -> *const i32 {
152+
// In C, `*NULL` is usually undefined behavior, even in cases like `sizeof(*(char*)0)` that
153+
// don't involve an actual memory access. However, there are two special cases in the
154+
// standard:
155+
//
156+
// 1. `&*p == p` for all `p`, even if `p == NULL`.
157+
// 2. `&p[i] == p + i` for all `p`, even if `p == NULL`. Note that `NULL + 0 == NULL`, but
158+
// `NULL + i` with nonzero `i` is undefined.
159+
//
160+
// Here we test these two cases to see if rewriting introduces a panic even in cases where the
161+
// C operation is valid.
162+
163+
// Make `p` nullable.
164+
let mut p = p;
165+
if cond {
166+
p = ptr::null();
167+
}
168+
169+
// Currently, `&*p` rewrites to `Some(&*p.unwrap())`, which panics when `p` is null.
170+
//
171+
// CHECK: Some(&(*(p).unwrap()));
172+
let q = ptr::addr_of!(*p);
173+
174+
// `offset(i)` rewrites to `map`, which passes null/`None` through unchanged.
175+
//
176+
// CHECK: let (arr, idx, ) = ((q), (0) as usize, );
177+
// CHECK-NEXT: arr.map(|arr| &arr[idx ..])
178+
let r = q.offset(0);
179+
180+
// Because we use `Option::map` for this rewrite, it returns `None` if `p` is `None`, even when
181+
// `i != 0`. This is different from the concrete behavior of most C compilers, where `NULL + i
182+
// != NULL`. Adding `NULL + i` (with nonzero `i`) is undefined behavior in C, so it's legal
183+
// for us to define it this way, though it may produce surprising results in some cases like
184+
// handrolled `offsetof` macros.
185+
//
186+
// CHECK: let (arr, idx, ) = ((r), (i) as usize, );
187+
// CHECK-NEXT: arr.map(|arr| &arr[idx ..])
188+
let s = r.offset(i);
189+
190+
s
191+
}

0 commit comments

Comments
 (0)