]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_typeck/src/hir_wf_check.rs
Auto merge of #87250 - robojumper:87199-sized-relaxation, r=nikomatsakis
[rust.git] / compiler / rustc_typeck / src / hir_wf_check.rs
1 use crate::collect::ItemCtxt;
2 use rustc_hir as hir;
3 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
4 use rustc_hir::HirId;
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;
11
12 pub fn provide(providers: &mut Providers) {
13     *providers = Providers { diagnostic_hir_wf_check, ..*providers };
14 }
15
16 // Ideally, this would be in `rustc_trait_selection`, but we
17 // need access to `ItemCtxt`
18 fn diagnostic_hir_wf_check<'tcx>(
19     tcx: TyCtxt<'tcx>,
20     (predicate, hir_id): (ty::Predicate<'tcx>, HirId),
21 ) -> Option<ObligationCause<'tcx>> {
22     let hir = tcx.hir();
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!");
25
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,
30         None => return None,
31     };
32
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());
36
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).
50     //
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> {
57         tcx: TyCtxt<'tcx>,
58         predicate: ty::Predicate<'tcx>,
59         cause: Option<ObligationCause<'tcx>>,
60         cause_depth: usize,
61         icx: ItemCtxt<'tcx>,
62         hir_id: HirId,
63         param_env: ty::ParamEnv<'tcx>,
64         depth: usize,
65     }
66
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
71         }
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(
77                     ty.span,
78                     self.hir_id,
79                     traits::ObligationCauseCode::MiscObligation,
80                 );
81                 fulfill.register_predicate_obligation(
82                     &infcx,
83                     traits::Obligation::new(
84                         cause,
85                         self.param_env,
86                         ty::PredicateKind::WellFormed(tcx_ty.into()).to_predicate(self.tcx),
87                     ),
88                 );
89
90                 if let Err(errors) = fulfill.select_all_or_error(&infcx) {
91                     tracing::debug!("Wf-check got errors for {:?}: {:?}", ty, errors);
92                     for error in 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
100                             }
101                         }
102                     }
103                 }
104             });
105             self.depth += 1;
106             intravisit::walk_ty(self, ty);
107             self.depth -= 1;
108         }
109     }
110
111     let mut visitor = HirWfCheck {
112         tcx,
113         predicate,
114         cause: None,
115         cause_depth: 0,
116         icx,
117         hir_id,
118         param_env: tcx.param_env(def_id.to_def_id()),
119         depth: 0,
120     };
121
122     let ty = match tcx.hir().get(hir_id) {
123         hir::Node::ImplItem(item) => match item.kind {
124             hir::ImplItemKind::TyAlias(ty) => Some(ty),
125             _ => None,
126         },
127         hir::Node::TraitItem(item) => match item.kind {
128             hir::TraitItemKind::Type(_, ty) => ty,
129             _ => None,
130         },
131         _ => None,
132     };
133     if let Some(ty) = ty {
134         visitor.visit_ty(ty);
135     }
136     visitor.cause
137 }