1 use crate::collect::ItemCtxt;
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, ToPredicate, TyCtxt, TypeFoldable, TypeFolder};
9 use rustc_trait_selection::traits;
11 pub fn provide(providers: &mut Providers) {
12 *providers = Providers { diagnostic_hir_wf_check, ..*providers };
15 // Ideally, this would be in `rustc_trait_selection`, but we
16 // need access to `ItemCtxt`
17 fn diagnostic_hir_wf_check<'tcx>(
19 (predicate, loc): (ty::Predicate<'tcx>, WellFormedLoc),
20 ) -> Option<ObligationCause<'tcx>> {
23 let def_id = match loc {
24 WellFormedLoc::Ty(def_id) => def_id,
25 WellFormedLoc::Param { function, param_idx: _ } => function,
27 let hir_id = hir.local_def_id_to_hir_id(def_id);
29 // HIR wfcheck should only ever happen as part of improving an existing error
31 .delay_span_bug(tcx.def_span(def_id), "Performed HIR wfcheck without an existing error!");
33 let icx = ItemCtxt::new(tcx, def_id.to_def_id());
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).
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> {
56 predicate: ty::Predicate<'tcx>,
57 cause: Option<ObligationCause<'tcx>>,
61 param_env: ty::ParamEnv<'tcx>,
65 impl<'tcx> Visitor<'tcx> for HirWfCheck<'tcx> {
66 fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
67 self.tcx.infer_ctxt().enter(|infcx| {
69 self.icx.to_ty(ty).fold_with(&mut EraseAllBoundRegions { tcx: self.tcx });
70 let cause = traits::ObligationCause::new(
73 traits::ObligationCauseCode::WellFormed(None),
75 let errors = traits::fully_solve_obligation(
77 traits::Obligation::new(
80 ty::Binder::dummy(ty::PredicateKind::WellFormed(tcx_ty.into()))
81 .to_predicate(self.tcx),
84 if !errors.is_empty() {
85 debug!("Wf-check got errors for {:?}: {:?}", ty, errors);
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
100 intravisit::walk_ty(self, ty);
105 let mut visitor = HirWfCheck {
112 param_env: tcx.param_env(def_id.to_def_id()),
116 // Get the starting `hir::Ty` using our `WellFormedLoc`.
117 // We will walk 'into' this type to try to find
118 // a more precise span for our predicate.
120 WellFormedLoc::Ty(_) => match hir.get(hir_id) {
121 hir::Node::ImplItem(item) => match item.kind {
122 hir::ImplItemKind::TyAlias(ty) => Some(ty),
123 hir::ImplItemKind::Const(ty, _) => Some(ty),
124 ref item => bug!("Unexpected ImplItem {:?}", item),
126 hir::Node::TraitItem(item) => match item.kind {
127 hir::TraitItemKind::Type(_, ty) => ty,
128 hir::TraitItemKind::Const(ty, _) => Some(ty),
129 ref item => bug!("Unexpected TraitItem {:?}", item),
131 hir::Node::Item(item) => match item.kind {
132 hir::ItemKind::Static(ty, _, _) | hir::ItemKind::Const(ty, _) => Some(ty),
133 hir::ItemKind::Impl(ref impl_) => {
134 assert!(impl_.of_trait.is_none(), "Unexpected trait impl: {:?}", impl_);
137 ref item => bug!("Unexpected item {:?}", item),
139 hir::Node::Field(field) => Some(field.ty),
140 hir::Node::ForeignItem(ForeignItem {
141 kind: ForeignItemKind::Static(ty, _), ..
143 ref node => bug!("Unexpected node {:?}", node),
145 WellFormedLoc::Param { function: _, param_idx } => {
146 let fn_decl = hir.fn_decl_by_hir_id(hir_id).unwrap();
148 if param_idx as usize == fn_decl.inputs.len() {
149 match fn_decl.output {
150 hir::FnRetTy::Return(ty) => Some(ty),
151 // The unit type `()` is always well-formed
152 hir::FnRetTy::DefaultReturn(_span) => None,
155 Some(&fn_decl.inputs[param_idx as usize])
159 if let Some(ty) = ty {
160 visitor.visit_ty(ty);
165 struct EraseAllBoundRegions<'tcx> {
169 // Higher ranked regions are complicated.
170 // To make matters worse, the HIR WF check can instantiate them
171 // outside of a `Binder`, due to the way we (ab)use
172 // `ItemCtxt::to_ty`. To make things simpler, we just erase all
173 // of them, regardless of depth. At worse, this will give
174 // us an inaccurate span for an error message, but cannot
175 // lead to unsoundness (we call `delay_span_bug` at the start
176 // of `diagnostic_hir_wf_check`).
177 impl<'tcx> TypeFolder<'tcx> for EraseAllBoundRegions<'tcx> {
178 fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
181 fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> {
182 if r.is_late_bound() { self.tcx.lifetimes.re_erased } else { r }