1
Fork 0

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:
Matthias Krüger 2022-01-18 04:41:58 +01:00 committed by GitHub
commit cb5ecff8b2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 84 additions and 36 deletions

View file

@ -425,7 +425,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> {
// FIXME: perf problem described in #55921. // FIXME: perf problem described in #55921.
ui = ty::UniverseIndex::ROOT; ui = ty::UniverseIndex::ROOT;
return self.canonicalize_const_var( return self.canonicalize_const_var(
CanonicalVarInfo { kind: CanonicalVarKind::Const(ui) }, CanonicalVarInfo { kind: CanonicalVarKind::Const(ui, ct.ty) },
ct, ct,
); );
} }

View file

@ -137,12 +137,9 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
self.tcx.mk_region(ty::RePlaceholder(placeholder_mapped)).into() self.tcx.mk_region(ty::RePlaceholder(placeholder_mapped)).into()
} }
CanonicalVarKind::Const(ui) => self CanonicalVarKind::Const(ui, ty) => self
.next_const_var_in_universe( .next_const_var_in_universe(
self.next_ty_var_in_universe( ty,
TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span },
universe_map(ui),
),
ConstVariableOrigin { kind: ConstVariableOriginKind::MiscVariable, span }, ConstVariableOrigin { kind: ConstVariableOriginKind::MiscVariable, span },
universe_map(ui), universe_map(ui),
) )

View file

@ -23,7 +23,7 @@
use crate::infer::MemberConstraint; use crate::infer::MemberConstraint;
use crate::ty::subst::GenericArg; 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_index::vec::IndexVec;
use rustc_macros::HashStable; use rustc_macros::HashStable;
use smallvec::SmallVec; use smallvec::SmallVec;
@ -104,7 +104,7 @@ impl<'tcx> CanonicalVarInfo<'tcx> {
CanonicalVarKind::PlaceholderTy(_) => false, CanonicalVarKind::PlaceholderTy(_) => false,
CanonicalVarKind::Region(_) => true, CanonicalVarKind::Region(_) => true,
CanonicalVarKind::PlaceholderRegion(..) => false, CanonicalVarKind::PlaceholderRegion(..) => false,
CanonicalVarKind::Const(_) => true, CanonicalVarKind::Const(..) => true,
CanonicalVarKind::PlaceholderConst(_) => false, CanonicalVarKind::PlaceholderConst(_) => false,
} }
} }
@ -130,7 +130,7 @@ pub enum CanonicalVarKind<'tcx> {
PlaceholderRegion(ty::PlaceholderRegion), PlaceholderRegion(ty::PlaceholderRegion),
/// Some kind of const inference variable. /// Some kind of const inference variable.
Const(ty::UniverseIndex), Const(ty::UniverseIndex, Ty<'tcx>),
/// A "placeholder" that represents "any const". /// A "placeholder" that represents "any const".
PlaceholderConst(ty::PlaceholderConst<'tcx>), PlaceholderConst(ty::PlaceholderConst<'tcx>),
@ -147,7 +147,7 @@ impl<'tcx> CanonicalVarKind<'tcx> {
CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.universe, CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.universe,
CanonicalVarKind::Region(ui) => ui, CanonicalVarKind::Region(ui) => ui,
CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.universe, CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.universe,
CanonicalVarKind::Const(ui) => ui, CanonicalVarKind::Const(ui, _) => ui,
CanonicalVarKind::PlaceholderConst(placeholder) => placeholder.universe, CanonicalVarKind::PlaceholderConst(placeholder) => placeholder.universe,
} }
} }

View file

@ -85,7 +85,7 @@ crate fn evaluate_goal<'tcx>(
chalk_ir::VariableKind::Lifetime, chalk_ir::VariableKind::Lifetime,
chalk_ir::UniverseIndex { counter: ui.index() }, chalk_ir::UniverseIndex { counter: ui.index() },
), ),
CanonicalVarKind::Const(_ui) => unimplemented!(), CanonicalVarKind::Const(_ui, _ty) => unimplemented!(),
CanonicalVarKind::PlaceholderConst(_pc) => unimplemented!(), CanonicalVarKind::PlaceholderConst(_pc) => unimplemented!(),
}), }),
), ),
@ -127,9 +127,9 @@ crate fn evaluate_goal<'tcx>(
chalk_ir::VariableKind::Lifetime => CanonicalVarKind::Region( chalk_ir::VariableKind::Lifetime => CanonicalVarKind::Region(
ty::UniverseIndex::from_usize(var.skip_kind().counter), ty::UniverseIndex::from_usize(var.skip_kind().counter),
), ),
chalk_ir::VariableKind::Const(_) => CanonicalVarKind::Const( // FIXME(compiler-errors): We don't currently have a way of turning
ty::UniverseIndex::from_usize(var.skip_kind().counter), // a Chalk ty back into a rustc ty, right?
), chalk_ir::VariableKind::Const(_) => todo!(),
}; };
CanonicalVarInfo { kind } CanonicalVarInfo { kind }
}) })

