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(¶m_lt)
151 && !input_parameters.contains(¶m_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}