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::{self, TyCtxt, TypeVisitableExt, TypingMode, fold_regions};
8use rustc_span::def_id::LocalDefId;
9use rustc_trait_selection::traits::{self, ObligationCtxt};
10use tracing::debug;
11
12use crate::collect::ItemCtxt;
13
14pub(crate) fn provide(providers: &mut Providers) {
15 *providers = Providers { diagnostic_hir_wf_check, ..*providers };
16}
17
18fn diagnostic_hir_wf_check<'tcx>(
21 tcx: TyCtxt<'tcx>,
22 (predicate, loc): (ty::Predicate<'tcx>, WellFormedLoc),
23) -> Option<ObligationCause<'tcx>> {
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 = tcx.local_def_id_to_hir_id(def_id);
29
30 tcx.dcx()
32 .span_delayed_bug(tcx.def_span(def_id), "Performed HIR wfcheck without an existing error!");
33
34 let icx = ItemCtxt::new(tcx, def_id);
35
36 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, AmbigArg>) {
68 let infcx = self.tcx.infer_ctxt().build(TypingMode::non_body_analysis());
69 let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
70
71 let tcx_ty = self.icx.lower_ty(ty.as_unambig_ty());
74 let tcx_ty = fold_regions(self.tcx, tcx_ty, |r, _| {
78 if r.is_bound() { self.tcx.lifetimes.re_erased } else { r }
79 });
80
81 if tcx_ty.has_escaping_bound_vars() {
86 return;
87 }
88
89 let cause = traits::ObligationCause::new(
90 ty.span,
91 self.def_id,
92 traits::ObligationCauseCode::WellFormed(None),
93 );
94
95 ocx.register_obligation(traits::Obligation::new(
96 self.tcx,
97 cause,
98 self.param_env,
99 ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(tcx_ty.into())),
100 ));
101
102 for error in ocx.select_all_or_error() {
103 debug!("Wf-check got error for {:?}: {:?}", ty, error);
104 if error.obligation.predicate == self.predicate {
105 if self.depth >= self.cause_depth {
109 self.cause = Some(error.obligation.cause);
110 self.cause_depth = self.depth
111 }
112 }
113 }
114
115 self.depth += 1;
116 intravisit::walk_ty(self, ty);
117 self.depth -= 1;
118 }
119 }
120
121 let mut visitor = HirWfCheck {
122 tcx,
123 predicate,
124 cause: None,
125 cause_depth: 0,
126 icx,
127 def_id,
128 param_env: tcx.param_env(def_id.to_def_id()),
129 depth: 0,
130 };
131
132 let tys = match loc {
136 WellFormedLoc::Ty(_) => match tcx.hir_node(hir_id) {
137 hir::Node::ImplItem(item) => match item.kind {
138 hir::ImplItemKind::Type(ty) => vec![ty],
139 hir::ImplItemKind::Const(ty, _) => vec![ty],
140 ref item => bug!("Unexpected ImplItem {:?}", item),
141 },
142 hir::Node::TraitItem(item) => match item.kind {
143 hir::TraitItemKind::Type(_, ty) => ty.into_iter().collect(),
144 hir::TraitItemKind::Const(ty, _) => vec![ty],
145 ref item => bug!("Unexpected TraitItem {:?}", item),
146 },
147 hir::Node::Item(item) => match item.kind {
148 hir::ItemKind::TyAlias(_, ty, _)
149 | hir::ItemKind::Static(_, ty, _, _)
150 | hir::ItemKind::Const(_, ty, _, _) => vec![ty],
151 hir::ItemKind::Impl(impl_) => match &impl_.of_trait {
152 Some(t) => t
153 .path
154 .segments
155 .last()
156 .iter()
157 .flat_map(|seg| seg.args().args)
158 .filter_map(|arg| {
159 if let hir::GenericArg::Type(ty) = arg {
160 Some(ty.as_unambig_ty())
161 } else {
162 None
163 }
164 })
165 .chain([impl_.self_ty])
166 .collect(),
167 None => {
168 vec![impl_.self_ty]
169 }
170 },
171 ref item => bug!("Unexpected item {:?}", item),
172 },
173 hir::Node::Field(field) => vec![field.ty],
174 hir::Node::ForeignItem(ForeignItem {
175 kind: ForeignItemKind::Static(ty, _, _), ..
176 }) => vec![*ty],
177 hir::Node::GenericParam(hir::GenericParam {
178 kind: hir::GenericParamKind::Type { default: Some(ty), .. },
179 ..
180 }) => vec![*ty],
181 hir::Node::AnonConst(_) => {
182 if let Some(const_param_id) = tcx.hir_opt_const_param_default_param_def_id(hir_id)
183 && let hir::Node::GenericParam(hir::GenericParam {
184 kind: hir::GenericParamKind::Const { ty, .. },
185 ..
186 }) = tcx.hir_node_by_def_id(const_param_id)
187 {
188 vec![*ty]
189 } else {
190 vec![]
191 }
192 }
193 ref node => bug!("Unexpected node {:?}", node),
194 },
195 WellFormedLoc::Param { function: _, param_idx } => {
196 let fn_decl = tcx.hir_fn_decl_by_hir_id(hir_id).unwrap();
197 if param_idx as usize == fn_decl.inputs.len() {
199 match fn_decl.output {
200 hir::FnRetTy::Return(ty) => vec![ty],
201 hir::FnRetTy::DefaultReturn(_span) => vec![],
203 }
204 } else {
205 vec![&fn_decl.inputs[param_idx as usize]]
206 }
207 }
208 };
209 for ty in tys {
210 visitor.visit_ty_unambig(ty);
211 }
212 visitor.cause
213}