codegen/mir: support polymorphic InstanceDefs

This commit modifies the use of `subst_and_normalize_erasing_regions` on
parts of the MIR bodies returned from `instance_mir`, so that
`InstanceDef::CloneShim` and `InstanceDef::DropGlue` (where there is a
type) do not perform substitutions. This avoids double substitutions and
enables polymorphic `InstanceDef`s.

Signed-off-by: David Wood <david@davidtw.co>
This commit is contained in:
David Wood 2020-03-11 19:36:07 +00:00
parent 303d8aff60
commit bee151308d
No known key found for this signature in database
GPG Key ID: 2592E76C87381FD9
7 changed files with 103 additions and 84 deletions

View File

@ -369,6 +369,32 @@ pub fn fn_once_adapter_instance(
Instance { def, substs }
}
/// FIXME(#69925) Depending on the kind of `InstanceDef`, the MIR body associated with an
/// instance is expressed in terms of the generic parameters of `self.def_id()`, and in other
/// cases the MIR body is expressed in terms of the types found in the substitution array.
/// In the former case, we want to substitute those generic types and replace them with the
/// values from the substs when monomorphizing the function body. But in the latter case, we
/// don't want to do that substitution, since it has already been done effectively.
///
/// This function returns `Some(substs)` in the former case and None otherwise -- i.e., if
/// this function returns `None`, then the MIR body does not require substitution during
/// monomorphization.
pub fn substs_for_mir_body(&self) -> Option<SubstsRef<'tcx>> {
match self.def {
InstanceDef::CloneShim(..)
| InstanceDef::DropGlue(_, Some(_)) => None,
InstanceDef::ClosureOnceShim { .. }
| InstanceDef::DropGlue(..)
// FIXME(#69925): `FnPtrShim` should be in the other branch.
| InstanceDef::FnPtrShim(..)
| InstanceDef::Item(_)
| InstanceDef::Intrinsic(..)
| InstanceDef::ReifyShim(..)
| InstanceDef::Virtual(..)
| InstanceDef::VtableShim(..) => Some(self.substs),
}
}
pub fn is_vtable_shim(&self) -> bool {
if let InstanceDef::VtableShim(..) = self.def { true } else { false }
}

View File

@ -86,13 +86,18 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
pub fn monomorphize<T>(&self, value: &T) -> T
where
T: TypeFoldable<'tcx>,
T: Copy + TypeFoldable<'tcx>,
{
self.cx.tcx().subst_and_normalize_erasing_regions(
self.instance.substs,
ty::ParamEnv::reveal_all(),
value,
)
debug!("monomorphize: self.instance={:?}", self.instance);
if let Some(substs) = self.instance.substs_for_mir_body() {
self.cx.tcx().subst_and_normalize_erasing_regions(
substs,
ty::ParamEnv::reveal_all(),
&value,
)
} else {
self.cx.tcx().normalize_erasing_regions(ty::ParamEnv::reveal_all(), *value)
}
}
}

View File

