1use rustc_hir::def::DefKind;
2use rustc_hir::intravisit::{self, Visitor, VisitorExt};
3use rustc_hir::{selfas 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;
1112use crate::collect::ItemCtxt;
1314// Ideally, this would be in `rustc_trait_selection`, but we
15// need access to `ItemCtxt`
16pub(super) fn diagnostic_hir_wf_check<'tcx>(
17 tcx: TyCtxt<'tcx>,
18 (predicate, loc): (ty::Predicate<'tcx>, WellFormedLoc),
19) -> Option<ObligationCause<'tcx>> {
20let def_id = match loc {
21 WellFormedLoc::Ty(def_id) => def_id,
22 WellFormedLoc::Param { function, param_idx: _ } => function,
23 };
24let hir_id = tcx.local_def_id_to_hir_id(def_id);
2526// HIR wfcheck should only ever happen as part of improving an existing error
27tcx.dcx()
28 .span_delayed_bug(tcx.def_span(def_id), "Performed HIR wfcheck without an existing error!");
2930let icx = ItemCtxt::new(tcx, def_id);
3132// To perform HIR-based WF checking, we iterate over all HIR types
33 // that occur 'inside' the item we're checking. For example,
34 // given the type `Option<MyStruct<u8>>`, we will check
35 // `Option<MyStruct<u8>>`, `MyStruct<u8>`, and `u8`.
36 // For each type, we perform a well-formed check, and see if we get
37 // an error that matches our expected predicate. We save
38 // the `ObligationCause` corresponding to the *innermost* type,
39 // which is the most specific type that we can point to.
40 // In general, the different components of an `hir::Ty` may have
41 // completely different spans due to macro invocations. Pointing
42 // to the most accurate part of the type can be the difference
43 // between a useless span (e.g. the macro invocation site)
44 // and a useful span (e.g. a user-provided type passed into the macro).
45 //
46 // This approach is quite inefficient - we redo a lot of work done
47 // by the normal WF checker. However, this code is run at most once
48 // per reported error - it will have no impact when compilation succeeds,
49 // and should only have an impact if a very large number of errors is
50 // displayed to the user.
51struct 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 }
6162impl<'tcx> Visitor<'tcx> for HirWfCheck<'tcx> {
63fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) {
64let infcx = self.tcx.infer_ctxt().build(TypingMode::non_body_analysis());
65let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
6667// We don't handle infer vars but we wouldn't handle them anyway as we're creating a
68 // fresh `InferCtxt` in this function.
69let tcx_ty = self.icx.lower_ty(ty.as_unambig_ty());
70// This visitor can walk into binders, resulting in the `tcx_ty` to
71 // potentially reference escaping bound variables. We simply erase
72 // those here.
73let tcx_ty = fold_regions(self.tcx, tcx_ty, |r, _| {
74if r.is_bound() { self.tcx.lifetimes.re_erased } else { r }
75 });
7677// We may be checking the WFness of a type in an opaque with a non-lifetime bound.
78 // Perhaps we could rebind all the escaping bound vars, but they're coming from
79 // arbitrary debruijn indices and aren't particularly important anyways, since they
80 // are only coming from `feature(non_lifetime_binders)` anyways.
81if tcx_ty.has_escaping_bound_vars() {
82return;
83 }
8485let cause = traits::ObligationCause::new(
86ty.span,
87self.def_id,
88 traits::ObligationCauseCode::WellFormed(None),
89 );
9091ocx.register_obligation(traits::Obligation::new(
92self.tcx,
93cause,
94self.param_env,
95 ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(tcx_ty.into())),
96 ));
9798for error in ocx.evaluate_obligations_error_on_ambiguity() {
99{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_hir_analysis/src/hir_wf_check.rs:99",
"rustc_hir_analysis::hir_wf_check", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_analysis/src/hir_wf_check.rs"),
::tracing_core::__macro_support::Option::Some(99u32),
::tracing_core::__macro_support::Option::Some("rustc_hir_analysis::hir_wf_check"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("Wf-check got error for {0:?}: {1:?}",
ty, error) as &dyn Value))])
});
} else { ; }
};debug!("Wf-check got error for {:?}: {:?}", ty, error);
100if error.obligation.predicate == self.predicate {
101// Save the cause from the greatest depth - this corresponds
102 // to picking more-specific types (e.g. `MyStruct<u8>`)
103 // over less-specific types (e.g. `Option<MyStruct<u8>>`)
104if self.depth >= self.cause_depth {
105self.cause = Some(error.obligation.cause);
106if let hir::TyKind::TraitObject(..) = ty.kind
107 && let DefKind::AssocTy | DefKind::AssocConst | DefKind::AssocFn =
108self.tcx.def_kind(self.def_id)
109 {
110self.cause = Some(ObligationCause::new(
111 ty.span,
112self.def_id,
113 ObligationCauseCode::DynCompatible(ty.span),
114 ));
115 }
116self.cause_depth = self.depth
117 }
118 }
119 }
120121self.depth += 1;
122 intravisit::walk_ty(self, ty);
123self.depth -= 1;
124 }
125 }
126127let mut visitor = HirWfCheck {
128tcx,
129predicate,
130 cause: None,
131 cause_depth: 0,
132icx,
133def_id,
134 param_env: tcx.param_env(def_id.to_def_id()),
135 depth: 0,
136 };
137138// Get the starting `hir::Ty` using our `WellFormedLoc`.
139 // We will walk 'into' this type to try to find
140 // a more precise span for our predicate.
141let tys = match loc {
142 WellFormedLoc::Ty(_) => match tcx.hir_node(hir_id) {
143 hir::Node::ImplItem(item) => match item.kind {
144 hir::ImplItemKind::Type(ty) => <[_]>::into_vec(::alloc::boxed::box_new([ty]))vec![ty],
145 hir::ImplItemKind::Const(ty, _) => <[_]>::into_vec(::alloc::boxed::box_new([ty]))vec![ty],
146ref item => ::rustc_middle::util::bug::bug_fmt(format_args!("Unexpected ImplItem {0:?}",
item))bug!("Unexpected ImplItem {:?}", item),
147 },
148 hir::Node::TraitItem(item) => match item.kind {
149 hir::TraitItemKind::Type(_, ty) => ty.into_iter().collect(),
150 hir::TraitItemKind::Const(ty, _) => <[_]>::into_vec(::alloc::boxed::box_new([ty]))vec![ty],
151ref item => ::rustc_middle::util::bug::bug_fmt(format_args!("Unexpected TraitItem {0:?}",
item))bug!("Unexpected TraitItem {:?}", item),
152 },
153 hir::Node::Item(item) => match item.kind {
154 hir::ItemKind::TyAlias(_, _, ty)
155 | hir::ItemKind::Static(_, _, ty, _)
156 | hir::ItemKind::Const(_, _, ty, _) => <[_]>::into_vec(::alloc::boxed::box_new([ty]))vec![ty],
157 hir::ItemKind::Impl(impl_) => match impl_.of_trait {
158Some(of_trait) => of_trait159 .trait_ref
160 .path
161 .segments
162 .last()
163 .iter()
164 .flat_map(|seg| seg.args().args)
165 .filter_map(|arg| {
166if let hir::GenericArg::Type(ty) = arg {
167Some(ty.as_unambig_ty())
168 } else {
169None170 }
171 })
172 .chain([impl_.self_ty])
173 .collect(),
174None => {
175<[_]>::into_vec(::alloc::boxed::box_new([impl_.self_ty]))vec![impl_.self_ty]176 }
177 },
178ref item => ::rustc_middle::util::bug::bug_fmt(format_args!("Unexpected item {0:?}",
item))bug!("Unexpected item {:?}", item),
179 },
180 hir::Node::Field(field) => <[_]>::into_vec(::alloc::boxed::box_new([field.ty]))vec![field.ty],
181 hir::Node::ForeignItem(ForeignItem {
182 kind: ForeignItemKind::Static(ty, _, _), ..
183 }) => <[_]>::into_vec(::alloc::boxed::box_new([*ty]))vec![*ty],
184 hir::Node::GenericParam(hir::GenericParam {
185 kind: hir::GenericParamKind::Type { default: Some(ty), .. },
186 ..
187 }) => <[_]>::into_vec(::alloc::boxed::box_new([*ty]))vec![*ty],
188 hir::Node::AnonConst(_) => {
189if 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<[_]>::into_vec(::alloc::boxed::box_new([*ty]))vec![*ty]196 } else {
197::alloc::vec::Vec::new()vec![]198 }
199 }
200ref node => ::rustc_middle::util::bug::bug_fmt(format_args!("Unexpected node {0:?}",
node))bug!("Unexpected node {:?}", node),
201 },
202 WellFormedLoc::Param { function: _, param_idx } => {
203let fn_decl = tcx.hir_fn_decl_by_hir_id(hir_id).unwrap();
204// Get return type
205if param_idxas usize == fn_decl.inputs.len() {
206match fn_decl.output {
207 hir::FnRetTy::Return(ty) => <[_]>::into_vec(::alloc::boxed::box_new([ty]))vec![ty],
208// The unit type `()` is always well-formed
209 hir::FnRetTy::DefaultReturn(_span) => ::alloc::vec::Vec::new()vec![],
210 }
211 } else {
212<[_]>::into_vec(::alloc::boxed::box_new([&fn_decl.inputs[param_idx as
usize]]))vec![&fn_decl.inputs[param_idx as usize]]213 }
214 }
215 };
216for ty in tys {
217 visitor.visit_ty_unambig(ty);
218 }
219visitor.cause
220}