also normalize constants when comparing types

This commit is contained in:
Ralf Jung 2020-05-31 15:02:33 +02:00
parent 50d7deac4d
commit 93e3552d04
2 changed files with 36 additions and 33 deletions

View File

@ -239,6 +239,7 @@ pub(super) fn mir_assign_valid_types<'tcx>(
// 2. Subtyping is used. While all normal lifetimes are erased, higher-ranked types
// with their late-bound lifetimes are still around and can lead to type differences.
// Normalize both of them away.
// Also see the related but slightly different pre-monomorphization method in `transform/validate.rs`.
let normalize = |ty: Ty<'tcx>| {
ty.fold_with(&mut BottomUpFolder {
tcx,

View File

@ -81,40 +81,42 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
self.fail(location, format!("encountered jump to invalid basic block {:?}", bb))
}
}
}
/// Check if src can be assigned into dest.
/// This is not precise, it will accept some incorrect assignments.
fn mir_assign_valid_types<'tcx>(tcx: TyCtxt<'tcx>, src: Ty<'tcx>, dest: Ty<'tcx>) -> bool {
if src == dest {
// Equal types, all is good.
return true;
/// Check if src can be assigned into dest.
/// This is not precise, it will accept some incorrect assignments.
fn mir_assign_valid_types(&self, src: Ty<'tcx>, dest: Ty<'tcx>) -> bool {
if src == dest {
// Equal types, all is good.
return true;
}
// Type-changing assignments can happen for (at least) two reasons:
// 1. `&mut T` -> `&T` gets optimized from a reborrow to a mere assignment.
// 2. Subtyping is used. While all normal lifetimes are erased, higher-ranked types
// with their late-bound lifetimes are still around and can lead to type differences.
// Normalize both of them away.
// Also see the related but slightly different post-monomorphization method in `interpret/eval_context.rs`.
let normalize = |ty: Ty<'tcx>| {
ty.fold_with(&mut BottomUpFolder {
tcx: self.tcx,
// Normalize all references to immutable.
ty_op: |ty| match ty.kind {
ty::Ref(_, pointee, _) => {
self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, pointee)
}
_ => ty,
},
// We just erase all late-bound lifetimes, but this is not fully correct (FIXME):
// lifetimes in invariant positions could matter (e.g. through associated types).
// But that just means we miss some potential incompatible types, it will not
// lead to wrong errors.
lt_op: |_| self.tcx.lifetimes.re_erased,
// Evaluate consts.
ct_op: |ct| ct.eval(self.tcx, self.param_env),
})
};
normalize(src) == normalize(dest)
}
// Type-changing assignments can happen for (at least) two reasons:
// 1. `&mut T` -> `&T` gets optimized from a reborrow to a mere assignment.
// 2. Subtyping is used. While all normal lifetimes are erased, higher-ranked types
// with their late-bound lifetimes are still around and can lead to type differences.
// Normalize both of them away.
// FIXME: Share this code with `interpret/eval_context.rs`.
let normalize = |ty: Ty<'tcx>| {
ty.fold_with(&mut BottomUpFolder {
tcx,
// Normalize all references to immutable.
ty_op: |ty| match ty.kind {
ty::Ref(_, pointee, _) => tcx.mk_imm_ref(tcx.lifetimes.re_erased, pointee),
_ => ty,
},
// We just erase all late-bound lifetimes, but this is not fully correct (FIXME):
// lifetimes in invariant positions could matter (e.g. through associated types).
// But that just means we miss some potential incompatible types, it will not
// lead to wrong errors.
lt_op: |_| tcx.lifetimes.re_erased,
// Leave consts unchanged.
ct_op: |ct| ct,
})
};
normalize(src) == normalize(dest)
}
impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
@ -138,7 +140,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
// LHS and RHS of the assignment must have the same type.
let left_ty = dest.ty(&self.body.local_decls, self.tcx).ty;
let right_ty = rvalue.ty(&self.body.local_decls, self.tcx);
if !mir_assign_valid_types(self.tcx, right_ty, left_ty) {
if !self.mir_assign_valid_types(right_ty, left_ty) {
self.fail(
location,
format!(