Skip to content
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

Implement Deref{Mut} for arrays #108650

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 30 additions & 29 deletions compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -512,46 +512,47 @@ fn method_autoderef_steps<'tcx>(
.include_raw_pointers()
.silence_errors();
let mut reached_raw_pointer = false;
let mut steps: Vec<_> = autoderef
.by_ref()
.map(|(ty, d)| {
let mut steps = Vec::new();
for (ty, d) in &mut autoderef {
let step = CandidateStep {
self_ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, ty),
autoderefs: d,
from_unsafe_deref: reached_raw_pointer,
unsize: false,
};
steps.push(step);
if let ty::RawPtr(_) = ty.kind() {
// all the subsequent steps will be from_unsafe_deref
reached_raw_pointer = true;
} else if let ty::Array(elem_ty, _) = ty.kind() {
// Array implements Deref/DerefMut so we would always deref instead of unsizing the
// array without this check. This is fine in theory, but it is currently not possible
// to deref in a const context on stable, while unsizing is so to keep that working
// we need to still special case the autoderef step handling here.
let step = CandidateStep {
self_ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, ty),
self_ty: infcx.make_query_response_ignoring_pending_obligations(
inference_vars,
infcx.tcx.mk_slice(*elem_ty),
),
autoderefs: d,
// this could be from an unsafe deref if we had
// a *mut/const [T; N]
from_unsafe_deref: reached_raw_pointer,
unsize: false,
unsize: true,
};
if let ty::RawPtr(_) = ty.kind() {
// all the subsequent steps will be from_unsafe_deref
reached_raw_pointer = true;
}
step
})
.collect();
steps.push(step);
// We just added the slice step manually, so break out early. Slices don't deref
// into anything so the final type of the autoderef is set up correctly.
break;
}
}

let final_ty = autoderef.final_ty(true);
let opt_bad_ty = match final_ty.kind() {
ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy {
reached_raw_pointer,
ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty),
}),
ty::Array(elem_ty, _) => {
let dereferences = steps.len() - 1;

steps.push(CandidateStep {
self_ty: infcx.make_query_response_ignoring_pending_obligations(
inference_vars,
infcx.tcx.mk_slice(*elem_ty),
),
autoderefs: dereferences,
// this could be from an unsafe deref if we had
// a *mut/const [T; N]
from_unsafe_deref: reached_raw_pointer,
unsize: true,
});

None
}
_ => None,
};

Expand Down
24 changes: 24 additions & 0 deletions library/core/src/array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use crate::mem::{self, MaybeUninit};
use crate::ops::{
ChangeOutputType, ControlFlow, FromResidual, Index, IndexMut, NeverShortCircuit, Residual, Try,
};
#[cfg(not(bootstrap))]
use crate::ops::{Deref, DerefMut};
use crate::slice::{Iter, IterMut};

mod drain;
Expand Down Expand Up @@ -187,6 +189,28 @@ impl<T, const N: usize> const BorrowMut<[T]> for [T; N] {
}
}

#[cfg(not(bootstrap))]
#[stable(feature = "array_deref", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_unstable(feature = "const_array_deref", issue = "none")]
impl<T, const N: usize> const Deref for [T; N] {
type Target = [T];

fn deref(&self) -> &Self::Target {
// SAFETY: self points to `N` consecutive properly initialized values of type `T`.
unsafe { core::slice::from_raw_parts(self as *const [T] as *const T, N) }
}
}

#[cfg(not(bootstrap))]
#[stable(feature = "array_deref", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_unstable(feature = "const_array_deref", issue = "none")]
impl<T, const N: usize> const DerefMut for [T; N] {
fn deref_mut(&mut self) -> &mut Self::Target {
// SAFETY: self points to `N` consecutive properly initialized values of type `T`.
unsafe { core::slice::from_raw_parts_mut(self as *mut [T] as *mut T, N) }
}
}

/// Tries to create an array `[T; N]` by copying from a slice `&[T]`. Succeeds if
/// `slice.len() == N`.
///
Expand Down
4 changes: 2 additions & 2 deletions src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ fn main() {

require_slice(slice);
require_slice(&Cow::from(slice));
require_slice(array.as_ref());
require_slice(array_ref.as_ref());
require_slice(&array);
require_slice(array_ref);
require_slice(slice);
require_slice(&x_ref.to_owned()); // No longer flagged because of #8759.

Expand Down
6 changes: 3 additions & 3 deletions src/tools/clippy/tests/ui/unnecessary_to_owned.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -146,16 +146,16 @@ LL | require_slice(&Cow::from(slice).into_owned());
| ^^^^^^^^^^^^^ help: remove this

error: unnecessary use of `to_owned`
--> $DIR/unnecessary_to_owned.rs:77:19
--> $DIR/unnecessary_to_owned.rs:77:25
|
LL | require_slice(&array.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()`
| ^^^^^^^^^^^ help: remove this

error: unnecessary use of `to_owned`
--> $DIR/unnecessary_to_owned.rs:78:19
|
LL | require_slice(&array_ref.to_owned());
| ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()`
| ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`

error: unnecessary use of `to_owned`
--> $DIR/unnecessary_to_owned.rs:79:19
Expand Down
3 changes: 1 addition & 2 deletions tests/ui/coercion/coerce-block-tail-83850.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// check-fail
// run-pass
fn f(_: &[i32]) {}

fn main() {
f(&Box::new([1, 2]));
//~^ ERROR mismatched types
}
19 changes: 0 additions & 19 deletions tests/ui/coercion/coerce-block-tail-83850.stderr

This file was deleted.

8 changes: 1 addition & 7 deletions tests/ui/coercion/issue-73886.stderr
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
error[E0605]: non-primitive cast: `&&[i32; 1]` as `&[_]`
--> $DIR/issue-73886.rs:2:13
|
LL | let _ = &&[0] as &[_];
| ^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object

error[E0605]: non-primitive cast: `u32` as `Option<_>`
--> $DIR/issue-73886.rs:4:13
|
Expand All @@ -12,6 +6,6 @@ LL | let _ = 7u32 as Option<_>;
|
= note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object

error: aborting due to 2 previous errors
error: aborting due to previous error

For more information about this error, try `rustc --explain E0605`.