@ -335,15 +335,25 @@ pub fn load_mir(
/// Call this on things you got out of the MIR (so it is as generic as the current
/// stack frame), to bring it into the proper environment for this interpreter.
pub(super) fn subst_from_frame_and_normalize_erasing_regions<T: TypeFoldable<'tcx>>(
pub(super) fn subst_from_current_frame_and_normalize_erasing_regions<T: TypeFoldable<'tcx>>(
&self,
value: T,
) -> T {
self.tcx.subst_and_normalize_erasing_regions(
self.frame().instance.substs,
self.param_env,
&value,
)
self.subst_from_frame_and_normalize_erasing_regions(self.frame(), value)
}
/// Call this on things you got out of the MIR (so it is as generic as the provided
/// stack frame), to bring it into the proper environment for this interpreter.
pub(super) fn subst_from_frame_and_normalize_erasing_regions<T: TypeFoldable<'tcx>>(
&self,
frame: &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>,
value: T,
) -> T {
if let Some(substs) = frame.instance.substs_for_mir_body() {
self.tcx.subst_and_normalize_erasing_regions(substs, self.param_env, &value)
} else {
self.tcx.normalize_erasing_regions(self.param_env, value)
}
}
/// The `substs` are assumed to already be in our interpreter "universe" (param_env).
@ -371,11 +381,8 @@ pub fn layout_of_local(
None => {
let layout = crate::interpret::operand::from_known_layout(layout, || {
let local_ty = frame.body.local_decls[local].ty;
let local_ty = self.tcx.subst_and_normalize_erasing_regions(
frame.instance.substs,
self.param_env,
&local_ty,
);
let local_ty =
self.subst_from_frame_and_normalize_erasing_regions(frame, local_ty);
self.layout_of(local_ty)
})?;
if let Some(state) = frame.locals.get(local) {

View File

@ -491,7 +491,8 @@ pub fn eval_operand(
Copy(ref place) | Move(ref place) => self.eval_place_to_op(place, layout)?,
Constant(ref constant) => {
let val = self.subst_from_frame_and_normalize_erasing_regions(constant.literal);
let val =
self.subst_from_current_frame_and_normalize_erasing_regions(constant.literal);
self.eval_const_to_op(val, layout)?
}
};

View File

@ -648,9 +648,11 @@ pub fn eval_place(
// bail out.
None => Place::null(&*self),
},
layout: self.layout_of(self.subst_from_frame_and_normalize_erasing_regions(
self.frame().body.return_ty(),
))?,
layout: self.layout_of(
self.subst_from_current_frame_and_normalize_erasing_regions(
self.frame().body.return_ty(),
),
)?,
}
}
local => PlaceTy {

View File

@ -248,7 +248,7 @@ pub fn eval_rvalue_into_place(
}
NullaryOp(mir::NullOp::SizeOf, ty) => {
let ty = self.subst_from_frame_and_normalize_erasing_regions(ty);
let ty = self.subst_from_current_frame_and_normalize_erasing_regions(ty);
let layout = self.layout_of(ty)?;
assert!(
!layout.is_unsized(),

View File

@ -186,7 +186,7 @@
use rustc::session::config::EntryFnType;
use rustc::ty::adjustment::{CustomCoerceUnsized, PointerCast};
use rustc::ty::print::obsolete::DefPathBasedNames;
use rustc::ty::subst::{InternalSubsts, SubstsRef};
use rustc::ty::subst::InternalSubsts;
use rustc::ty::{self, GenericParamDefKind, Instance, Ty, TyCtxt, TypeFoldable};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::sync::{par_iter, MTLock, MTRef, ParallelIterator};
@ -493,7 +493,21 @@ struct MirNeighborCollector<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
body: &'a mir::Body<'tcx>,
output: &'a mut Vec<MonoItem<'tcx>>,
param_substs: SubstsRef<'tcx>,
instance: Instance<'tcx>,
}
impl<'a, 'tcx> MirNeighborCollector<'a, 'tcx> {
pub fn monomorphize<T>(&self, value: T) -> T
where
T: TypeFoldable<'tcx>,
{
debug!("monomorphize: self.instance={:?}", self.instance);
if let Some(substs) = self.instance.substs_for_mir_body() {
self.tcx.subst_and_normalize_erasing_regions(substs, ty::ParamEnv::reveal_all(), &value)
} else {
self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), value)
}
}
}
impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
@ -509,17 +523,9 @@ fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
ref operand,
target_ty,
) => {
let target_ty = self.tcx.subst_and_normalize_erasing_regions(
self.param_substs,
ty::ParamEnv::reveal_all(),
&target_ty,
);
let target_ty = self.monomorphize(target_ty);
let source_ty = operand.ty(self.body, self.tcx);
let source_ty = self.tcx.subst_and_normalize_erasing_regions(
self.param_substs,
ty::ParamEnv::reveal_all(),
&source_ty,
);
let source_ty = self.monomorphize(source_ty);
let (source_ty, target_ty) =
find_vtable_types_for_unsizing(self.tcx, source_ty, target_ty);
// This could also be a different Unsize instruction, like
@ -540,11 +546,7 @@ fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
_,
) => {
let fn_ty = operand.ty(self.body, self.tcx);
let fn_ty = self.tcx.subst_and_normalize_erasing_regions(
self.param_substs,
ty::ParamEnv::reveal_all(),
&fn_ty,
);
let fn_ty = self.monomorphize(fn_ty);
visit_fn_use(self.tcx, fn_ty, false, &mut self.output);
}
mir::Rvalue::Cast(
@ -553,11 +555,7 @@ fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
_,
) => {
let source_ty = operand.ty(self.body, self.tcx);
let source_ty = self.tcx.subst_and_normalize_erasing_regions(
self.param_substs,
ty::ParamEnv::reveal_all(),
&source_ty,
);
let source_ty = self.monomorphize(source_ty);
match source_ty.kind {
ty::Closure(def_id, substs) => {
let instance = Instance::resolve_closure(
@ -593,7 +591,23 @@ fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, location: Location) {
debug!("visiting const {:?} @ {:?}", *constant, location);
collect_const(self.tcx, *constant, self.param_substs, self.output);
let substituted_constant = self.monomorphize(*constant);
let param_env = ty::ParamEnv::reveal_all();
match substituted_constant.val {
ty::ConstKind::Value(val) => collect_const_value(self.tcx, val, self.output),
ty::ConstKind::Unevaluated(def_id, substs, promoted) => {
match self.tcx.const_eval_resolve(param_env, def_id, substs, promoted, None) {
Ok(val) => collect_const_value(self.tcx, val, self.output),
Err(ErrorHandled::Reported) => {}
Err(ErrorHandled::TooGeneric) => span_bug!(
self.tcx.def_span(def_id),
"collection encountered polymorphic constant",
),
}
}
_ => {}
}
self.super_const(constant);
}
@ -605,21 +619,13 @@ fn visit_terminator_kind(&mut self, kind: &mir::TerminatorKind<'tcx>, location:
match *kind {
mir::TerminatorKind::Call { ref func, .. } => {
let callee_ty = func.ty(self.body, tcx);
let callee_ty = tcx.subst_and_normalize_erasing_regions(
self.param_substs,
ty::ParamEnv::reveal_all(),
&callee_ty,
);
let callee_ty = self.monomorphize(callee_ty);
visit_fn_use(self.tcx, callee_ty, true, &mut self.output);
}
mir::TerminatorKind::Drop { ref location, .. }
| mir::TerminatorKind::DropAndReplace { ref location, .. } => {
let ty = location.ty(self.body, self.tcx).ty;
let ty = tcx.subst_and_normalize_erasing_regions(
self.param_substs,
ty::ParamEnv::reveal_all(),
&ty,
);
let ty = self.monomorphize(ty);
visit_drop_use(self.tcx, ty, true, self.output);
}
mir::TerminatorKind::Goto { .. }
@ -1156,8 +1162,7 @@ fn collect_neighbours<'tcx>(
debug!("collect_neighbours: {:?}", instance.def_id());
let body = tcx.instance_mir(instance.def);
MirNeighborCollector { tcx, body: &body, output, param_substs: instance.substs }
.visit_body(body);
MirNeighborCollector { tcx, body: &body, output, instance }.visit_body(body);
}
fn def_id_to_string(tcx: TyCtxt<'_>, def_id: DefId) -> String {
@ -1167,33 +1172,6 @@ fn def_id_to_string(tcx: TyCtxt<'_>, def_id: DefId) -> String {
output
}
fn collect_const<'tcx>(
tcx: TyCtxt<'tcx>,
constant: &'tcx ty::Const<'tcx>,
param_substs: SubstsRef<'tcx>,
output: &mut Vec<MonoItem<'tcx>>,
) {
debug!("visiting const {:?}", constant);
let param_env = ty::ParamEnv::reveal_all();
let substituted_constant =
tcx.subst_and_normalize_erasing_regions(param_substs, param_env, &constant);
match substituted_constant.val {
ty::ConstKind::Value(val) => collect_const_value(tcx, val, output),
ty::ConstKind::Unevaluated(def_id, substs, promoted) => {
match tcx.const_eval_resolve(param_env, def_id, substs, promoted, None) {
Ok(val) => collect_const_value(tcx, val, output),
Err(ErrorHandled::Reported) => {}
Err(ErrorHandled::TooGeneric) => {
span_bug!(tcx.def_span(def_id), "collection encountered polymorphic constant",)
}
}
}
_ => {}
}
}
fn collect_const_value<'tcx>(
tcx: TyCtxt<'tcx>,
value: ConstValue<'tcx>,