1use rustc_hir::def::DefKind;
2use rustc_hir::intravisit::{self, Visitor, VisitorExt};
3use rustc_hir::{self as hir, AmbigArg, ForeignItem, ForeignItemKind};
4use rustc_infer::infer::TyCtxtInferExt;
5use rustc_infer::traits::{ObligationCause, ObligationCauseCode, WellFormedLoc};
6use rustc_middle::bug;
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(super) fn diagnostic_hir_wf_check<'tcx>(
17 tcx: TyCtxt<'tcx>,
18 (predicate, loc): (ty::Predicate<'tcx>, WellFormedLoc),
19) -> Option<ObligationCause<'tcx>> {
20 let def_id = match loc {
21 WellFormedLoc::Ty(def_id) => def_id,
22 WellFormedLoc::Param { function, param_idx: _ } => function,
23 };
24 let hir_id = tcx.local_def_id_to_hir_id(def_id);
25
26 tcx.dcx()
28 .span_delayed_bug(tcx.def_span(def_id), "Performed HIR wfcheck without an existing error!");
29
30 let icx = ItemCtxt::new(tcx, def_id);
31
32 struct HirWfCheck<'tcx> {
52 tcx: TyCtxt<'tcx>,
53 predicate: ty::Predicate<'tcx>,
54 cause: Option<ObligationCause<'tcx>>,
55 cause_depth: usize,
56 icx: ItemCtxt<'tcx>,
57 def_id: LocalDefId,
58 param_env: ty::ParamEnv<'tcx>,
59 depth: usize,
60 }
61
62 impl<'tcx> Visitor<'tcx> for HirWfCheck<'tcx> {
63 fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) {
64 let infcx = self.tcx.infer_ctxt().build(TypingMode::non_body_analysis());
65 let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
66
67 let tcx_ty = self.icx.lower_ty(ty.as_unambig_ty());
70 let tcx_ty = fold_regions(self.tcx, tcx_ty, |r, _| {
74 if r.is_bound() { self.tcx.lifetimes.re_erased } else { r }
75 });
76
77 if tcx_ty.has_escaping_bound_vars() {
82 return;
83 }
84
85 let cause = traits::ObligationCause::new(
86 ty.span,
87 self.def_id,
88 traits::ObligationCauseCode::WellFormed(None),
89 );
90
91 ocx.register_obligation(traits::Obligation::new(
92 self.tcx,
93 cause,
94 self.param_env,
95 ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(tcx_ty.into())),
96 ));
97
98 for error in ocx.select_all_or_error() {
99 debug!("Wf-check got error for {:?}: {:?}", ty, error);
100 if error.obligation.predicate == self.predicate {
101 if self.depth >= self.cause_depth {
105 self.cause = Some(error.obligation.cause);
106 if let hir::TyKind::TraitObject(..) = ty.kind {
107 if let DefKind::AssocTy | DefKind::AssocConst | DefKind::AssocFn =
108 self.tcx.def_kind(self.def_id)
109 {
110 self.cause = Some(ObligationCause::new(
111 ty.span,
112 self.def_id,
113 ObligationCauseCode::DynCompatible(ty.span),
114 ));
115 }
116 }
117 self.cause_depth = self.depth
118 }
119 }
120 }
121
122 self.depth += 1;
123 intravisit::walk_ty(self, ty);
124 self.depth -= 1;
125 }
126 }
127
128 let mut visitor = HirWfCheck {
129 tcx,
130 predicate,
131 cause: None,
132 cause_depth: 0,
133 icx,
134 def_id,
135 param_env: tcx.param_env(def_id.to_def_id()),
136 depth: 0,
137 };
138
139 let tys = match loc {
143 WellFormedLoc::Ty(_) => match tcx.hir_node(hir_id) {
144 hir::Node::ImplItem(item) => match item.kind {
145 hir::ImplItemKind::Type(ty) => vec![ty],
146 hir::ImplItemKind::Const(ty, _) => vec![ty],
147 ref item => bug!("Unexpected ImplItem {:?}", item),
148 },
149 hir::Node::TraitItem(item) => match item.kind {
150 hir::TraitItemKind::Type(_, ty) => ty.into_iter().collect(),
151 hir::TraitItemKind::Const(ty, _) => vec![ty],
152 ref item => bug!("Unexpected TraitItem {:?}", item),
153 },
154 hir::Node::Item(item) => match item.kind {
155 hir::ItemKind::TyAlias(_, _, ty)
156 | hir::ItemKind::Static(_, _, ty, _)
157 | hir::ItemKind::Const(_, _, ty, _) => vec![ty],
158 hir::ItemKind::Impl(impl_) => match &impl_.of_trait {
159 Some(t) => t
160 .path
161 .segments
162 .last()
163 .iter()
164 .flat_map(|seg| seg.args().args)
165 .filter_map(|arg| {
166 if let hir::GenericArg::Type(ty) = arg {
167 Some(ty.as_unambig_ty())
168 } else {
169 None
170 }
171 })
172 .chain([impl_.self_ty])
173 .collect(),
174 None => {
175 vec![impl_.self_ty]
176 }
177 },
178 ref item => bug!("Unexpected item {:?}", item),
179 },
180 hir::Node::Field(field) => vec![field.ty],
181 hir::Node::ForeignItem(ForeignItem {
182 kind: ForeignItemKind::Static(ty, _, _), ..
183 }) => vec![*ty],
184 hir::Node::GenericParam(hir::GenericParam {
185 kind: hir::GenericParamKind::Type { default: Some(ty), .. },
186 ..
187 }) => vec![*ty],
188 hir::Node::AnonConst(_) => {
189 if let Some(const_param_id) = tcx.hir_opt_const_param_default_param_def_id(hir_id)
190 && let hir::Node::GenericParam(hir::GenericParam {
191 kind: hir::GenericParamKind::Const { ty, .. },
192 ..
193 }) = tcx.hir_node_by_def_id(const_param_id)
194 {
195 vec![*ty]
196 } else {
197 vec![]
198 }
199 }
200 ref node => bug!("Unexpected node {:?}", node),
201 },
202 WellFormedLoc::Param { function: _, param_idx } => {
203 let fn_decl = tcx.hir_fn_decl_by_hir_id(hir_id).unwrap();
204 if param_idx as usize == fn_decl.inputs.len() {
206 match fn_decl.output {
207 hir::FnRetTy::Return(ty) => vec![ty],
208 hir::FnRetTy::DefaultReturn(_span) => vec![],
210 }
211 } else {
212 vec![&fn_decl.inputs[param_idx as usize]]
213 }
214 }
215 };
216 for ty in tys {
217 visitor.visit_ty_unambig(ty);
218 }
219 visitor.cause
220}