use std::collections::HashMap;
use log::trace;
use rustc_hir::def_id::DefId;
use rustc_infer::{
infer::{InferCtxt, TyCtxtInferExt},
traits::{Obligation, ObligationCause},
};
use rustc_middle::ty::Ty;
use rustc_span::DUMMY_SP;
use rustc_trait_selection::traits::ObligationCtxt;
use crate::{Args, BevyCtxt};
pub(crate) fn find_trait_impls(ctxt: &mut BevyCtxt<'_>, _args: &Args) -> bool {
let tcx = &ctxt.tcx;
ctxt.reflect_types.retain(|reflect_ty_did, _| {
let retaining = type_impl_of_trait(
tcx,
ctxt.cached_traits.mlua_from_lua_multi.unwrap(),
reflect_ty_did,
)
.is_empty()
|| type_impl_of_trait(
tcx,
ctxt.cached_traits.mlua_into_lua_multi.unwrap(),
reflect_ty_did,
)
.is_empty();
if !retaining {
trace!(
"Type: `{:?}` implements both from and into lua, removing from reflect types",
tcx.def_path_str(*reflect_ty_did)
);
}
retaining
});
log::trace!(
"Looking for impls of the traits: [{}]",
ctxt.cached_traits
.std_source_traits
.values()
.map(|d| tcx.def_path_str(*d))
.collect::<Vec<_>>()
.join(", ")
);
for (reflect_ty_did, type_ctxt) in ctxt.reflect_types.iter_mut() {
let mut impls = Vec::default();
for trait_did in ctxt.cached_traits.std_source_traits.values() {
let matching_impls = type_impl_of_trait(tcx, *trait_did, reflect_ty_did);
if !matching_impls.is_empty() {
impls.push((*trait_did, matching_impls));
}
}
assert!(type_ctxt.trait_impls.is_none(), "trait impls already set!");
type_ctxt.trait_impls = Some(HashMap::from_iter(impls));
}
true
}
fn type_impl_of_trait(
tcx: &rustc_middle::ty::TyCtxt<'_>,
trait_did: DefId,
reflect_ty_did: &rustc_hir::def_id::DefId,
) -> Vec<DefId> {
log::trace!(
"Finding impl for trait: {:?} on type: {:?}",
tcx.def_path_str(trait_did),
tcx.def_path_str(*reflect_ty_did)
);
let mut out = Vec::default();
tcx.for_each_relevant_impl(
trait_did,
tcx.type_of(reflect_ty_did).instantiate_identity(),
|impl_did| {
trace!(
"Possible impl for trait: {:?} on type: {:?} found: {:?}",
tcx.def_path_str(trait_did),
tcx.def_path_str(reflect_ty_did),
impl_did,
);
let ty = tcx.type_of(reflect_ty_did).instantiate_identity();
let infcx = tcx.infer_ctxt().build();
let result = impl_matches(&infcx, ty, impl_did);
log::trace!("Result: {:#?}", result);
if result {
trace!(
"Type: `{}` implements trait: `{}`",
ty,
tcx.item_name(trait_did)
);
out.push(impl_did)
} else {
trace!(
"Type: `{}` does not implement trait: `{}`",
ty,
tcx.item_name(trait_did)
);
}
},
);
out
}
fn impl_matches<'tcx>(infcx: &InferCtxt<'tcx>, ty: Ty<'tcx>, impl_def_id: DefId) -> bool {
let tcx = infcx.tcx;
let impl_may_apply = |impl_def_id| {
let ocx = ObligationCtxt::new(infcx);
let param_env = tcx.param_env_reveal_all_normalized(impl_def_id);
let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id);
let impl_trait_ref = tcx
.impl_trait_ref(impl_def_id)
.expect("Expected defid to be an impl for a trait")
.instantiate(tcx, impl_args);
let impl_trait_ref = ocx.normalize(&ObligationCause::dummy(), param_env, impl_trait_ref);
let impl_trait_ref_ty = impl_trait_ref.self_ty();
if ocx
.eq(&ObligationCause::dummy(), param_env, impl_trait_ref_ty, ty)
.is_err()
{
return false;
}
let impl_predicates = tcx.predicates_of(impl_def_id).instantiate(tcx, impl_args);
ocx.register_obligations(impl_predicates.predicates.iter().map(|&predicate| {
Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate)
}));
ocx.select_where_possible().is_empty()
};
infcx.probe(|_| impl_may_apply(impl_def_id))
}