in checker/src/call_visitor.rs [185:288]
fn try_to_devirtualize(&mut self) {
let tcx = self.block_visitor.bv.tcx;
if self
.block_visitor
.bv
.tcx
.is_mir_available(self.callee_def_id)
&& !utils::is_trait_method(self.callee_def_id, tcx)
{
return;
}
if let Some(gen_args) = self.callee_generic_arguments {
// The parameter environment of the caller provides a resolution context for the callee.
let param_env = rustc_middle::ty::ParamEnv::reveal_all();
trace!(
"devirtualize resolving def_id {:?}: {:?}",
self.callee_def_id,
tcx.type_of(self.callee_def_id)
);
trace!("devirtualize resolving func_ref {:?}", self.callee_func_ref,);
trace!("gen_args {:?}", gen_args);
if let Some(arg0_ty) = gen_args.types().next() {
if matches!(arg0_ty.kind(), TyKind::Dynamic(..)) {
// Instance::resolve panics if it can't find a vtable entry for the given def_id
// It is hard to figure out exactly when this will be the case, but it does
// happen in a case where the first generic argument type is Dynamic.
return;
}
}
let abi = tcx.type_of(self.callee_def_id).fn_sig(tcx).abi();
let resolved_instance = if abi == rustc_target::spec::abi::Abi::Rust {
Some(rustc_middle::ty::Instance::resolve(
tcx,
param_env,
self.callee_def_id,
gen_args,
))
} else {
None
};
if let Some(Ok(Some(instance))) = resolved_instance {
let resolved_def_id = instance.def.def_id();
let tcx = tcx;
let has_mir = tcx.is_mir_available(resolved_def_id);
if !has_mir && self.callee_known_name == KnownNames::StdCloneClone {
return;
}
self.callee_def_id = resolved_def_id;
let resolved_ty = tcx.type_of(resolved_def_id);
let resolved_map = self.type_visitor().get_generic_arguments_map(
resolved_def_id,
instance.substs,
&[],
);
let specialized_resolved_ty = self
.type_visitor()
.specialize_generic_argument_type(resolved_ty, &resolved_map);
trace!(
"devirtualize resolved def_id {:?}: {:?}",
resolved_def_id,
specialized_resolved_ty
);
let func_const = self
.block_visitor
.visit_function_reference(
resolved_def_id,
specialized_resolved_ty,
Some(instance.substs),
)
.clone();
self.callee_func_ref = if let ConstantDomain::Function(fr) = &func_const {
self.callee_known_name = fr.known_name;
Some(fr.clone())
} else {
None
};
self.callee_fun_val = Rc::new(func_const.into());
self.callee_generic_arguments = Some(instance.substs);
self.callee_generic_argument_map = self.type_visitor().get_generic_arguments_map(
resolved_def_id,
instance.substs,
&self.actual_argument_types,
);
if has_mir && specialized_resolved_ty.is_closure() {
let mir = tcx.optimized_mir(resolved_def_id);
if self.actual_argument_types.len() + 1 == mir.arg_count {
// When the closure has no captured variables, the first argument is just the function pointer.
// Sadly, MIR omits this argument (because the call is via a trait), so we have to add it here.
self.actual_args
.insert(0, (Path::new_parameter(1), self.callee_fun_val.clone()));
self.actual_argument_types.insert(
0,
tcx.mk_mut_ref(tcx.lifetimes.re_static, specialized_resolved_ty),
);
}
}
} else {
debug!(
"could not resolve function {:?}, {:?}, {:?}",
self.callee_def_id, param_env, gen_args,
)
}
}
}