Open nikomatsakis opened 8 years ago
The traditional example is:
fn example_optimizable(b: &mut Context, bigs: &[Big]) {
let index: usize = calculate_index(b);
let tmp = unsafe { <[_]>::unchecked_get(bigs, index) };
b.big = tmp;
}
Which is α-equivalent to
fn get_pointer_to_big(b: &Context) -> usize {
&b.big as *const _ as usize
}
unsafe fn mutate_through_pointer(bigs: &[Big], index: usize) -> Big {
Big {
x: 0,
y: (index as *const Big).x,
...
}
}
fn example_nonoptimizable(b: &mut Context, bigs: &[Big]) {
let index: usize = get_pointer_to_big(b);
let tmp = unsafe { mutate_through_pointer(bigs, index) };
b.big = tmp;
}
Disregarding panics, we want to do copy elimination in the first case, but not the second, but that is not really possible.
This is a problem in Tootsie Pop style models, but not in most others (such as Stacked Borrows) where unsafe
blocks do not have any operational effect.
The unchecked-get use case consists of safe code that contains one or two small unsafe calls which check application-level constraints -- for example, eliding a bounds check, or asserting that a string is utf-8:
Ideally, code like this would be optimized just as well as the original safe code (and of course would not have a bounds check). However, the introduction of an unsafe block and a call to an unsafe function can be a barrier to optimization, such as in the Tootsie Pop model (#21).