1 use crate::collect
::ItemCtxt
;
3 use rustc_hir
::intravisit
::{self, Visitor}
;
4 use rustc_hir
::{ForeignItem, ForeignItemKind}
;
5 use rustc_infer
::infer
::TyCtxtInferExt
;
6 use rustc_infer
::traits
::{ObligationCause, WellFormedLoc}
;
7 use rustc_middle
::query
::Providers
;
8 use rustc_middle
::ty
::{self, Region, TyCtxt, TypeFoldable, TypeFolder}
;
9 use rustc_span
::def_id
::LocalDefId
;
10 use rustc_trait_selection
::traits
::{self, ObligationCtxt}
;
12 pub fn provide(providers
: &mut Providers
) {
13 *providers
= Providers { diagnostic_hir_wf_check, ..*providers }
;
16 // Ideally, this would be in `rustc_trait_selection`, but we
17 // need access to `ItemCtxt`
18 fn diagnostic_hir_wf_check
<'tcx
>(
20 (predicate
, loc
): (ty
::Predicate
<'tcx
>, WellFormedLoc
),
21 ) -> Option
<ObligationCause
<'tcx
>> {
24 let def_id
= match loc
{
25 WellFormedLoc
::Ty(def_id
) => def_id
,
26 WellFormedLoc
::Param { function, param_idx: _ }
=> function
,
28 let hir_id
= hir
.local_def_id_to_hir_id(def_id
);
30 // HIR wfcheck should only ever happen as part of improving an existing error
32 .delay_span_bug(tcx
.def_span(def_id
), "Performed HIR wfcheck without an existing error!");
34 let icx
= ItemCtxt
::new(tcx
, def_id
);
36 // To perform HIR-based WF checking, we iterate over all HIR types
37 // that occur 'inside' the item we're checking. For example,
38 // given the type `Option<MyStruct<u8>>`, we will check
39 // `Option<MyStruct<u8>>`, `MyStruct<u8>`, and `u8`.
40 // For each type, we perform a well-formed check, and see if we get
41 // an error that matches our expected predicate. We save
42 // the `ObligationCause` corresponding to the *innermost* type,
43 // which is the most specific type that we can point to.
44 // In general, the different components of an `hir::Ty` may have
45 // completely different spans due to macro invocations. Pointing
46 // to the most accurate part of the type can be the difference
47 // between a useless span (e.g. the macro invocation site)
48 // and a useful span (e.g. a user-provided type passed into the macro).
50 // This approach is quite inefficient - we redo a lot of work done
51 // by the normal WF checker. However, this code is run at most once
52 // per reported error - it will have no impact when compilation succeeds,
53 // and should only have an impact if a very large number of errors is
54 // displayed to the user.
55 struct HirWfCheck
<'tcx
> {
57 predicate
: ty
::Predicate
<'tcx
>,
58 cause
: Option
<ObligationCause
<'tcx
>>,
62 param_env
: ty
::ParamEnv
<'tcx
>,
66 impl<'tcx
> Visitor
<'tcx
> for HirWfCheck
<'tcx
> {
67 fn visit_ty(&mut self, ty
: &'tcx hir
::Ty
<'tcx
>) {
68 let infcx
= self.tcx
.infer_ctxt().build();
69 let ocx
= ObligationCtxt
::new(&infcx
);
71 let tcx_ty
= self.icx
.to_ty(ty
).fold_with(&mut EraseAllBoundRegions { tcx: self.tcx }
);
72 let cause
= traits
::ObligationCause
::new(
75 traits
::ObligationCauseCode
::WellFormed(None
),
78 ocx
.register_obligation(traits
::Obligation
::new(
82 ty
::PredicateKind
::Clause(ty
::ClauseKind
::WellFormed(tcx_ty
.into())),
85 for error
in ocx
.select_all_or_error() {
86 debug
!("Wf-check got error for {:?}: {:?}", ty
, error
);
87 if error
.obligation
.predicate
== self.predicate
{
88 // Save the cause from the greatest depth - this corresponds
89 // to picking more-specific types (e.g. `MyStruct<u8>`)
90 // over less-specific types (e.g. `Option<MyStruct<u8>>`)
91 if self.depth
>= self.cause_depth
{
92 self.cause
= Some(error
.obligation
.cause
);
93 self.cause_depth
= self.depth
99 intravisit
::walk_ty(self, ty
);
104 let mut visitor
= HirWfCheck
{
111 param_env
: tcx
.param_env(def_id
.to_def_id()),
115 // Get the starting `hir::Ty` using our `WellFormedLoc`.
116 // We will walk 'into' this type to try to find
117 // a more precise span for our predicate.
118 let tys
= match loc
{
119 WellFormedLoc
::Ty(_
) => match hir
.get(hir_id
) {
120 hir
::Node
::ImplItem(item
) => match item
.kind
{
121 hir
::ImplItemKind
::Type(ty
) => vec
![ty
],
122 hir
::ImplItemKind
::Const(ty
, _
) => vec
![ty
],
123 ref item
=> bug
!("Unexpected ImplItem {:?}", item
),
125 hir
::Node
::TraitItem(item
) => match item
.kind
{
126 hir
::TraitItemKind
::Type(_
, ty
) => ty
.into_iter().collect(),
127 hir
::TraitItemKind
::Const(ty
, _
) => vec
![ty
],
128 ref item
=> bug
!("Unexpected TraitItem {:?}", item
),
130 hir
::Node
::Item(item
) => match item
.kind
{
131 hir
::ItemKind
::TyAlias(ty
, _
)
132 | hir
::ItemKind
::Static(ty
, _
, _
)
133 | hir
::ItemKind
::Const(ty
, _
, _
) => vec
![ty
],
134 hir
::ItemKind
::Impl(impl_
) => match &impl_
.of_trait
{
140 .flat_map(|seg
| seg
.args().args
)
142 if let hir
::GenericArg
::Type(ty
) = arg { Some(*ty) }
else { None }
144 .chain([impl_
.self_ty
])
150 ref item
=> bug
!("Unexpected item {:?}", item
),
152 hir
::Node
::Field(field
) => vec
![field
.ty
],
153 hir
::Node
::ForeignItem(ForeignItem
{
154 kind
: ForeignItemKind
::Static(ty
, _
), ..
156 hir
::Node
::GenericParam(hir
::GenericParam
{
157 kind
: hir
::GenericParamKind
::Type { default: Some(ty), .. }
,
160 ref node
=> bug
!("Unexpected node {:?}", node
),
162 WellFormedLoc
::Param { function: _, param_idx }
=> {
163 let fn_decl
= hir
.fn_decl_by_hir_id(hir_id
).unwrap();
165 if param_idx
as usize == fn_decl
.inputs
.len() {
166 match fn_decl
.output
{
167 hir
::FnRetTy
::Return(ty
) => vec
![ty
],
168 // The unit type `()` is always well-formed
169 hir
::FnRetTy
::DefaultReturn(_span
) => vec
![],
172 vec
![&fn_decl
.inputs
[param_idx
as usize]]
177 visitor
.visit_ty(ty
);
182 struct EraseAllBoundRegions
<'tcx
> {
186 // Higher ranked regions are complicated.
187 // To make matters worse, the HIR WF check can instantiate them
188 // outside of a `Binder`, due to the way we (ab)use
189 // `ItemCtxt::to_ty`. To make things simpler, we just erase all
190 // of them, regardless of depth. At worse, this will give
191 // us an inaccurate span for an error message, but cannot
192 // lead to unsoundness (we call `delay_span_bug` at the start
193 // of `diagnostic_hir_wf_check`).
194 impl<'tcx
> TypeFolder
<TyCtxt
<'tcx
>> for EraseAllBoundRegions
<'tcx
> {
195 fn interner(&self) -> TyCtxt
<'tcx
> {
198 fn fold_region(&mut self, r
: Region
<'tcx
>) -> Region
<'tcx
> {
199 if r
.is_late_bound() { self.tcx.lifetimes.re_erased }
else { r }