mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-14 02:49:40 +00:00
Rollup merge of #92640 - compiler-errors:array-deref-on-newtype, r=lcnr
Fix ICEs related to `Deref<Target=[T; N]>` on newtypes 1. Stash a const infer's type into the canonical var during canonicalization, so we can recreate the fresh const infer with that same type. For example, given `[T; _]` we know `_` is a `usize`. If we go from infer => canonical => infer, we shouldn't forget that variable is a usize. Fixes #92626 Fixes #83704 2. Don't stash the autoderef'd slice type that we get from method lookup, but instead recreate it during method confirmation. We need to do this because the type we receive back after picking the method references a type variable that does not exist after probing is done. Fixes #92637 ... A better solution for the second issue would be to actually _properly_ implement `Deref` for `[T; N]` instead of fixing this autoderef hack to stop leaking inference variables. But I actually looked into this, and there are many complications with const impls.
This commit is contained in:
commit
cb5ecff8b2
@ -425,7 +425,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> {
|
||||
// FIXME: perf problem described in #55921.
|
||||
ui = ty::UniverseIndex::ROOT;
|
||||
return self.canonicalize_const_var(
|
||||
CanonicalVarInfo { kind: CanonicalVarKind::Const(ui) },
|
||||
CanonicalVarInfo { kind: CanonicalVarKind::Const(ui, ct.ty) },
|
||||
ct,
|
||||
);
|
||||
}
|
||||
|
@ -137,12 +137,9 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
|
||||
self.tcx.mk_region(ty::RePlaceholder(placeholder_mapped)).into()
|
||||
}
|
||||
|
||||
CanonicalVarKind::Const(ui) => self
|
||||
CanonicalVarKind::Const(ui, ty) => self
|
||||
.next_const_var_in_universe(
|
||||
self.next_ty_var_in_universe(
|
||||
TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span },
|
||||
universe_map(ui),
|
||||
),
|
||||
ty,
|
||||
ConstVariableOrigin { kind: ConstVariableOriginKind::MiscVariable, span },
|
||||
universe_map(ui),
|
||||
)
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
use crate::infer::MemberConstraint;
|
||||
use crate::ty::subst::GenericArg;
|
||||
use crate::ty::{self, BoundVar, List, Region, TyCtxt};
|
||||
use crate::ty::{self, BoundVar, List, Region, Ty, TyCtxt};
|
||||
use rustc_index::vec::IndexVec;
|
||||
use rustc_macros::HashStable;
|
||||
use smallvec::SmallVec;
|
||||
@ -104,7 +104,7 @@ impl<'tcx> CanonicalVarInfo<'tcx> {
|
||||
CanonicalVarKind::PlaceholderTy(_) => false,
|
||||
CanonicalVarKind::Region(_) => true,
|
||||
CanonicalVarKind::PlaceholderRegion(..) => false,
|
||||
CanonicalVarKind::Const(_) => true,
|
||||
CanonicalVarKind::Const(..) => true,
|
||||
CanonicalVarKind::PlaceholderConst(_) => false,
|
||||
}
|
||||
}
|
||||
@ -130,7 +130,7 @@ pub enum CanonicalVarKind<'tcx> {
|
||||
PlaceholderRegion(ty::PlaceholderRegion),
|
||||
|
||||
/// Some kind of const inference variable.
|
||||
Const(ty::UniverseIndex),
|
||||
Const(ty::UniverseIndex, Ty<'tcx>),
|
||||
|
||||
/// A "placeholder" that represents "any const".
|
||||
PlaceholderConst(ty::PlaceholderConst<'tcx>),
|
||||
@ -147,7 +147,7 @@ impl<'tcx> CanonicalVarKind<'tcx> {
|
||||
CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.universe,
|
||||
CanonicalVarKind::Region(ui) => ui,
|
||||
CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.universe,
|
||||
CanonicalVarKind::Const(ui) => ui,
|
||||
CanonicalVarKind::Const(ui, _) => ui,
|
||||
CanonicalVarKind::PlaceholderConst(placeholder) => placeholder.universe,
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ crate fn evaluate_goal<'tcx>(
|
||||
chalk_ir::VariableKind::Lifetime,
|
||||
chalk_ir::UniverseIndex { counter: ui.index() },
|
||||
),
|
||||
CanonicalVarKind::Const(_ui) => unimplemented!(),
|
||||
CanonicalVarKind::Const(_ui, _ty) => unimplemented!(),
|
||||
CanonicalVarKind::PlaceholderConst(_pc) => unimplemented!(),
|
||||
}),
|
||||
),
|
||||
@ -127,9 +127,9 @@ crate fn evaluate_goal<'tcx>(
|
||||
chalk_ir::VariableKind::Lifetime => CanonicalVarKind::Region(
|
||||
ty::UniverseIndex::from_usize(var.skip_kind().counter),
|
||||
),
|
||||
chalk_ir::VariableKind::Const(_) => CanonicalVarKind::Const(
|
||||
ty::UniverseIndex::from_usize(var.skip_kind().counter),
|
||||
),
|
||||
// FIXME(compiler-errors): We don't currently have a way of turning
|
||||
// a Chalk ty back into a rustc ty, right?
|
||||
chalk_ir::VariableKind::Const(_) => todo!(),
|
||||
};
|
||||
CanonicalVarInfo { kind }
|
||||
})
|
||||
|
@ -149,7 +149,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
|
||||
// time writing the results into the various typeck results.
|
||||
let mut autoderef =
|
||||
self.autoderef_overloaded_span(self.span, unadjusted_self_ty, self.call_expr.span);
|
||||
let (_, n) = match autoderef.nth(pick.autoderefs) {
|
||||
let (ty, n) = match autoderef.nth(pick.autoderefs) {
|
||||
Some(n) => n,
|
||||
None => {
|
||||
return self.tcx.ty_error_with_message(
|
||||
@ -161,14 +161,15 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
|
||||
assert_eq!(n, pick.autoderefs);
|
||||
|
||||
let mut adjustments = self.adjust_steps(&autoderef);
|
||||
let mut target = self.structurally_resolved_type(autoderef.span(), ty);
|
||||
|
||||
let mut target =
|
||||
self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
|
||||
|
||||
match &pick.autoref_or_ptr_adjustment {
|
||||
match pick.autoref_or_ptr_adjustment {
|
||||
Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, unsize }) => {
|
||||
let region = self.next_region_var(infer::Autoref(self.span));
|
||||
target = self.tcx.mk_ref(region, ty::TypeAndMut { mutbl: *mutbl, ty: target });
|
||||
// Type we're wrapping in a reference, used later for unsizing
|
||||
let base_ty = target;
|
||||
|
||||
target = self.tcx.mk_ref(region, ty::TypeAndMut { mutbl, ty: target });
|
||||
let mutbl = match mutbl {
|
||||
hir::Mutability::Not => AutoBorrowMutability::Not,
|
||||
hir::Mutability::Mut => AutoBorrowMutability::Mut {
|
||||
@ -182,10 +183,18 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
|
||||
target,
|
||||
});
|
||||
|
||||
if let Some(unsize_target) = unsize {
|
||||
if unsize {
|
||||
let unsized_ty = if let ty::Array(elem_ty, _) = base_ty.kind() {
|
||||
self.tcx.mk_slice(elem_ty)
|
||||
} else {
|
||||
bug!(
|
||||
"AutorefOrPtrAdjustment's unsize flag should only be set for array ty, found {}",
|
||||
base_ty
|
||||
)
|
||||
};
|
||||
target = self
|
||||
.tcx
|
||||
.mk_ref(region, ty::TypeAndMut { mutbl: mutbl.into(), ty: unsize_target });
|
||||
.mk_ref(region, ty::TypeAndMut { mutbl: mutbl.into(), ty: unsized_ty });
|
||||
adjustments
|
||||
.push(Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target });
|
||||
}
|
||||
|
@ -167,26 +167,26 @@ enum ProbeResult {
|
||||
/// T`, we could convert it to `*const T`, then autoref to `&*const T`. However, currently we do
|
||||
/// (at most) one of these. Either the receiver has type `T` and we convert it to `&T` (or with
|
||||
/// `mut`), or it has type `*mut T` and we convert it to `*const T`.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum AutorefOrPtrAdjustment<'tcx> {
|
||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||
pub enum AutorefOrPtrAdjustment {
|
||||
/// Receiver has type `T`, add `&` or `&mut` (it `T` is `mut`), and maybe also "unsize" it.
|
||||
/// Unsizing is used to convert a `[T; N]` to `[T]`, which only makes sense when autorefing.
|
||||
Autoref {
|
||||
mutbl: hir::Mutability,
|
||||
|
||||
/// Indicates that the source expression should be "unsized" to a target type. This should
|
||||
/// probably eventually go away in favor of just coercing method receivers.
|
||||
unsize: Option<Ty<'tcx>>,
|
||||
/// Indicates that the source expression should be "unsized" to a target type.
|
||||
/// This is special-cased for just arrays unsizing to slices.
|
||||
unsize: bool,
|
||||
},
|
||||
/// Receiver has type `*mut T`, convert to `*const T`
|
||||
ToConstPtr,
|
||||
}
|
||||
|
||||
impl<'tcx> AutorefOrPtrAdjustment<'tcx> {
|
||||
fn get_unsize(&self) -> Option<Ty<'tcx>> {
|
||||
impl AutorefOrPtrAdjustment {
|
||||
fn get_unsize(&self) -> bool {
|
||||
match self {
|
||||
AutorefOrPtrAdjustment::Autoref { mutbl: _, unsize } => *unsize,
|
||||
AutorefOrPtrAdjustment::ToConstPtr => None,
|
||||
AutorefOrPtrAdjustment::ToConstPtr => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -204,7 +204,7 @@ pub struct Pick<'tcx> {
|
||||
|
||||
/// Indicates that we want to add an autoref (and maybe also unsize it), or if the receiver is
|
||||
/// `*mut T`, convert it to `*const T`.
|
||||
pub autoref_or_ptr_adjustment: Option<AutorefOrPtrAdjustment<'tcx>>,
|
||||
pub autoref_or_ptr_adjustment: Option<AutorefOrPtrAdjustment>,
|
||||
pub self_ty: Ty<'tcx>,
|
||||
}
|
||||
|
||||
@ -1202,7 +1202,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
||||
pick.autoderefs += 1;
|
||||
pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::Autoref {
|
||||
mutbl,
|
||||
unsize: pick.autoref_or_ptr_adjustment.and_then(|a| a.get_unsize()),
|
||||
unsize: pick.autoref_or_ptr_adjustment.map_or(false, |a| a.get_unsize()),
|
||||
})
|
||||
}
|
||||
|
||||
@ -1227,10 +1227,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
||||
self.pick_method(autoref_ty, unstable_candidates).map(|r| {
|
||||
r.map(|mut pick| {
|
||||
pick.autoderefs = step.autoderefs;
|
||||
pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::Autoref {
|
||||
mutbl,
|
||||
unsize: step.unsize.then_some(self_ty),
|
||||
});
|
||||
pick.autoref_or_ptr_adjustment =
|
||||
Some(AutorefOrPtrAdjustment::Autoref { mutbl, unsize: step.unsize });
|
||||
pick
|
||||
})
|
||||
})
|
||||
|
17
src/test/ui/autoref-autoderef/deref-into-array.rs
Normal file
17
src/test/ui/autoref-autoderef/deref-into-array.rs
Normal file
@ -0,0 +1,17 @@
|
||||
// check-pass
|
||||
|
||||
struct Test<T>([T; 1]);
|
||||
|
||||
impl<T> std::ops::Deref for Test<T> {
|
||||
type Target = [T; 1];
|
||||
|
||||
fn deref(&self) -> &[T; 1] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let out = Test([(); 1]);
|
||||
let blah = out.len();
|
||||
println!("{}", blah);
|
||||
}
|
27
src/test/ui/const-generics/deref-into-array-generic.rs
Normal file
27
src/test/ui/const-generics/deref-into-array-generic.rs
Normal file
@ -0,0 +1,27 @@
|
||||
// check-pass
|
||||
|
||||
struct Test<T, const N: usize>([T; N]);
|
||||
|
||||
impl<T: Copy + Default, const N: usize> Default for Test<T, N> {
|
||||
fn default() -> Self {
|
||||
Self([T::default(); N])
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> std::ops::Deref for Test<T, N> {
|
||||
type Target = [T; N];
|
||||
|
||||
fn deref(&self) -> &[T; N] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
fn test() -> Test<u64, 16> {
|
||||
let test = Test::default();
|
||||
println!("{}", test.len());
|
||||
test
|
||||
}
|
||||
|
||||
fn main() {
|
||||
test();
|
||||
}
|
Loading…
Reference in New Issue
Block a user