@@ -146,3 +146,46 @@ unsafe fn local_field_projection(cond: bool) -> i32 {
146
146
}
147
147
* p
148
148
}
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