]>
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. | |
9fa01778 | 61 | for &module in tcx.hir().krate().modules.keys() { |
f035d41b | 62 | tcx.ensure().check_mod_impl_wf(tcx.hir().local_def_id(module)); |
9fa01778 XL |
63 | } |
64 | } | |
65 | ||
f035d41b | 66 | fn check_mod_impl_wf(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { |
ba9703b0 XL |
67 | let min_specialization = tcx.features().min_specialization; |
68 | tcx.hir() | |
69 | .visit_item_likes_in_module(module_def_id, &mut ImplWfCheck { tcx, min_specialization }); | |
9fa01778 XL |
70 | } |
71 | ||
f035d41b | 72 | pub fn provide(providers: &mut Providers) { |
dfeec247 | 73 | *providers = Providers { check_mod_impl_wf, ..*providers }; |
476ff2be SL |
74 | } |
75 | ||
dc9dc135 XL |
76 | struct ImplWfCheck<'tcx> { |
77 | tcx: TyCtxt<'tcx>, | |
ba9703b0 | 78 | min_specialization: bool, |
476ff2be SL |
79 | } |
80 | ||
dc9dc135 | 81 | impl ItemLikeVisitor<'tcx> for ImplWfCheck<'tcx> { |
dfeec247 XL |
82 | fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { |
83 | if let hir::ItemKind::Impl { ref items, .. } = item.kind { | |
416331ca | 84 | let impl_def_id = self.tcx.hir().local_def_id(item.hir_id); |
dfeec247 XL |
85 | enforce_impl_params_are_constrained(self.tcx, impl_def_id, items); |
86 | enforce_impl_items_are_distinct(self.tcx, items); | |
ba9703b0 | 87 | if self.min_specialization { |
f9f354fc | 88 | check_min_specialization(self.tcx, impl_def_id.to_def_id(), item.span); |
ba9703b0 | 89 | } |
476ff2be SL |
90 | } |
91 | } | |
92 | ||
dfeec247 | 93 | fn visit_trait_item(&mut self, _trait_item: &'tcx hir::TraitItem<'tcx>) {} |
32a655c1 | 94 | |
dfeec247 | 95 | fn visit_impl_item(&mut self, _impl_item: &'tcx hir::ImplItem<'tcx>) {} |
fc512014 XL |
96 | |
97 | fn visit_foreign_item(&mut self, _foreign_item: &'tcx hir::ForeignItem<'tcx>) {} | |
476ff2be SL |
98 | } |
99 | ||
416331ca XL |
100 | fn enforce_impl_params_are_constrained( |
101 | tcx: TyCtxt<'_>, | |
f9f354fc | 102 | impl_def_id: LocalDefId, |
dfeec247 | 103 | impl_item_refs: &[hir::ImplItemRef<'_>], |
dc9dc135 | 104 | ) { |
476ff2be | 105 | // Every lifetime used in an associated type must be constrained. |
7cac9316 | 106 | let impl_self_ty = tcx.type_of(impl_def_id); |
e1599b0c XL |
107 | if impl_self_ty.references_error() { |
108 | // Don't complain about unconstrained type params when self ty isn't known due to errors. | |
109 | // (#36836) | |
110 | tcx.sess.delay_span_bug( | |
111 | tcx.def_span(impl_def_id), | |
dfeec247 XL |
112 | &format!( |
113 | "potentially unconstrained type parameters weren't evaluated: {:?}", | |
114 | impl_self_ty, | |
115 | ), | |
e1599b0c XL |
116 | ); |
117 | return; | |
118 | } | |
7cac9316 XL |
119 | let impl_generics = tcx.generics_of(impl_def_id); |
120 | let impl_predicates = tcx.predicates_of(impl_def_id); | |
8bb4bdeb | 121 | let impl_trait_ref = tcx.impl_trait_ref(impl_def_id); |
476ff2be | 122 | |
48663c56 XL |
123 | let mut input_parameters = cgp::parameters_for_impl(impl_self_ty, impl_trait_ref); |
124 | cgp::identify_constrained_generic_params( | |
dfeec247 XL |
125 | tcx, |
126 | impl_predicates, | |
127 | impl_trait_ref, | |
128 | &mut input_parameters, | |
129 | ); | |
476ff2be | 130 | |
476ff2be | 131 | // Disallow unconstrained lifetimes, but only if they appear in assoc types. |
dfeec247 XL |
132 | let lifetimes_in_associated_types: FxHashSet<_> = impl_item_refs |
133 | .iter() | |
416331ca | 134 | .map(|item_ref| tcx.hir().local_def_id(item_ref.id.hir_id)) |
74b04a01 | 135 | .flat_map(|def_id| { |
8bb4bdeb | 136 | let item = tcx.associated_item(def_id); |
74b04a01 XL |
137 | match item.kind { |
138 | ty::AssocKind::Type => { | |
139 | if item.defaultness.has_value() { | |
140 | cgp::parameters_for(&tcx.type_of(def_id), true) | |
141 | } else { | |
142 | Vec::new() | |
143 | } | |
144 | } | |
ba9703b0 | 145 | ty::AssocKind::Fn | ty::AssocKind::Const => Vec::new(), |
74b04a01 | 146 | } |
476ff2be | 147 | }) |
dfeec247 | 148 | .collect(); |
476ff2be | 149 | |
94b46f34 XL |
150 | for param in &impl_generics.params { |
151 | match param.kind { | |
152 | // Disallow ANY unconstrained type parameters. | |
532ac7d7 | 153 | ty::GenericParamDefKind::Type { .. } => { |
94b46f34 | 154 | let param_ty = ty::ParamTy::for_def(param); |
48663c56 | 155 | if !input_parameters.contains(&cgp::Parameter::from(param_ty)) { |
dfeec247 XL |
156 | report_unused_parameter( |
157 | tcx, | |
158 | tcx.def_span(param.def_id), | |
159 | "type", | |
160 | ¶m_ty.to_string(), | |
161 | ); | |
94b46f34 XL |
162 | } |
163 | } | |
164 | ty::GenericParamDefKind::Lifetime => { | |
48663c56 | 165 | let param_lt = cgp::Parameter::from(param.to_early_bound_region_data()); |
94b46f34 | 166 | if lifetimes_in_associated_types.contains(¶m_lt) && // (*) |
dfeec247 XL |
167 | !input_parameters.contains(¶m_lt) |
168 | { | |
169 | report_unused_parameter( | |
170 | tcx, | |
171 | tcx.def_span(param.def_id), | |
172 | "lifetime", | |
173 | ¶m.name.to_string(), | |
174 | ); | |
94b46f34 XL |
175 | } |
176 | } | |
532ac7d7 XL |
177 | ty::GenericParamDefKind::Const => { |
178 | let param_ct = ty::ParamConst::for_def(param); | |
48663c56 | 179 | if !input_parameters.contains(&cgp::Parameter::from(param_ct)) { |
dfeec247 XL |
180 | report_unused_parameter( |
181 | tcx, | |
182 | tcx.def_span(param.def_id), | |
183 | "const", | |
184 | ¶m_ct.to_string(), | |
185 | ); | |
532ac7d7 XL |
186 | } |
187 | } | |
476ff2be SL |
188 | } |
189 | } | |
190 | ||
191 | // (*) This is a horrible concession to reality. I think it'd be | |
1b1a35ee | 192 | // better to just ban unconstrained lifetimes outright, but in |
476ff2be SL |
193 | // practice people do non-hygenic macros like: |
194 | // | |
195 | // ``` | |
196 | // macro_rules! __impl_slice_eq1 { | |
197 | // ($Lhs: ty, $Rhs: ty, $Bound: ident) => { | |
198 | // impl<'a, 'b, A: $Bound, B> PartialEq<$Rhs> for $Lhs where A: PartialEq<B> { | |
199 | // .... | |
200 | // } | |
201 | // } | |
202 | // } | |
203 | // ``` | |
204 | // | |
b7449926 | 205 | // In a concession to backwards compatibility, we continue to |
476ff2be SL |
206 | // permit those, so long as the lifetimes aren't used in |
207 | // associated types. I believe this is sound, because lifetimes | |
208 | // used elsewhere are not projected back out. | |
209 | } | |
210 | ||
dc9dc135 | 211 | fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: &str) { |
1b1a35ee | 212 | let mut err = struct_span_err!( |
dfeec247 XL |
213 | tcx.sess, |
214 | span, | |
215 | E0207, | |
476ff2be SL |
216 | "the {} parameter `{}` is not constrained by the \ |
217 | impl trait, self type, or predicates", | |
dfeec247 XL |
218 | kind, |
219 | name | |
1b1a35ee XL |
220 | ); |
221 | err.span_label(span, format!("unconstrained {} parameter", kind)); | |
222 | if kind == "const" { | |
223 | err.note( | |
224 | "expressions using a const parameter must map each value to a distinct output value", | |
225 | ); | |
226 | err.note( | |
227 | "proving the result of expressions other than the parameter are unique is not supported", | |
228 | ); | |
229 | } | |
230 | err.emit(); | |
476ff2be SL |
231 | } |
232 | ||
233 | /// Enforce that we do not have two items in an impl with the same name. | |
dfeec247 | 234 | fn enforce_impl_items_are_distinct(tcx: TyCtxt<'_>, impl_item_refs: &[hir::ImplItemRef<'_>]) { |
0bf4aa26 XL |
235 | let mut seen_type_items = FxHashMap::default(); |
236 | let mut seen_value_items = FxHashMap::default(); | |
476ff2be | 237 | for impl_item_ref in impl_item_refs { |
0731742a | 238 | let impl_item = tcx.hir().impl_item(impl_item_ref.id); |
e74abb32 | 239 | let seen_items = match impl_item.kind { |
416331ca | 240 | hir::ImplItemKind::TyAlias(_) => &mut seen_type_items, |
dfeec247 | 241 | _ => &mut seen_value_items, |
476ff2be | 242 | }; |
ba9703b0 | 243 | match seen_items.entry(impl_item.ident.normalize_to_macros_2_0()) { |
476ff2be | 244 | Occupied(entry) => { |
dfeec247 XL |
245 | let mut err = struct_span_err!( |
246 | tcx.sess, | |
247 | impl_item.span, | |
248 | E0201, | |
249 | "duplicate definitions with name `{}`:", | |
250 | impl_item.ident | |
251 | ); | |
252 | err.span_label( | |
253 | *entry.get(), | |
254 | format!("previous definition of `{}` here", impl_item.ident), | |
255 | ); | |
7cac9316 | 256 | err.span_label(impl_item.span, "duplicate definition"); |
476ff2be SL |
257 | err.emit(); |
258 | } | |
259 | Vacant(entry) => { | |
260 | entry.insert(impl_item.span); | |
261 | } | |
262 | } | |
263 | } | |
264 | } |