]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_typeck/src/hir_wf_check.rs
Auto merge of #87237 - jonas-schievink:const-for-and-try, r=oli-obk
[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::TraitEngine;
7 use rustc_infer::traits::{ObligationCause, WellFormedLoc};
8 use rustc_middle::ty::query::Providers;
9 use rustc_middle::ty::{self, Region, ToPredicate, TyCtxt, TypeFoldable, TypeFolder};
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, loc): (ty::Predicate<'tcx>, WellFormedLoc),
21 ) -> Option<ObligationCause<'tcx>> {
22     let hir = tcx.hir();
23
24     let def_id = match loc {
25         WellFormedLoc::Ty(def_id) => def_id,
26         WellFormedLoc::Param { function, param_idx: _ } => function,
27     };
28     let hir_id = hir.local_def_id_to_hir_id(def_id);
29
30     // HIR wfcheck should only ever happen as part of improving an existing error
31     tcx.sess
32         .delay_span_bug(tcx.def_span(def_id), "Performed HIR wfcheck without an existing error!");
33
34     let icx = ItemCtxt::new(tcx, def_id.to_def_id());
35
36     // To perform HIR-based WF checking, we iterate over all HIR types
37     // that occur 'inside' the item we're checking. For example,
38     // given the type `Option<MyStruct<u8>>`, we will check
39     // `Option<MyStruct<u8>>`, `MyStruct<u8>`, and `u8`.
40     // For each type, we perform a well-formed check, and see if we get
41     // an error that matches our expected predicate. We save
42     // the `ObligationCause` corresponding to the *innermost* type,
43     // which is the most specific type that we can point to.
44     // In general, the different components of an `hir::Ty` may have
45     // completely different spans due to macro invocations. Pointing
46     // to the most accurate part of the type can be the difference
47     // between a useless span (e.g. the macro invocation site)
48     // and a useful span (e.g. a user-provided type passed into the macro).
49     //
50     // This approach is quite inefficient - we redo a lot of work done
51     // by the normal WF checker. However, this code is run at most once
52     // per reported error - it will have no impact when compilation succeeds,
53     // and should only have an impact if a very large number of errors is
54     // displayed to the user.
55     struct HirWfCheck<'tcx> {
56         tcx: TyCtxt<'tcx>,
57         predicate: ty::Predicate<'tcx>,
58         cause: Option<ObligationCause<'tcx>>,
59         cause_depth: usize,
60         icx: ItemCtxt<'tcx>,
61         hir_id: HirId,
62         param_env: ty::ParamEnv<'tcx>,
63         depth: usize,
64     }
65
66     impl<'tcx> Visitor<'tcx> for HirWfCheck<'tcx> {
67         type Map = intravisit::ErasedMap<'tcx>;
68         fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
69             NestedVisitorMap::None
70         }
71         fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
72             self.tcx.infer_ctxt().enter(|infcx| {
73                 let mut fulfill = traits::FulfillmentContext::new();
74                 let tcx_ty =
75                     self.icx.to_ty(ty).fold_with(&mut EraseAllBoundRegions { tcx: self.tcx });
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     // Get the starting `hir::Ty` using our `WellFormedLoc`.
123     // We will walk 'into' this type to try to find
124     // a more precise span for our predicate.
125     let ty = match loc {
126         WellFormedLoc::Ty(_) => match hir.get(hir_id) {
127             hir::Node::ImplItem(item) => match item.kind {
128                 hir::ImplItemKind::TyAlias(ty) => Some(ty),
129                 hir::ImplItemKind::Const(ty, _) => Some(ty),
130                 ref item => bug!("Unexpected ImplItem {:?}", item),
131             },
132             hir::Node::TraitItem(item) => match item.kind {
133                 hir::TraitItemKind::Type(_, ty) => ty,
134                 hir::TraitItemKind::Const(ty, _) => Some(ty),
135                 ref item => bug!("Unexpected TraitItem {:?}", item),
136             },
137             hir::Node::Item(item) => match item.kind {
138                 hir::ItemKind::Static(ty, _, _) | hir::ItemKind::Const(ty, _) => Some(ty),
139                 hir::ItemKind::Impl(ref impl_) => {
140                     assert!(impl_.of_trait.is_none(), "Unexpected trait impl: {:?}", impl_);
141                     Some(impl_.self_ty)
142                 }
143                 ref item => bug!("Unexpected item {:?}", item),
144             },
145             hir::Node::Field(field) => Some(field.ty),
146             ref node => bug!("Unexpected node {:?}", node),
147         },
148         WellFormedLoc::Param { function: _, param_idx } => {
149             let fn_decl = hir.fn_decl_by_hir_id(hir_id).unwrap();
150             // Get return type
151             if param_idx as usize == fn_decl.inputs.len() {
152                 match fn_decl.output {
153                     hir::FnRetTy::Return(ty) => Some(ty),
154                     // The unit type `()` is always well-formed
155                     hir::FnRetTy::DefaultReturn(_span) => None,
156                 }
157             } else {
158                 Some(&fn_decl.inputs[param_idx as usize])
159             }
160         }
161     };
162     if let Some(ty) = ty {
163         visitor.visit_ty(ty);
164     }
165     visitor.cause
166 }
167
168 struct EraseAllBoundRegions<'tcx> {
169     tcx: TyCtxt<'tcx>,
170 }
171
172 // Higher ranked regions are complicated.
173 // To make matters worse, the HIR WF check can instantiate them
174 // outside of a `Binder`, due to the way we (ab)use
175 // `ItemCtxt::to_ty`. To make things simpler, we just erase all
176 // of them, regardless of depth. At worse, this will give
177 // us an inaccurate span for an error message, but cannot
178 // lead to unsoundess (we call `delay_span_bug` at the start
179 // of `diagnostic_hir_wf_check`).
180 impl<'tcx> TypeFolder<'tcx> for EraseAllBoundRegions<'tcx> {
181     fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
182         self.tcx
183     }
184     fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> {
185         if let ty::ReLateBound(..) = r { &ty::ReErased } else { r }
186     }
187 }