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