Skip to main content

rustc_hir_analysis/
impl_wf_check.rs

1//! This pass enforces various "well-formedness constraints" on impls.
2//! Logically, it is part of wfcheck -- but we do it early so that we
3//! can stop compilation afterwards, since part of the trait matching
4//! infrastructure gets very grumpy if these conditions don't hold. In
5//! particular, if there are type parameters that are not part of the
6//! impl, then coherence will report strange inference ambiguity
7//! errors; if impls have duplicate items, we get misleading
8//! specialization errors. These things can (and probably should) be
9//! fixed, but for the moment it's easier to do these checks early.
10
11use std::debug_assert_matches;
12
13use min_specialization::check_min_specialization;
14use rustc_data_structures::fx::FxHashSet;
15use rustc_errors::Applicability;
16use rustc_errors::codes::*;
17use rustc_hir::def::DefKind;
18use rustc_hir::def_id::LocalDefId;
19use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt, Unnormalized};
20use rustc_span::{ErrorGuaranteed, kw};
21
22use crate::constrained_generic_params as cgp;
23use crate::errors::UnconstrainedGenericParameter;
24
25mod min_specialization;
26
27/// Checks that all the type/lifetime parameters on an impl also
28/// appear in the trait ref or self type (or are constrained by a
29/// where-clause). These rules are needed to ensure that, given a
30/// trait ref like `<T as Trait<U>>`, we can derive the values of all
31/// parameters on the impl (which is needed to make specialization
32/// possible).
33///
34/// However, in the case of lifetimes, we only enforce these rules if
35/// the lifetime parameter is used in an associated type. This is a
36/// concession to backwards compatibility; see comment at the end of
37/// the fn for details.
38///
39/// Example:
40///
41/// ```rust,ignore (pseudo-Rust)
42/// impl<T> Trait<Foo> for Bar { ... }
43/// //   ^ T does not appear in `Foo` or `Bar`, error!
44///
45/// impl<T> Trait<Foo<T>> for Bar { ... }
46/// //   ^ T appears in `Foo<T>`, ok.
47///
48/// impl<T> Trait<Foo> for Bar where Bar: Iterator<Item = T> { ... }
49/// //   ^ T is bound to `<Bar as Iterator>::Item`, ok.
50///
51/// impl<'a> Trait<Foo> for Bar { }
52/// //   ^ 'a is unused, but for back-compat we allow it
53///
54/// impl<'a> Trait<Foo> for Bar { type X = &'a i32; }
55/// //   ^ 'a is unused and appears in assoc type, error
56/// ```
57pub(crate) fn check_impl_wf(
58    tcx: TyCtxt<'_>,
59    impl_def_id: LocalDefId,
60    of_trait: bool,
61) -> Result<(), ErrorGuaranteed> {
62    if true {
    {
        match tcx.def_kind(impl_def_id) {
            DefKind::Impl { .. } => {}
            ref left_val => {
                ::core::panicking::assert_matches_failed(left_val,
                    "DefKind::Impl { .. }", ::core::option::Option::None);
            }
        }
    };
};debug_assert_matches!(tcx.def_kind(impl_def_id), DefKind::Impl { .. });
63
64    // Check that the args are constrained. We queryfied the check for ty/const params
65    // since unconstrained type/const params cause ICEs in projection, so we want to
66    // detect those specifically and project those to `TyKind::Error`.
67    let mut res = tcx.ensure_result().enforce_impl_non_lifetime_params_are_constrained(impl_def_id);
68    res = res.and(enforce_impl_lifetime_params_are_constrained(tcx, impl_def_id, of_trait));
69
70    if of_trait && tcx.features().min_specialization() {
71        res = res.and(check_min_specialization(tcx, impl_def_id));
72    }
73    res
74}
75
76pub(crate) fn enforce_impl_lifetime_params_are_constrained(
77    tcx: TyCtxt<'_>,
78    impl_def_id: LocalDefId,
79    of_trait: bool,
80) -> Result<(), ErrorGuaranteed> {
81    let impl_self_ty = tcx.type_of(impl_def_id).instantiate_identity().skip_norm_wip();
82
83    // Don't complain about unconstrained type params when self ty isn't known due to errors.
84    // (#36836)
85    impl_self_ty.error_reported()?;
86
87    let impl_generics = tcx.generics_of(impl_def_id);
88    let impl_predicates = tcx.predicates_of(impl_def_id);
89    let impl_trait_ref =
90        of_trait.then(|| tcx.impl_trait_ref(impl_def_id).instantiate_identity().skip_norm_wip());
91
92    impl_trait_ref.error_reported()?;
93
94    let mut input_parameters = cgp::parameters_for_impl(tcx, impl_self_ty, impl_trait_ref);
95    cgp::identify_constrained_generic_params(
96        tcx,
97        impl_predicates,
98        impl_trait_ref,
99        &mut input_parameters,
100    );
101
102    // Disallow unconstrained lifetimes, but only if they appear in assoc types.
103    let lifetimes_in_associated_types: FxHashSet<_> = tcx
104        .associated_item_def_ids(impl_def_id)
105        .iter()
106        .flat_map(|&def_id| {
107            let item = tcx.associated_item(def_id);
108            match item.kind {
109                ty::AssocKind::Type { .. } => {
110                    if item.defaultness(tcx).has_value() {
111                        cgp::parameters_for(
112                            tcx,
113                            tcx.type_of(def_id).instantiate_identity().skip_norm_wip(),
114                            true,
115                        )
116                    } else {
117                        ::alloc::vec::Vec::new()vec![]
118                    }
119                }
120                ty::AssocKind::Fn { .. } | ty::AssocKind::Const { .. } => ::alloc::vec::Vec::new()vec![],
121            }
122        })
123        .collect();
124
125    let mut res = Ok(());
126    for param in &impl_generics.own_params {
127        match param.kind {
128            ty::GenericParamDefKind::Lifetime => {
129                // This is a horrible concession to reality. I think it'd be
130                // better to just ban unconstrained lifetimes outright, but in
131                // practice people do non-hygienic macros like:
132                //
133                // ```
134                // macro_rules! __impl_slice_eq1 {
135                //   ($Lhs: ty, $Rhs: ty, $Bound: ident) => {
136                //     impl<'a, 'b, A: $Bound, B> PartialEq<$Rhs> for $Lhs where A: PartialEq<B> {
137                //        ....
138                //     }
139                //   }
140                // }
141                // ```
142                //
143                // In a concession to backwards compatibility, we continue to
144                // permit those, so long as the lifetimes aren't used in
145                // associated types. I believe this is sound, because lifetimes
146                // used elsewhere are not projected back out.
147                let param_lt = cgp::Parameter::from(param.to_early_bound_region_data());
148                if lifetimes_in_associated_types.contains(&param_lt)
149                    && !input_parameters.contains(&param_lt)
150                {
151                    let mut diag = tcx.dcx().create_err(UnconstrainedGenericParameter {
152                        span: tcx.def_span(param.def_id),
153                        param_name: tcx.item_ident(param.def_id),
154                        param_def_kind: tcx.def_descr(param.def_id),
155                        const_param_note: false,
156                        const_param_note2: false,
157                    });
158                    diag.code(E0207);
159                    for p in &impl_generics.own_params {
160                        if p.name == kw::UnderscoreLifetime {
161                            let span = tcx.def_span(p.def_id);
162                            let Ok(snippet) = tcx.sess.source_map().span_to_snippet(span) else {
163                                continue;
164                            };
165
166                            let (span, sugg) = if &snippet == "'_" {
167                                (span, param.name.to_string())
168                            } else {
169                                (span.shrink_to_hi(), ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0} ", param.name))
    })format!("{} ", param.name))
170                            };
171                            diag.span_suggestion_verbose(
172                                span,
173                                "consider using the named lifetime here instead of an implicit \
174                                 lifetime",
175                                sugg,
176                                Applicability::MaybeIncorrect,
177                            );
178                        }
179                    }
180                    res = Err(diag.emit());
181                }
182            }
183            ty::GenericParamDefKind::Type { .. } | ty::GenericParamDefKind::Const { .. } => {
184                // Enforced in `enforce_impl_non_lifetime_params_are_constrained`.
185            }
186        }
187    }
188    res
189}
190
191pub(crate) fn enforce_impl_non_lifetime_params_are_constrained(
192    tcx: TyCtxt<'_>,
193    impl_def_id: LocalDefId,
194) -> Result<(), ErrorGuaranteed> {
195    let impl_self_ty = tcx.type_of(impl_def_id).instantiate_identity().skip_norm_wip();
196
197    // Don't complain about unconstrained type params when self ty isn't known due to errors.
198    // (#36836)
199    impl_self_ty.error_reported()?;
200
201    let impl_generics = tcx.generics_of(impl_def_id);
202    let impl_predicates = tcx.predicates_of(impl_def_id);
203    let impl_trait_ref = tcx
204        .impl_opt_trait_ref(impl_def_id)
205        .map(ty::EarlyBinder::instantiate_identity)
206        .map(Unnormalized::skip_norm_wip);
207
208    impl_trait_ref.error_reported()?;
209
210    let mut input_parameters = cgp::parameters_for_impl(tcx, impl_self_ty, impl_trait_ref);
211    cgp::identify_constrained_generic_params(
212        tcx,
213        impl_predicates,
214        impl_trait_ref,
215        &mut input_parameters,
216    );
217
218    let mut res = Ok(());
219    for param in &impl_generics.own_params {
220        let err = match param.kind {
221            // Disallow ANY unconstrained type parameters.
222            ty::GenericParamDefKind::Type { .. } => {
223                let param_ty = ty::ParamTy::for_def(param);
224                !input_parameters.contains(&cgp::Parameter::from(param_ty))
225            }
226            ty::GenericParamDefKind::Const { .. } => {
227                let param_ct = ty::ParamConst::for_def(param);
228                !input_parameters.contains(&cgp::Parameter::from(param_ct))
229            }
230            ty::GenericParamDefKind::Lifetime => {
231                // Enforced in `enforce_impl_type_params_are_constrained`.
232                false
233            }
234        };
235        if err {
236            let const_param_note = #[allow(non_exhaustive_omitted_patterns)] match param.kind {
    ty::GenericParamDefKind::Const { .. } => true,
    _ => false,
}matches!(param.kind, ty::GenericParamDefKind::Const { .. });
237            let mut diag = tcx.dcx().create_err(UnconstrainedGenericParameter {
238                span: tcx.def_span(param.def_id),
239                param_name: tcx.item_ident(param.def_id),
240                param_def_kind: tcx.def_descr(param.def_id),
241                const_param_note,
242                const_param_note2: const_param_note,
243            });
244            diag.code(E0207);
245            res = Err(diag.emit());
246        }
247    }
248    res
249}