1
Fork 0

Also validate types before inlining.

This commit is contained in:
Camille GILLOT 2022-08-15 11:58:27 +02:00
parent e4fd2bd721
commit 10e71dfdb8
2 changed files with 114 additions and 16 deletions

View file

@ -89,22 +89,20 @@ pub fn equal_up_to_regions<'tcx>(
// Normalize lifetimes away on both sides, then compare. // Normalize lifetimes away on both sides, then compare.
let normalize = |ty: Ty<'tcx>| { let normalize = |ty: Ty<'tcx>| {
tcx.normalize_erasing_regions( let ty = ty.fold_with(&mut BottomUpFolder {
param_env, tcx,
ty.fold_with(&mut BottomUpFolder { // FIXME: We erase all late-bound lifetimes, but this is not fully correct.
tcx, // If you have a type like `<for<'a> fn(&'a u32) as SomeTrait>::Assoc`,
// FIXME: We erase all late-bound lifetimes, but this is not fully correct. // this is not necessarily equivalent to `<fn(&'static u32) as SomeTrait>::Assoc`,
// If you have a type like `<for<'a> fn(&'a u32) as SomeTrait>::Assoc`, // since one may have an `impl SomeTrait for fn(&32)` and
// this is not necessarily equivalent to `<fn(&'static u32) as SomeTrait>::Assoc`, // `impl SomeTrait for fn(&'static u32)` at the same time which
// since one may have an `impl SomeTrait for fn(&32)` and // specify distinct values for Assoc. (See also #56105)
// `impl SomeTrait for fn(&'static u32)` at the same time which lt_op: |_| tcx.lifetimes.re_erased,
// specify distinct values for Assoc. (See also #56105) // Leave consts and types unchanged.
lt_op: |_| tcx.lifetimes.re_erased, ct_op: |ct| ct,
// Leave consts and types unchanged. ty_op: |ty| ty,
ct_op: |ct| ct, });
ty_op: |ty| ty, tcx.try_normalize_erasing_regions(param_env, ty).unwrap_or(ty)
}),
)
}; };
tcx.infer_ctxt().enter(|infcx| infcx.can_eq(param_env, normalize(src), normalize(dest)).is_ok()) tcx.infer_ctxt().enter(|infcx| infcx.can_eq(param_env, normalize(src), normalize(dest)).is_ok())
} }

View file

@ -11,6 +11,7 @@ use rustc_middle::ty::subst::Subst;
use rustc_middle::ty::{self, ConstKind, Instance, InstanceDef, ParamEnv, Ty, TyCtxt}; use rustc_middle::ty::{self, ConstKind, Instance, InstanceDef, ParamEnv, Ty, TyCtxt};
use rustc_session::config::OptLevel; use rustc_session::config::OptLevel;
use rustc_span::{hygiene::ExpnKind, ExpnData, LocalExpnId, Span}; use rustc_span::{hygiene::ExpnKind, ExpnData, LocalExpnId, Span};
use rustc_target::abi::VariantIdx;
use rustc_target::spec::abi::Abi; use rustc_target::spec::abi::Abi;
use super::simplify::{remove_dead_blocks, CfgSimplifier}; use super::simplify::{remove_dead_blocks, CfgSimplifier};
@ -423,6 +424,7 @@ impl<'tcx> Inliner<'tcx> {
instance: callsite.callee, instance: callsite.callee,
callee_body, callee_body,
cost: 0, cost: 0,
validation: Ok(()),
}; };
// Traverse the MIR manually so we can account for the effects of inlining on the CFG. // Traverse the MIR manually so we can account for the effects of inlining on the CFG.
@ -458,6 +460,9 @@ impl<'tcx> Inliner<'tcx> {
checker.visit_local_decl(v, &callee_body.local_decls[v]); checker.visit_local_decl(v, &callee_body.local_decls[v]);
} }
// Abort if type validation found anything fishy.
checker.validation?;
let cost = checker.cost; let cost = checker.cost;
if let InlineAttr::Always = callee_attrs.inline { if let InlineAttr::Always = callee_attrs.inline {
debug!("INLINING {:?} because inline(always) [cost={}]", callsite, cost); debug!("INLINING {:?} because inline(always) [cost={}]", callsite, cost);
@ -738,6 +743,7 @@ struct CostChecker<'b, 'tcx> {
cost: usize, cost: usize,
callee_body: &'b Body<'tcx>, callee_body: &'b Body<'tcx>,
instance: ty::Instance<'tcx>, instance: ty::Instance<'tcx>,
validation: Result<(), &'static str>,
} }
impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
@ -818,6 +824,100 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
self.super_local_decl(local, local_decl) self.super_local_decl(local, local_decl)
} }
/// This method duplicates code from MIR validation in an attempt to detect type mismatches due
/// to normalization failure.
fn visit_projection_elem(
&mut self,
local: Local,
proj_base: &[PlaceElem<'tcx>],
elem: PlaceElem<'tcx>,
context: PlaceContext,
location: Location,
) {
if let ProjectionElem::Field(f, ty) = elem {
let parent = Place { local, projection: self.tcx.intern_place_elems(proj_base) };
let parent_ty = parent.ty(&self.callee_body.local_decls, self.tcx);
let check_equal = |this: &mut Self, f_ty| {
if !equal_up_to_regions(this.tcx, this.param_env, ty, f_ty) {
trace!(?ty, ?f_ty);
this.validation = Err("failed to normalize projection type");
return;
}
};
let kind = match parent_ty.ty.kind() {
&ty::Opaque(def_id, substs) => {
self.tcx.bound_type_of(def_id).subst(self.tcx, substs).kind()
}
kind => kind,
};
match kind {
ty::Tuple(fields) => {
let Some(f_ty) = fields.get(f.as_usize()) else {
self.validation = Err("malformed MIR");
return;
};
check_equal(self, *f_ty);
}
ty::Adt(adt_def, substs) => {
let var = parent_ty.variant_index.unwrap_or(VariantIdx::from_u32(0));
let Some(field) = adt_def.variant(var).fields.get(f.as_usize()) else {
self.validation = Err("malformed MIR");
return;
};
check_equal(self, field.ty(self.tcx, substs));
}
ty::Closure(_, substs) => {
let substs = substs.as_closure();
let Some(f_ty) = substs.upvar_tys().nth(f.as_usize()) else {
self.validation = Err("malformed MIR");
return;
};
check_equal(self, f_ty);
}
&ty::Generator(def_id, substs, _) => {
let f_ty = if let Some(var) = parent_ty.variant_index {
let gen_body = if def_id == self.callee_body.source.def_id() {
self.callee_body
} else {
self.tcx.optimized_mir(def_id)
};
let Some(layout) = gen_body.generator_layout() else {
self.validation = Err("malformed MIR");
return;
};
let Some(&local) = layout.variant_fields[var].get(f) else {
self.validation = Err("malformed MIR");
return;
};
let Some(&f_ty) = layout.field_tys.get(local) else {
self.validation = Err("malformed MIR");
return;
};
f_ty
} else {
let Some(f_ty) = substs.as_generator().prefix_tys().nth(f.index()) else {
self.validation = Err("malformed MIR");
return;
};
f_ty
};
check_equal(self, f_ty);
}
_ => self.validation = Err("malformed MIR"),
}
}
self.super_projection_elem(local, proj_base, elem, context, location);
}
} }
/** /**