View file

@ -149,7 +149,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
// time writing the results into the various typeck results. // time writing the results into the various typeck results.
let mut autoderef = let mut autoderef =
self.autoderef_overloaded_span(self.span, unadjusted_self_ty, self.call_expr.span); 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, Some(n) => n,
None => { None => {
return self.tcx.ty_error_with_message( return self.tcx.ty_error_with_message(
@ -161,14 +161,15 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
assert_eq!(n, pick.autoderefs); assert_eq!(n, pick.autoderefs);
let mut adjustments = self.adjust_steps(&autoderef); let mut adjustments = self.adjust_steps(&autoderef);
let mut target = self.structurally_resolved_type(autoderef.span(), ty);
let mut target = match pick.autoref_or_ptr_adjustment {
self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
match &pick.autoref_or_ptr_adjustment {
Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, unsize }) => { Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, unsize }) => {
let region = self.next_region_var(infer::Autoref(self.span)); 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 { let mutbl = match mutbl {
hir::Mutability::Not => AutoBorrowMutability::Not, hir::Mutability::Not => AutoBorrowMutability::Not,
hir::Mutability::Mut => AutoBorrowMutability::Mut { hir::Mutability::Mut => AutoBorrowMutability::Mut {
@ -182,10 +183,18 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
target, 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 target = self
.tcx .tcx
.mk_ref(region, ty::TypeAndMut { mutbl: mutbl.into(), ty: unsize_target }); .mk_ref(region, ty::TypeAndMut { mutbl: mutbl.into(), ty: unsized_ty });
adjustments adjustments
.push(Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target }); .push(Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target });
} }

View file

@ -167,26 +167,26 @@ enum ProbeResult {
/// T`, we could convert it to `*const T`, then autoref to `&*const T`. However, currently we do /// 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 /// (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`. /// `mut`), or it has type `*mut T` and we convert it to `*const T`.
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Copy, Clone)]
pub enum AutorefOrPtrAdjustment<'tcx> { pub enum AutorefOrPtrAdjustment {
/// Receiver has type `T`, add `&` or `&mut` (it `T` is `mut`), and maybe also "unsize" it. /// 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. /// Unsizing is used to convert a `[T; N]` to `[T]`, which only makes sense when autorefing.
Autoref { Autoref {
mutbl: hir::Mutability, mutbl: hir::Mutability,
/// Indicates that the source expression should be "unsized" to a target type. This should /// Indicates that the source expression should be "unsized" to a target type.
/// probably eventually go away in favor of just coercing method receivers. /// This is special-cased for just arrays unsizing to slices.
unsize: Option<Ty<'tcx>>, unsize: bool,
}, },
/// Receiver has type `*mut T`, convert to `*const T` /// Receiver has type `*mut T`, convert to `*const T`
ToConstPtr, ToConstPtr,
} }
impl<'tcx> AutorefOrPtrAdjustment<'tcx> { impl AutorefOrPtrAdjustment {
fn get_unsize(&self) -> Option<Ty<'tcx>> { fn get_unsize(&self) -> bool {
match self { match self {
AutorefOrPtrAdjustment::Autoref { mutbl: _, unsize } => *unsize, 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 /// 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`. /// `*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>, pub self_ty: Ty<'tcx>,
} }
@ -1202,7 +1202,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
pick.autoderefs += 1; pick.autoderefs += 1;
pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::Autoref { pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::Autoref {
mutbl, 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| { self.pick_method(autoref_ty, unstable_candidates).map(|r| {
r.map(|mut pick| { r.map(|mut pick| {
pick.autoderefs = step.autoderefs; pick.autoderefs = step.autoderefs;
pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::Autoref { pick.autoref_or_ptr_adjustment =
mutbl, Some(AutorefOrPtrAdjustment::Autoref { mutbl, unsize: step.unsize });
unsize: step.unsize.then_some(self_ty),
});
pick pick
}) })
}) })

View 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);
}

View 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();
}