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::codes::*;
16use rustc_hir::def::DefKind;
17use rustc_hir::def_id::LocalDefId;
18use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
19use rustc_span::ErrorGuaranteed;
20
21use crate::constrained_generic_params as cgp;
22use crate::errors::UnconstrainedGenericParameter;
23
24mod min_specialization;
25
26/// Checks that all the type/lifetime parameters on an impl also
27/// appear in the trait ref or self type (or are constrained by a
28/// where-clause). These rules are needed to ensure that, given a
29/// trait ref like `<T as Trait<U>>`, we can derive the values of all
30/// parameters on the impl (which is needed to make specialization
31/// possible).
32///
33/// However, in the case of lifetimes, we only enforce these rules if
34/// the lifetime parameter is used in an associated type. This is a
35/// concession to backwards compatibility; see comment at the end of
36/// the fn for details.
37///
38/// Example:
39///
40/// ```rust,ignore (pseudo-Rust)
41/// impl<T> Trait<Foo> for Bar { ... }
42/// //   ^ T does not appear in `Foo` or `Bar`, error!
43///
44/// impl<T> Trait<Foo<T>> for Bar { ... }
45/// //   ^ T appears in `Foo<T>`, ok.
46///
47/// impl<T> Trait<Foo> for Bar where Bar: Iterator<Item = T> { ... }
48/// //   ^ T is bound to `<Bar as Iterator>::Item`, ok.
49///
50/// impl<'a> Trait<Foo> for Bar { }
51/// //   ^ 'a is unused, but for back-compat we allow it
52///
53/// impl<'a> Trait<Foo> for Bar { type X = &'a i32; }
54/// //   ^ 'a is unused and appears in assoc type, error
55/// ```
56pub(crate) fn check_impl_wf(
57    tcx: TyCtxt<'_>,
58    impl_def_id: LocalDefId,
59) -> Result<(), ErrorGuaranteed> {
60    debug_assert_matches!(tcx.def_kind(impl_def_id), DefKind::Impl { .. });
61
62    // Check that the args are constrained. We queryfied the check for ty/const params
63    // since unconstrained type/const params cause ICEs in projection, so we want to
64    // detect those specifically and project those to `TyKind::Error`.
65    let mut res = tcx.ensure_ok().enforce_impl_non_lifetime_params_are_constrained(impl_def_id);
66    res = res.and(enforce_impl_lifetime_params_are_constrained(tcx, impl_def_id));
67
68    if tcx.features().min_specialization() {
69        res = res.and(check_min_specialization(tcx, impl_def_id));
70    }
71    res
72}
73
74pub(crate) fn enforce_impl_lifetime_params_are_constrained(
75    tcx: TyCtxt<'_>,
76    impl_def_id: LocalDefId,
77) -> Result<(), ErrorGuaranteed> {
78    let impl_self_ty = tcx.type_of(impl_def_id).instantiate_identity();
79    if impl_self_ty.references_error() {
80        // Don't complain about unconstrained type params when self ty isn't known due to errors.
81        // (#36836)
82        tcx.dcx().span_delayed_bug(
83            tcx.def_span(impl_def_id),
84            format!(
85                "potentially unconstrained type parameters weren't evaluated: {impl_self_ty:?}",
86            ),
87        );
88        // This is super fishy, but our current `rustc_hir_analysis::check_crate` pipeline depends on
89        // `type_of` having been called much earlier, and thus this value being read from cache.
90        // Compilation must continue in order for other important diagnostics to keep showing up.
91        return Ok(());
92    }
93
94    let impl_generics = tcx.generics_of(impl_def_id);
95    let impl_predicates = tcx.predicates_of(impl_def_id);
96    let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).map(ty::EarlyBinder::instantiate_identity);
97
98    impl_trait_ref.error_reported()?;
99
100    let mut input_parameters = cgp::parameters_for_impl(tcx, impl_self_ty, impl_trait_ref);
101    cgp::identify_constrained_generic_params(
102        tcx,
103        impl_predicates,
104        impl_trait_ref,
105        &mut input_parameters,
106    );
107
108    // Disallow unconstrained lifetimes, but only if they appear in assoc types.
109    let lifetimes_in_associated_types: FxHashSet<_> = tcx
110        .associated_item_def_ids(impl_def_id)
111        .iter()
112        .flat_map(|def_id| {
113            let item = tcx.associated_item(def_id);
114            match item.kind {
115                ty::AssocKind::Type => {
116                    if item.defaultness(tcx).has_value() {
117                        cgp::parameters_for(tcx, tcx.type_of(def_id).instantiate_identity(), true)
118                    } else {
119                        vec![]
120                    }
121                }
122                ty::AssocKind::Fn | ty::AssocKind::Const => vec![],
123            }
124        })
125        .collect();
126
127    let mut res = Ok(());
128    for param in &impl_generics.own_params {
129        match param.kind {
130            ty::GenericParamDefKind::Lifetime => {
131                // This is a horrible concession to reality. I think it'd be
132                // better to just ban unconstrained lifetimes outright, but in
133                // practice people do non-hygienic macros like:
134                //
135                // ```
136                // macro_rules! __impl_slice_eq1 {
137                //   ($Lhs: ty, $Rhs: ty, $Bound: ident) => {
138                //     impl<'a, 'b, A: $Bound, B> PartialEq<$Rhs> for $Lhs where A: PartialEq<B> {
139                //        ....
140                //     }
141                //   }
142                // }
143                // ```
144                //
145                // In a concession to backwards compatibility, we continue to
146                // permit those, so long as the lifetimes aren't used in
147                // associated types. I believe this is sound, because lifetimes
148                // used elsewhere are not projected back out.
149                let param_lt = cgp::Parameter::from(param.to_early_bound_region_data());
150                if lifetimes_in_associated_types.contains(&param_lt)
151                    && !input_parameters.contains(&param_lt)
152                {
153                    let mut diag = tcx.dcx().create_err(UnconstrainedGenericParameter {
154                        span: tcx.def_span(param.def_id),
155                        param_name: tcx.item_ident(param.def_id),
156                        param_def_kind: tcx.def_descr(param.def_id),
157                        const_param_note: false,
158                        const_param_note2: false,
159                    });
160                    diag.code(E0207);
161                    res = Err(diag.emit());
162                }
163            }
164            ty::GenericParamDefKind::Type { .. } | ty::GenericParamDefKind::Const { .. } => {
165                // Enforced in `enforce_impl_non_lifetime_params_are_constrained`.
166            }
167        }
168    }
169    res
170}
171
172pub(crate) fn enforce_impl_non_lifetime_params_are_constrained(
173    tcx: TyCtxt<'_>,
174    impl_def_id: LocalDefId,
175) -> Result<(), ErrorGuaranteed> {
176    let impl_self_ty = tcx.type_of(impl_def_id).instantiate_identity();
177    if impl_self_ty.references_error() {
178        // Don't complain about unconstrained type params when self ty isn't known due to errors.
179        // (#36836)
180        tcx.dcx().span_delayed_bug(
181            tcx.def_span(impl_def_id),
182            format!(
183                "potentially unconstrained type parameters weren't evaluated: {impl_self_ty:?}",
184            ),
185        );
186        // This is super fishy, but our current `rustc_hir_analysis::check_crate` pipeline depends on
187        // `type_of` having been called much earlier, and thus this value being read from cache.
188        // Compilation must continue in order for other important diagnostics to keep showing up.
189        return Ok(());
190    }
191    let impl_generics = tcx.generics_of(impl_def_id);
192    let impl_predicates = tcx.predicates_of(impl_def_id);
193    let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).map(ty::EarlyBinder::instantiate_identity);
194
195    impl_trait_ref.error_reported()?;
196
197    let mut input_parameters = cgp::parameters_for_impl(tcx, impl_self_ty, impl_trait_ref);
198    cgp::identify_constrained_generic_params(
199        tcx,
200        impl_predicates,
201        impl_trait_ref,
202        &mut input_parameters,
203    );
204
205    let mut res = Ok(());
206    for param in &impl_generics.own_params {
207        let err = match param.kind {
208            // Disallow ANY unconstrained type parameters.
209            ty::GenericParamDefKind::Type { .. } => {
210                let param_ty = ty::ParamTy::for_def(param);
211                !input_parameters.contains(&cgp::Parameter::from(param_ty))
212            }
213            ty::GenericParamDefKind::Const { .. } => {
214                let param_ct = ty::ParamConst::for_def(param);
215                !input_parameters.contains(&cgp::Parameter::from(param_ct))
216            }
217            ty::GenericParamDefKind::Lifetime => {
218                // Enforced in `enforce_impl_type_params_are_constrained`.
219                false
220            }
221        };
222        if err {
223            let const_param_note = matches!(param.kind, ty::GenericParamDefKind::Const { .. });
224            let mut diag = tcx.dcx().create_err(UnconstrainedGenericParameter {
225                span: tcx.def_span(param.def_id),
226                param_name: tcx.item_ident(param.def_id),
227                param_def_kind: tcx.def_descr(param.def_id),
228                const_param_note,
229                const_param_note2: const_param_note,
230            });
231            diag.code(E0207);
232            res = Err(diag.emit());
233        }
234    }
235    res
236}