]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_hir_analysis/src/hir_wf_check.rs
Rollup merge of #106951 - tmiasko:rm-simplify-initial, r=oli-obk
[rust.git] / compiler / rustc_hir_analysis / src / hir_wf_check.rs
1 use crate::collect::ItemCtxt;
2 use rustc_hir as hir;
3 use rustc_hir::intravisit::{self, Visitor};
4 use rustc_hir::{ForeignItem, ForeignItemKind, HirId};
5 use rustc_infer::infer::TyCtxtInferExt;
6 use rustc_infer::traits::{ObligationCause, WellFormedLoc};
7 use rustc_middle::ty::query::Providers;
8 use rustc_middle::ty::{self, Region, TyCtxt, TypeFoldable, TypeFolder};
9 use rustc_trait_selection::traits;
10
11 pub fn provide(providers: &mut Providers) {
12     *providers = Providers { diagnostic_hir_wf_check, ..*providers };
13 }
14
15 // Ideally, this would be in `rustc_trait_selection`, but we
16 // need access to `ItemCtxt`
17 fn diagnostic_hir_wf_check<'tcx>(
18     tcx: TyCtxt<'tcx>,
19     (predicate, loc): (ty::Predicate<'tcx>, WellFormedLoc),
20 ) -> Option<ObligationCause<'tcx>> {
21     let hir = tcx.hir();
22
23     let def_id = match loc {
24         WellFormedLoc::Ty(def_id) => def_id,
25         WellFormedLoc::Param { function, param_idx: _ } => function,
26     };
27     let hir_id = hir.local_def_id_to_hir_id(def_id);
28
29     // HIR wfcheck should only ever happen as part of improving an existing error
30     tcx.sess
31         .delay_span_bug(tcx.def_span(def_id), "Performed HIR wfcheck without an existing error!");
32
33     let icx = ItemCtxt::new(tcx, def_id.to_def_id());
34
35     // To perform HIR-based WF checking, we iterate over all HIR types
36     // that occur 'inside' the item we're checking. For example,
37     // given the type `Option<MyStruct<u8>>`, we will check
38     // `Option<MyStruct<u8>>`, `MyStruct<u8>`, and `u8`.
39     // For each type, we perform a well-formed check, and see if we get
40     // an error that matches our expected predicate. We save
41     // the `ObligationCause` corresponding to the *innermost* type,
42     // which is the most specific type that we can point to.
43     // In general, the different components of an `hir::Ty` may have
44     // completely different spans due to macro invocations. Pointing
45     // to the most accurate part of the type can be the difference
46     // between a useless span (e.g. the macro invocation site)
47     // and a useful span (e.g. a user-provided type passed into the macro).
48     //
49     // This approach is quite inefficient - we redo a lot of work done
50     // by the normal WF checker. However, this code is run at most once
51     // per reported error - it will have no impact when compilation succeeds,
52     // and should only have an impact if a very large number of errors is
53     // displayed to the user.
54     struct HirWfCheck<'tcx> {
55         tcx: TyCtxt<'tcx>,
56         predicate: ty::Predicate<'tcx>,
57         cause: Option<ObligationCause<'tcx>>,
58         cause_depth: usize,
59         icx: ItemCtxt<'tcx>,
60         hir_id: HirId,
61         param_env: ty::ParamEnv<'tcx>,
62         depth: usize,
63     }
64
65     impl<'tcx> Visitor<'tcx> for HirWfCheck<'tcx> {
66         fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
67             let infcx = self.tcx.infer_ctxt().build();
68             let tcx_ty = self.icx.to_ty(ty).fold_with(&mut EraseAllBoundRegions { tcx: self.tcx });
69             let cause = traits::ObligationCause::new(
70                 ty.span,
71                 self.hir_id,
72                 traits::ObligationCauseCode::WellFormed(None),
73             );
74             let errors = traits::fully_solve_obligation(
75                 &infcx,
76                 traits::Obligation::new(
77                     self.tcx,
78                     cause,
79                     self.param_env,
80                     ty::Binder::dummy(ty::PredicateKind::WellFormed(tcx_ty.into())),
81                 ),
82             );
83             if !errors.is_empty() {
84                 debug!("Wf-check got errors for {:?}: {:?}", ty, errors);
85                 for error in errors {
86                     if error.obligation.predicate == self.predicate {
87                         // Save the cause from the greatest depth - this corresponds
88                         // to picking more-specific types (e.g. `MyStruct<u8>`)
89                         // over less-specific types (e.g. `Option<MyStruct<u8>>`)
90                         if self.depth >= self.cause_depth {
91                             self.cause = Some(error.obligation.cause);
92                             self.cause_depth = self.depth
93                         }
94                     }
95                 }
96             }
97             self.depth += 1;
98             intravisit::walk_ty(self, ty);
99             self.depth -= 1;
100         }
101     }
102
103     let mut visitor = HirWfCheck {
104         tcx,
105         predicate,
106         cause: None,
107         cause_depth: 0,
108         icx,
109         hir_id,
110         param_env: tcx.param_env(def_id.to_def_id()),
111         depth: 0,
112     };
113
114     // Get the starting `hir::Ty` using our `WellFormedLoc`.
115     // We will walk 'into' this type to try to find
116     // a more precise span for our predicate.
117     let tys = match loc {
118         WellFormedLoc::Ty(_) => match hir.get(hir_id) {
119             hir::Node::ImplItem(item) => match item.kind {
120                 hir::ImplItemKind::Type(ty) => vec![ty],
121                 hir::ImplItemKind::Const(ty, _) => vec![ty],
122                 ref item => bug!("Unexpected ImplItem {:?}", item),
123             },
124             hir::Node::TraitItem(item) => match item.kind {
125                 hir::TraitItemKind::Type(_, ty) => ty.into_iter().collect(),
126                 hir::TraitItemKind::Const(ty, _) => vec![ty],
127                 ref item => bug!("Unexpected TraitItem {:?}", item),
128             },
129             hir::Node::Item(item) => match item.kind {
130                 hir::ItemKind::Static(ty, _, _) | hir::ItemKind::Const(ty, _) => vec![ty],
131                 hir::ItemKind::Impl(ref impl_) => match &impl_.of_trait {
132                     Some(t) => t
133                         .path
134                         .segments
135                         .last()
136                         .iter()
137                         .flat_map(|seg| seg.args().args)
138                         .filter_map(|arg| {
139                             if let hir::GenericArg::Type(ty) = arg { Some(*ty) } else { None }
140                         })
141                         .chain([impl_.self_ty])
142                         .collect(),
143                     None => {
144                         vec![impl_.self_ty]
145                     }
146                 },
147                 ref item => bug!("Unexpected item {:?}", item),
148             },
149             hir::Node::Field(field) => vec![field.ty],
150             hir::Node::ForeignItem(ForeignItem {
151                 kind: ForeignItemKind::Static(ty, _), ..
152             }) => vec![*ty],
153             hir::Node::GenericParam(hir::GenericParam {
154                 kind: hir::GenericParamKind::Type { default: Some(ty), .. },
155                 ..
156             }) => vec![*ty],
157             ref node => bug!("Unexpected node {:?}", node),
158         },
159         WellFormedLoc::Param { function: _, param_idx } => {
160             let fn_decl = hir.fn_decl_by_hir_id(hir_id).unwrap();
161             // Get return type
162             if param_idx as usize == fn_decl.inputs.len() {
163                 match fn_decl.output {
164                     hir::FnRetTy::Return(ty) => vec![ty],
165                     // The unit type `()` is always well-formed
166                     hir::FnRetTy::DefaultReturn(_span) => vec![],
167                 }
168             } else {
169                 vec![&fn_decl.inputs[param_idx as usize]]
170             }
171         }
172     };
173     for ty in tys {
174         visitor.visit_ty(ty);
175     }
176     visitor.cause
177 }
178
179 struct EraseAllBoundRegions<'tcx> {
180     tcx: TyCtxt<'tcx>,
181 }
182
183 // Higher ranked regions are complicated.
184 // To make matters worse, the HIR WF check can instantiate them
185 // outside of a `Binder`, due to the way we (ab)use
186 // `ItemCtxt::to_ty`. To make things simpler, we just erase all
187 // of them, regardless of depth. At worse, this will give
188 // us an inaccurate span for an error message, but cannot
189 // lead to unsoundness (we call `delay_span_bug` at the start
190 // of `diagnostic_hir_wf_check`).
191 impl<'tcx> TypeFolder<'tcx> for EraseAllBoundRegions<'tcx> {
192     fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
193         self.tcx
194     }
195     fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> {
196         if r.is_late_bound() { self.tcx.lifetimes.re_erased } else { r }
197     }
198 }