1 use crate::collect::ItemCtxt;
3 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
5 use rustc_infer::infer::TyCtxtInferExt;
6 use rustc_infer::traits::ObligationCause;
7 use rustc_infer::traits::TraitEngine;
8 use rustc_middle::ty::query::Providers;
9 use rustc_middle::ty::{self, ToPredicate, TyCtxt};
10 use rustc_trait_selection::traits;
12 pub fn provide(providers: &mut Providers) {
13 *providers = Providers { diagnostic_hir_wf_check, ..*providers };
16 // Ideally, this would be in `rustc_trait_selection`, but we
17 // need access to `ItemCtxt`
18 fn diagnostic_hir_wf_check<'tcx>(
20 (predicate, hir_id): (ty::Predicate<'tcx>, HirId),
21 ) -> Option<ObligationCause<'tcx>> {
23 // HIR wfcheck should only ever happen as part of improving an existing error
24 tcx.sess.delay_span_bug(hir.span(hir_id), "Performed HIR wfcheck without an existing error!");
26 // Currently, we only handle WF checking for items (e.g. associated items).
27 // It would be nice to extend this to handle wf checks inside functions.
28 let def_id = match tcx.hir().opt_local_def_id(hir_id) {
29 Some(def_id) => def_id,
33 // FIXME - figure out how we want to handle wf-checking for
34 // things inside a function body.
35 let icx = ItemCtxt::new(tcx, def_id.to_def_id());
37 // To perform HIR-based WF checking, we iterate over all HIR types
38 // that occur 'inside' the item we're checking. For example,
39 // given the type `Option<MyStruct<u8>>`, we will check
40 // `Option<MyStruct<u8>>`, `MyStruct<u8>`, and `u8`.
41 // For each type, we perform a well-formed check, and see if we get
42 // an erorr that matches our expected predicate. We keep save
43 // the `ObligationCause` corresponding to the *innermost* type,
44 // which is the most specific type that we can point to.
45 // In general, the different components of an `hir::Ty` may have
46 // completely differentr spans due to macro invocations. Pointing
47 // to the most accurate part of the type can be the difference
48 // between a useless span (e.g. the macro invocation site)
49 // and a useful span (e.g. a user-provided type passed in to the macro).
51 // This approach is quite inefficient - we redo a lot of work done
52 // by the normal WF checker. However, this code is run at most once
53 // per reported error - it will have no impact when compilation succeeds,
54 // and should only have an impact if a very large number of errors are
55 // displaydd to the user.
56 struct HirWfCheck<'tcx> {
58 predicate: ty::Predicate<'tcx>,
59 cause: Option<ObligationCause<'tcx>>,
63 param_env: ty::ParamEnv<'tcx>,
67 impl<'tcx> Visitor<'tcx> for HirWfCheck<'tcx> {
68 type Map = intravisit::ErasedMap<'tcx>;
69 fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
70 NestedVisitorMap::None
72 fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
73 self.tcx.infer_ctxt().enter(|infcx| {
74 let mut fulfill = traits::FulfillmentContext::new();
75 let tcx_ty = self.icx.to_ty(ty);
76 let cause = traits::ObligationCause::new(
79 traits::ObligationCauseCode::MiscObligation,
81 fulfill.register_predicate_obligation(
83 traits::Obligation::new(
86 ty::PredicateKind::WellFormed(tcx_ty.into()).to_predicate(self.tcx),
90 if let Err(errors) = fulfill.select_all_or_error(&infcx) {
91 tracing::debug!("Wf-check got errors for {:?}: {:?}", ty, errors);
93 if error.obligation.predicate == self.predicate {
94 // Save the cause from the greatest depth - this corresponds
95 // to picking more-specific types (e.g. `MyStruct<u8>`)
96 // over less-specific types (e.g. `Option<MyStruct<u8>>`)
97 if self.depth >= self.cause_depth {
98 self.cause = Some(error.obligation.cause);
99 self.cause_depth = self.depth
106 intravisit::walk_ty(self, ty);
111 let mut visitor = HirWfCheck {
118 param_env: tcx.param_env(def_id.to_def_id()),
122 let ty = match tcx.hir().get(hir_id) {
123 hir::Node::ImplItem(item) => match item.kind {
124 hir::ImplItemKind::TyAlias(ty) => Some(ty),
127 hir::Node::TraitItem(item) => match item.kind {
128 hir::TraitItemKind::Type(_, ty) => ty,
133 if let Some(ty) = ty {
134 visitor.visit_ty(ty);