]>
Commit | Line | Data |
---|---|---|
476ff2be SL |
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 | ||
48663c56 | 11 | use crate::constrained_generic_params as cgp; |
ba9703b0 XL |
12 | use min_specialization::check_min_specialization; |
13 | ||
dfeec247 XL |
14 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; |
15 | use rustc_errors::struct_span_err; | |
16 | use rustc_hir as hir; | |
f035d41b | 17 | use rustc_hir::def_id::LocalDefId; |
dfeec247 | 18 | use rustc_hir::itemlikevisit::ItemLikeVisitor; |
ba9703b0 XL |
19 | use rustc_middle::ty::query::Providers; |
20 | use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; | |
21 | use rustc_span::Span; | |
22 | ||
476ff2be SL |
23 | use std::collections::hash_map::Entry::{Occupied, Vacant}; |
24 | ||
ba9703b0 | 25 | mod min_specialization; |
60c5eb7d | 26 | |
476ff2be | 27 | /// Checks that all the type/lifetime parameters on an impl also |
9fa01778 | 28 | /// appear in the trait ref or self type (or are constrained by a |
476ff2be SL |
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 | |
9fa01778 | 35 | /// the lifetime parameter is used in an associated type. This is a |
476ff2be SL |
36 | /// concession to backwards compatibility; see comment at the end of |
37 | /// the fn for details. | |
38 | /// | |
39 | /// Example: | |
40 | /// | |
83c7162d | 41 | /// ```rust,ignore (pseudo-Rust) |
476ff2be | 42 | /// impl<T> Trait<Foo> for Bar { ... } |
83c7162d | 43 | /// // ^ T does not appear in `Foo` or `Bar`, error! |
476ff2be SL |
44 | /// |
45 | /// impl<T> Trait<Foo<T>> for Bar { ... } | |
83c7162d | 46 | /// // ^ T appears in `Foo<T>`, ok. |
476ff2be | 47 | /// |
9fa01778 | 48 | /// impl<T> Trait<Foo> for Bar where Bar: Iterator<Item = T> { ... } |
83c7162d | 49 | /// // ^ T is bound to `<Bar as Iterator>::Item`, ok. |
476ff2be SL |
50 | /// |
51 | /// impl<'a> Trait<Foo> for Bar { } | |
83c7162d | 52 | /// // ^ 'a is unused, but for back-compat we allow it |
476ff2be SL |
53 | /// |
54 | /// impl<'a> Trait<Foo> for Bar { type X = &'a i32; } | |
83c7162d | 55 | /// // ^ 'a is unused and appears in assoc type, error |
476ff2be | 56 | /// ``` |
416331ca | 57 | pub fn impl_wf_check(tcx: TyCtxt<'_>) { |
476ff2be SL |
58 | // We will tag this as part of the WF check -- logically, it is, |
59 | // but it's one that we must perform earlier than the rest of | |
60 | // WfCheck. | |
c295e0f8 | 61 | tcx.hir().for_each_module(|module| tcx.ensure().check_mod_impl_wf(module)) |
9fa01778 XL |
62 | } |
63 | ||
f035d41b | 64 | fn check_mod_impl_wf(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { |
ba9703b0 XL |
65 | let min_specialization = tcx.features().min_specialization; |
66 | tcx.hir() | |
67 | .visit_item_likes_in_module(module_def_id, &mut ImplWfCheck { tcx, min_specialization }); | |
9fa01778 XL |
68 | } |
69 | ||
f035d41b | 70 | pub fn provide(providers: &mut Providers) { |
dfeec247 | 71 | *providers = Providers { check_mod_impl_wf, ..*providers }; |
476ff2be SL |
72 | } |
73 | ||
dc9dc135 XL |
74 | struct ImplWfCheck<'tcx> { |
75 | tcx: TyCtxt<'tcx>, | |
ba9703b0 | 76 | min_specialization: bool, |
476ff2be SL |
77 | } |
78 | ||
a2a8927a | 79 | impl<'tcx> ItemLikeVisitor<'tcx> for ImplWfCheck<'tcx> { |
dfeec247 | 80 | fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { |
5869c6ff | 81 | if let hir::ItemKind::Impl(ref impl_) = item.kind { |
6a06907d | 82 | enforce_impl_params_are_constrained(self.tcx, item.def_id, impl_.items); |
5869c6ff | 83 | enforce_impl_items_are_distinct(self.tcx, impl_.items); |
ba9703b0 | 84 | if self.min_specialization { |
6a06907d | 85 | check_min_specialization(self.tcx, item.def_id.to_def_id(), item.span); |
ba9703b0 | 86 | } |
476ff2be SL |
87 | } |
88 | } | |
89 | ||
dfeec247 | 90 | fn visit_trait_item(&mut self, _trait_item: &'tcx hir::TraitItem<'tcx>) {} |
32a655c1 | 91 | |
dfeec247 | 92 | fn visit_impl_item(&mut self, _impl_item: &'tcx hir::ImplItem<'tcx>) {} |
fc512014 XL |
93 | |
94 | fn visit_foreign_item(&mut self, _foreign_item: &'tcx hir::ForeignItem<'tcx>) {} | |
476ff2be SL |
95 | } |
96 | ||
416331ca XL |
97 | fn enforce_impl_params_are_constrained( |
98 | tcx: TyCtxt<'_>, | |
f9f354fc | 99 | impl_def_id: LocalDefId, |
c295e0f8 | 100 | impl_item_refs: &[hir::ImplItemRef], |
dc9dc135 | 101 | ) { |
476ff2be | 102 | // Every lifetime used in an associated type must be constrained. |
7cac9316 | 103 | let impl_self_ty = tcx.type_of(impl_def_id); |
e1599b0c XL |
104 | if impl_self_ty.references_error() { |
105 | // Don't complain about unconstrained type params when self ty isn't known due to errors. | |
106 | // (#36836) | |
107 | tcx.sess.delay_span_bug( | |
108 | tcx.def_span(impl_def_id), | |
dfeec247 XL |
109 | &format!( |
110 | "potentially unconstrained type parameters weren't evaluated: {:?}", | |
111 | impl_self_ty, | |
112 | ), | |
e1599b0c XL |
113 | ); |
114 | return; | |
115 | } | |
7cac9316 XL |
116 | let impl_generics = tcx.generics_of(impl_def_id); |
117 | let impl_predicates = tcx.predicates_of(impl_def_id); | |
8bb4bdeb | 118 | let impl_trait_ref = tcx.impl_trait_ref(impl_def_id); |
476ff2be | 119 | |
5099ac24 | 120 | let mut input_parameters = cgp::parameters_for_impl(impl_self_ty, impl_trait_ref); |
48663c56 | 121 | cgp::identify_constrained_generic_params( |
dfeec247 XL |
122 | tcx, |
123 | impl_predicates, | |
124 | impl_trait_ref, | |
125 | &mut input_parameters, | |
126 | ); | |
476ff2be | 127 | |
476ff2be | 128 | // Disallow unconstrained lifetimes, but only if they appear in assoc types. |
dfeec247 XL |
129 | let lifetimes_in_associated_types: FxHashSet<_> = impl_item_refs |
130 | .iter() | |
6a06907d | 131 | .map(|item_ref| item_ref.id.def_id) |
74b04a01 | 132 | .flat_map(|def_id| { |
8bb4bdeb | 133 | let item = tcx.associated_item(def_id); |
74b04a01 XL |
134 | match item.kind { |
135 | ty::AssocKind::Type => { | |
136 | if item.defaultness.has_value() { | |
5099ac24 | 137 | cgp::parameters_for(&tcx.type_of(def_id), true) |
74b04a01 XL |
138 | } else { |
139 | Vec::new() | |
140 | } | |
141 | } | |
ba9703b0 | 142 | ty::AssocKind::Fn | ty::AssocKind::Const => Vec::new(), |
74b04a01 | 143 | } |
476ff2be | 144 | }) |
dfeec247 | 145 | .collect(); |
476ff2be | 146 | |
94b46f34 XL |
147 | for param in &impl_generics.params { |
148 | match param.kind { | |
149 | // Disallow ANY unconstrained type parameters. | |
532ac7d7 | 150 | ty::GenericParamDefKind::Type { .. } => { |
94b46f34 | 151 | let param_ty = ty::ParamTy::for_def(param); |
48663c56 | 152 | if !input_parameters.contains(&cgp::Parameter::from(param_ty)) { |
dfeec247 XL |
153 | report_unused_parameter( |
154 | tcx, | |
155 | tcx.def_span(param.def_id), | |
156 | "type", | |
157 | ¶m_ty.to_string(), | |
158 | ); | |
94b46f34 XL |
159 | } |
160 | } | |
161 | ty::GenericParamDefKind::Lifetime => { | |
48663c56 | 162 | let param_lt = cgp::Parameter::from(param.to_early_bound_region_data()); |
94b46f34 | 163 | if lifetimes_in_associated_types.contains(¶m_lt) && // (*) |
dfeec247 XL |
164 | !input_parameters.contains(¶m_lt) |
165 | { | |
166 | report_unused_parameter( | |
167 | tcx, | |
168 | tcx.def_span(param.def_id), | |
169 | "lifetime", | |
170 | ¶m.name.to_string(), | |
171 | ); | |
94b46f34 XL |
172 | } |
173 | } | |
cdc7bbd5 | 174 | ty::GenericParamDefKind::Const { .. } => { |
532ac7d7 | 175 | let param_ct = ty::ParamConst::for_def(param); |
48663c56 | 176 | if !input_parameters.contains(&cgp::Parameter::from(param_ct)) { |
dfeec247 XL |
177 | report_unused_parameter( |
178 | tcx, | |
179 | tcx.def_span(param.def_id), | |
180 | "const", | |
181 | ¶m_ct.to_string(), | |
182 | ); | |
532ac7d7 XL |
183 | } |
184 | } | |
476ff2be SL |
185 | } |
186 | } | |
187 | ||
188 | // (*) This is a horrible concession to reality. I think it'd be | |
1b1a35ee | 189 | // better to just ban unconstrained lifetimes outright, but in |
5e7ed085 | 190 | // practice people do non-hygienic macros like: |
476ff2be SL |
191 | // |
192 | // ``` | |
193 | // macro_rules! __impl_slice_eq1 { | |
194 | // ($Lhs: ty, $Rhs: ty, $Bound: ident) => { | |
195 | // impl<'a, 'b, A: $Bound, B> PartialEq<$Rhs> for $Lhs where A: PartialEq<B> { | |
196 | // .... | |
197 | // } | |
198 | // } | |
199 | // } | |
200 | // ``` | |
201 | // | |
b7449926 | 202 | // In a concession to backwards compatibility, we continue to |
476ff2be SL |
203 | // permit those, so long as the lifetimes aren't used in |
204 | // associated types. I believe this is sound, because lifetimes | |
205 | // used elsewhere are not projected back out. | |
206 | } | |
207 | ||
dc9dc135 | 208 | fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: &str) { |
1b1a35ee | 209 | let mut err = struct_span_err!( |
dfeec247 XL |
210 | tcx.sess, |
211 | span, | |
212 | E0207, | |
476ff2be SL |
213 | "the {} parameter `{}` is not constrained by the \ |
214 | impl trait, self type, or predicates", | |
dfeec247 XL |
215 | kind, |
216 | name | |
1b1a35ee XL |
217 | ); |
218 | err.span_label(span, format!("unconstrained {} parameter", kind)); | |
219 | if kind == "const" { | |
220 | err.note( | |
221 | "expressions using a const parameter must map each value to a distinct output value", | |
222 | ); | |
223 | err.note( | |
224 | "proving the result of expressions other than the parameter are unique is not supported", | |
225 | ); | |
226 | } | |
227 | err.emit(); | |
476ff2be SL |
228 | } |
229 | ||
230 | /// Enforce that we do not have two items in an impl with the same name. | |
c295e0f8 | 231 | fn enforce_impl_items_are_distinct(tcx: TyCtxt<'_>, impl_item_refs: &[hir::ImplItemRef]) { |
0bf4aa26 XL |
232 | let mut seen_type_items = FxHashMap::default(); |
233 | let mut seen_value_items = FxHashMap::default(); | |
476ff2be | 234 | for impl_item_ref in impl_item_refs { |
0731742a | 235 | let impl_item = tcx.hir().impl_item(impl_item_ref.id); |
e74abb32 | 236 | let seen_items = match impl_item.kind { |
416331ca | 237 | hir::ImplItemKind::TyAlias(_) => &mut seen_type_items, |
dfeec247 | 238 | _ => &mut seen_value_items, |
476ff2be | 239 | }; |
ba9703b0 | 240 | match seen_items.entry(impl_item.ident.normalize_to_macros_2_0()) { |
476ff2be | 241 | Occupied(entry) => { |
dfeec247 XL |
242 | let mut err = struct_span_err!( |
243 | tcx.sess, | |
244 | impl_item.span, | |
245 | E0201, | |
246 | "duplicate definitions with name `{}`:", | |
247 | impl_item.ident | |
248 | ); | |
249 | err.span_label( | |
250 | *entry.get(), | |
251 | format!("previous definition of `{}` here", impl_item.ident), | |
252 | ); | |
7cac9316 | 253 | err.span_label(impl_item.span, "duplicate definition"); |
476ff2be SL |
254 | err.emit(); |
255 | } | |
256 | Vacant(entry) => { | |
257 | entry.insert(impl_item.span); | |
258 | } | |
259 | } | |
260 | } | |
261 | } |