-
Notifications
You must be signed in to change notification settings - Fork 13.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
slice.get(i)
should use a slice projection in MIR, like slice[i]
does
#139118
base: master
Are you sure you want to change the base?
Conversation
rustbot has assigned @workingjubilee. Use |
bb2: { | ||
StorageDead(_3); | ||
StorageLive(_7); | ||
StorageLive(_5); | ||
_5 = &raw mut (*_1); | ||
StorageLive(_6); | ||
_6 = copy _5 as *mut u32 (PtrToPtr); | ||
_7 = Offset(copy _6, copy _2); | ||
StorageDead(_6); | ||
StorageDead(_5); | ||
_8 = &mut (*_7); | ||
_0 = Option::<&mut u32>::Some(copy _8); | ||
StorageDead(_7); | ||
_5 = &mut (*_1)[_2]; | ||
_0 = Option::<&mut u32>::Some(copy _5); | ||
goto -> bb3; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
annot: this pre-codegen test is the clearest demonstration of what the PR does.
library/core/src/intrinsics/mod.rs
Outdated
index: usize, | ||
) -> ItemPtr; | ||
|
||
pub trait SliceGetUnchecked<T> {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A brief doc comment would be good, it is a public trait after all. (I am surprised CI does not complain.)
library/core/src/intrinsics/mod.rs
Outdated
/// Equivalent to `{&, &mut, &raw const, &raw mut} (*slice_ptr)[index]`, | ||
/// depending on the types involved. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't say it is "equivalent" but then also say it has UB. Those two statements contradict each other, if it's equivalent it must be the same safety requirements.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused -- the MIR does have that UB, thus the safety requirements.
#![feature(core_intrinsics)]
#![feature(custom_mir)]
use std::intrinsics::mir::*;
#[custom_mir(dialect = "runtime", phase = "optimized")]
pub unsafe fn get_unchecked(p: *const [i32], i: usize) -> *const i32 {
mir! {
{
RET = &raw const (*p)[i];
Return()
}
}
}
fn main() {
let a = [1_i32; 10];
let p = a.as_ptr();
let slice_ptr = std::ptr::slice_from_raw_parts(p, 1);
unsafe {
get_unchecked(slice_ptr, 1);
}
}
Miri:
error: Undefined Behavior: indexing out of bounds: the len is 1 but the index is 1
--> src/main.rs:9:13
|
9 | RET = &raw const (*p)[i];
| ^^^^^^^^^^^^^^^^^^^^^^^^ indexing out of bounds: the len is 1 but the index is 1
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `get_unchecked` at src/main.rs:9:13: 9:37
note: inside `main`
--> src/main.rs:20:9
|
20 | get_unchecked(slice_ptr, 1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused -- the MIR does have that UB, thus the safety requirements.
Oh, when you say {&, &mut, &raw const, &raw mut} (*slice_ptr)[index]
you mean that as a MIR expression, not as a Rust expression? That's not clear at all.^^
This is an intrinsic, and I think it should be described in terms of the Rust surface language, not in terms of very unstable implementation details like MIR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To me intrinsics are "very unstable implementation details" :P
I'll update the description to be clearer, though.
@rustbot author
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To me intrinsics are "very unstable implementation details" :P
Absolutely. But they also form language primitives that need clear documentation so that everyone using and implementing them knows exactly what they are dealing with.
So, to elaborate, there are two separate points:
- I'm not sure how anyone was supposed to know from your original comment that this was meant to be MIR, not surface Rust. The default assumption will be surface Rust, given that this file is nowhere near the MIR infrastructure.
- When writing docs in files far away from the MIR infrastructure, even docs for unstable implementation details, I'd rather refer to the surface language than MIR. MIR can easily change and then one can easily forget updating all the docs that point to it. I'm also not aware of us documenting any other intrinsic in terms of MIR, and we have consumers of these docs that do not use MIR (mrustc, gcc-rs).
Reminder, once the PR becomes ready for a review, use |
4cf8aa1
to
bb5dd9b
Compare
Ok, updated:
@rustbot ready |
This comment has been minimized.
This comment has been minimized.
bb5dd9b
to
5d9cc7a
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
5d9cc7a
to
938fe84
Compare
The job Click to see the possible cause of the failure (guessed by this bot)
|
slice[i]
is built-in magic, so ends up being quite different fromslice.get(i)
in MIR, even though they're both doing nearly identical operations -- checking the length of the slice then getting a ref/ptr to the element if it's in-bounds.This PR adds a
slice_get_unchecked
intrinsic forimpl SliceIndex for usize
to use to fix that, so it no longer needs to do a bunch of lines of pointer math and instead just gets the obvious single statement. (This is not used for the range versions, sinceslice[i..]
andslice[..k]
can't use the mir Slice projection as they're using fenceposts, not indices.)I originally tried to do this with some kind of GVN pattern, but realized that I'm pretty sure it's not legal to optimize
BinOp::Offset
toPlaceElem::Index
without an extremely complicated condition. Basically, the problem is that theIndex
projection on a dereferenced slice pointer cares about the metadata, since it's UB toPlaceElem::Index
outside the range described by the metadata. But then you cast the fat pointer to a thin pointer then offset it, that ignores the slice length metadata, so it's possible to write things that are legal withOffset
but would be UB if translated in the obvious way toIndex
. Checking (or even determining) the necessary conditions for that would be complicated and error-prone, whereas this intrinsic-based approach is quite straight-forward.Zero backend changes, because it just lowers to MIR, so it's already supported naturally by CTFE/Miri/cg_llvm/cg_clif.