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    of_trait: bool,
60) -> Result<(), ErrorGuaranteed> {
61    debug_assert_matches!(tcx.def_kind(impl_def_id), DefKind::Impl { .. });
62
63    // Check that the args are constrained. We queryfied the check for ty/const params
64    // since unconstrained type/const params cause ICEs in projection, so we want to
65    // detect those specifically and project those to `TyKind::Error`.
66    let mut res = tcx.ensure_ok().enforce_impl_non_lifetime_params_are_constrained(impl_def_id);
67    res = res.and(enforce_impl_lifetime_params_are_constrained(tcx, impl_def_id, of_trait));
68
69    if of_trait && tcx.features().min_specialization() {
70        res = res.and(check_min_specialization(tcx, impl_def_id));
71    }
72    res
73}
74
75pub(crate) fn enforce_impl_lifetime_params_are_constrained(
76    tcx: TyCtxt<'_>,
77    impl_def_id: LocalDefId,
78    of_trait: bool,
79) -> Result<(), ErrorGuaranteed> {
80    let impl_self_ty = tcx.type_of(impl_def_id).instantiate_identity();
81
82    // Don't complain about unconstrained type params when self ty isn't known due to errors.
83    // (#36836)
84    impl_self_ty.error_reported()?;
85
86    let impl_generics = tcx.generics_of(impl_def_id);
87    let impl_predicates = tcx.predicates_of(impl_def_id);
88    let impl_trait_ref = of_trait.then(|| tcx.impl_trait_ref(impl_def_id).instantiate_identity());
89
90    impl_trait_ref.error_reported()?;
91
92    let mut input_parameters = cgp::parameters_for_impl(tcx, impl_self_ty, impl_trait_ref);
93    cgp::identify_constrained_generic_params(
94        tcx,
95        impl_predicates,
96        impl_trait_ref,
97        &mut input_parameters,
98    );
99
100    // Disallow unconstrained lifetimes, but only if they appear in assoc types.
101    let lifetimes_in_associated_types: FxHashSet<_> = tcx
102        .associated_item_def_ids(impl_def_id)
103        .iter()
104        .flat_map(|def_id| {
105            let item = tcx.associated_item(def_id);
106            match item.kind {
107                ty::AssocKind::Type { .. } => {
108                    if item.defaultness(tcx).has_value() {
109                        cgp::parameters_for(tcx, tcx.type_of(def_id).instantiate_identity(), true)
110                    } else {
111                        vec![]
112                    }
113                }
114                ty::AssocKind::Fn { .. } | ty::AssocKind::Const { .. } => vec![],
115            }
116        })
117        .collect();
118
119    let mut res = Ok(());
120    for param in &impl_generics.own_params {
121        match param.kind {
122            ty::GenericParamDefKind::Lifetime => {
123                // This is a horrible concession to reality. I think it'd be
124                // better to just ban unconstrained lifetimes outright, but in
125                // practice people do non-hygienic macros like:
126                //
127                // ```
128                // macro_rules! __impl_slice_eq1 {
129                //   ($Lhs: ty, $Rhs: ty, $Bound: ident) => {
130                //     impl<'a, 'b, A: $Bound, B> PartialEq<$Rhs> for $Lhs where A: PartialEq<B> {
131                //        ....
132                //     }
133                //   }
134                // }
135                // ```
136                //
137                // In a concession to backwards compatibility, we continue to
138                // permit those, so long as the lifetimes aren't used in
139                // associated types. I believe this is sound, because lifetimes
140                // used elsewhere are not projected back out.
141                let param_lt = cgp::Parameter::from(param.to_early_bound_region_data());
142                if lifetimes_in_associated_types.contains(&param_lt)
143                    && !input_parameters.contains(&param_lt)
144                {
145                    let mut diag = tcx.dcx().create_err(UnconstrainedGenericParameter {
146                        span: tcx.def_span(param.def_id),
147                        param_name: tcx.item_ident(param.def_id),
148                        param_def_kind: tcx.def_descr(param.def_id),
149                        const_param_note: false,
150                        const_param_note2: false,
151                    });
152                    diag.code(E0207);
153                    res = Err(diag.emit());
154                }
155            }
156            ty::GenericParamDefKind::Type { .. } | ty::GenericParamDefKind::Const { .. } => {
157                // Enforced in `enforce_impl_non_lifetime_params_are_constrained`.
158            }
159        }
160    }
161    res
162}
163
164pub(crate) fn enforce_impl_non_lifetime_params_are_constrained(
165    tcx: TyCtxt<'_>,
166    impl_def_id: LocalDefId,
167) -> Result<(), ErrorGuaranteed> {
168    let impl_self_ty = tcx.type_of(impl_def_id).instantiate_identity();
169
170    // Don't complain about unconstrained type params when self ty isn't known due to errors.
171    // (#36836)
172    impl_self_ty.error_reported()?;
173
174    let impl_generics = tcx.generics_of(impl_def_id);
175    let impl_predicates = tcx.predicates_of(impl_def_id);
176    let impl_trait_ref =
177        tcx.impl_opt_trait_ref(impl_def_id).map(ty::EarlyBinder::instantiate_identity);
178
179    impl_trait_ref.error_reported()?;
180
181    let mut input_parameters = cgp::parameters_for_impl(tcx, impl_self_ty, impl_trait_ref);
182    cgp::identify_constrained_generic_params(
183        tcx,
184        impl_predicates,
185        impl_trait_ref,
186        &mut input_parameters,
187    );
188
189    let mut res = Ok(());
190    for param in &impl_generics.own_params {
191        let err = match param.kind {
192            // Disallow ANY unconstrained type parameters.
193            ty::GenericParamDefKind::Type { .. } => {
194                let param_ty = ty::ParamTy::for_def(param);
195                !input_parameters.contains(&cgp::Parameter::from(param_ty))
196            }
197            ty::GenericParamDefKind::Const { .. } => {
198                let param_ct = ty::ParamConst::for_def(param);
199                !input_parameters.contains(&cgp::Parameter::from(param_ct))
200            }
201            ty::GenericParamDefKind::Lifetime => {
202                // Enforced in `enforce_impl_type_params_are_constrained`.
203                false
204            }
205        };
206        if err {
207            let const_param_note = matches!(param.kind, ty::GenericParamDefKind::Const { .. });
208            let mut diag = tcx.dcx().create_err(UnconstrainedGenericParameter {
209                span: tcx.def_span(param.def_id),
210                param_name: tcx.item_ident(param.def_id),
211                param_def_kind: tcx.def_descr(param.def_id),
212                const_param_note,
213                const_param_note2: const_param_note,
214            });
215            diag.code(E0207);
216            res = Err(diag.emit());
217        }
218    }
219    res
220}