]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_hir_analysis/src/hir_wf_check.rs
bump version to 1.74.1+dfsg1-1~bpo12+pve1
[rustc.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};
5 use rustc_infer::infer::TyCtxtInferExt;
6 use rustc_infer::traits::{ObligationCause, WellFormedLoc};
7 use rustc_middle::query::Providers;
8 use rustc_middle::ty::{self, Region, TyCtxt, TypeFoldable, TypeFolder};
9 use rustc_span::def_id::LocalDefId;
10 use rustc_trait_selection::traits::{self, ObligationCtxt};
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);
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 def_id: LocalDefId,
62 param_env: ty::ParamEnv<'tcx>,
63 depth: usize,
64 }
65
66 impl<'tcx> Visitor<'tcx> for HirWfCheck<'tcx> {
67 fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
68 let infcx = self.tcx.infer_ctxt().build();
69 let ocx = ObligationCtxt::new(&infcx);
70
71 let tcx_ty = self.icx.to_ty(ty).fold_with(&mut EraseAllBoundRegions { tcx: self.tcx });
72 let cause = traits::ObligationCause::new(
73 ty.span,
74 self.def_id,
75 traits::ObligationCauseCode::WellFormed(None),
76 );
77
78 ocx.register_obligation(traits::Obligation::new(
79 self.tcx,
80 cause,
81 self.param_env,
82 ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(tcx_ty.into())),
83 ));
84
85 for error in ocx.select_all_or_error() {
86 debug!("Wf-check got error for {:?}: {:?}", ty, error);
87 if error.obligation.predicate == self.predicate {
88 // Save the cause from the greatest depth - this corresponds
89 // to picking more-specific types (e.g. `MyStruct<u8>`)
90 // over less-specific types (e.g. `Option<MyStruct<u8>>`)
91 if self.depth >= self.cause_depth {
92 self.cause = Some(error.obligation.cause);
93 self.cause_depth = self.depth
94 }
95 }
96 }
97
98 self.depth += 1;
99 intravisit::walk_ty(self, ty);
100 self.depth -= 1;
101 }
102 }
103
104 let mut visitor = HirWfCheck {
105 tcx,
106 predicate,
107 cause: None,
108 cause_depth: 0,
109 icx,
110 def_id,
111 param_env: tcx.param_env(def_id.to_def_id()),
112 depth: 0,
113 };
114
115 // Get the starting `hir::Ty` using our `WellFormedLoc`.
116 // We will walk 'into' this type to try to find
117 // a more precise span for our predicate.
118 let tys = match loc {
119 WellFormedLoc::Ty(_) => match hir.get(hir_id) {
120 hir::Node::ImplItem(item) => match item.kind {
121 hir::ImplItemKind::Type(ty) => vec![ty],
122 hir::ImplItemKind::Const(ty, _) => vec![ty],
123 ref item => bug!("Unexpected ImplItem {:?}", item),
124 },
125 hir::Node::TraitItem(item) => match item.kind {
126 hir::TraitItemKind::Type(_, ty) => ty.into_iter().collect(),
127 hir::TraitItemKind::Const(ty, _) => vec![ty],
128 ref item => bug!("Unexpected TraitItem {:?}", item),
129 },
130 hir::Node::Item(item) => match item.kind {
131 hir::ItemKind::TyAlias(ty, _)
132 | hir::ItemKind::Static(ty, _, _)
133 | hir::ItemKind::Const(ty, _, _) => vec![ty],
134 hir::ItemKind::Impl(impl_) => match &impl_.of_trait {
135 Some(t) => t
136 .path
137 .segments
138 .last()
139 .iter()
140 .flat_map(|seg| seg.args().args)
141 .filter_map(|arg| {
142 if let hir::GenericArg::Type(ty) = arg { Some(*ty) } else { None }
143 })
144 .chain([impl_.self_ty])
145 .collect(),
146 None => {
147 vec![impl_.self_ty]
148 }
149 },
150 ref item => bug!("Unexpected item {:?}", item),
151 },
152 hir::Node::Field(field) => vec![field.ty],
153 hir::Node::ForeignItem(ForeignItem {
154 kind: ForeignItemKind::Static(ty, _), ..
155 }) => vec![*ty],
156 hir::Node::GenericParam(hir::GenericParam {
157 kind: hir::GenericParamKind::Type { default: Some(ty), .. },
158 ..
159 }) => vec![*ty],
160 ref node => bug!("Unexpected node {:?}", node),
161 },
162 WellFormedLoc::Param { function: _, param_idx } => {
163 let fn_decl = hir.fn_decl_by_hir_id(hir_id).unwrap();
164 // Get return type
165 if param_idx as usize == fn_decl.inputs.len() {
166 match fn_decl.output {
167 hir::FnRetTy::Return(ty) => vec![ty],
168 // The unit type `()` is always well-formed
169 hir::FnRetTy::DefaultReturn(_span) => vec![],
170 }
171 } else {
172 vec![&fn_decl.inputs[param_idx as usize]]
173 }
174 }
175 };
176 for ty in tys {
177 visitor.visit_ty(ty);
178 }
179 visitor.cause
180 }
181
182 struct EraseAllBoundRegions<'tcx> {
183 tcx: TyCtxt<'tcx>,
184 }
185
186 // Higher ranked regions are complicated.
187 // To make matters worse, the HIR WF check can instantiate them
188 // outside of a `Binder`, due to the way we (ab)use
189 // `ItemCtxt::to_ty`. To make things simpler, we just erase all
190 // of them, regardless of depth. At worse, this will give
191 // us an inaccurate span for an error message, but cannot
192 // lead to unsoundness (we call `delay_span_bug` at the start
193 // of `diagnostic_hir_wf_check`).
194 impl<'tcx> TypeFolder<TyCtxt<'tcx>> for EraseAllBoundRegions<'tcx> {
195 fn interner(&self) -> TyCtxt<'tcx> {
196 self.tcx
197 }
198 fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> {
199 if r.is_late_bound() { self.tcx.lifetimes.re_erased } else { r }
200 }
201 }