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::assert_matches::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};
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    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_ok().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();
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 = of_trait.then(|| tcx.impl_trait_ref(impl_def_id).instantiate_identity());
90
91    impl_trait_ref.error_reported()?;
92
93    let mut input_parameters = cgp::parameters_for_impl(tcx, impl_self_ty, impl_trait_ref);
94    cgp::identify_constrained_generic_params(
95        tcx,
96        impl_predicates,
97        impl_trait_ref,
98        &mut input_parameters,
99    );
100
101    // Disallow unconstrained lifetimes, but only if they appear in assoc types.
102    let lifetimes_in_associated_types: FxHashSet<_> = tcx
103        .associated_item_def_ids(impl_def_id)
104        .iter()
105        .flat_map(|def_id| {
106            let item = tcx.associated_item(def_id);
107            match item.kind {
108                ty::AssocKind::Type { .. } => {
109                    if item.defaultness(tcx).has_value() {
110                        cgp::parameters_for(tcx, tcx.type_of(def_id).instantiate_identity(), true)
111                    } else {
112                        vec![]
113                    }
114                }
115                ty::AssocKind::Fn { .. } | ty::AssocKind::Const { .. } => vec![],
116            }
117        })
118        .collect();
119
120    let mut res = Ok(());
121    for param in &impl_generics.own_params {
122        match param.kind {
123            ty::GenericParamDefKind::Lifetime => {
124                // This is a horrible concession to reality. I think it'd be
125                // better to just ban unconstrained lifetimes outright, but in
126                // practice people do non-hygienic macros like:
127                //
128                // ```
129                // macro_rules! __impl_slice_eq1 {
130                //   ($Lhs: ty, $Rhs: ty, $Bound: ident) => {
131                //     impl<'a, 'b, A: $Bound, B> PartialEq<$Rhs> for $Lhs where A: PartialEq<B> {
132                //        ....
133                //     }
134                //   }
135                // }
136                // ```
137                //
138                // In a concession to backwards compatibility, we continue to
139                // permit those, so long as the lifetimes aren't used in
140                // associated types. I believe this is sound, because lifetimes
141                // used elsewhere are not projected back out.
142                let param_lt = cgp::Parameter::from(param.to_early_bound_region_data());
143                if lifetimes_in_associated_types.contains(&param_lt)
144                    && !input_parameters.contains(&param_lt)
145                {
146                    let mut diag = tcx.dcx().create_err(UnconstrainedGenericParameter {
147                        span: tcx.def_span(param.def_id),
148                        param_name: tcx.item_ident(param.def_id),
149                        param_def_kind: tcx.def_descr(param.def_id),
150                        const_param_note: false,
151                        const_param_note2: false,
152                    });
153                    diag.code(E0207);
154                    for p in &impl_generics.own_params {
155                        if p.name == kw::UnderscoreLifetime {
156                            let span = tcx.def_span(p.def_id);
157                            let Ok(snippet) = tcx.sess.source_map().span_to_snippet(span) else {
158                                continue;
159                            };
160
161                            let (span, sugg) = if &snippet == "'_" {
162                                (span, param.name.to_string())
163                            } else {
164                                (span.shrink_to_hi(), format!("{} ", param.name))
165                            };
166                            diag.span_suggestion_verbose(
167                                span,
168                                "consider using the named lifetime here instead of an implicit \
169                                 lifetime",
170                                sugg,
171                                Applicability::MaybeIncorrect,
172                            );
173                        }
174                    }
175                    res = Err(diag.emit());
176                }
177            }
178            ty::GenericParamDefKind::Type { .. } | ty::GenericParamDefKind::Const { .. } => {
179                // Enforced in `enforce_impl_non_lifetime_params_are_constrained`.
180            }
181        }
182    }
183    res
184}
185
186pub(crate) fn enforce_impl_non_lifetime_params_are_constrained(
187    tcx: TyCtxt<'_>,
188    impl_def_id: LocalDefId,
189) -> Result<(), ErrorGuaranteed> {
190    let impl_self_ty = tcx.type_of(impl_def_id).instantiate_identity();
191
192    // Don't complain about unconstrained type params when self ty isn't known due to errors.
193    // (#36836)
194    impl_self_ty.error_reported()?;
195
196    let impl_generics = tcx.generics_of(impl_def_id);
197    let impl_predicates = tcx.predicates_of(impl_def_id);
198    let impl_trait_ref =
199        tcx.impl_opt_trait_ref(impl_def_id).map(ty::EarlyBinder::instantiate_identity);
200
201    impl_trait_ref.error_reported()?;
202
203    let mut input_parameters = cgp::parameters_for_impl(tcx, impl_self_ty, impl_trait_ref);
204    cgp::identify_constrained_generic_params(
205        tcx,
206        impl_predicates,
207        impl_trait_ref,
208        &mut input_parameters,
209    );
210
211    let mut res = Ok(());
212    for param in &impl_generics.own_params {
213        let err = match param.kind {
214            // Disallow ANY unconstrained type parameters.
215            ty::GenericParamDefKind::Type { .. } => {
216                let param_ty = ty::ParamTy::for_def(param);
217                !input_parameters.contains(&cgp::Parameter::from(param_ty))
218            }
219            ty::GenericParamDefKind::Const { .. } => {
220                let param_ct = ty::ParamConst::for_def(param);
221                !input_parameters.contains(&cgp::Parameter::from(param_ct))
222            }
223            ty::GenericParamDefKind::Lifetime => {
224                // Enforced in `enforce_impl_type_params_are_constrained`.
225                false
226            }
227        };
228        if err {
229            let const_param_note = matches!(param.kind, ty::GenericParamDefKind::Const { .. });
230            let mut diag = tcx.dcx().create_err(UnconstrainedGenericParameter {
231                span: tcx.def_span(param.def_id),
232                param_name: tcx.item_ident(param.def_id),
233                param_def_kind: tcx.def_descr(param.def_id),
234                const_param_note,
235                const_param_note2: const_param_note,
236            });
237            diag.code(E0207);
238            res = Err(diag.emit());
239        }
240    }
241    res
242}