1use rustc_hir::intravisit::{self, Visitor, VisitorExt};
2use rustc_hir::{self as hir, AmbigArg, ForeignItem, ForeignItemKind};
3use rustc_infer::infer::TyCtxtInferExt;
4use rustc_infer::traits::{ObligationCause, WellFormedLoc};
5use rustc_middle::bug;
6use rustc_middle::query::Providers;
7use rustc_middle::ty::fold::fold_regions;
8use rustc_middle::ty::{self, TyCtxt, TypingMode};
9use rustc_span::def_id::LocalDefId;
10use rustc_trait_selection::traits::{self, ObligationCtxt};
11use tracing::debug;
12
13use crate::collect::ItemCtxt;
14
15pub(crate) fn provide(providers: &mut Providers) {
16 *providers = Providers { diagnostic_hir_wf_check, ..*providers };
17}
18
19fn diagnostic_hir_wf_check<'tcx>(
22 tcx: TyCtxt<'tcx>,
23 (predicate, loc): (ty::Predicate<'tcx>, WellFormedLoc),
24) -> Option<ObligationCause<'tcx>> {
25 let hir = tcx.hir();
26
27 let def_id = match loc {
28 WellFormedLoc::Ty(def_id) => def_id,
29 WellFormedLoc::Param { function, param_idx: _ } => function,
30 };
31 let hir_id = tcx.local_def_id_to_hir_id(def_id);
32
33 tcx.dcx()
35 .span_delayed_bug(tcx.def_span(def_id), "Performed HIR wfcheck without an existing error!");
36
37 let icx = ItemCtxt::new(tcx, def_id);
38
39 struct HirWfCheck<'tcx> {
59 tcx: TyCtxt<'tcx>,
60 predicate: ty::Predicate<'tcx>,
61 cause: Option<ObligationCause<'tcx>>,
62 cause_depth: usize,
63 icx: ItemCtxt<'tcx>,
64 def_id: LocalDefId,
65 param_env: ty::ParamEnv<'tcx>,
66 depth: usize,
67 }
68
69 impl<'tcx> Visitor<'tcx> for HirWfCheck<'tcx> {
70 fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) {
71 let infcx = self.tcx.infer_ctxt().build(TypingMode::non_body_analysis());
72 let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
73
74 let tcx_ty = self.icx.lower_ty(ty.as_unambig_ty());
77 let tcx_ty = fold_regions(self.tcx, tcx_ty, |r, _| {
81 if r.is_bound() { self.tcx.lifetimes.re_erased } else { r }
82 });
83 let cause = traits::ObligationCause::new(
84 ty.span,
85 self.def_id,
86 traits::ObligationCauseCode::WellFormed(None),
87 );
88
89 ocx.register_obligation(traits::Obligation::new(
90 self.tcx,
91 cause,
92 self.param_env,
93 ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(tcx_ty.into())),
94 ));
95
96 for error in ocx.select_all_or_error() {
97 debug!("Wf-check got error for {:?}: {:?}", ty, error);
98 if error.obligation.predicate == self.predicate {
99 if self.depth >= self.cause_depth {
103 self.cause = Some(error.obligation.cause);
104 self.cause_depth = self.depth
105 }
106 }
107 }
108
109 self.depth += 1;
110 intravisit::walk_ty(self, ty);
111 self.depth -= 1;
112 }
113 }
114
115 let mut visitor = HirWfCheck {
116 tcx,
117 predicate,
118 cause: None,
119 cause_depth: 0,
120 icx,
121 def_id,
122 param_env: tcx.param_env(def_id.to_def_id()),
123 depth: 0,
124 };
125
126 let tys = match loc {
130 WellFormedLoc::Ty(_) => match tcx.hir_node(hir_id) {
131 hir::Node::ImplItem(item) => match item.kind {
132 hir::ImplItemKind::Type(ty) => vec![ty],
133 hir::ImplItemKind::Const(ty, _) => vec![ty],
134 ref item => bug!("Unexpected ImplItem {:?}", item),
135 },
136 hir::Node::TraitItem(item) => match item.kind {
137 hir::TraitItemKind::Type(_, ty) => ty.into_iter().collect(),
138 hir::TraitItemKind::Const(ty, _) => vec![ty],
139 ref item => bug!("Unexpected TraitItem {:?}", item),
140 },
141 hir::Node::Item(item) => match item.kind {
142 hir::ItemKind::TyAlias(ty, _)
143 | hir::ItemKind::Static(ty, _, _)
144 | hir::ItemKind::Const(ty, _, _) => vec![ty],
145 hir::ItemKind::Impl(impl_) => match &impl_.of_trait {
146 Some(t) => t
147 .path
148 .segments
149 .last()
150 .iter()
151 .flat_map(|seg| seg.args().args)
152 .filter_map(|arg| {
153 if let hir::GenericArg::Type(ty) = arg {
154 Some(ty.as_unambig_ty())
155 } else {
156 None
157 }
158 })
159 .chain([impl_.self_ty])
160 .collect(),
161 None => {
162 vec![impl_.self_ty]
163 }
164 },
165 ref item => bug!("Unexpected item {:?}", item),
166 },
167 hir::Node::Field(field) => vec![field.ty],
168 hir::Node::ForeignItem(ForeignItem {
169 kind: ForeignItemKind::Static(ty, _, _), ..
170 }) => vec![*ty],
171 hir::Node::GenericParam(hir::GenericParam {
172 kind: hir::GenericParamKind::Type { default: Some(ty), .. },
173 ..
174 }) => vec![*ty],
175 hir::Node::AnonConst(_) => {
176 if let Some(const_param_id) = tcx.hir().opt_const_param_default_param_def_id(hir_id)
177 && let hir::Node::GenericParam(hir::GenericParam {
178 kind: hir::GenericParamKind::Const { ty, .. },
179 ..
180 }) = tcx.hir_node_by_def_id(const_param_id)
181 {
182 vec![*ty]
183 } else {
184 vec![]
185 }
186 }
187 ref node => bug!("Unexpected node {:?}", node),
188 },
189 WellFormedLoc::Param { function: _, param_idx } => {
190 let fn_decl = hir.fn_decl_by_hir_id(hir_id).unwrap();
191 if param_idx as usize == fn_decl.inputs.len() {
193 match fn_decl.output {
194 hir::FnRetTy::Return(ty) => vec![ty],
195 hir::FnRetTy::DefaultReturn(_span) => vec![],
197 }
198 } else {
199 vec![&fn_decl.inputs[param_idx as usize]]
200 }
201 }
202 };
203 for ty in tys {
204 visitor.visit_ty_unambig(ty);
205 }
206 visitor.cause
207}