]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | //! Orphan checker: every impl either implements a trait defined in this |
2 | //! crate or pertains to a type defined in this crate. | |
3 | ||
5099ac24 | 4 | use rustc_data_structures::fx::FxHashSet; |
2b03887a | 5 | use rustc_errors::{struct_span_err, DelayDm}; |
064997fb | 6 | use rustc_errors::{Diagnostic, ErrorGuaranteed}; |
dfeec247 | 7 | use rustc_hir as hir; |
923072b8 FG |
8 | use rustc_middle::ty::subst::InternalSubsts; |
9 | use rustc_middle::ty::util::IgnoreRegions; | |
10 | use rustc_middle::ty::{ | |
064997fb | 11 | self, ImplPolarity, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, |
923072b8 | 12 | }; |
5099ac24 FG |
13 | use rustc_session::lint; |
14 | use rustc_span::def_id::{DefId, LocalDefId}; | |
3c0e092e | 15 | use rustc_span::Span; |
ba9703b0 | 16 | use rustc_trait_selection::traits; |
5099ac24 | 17 | use std::ops::ControlFlow; |
60c5eb7d | 18 | |
064997fb FG |
19 | #[instrument(skip(tcx), level = "debug")] |
20 | pub(crate) fn orphan_check_impl( | |
21 | tcx: TyCtxt<'_>, | |
22 | impl_def_id: LocalDefId, | |
23 | ) -> Result<(), ErrorGuaranteed> { | |
24 | let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); | |
487cf647 | 25 | trait_ref.error_reported()?; |
5099ac24 | 26 | |
064997fb FG |
27 | let ret = do_orphan_check_impl(tcx, trait_ref, impl_def_id); |
28 | if tcx.trait_is_auto(trait_ref.def_id) { | |
29 | lint_auto_trait_impl(tcx, trait_ref, impl_def_id); | |
3c0e092e | 30 | } |
064997fb FG |
31 | |
32 | ret | |
1a4d82fc JJ |
33 | } |
34 | ||
064997fb FG |
35 | fn do_orphan_check_impl<'tcx>( |
36 | tcx: TyCtxt<'tcx>, | |
37 | trait_ref: ty::TraitRef<'tcx>, | |
38 | def_id: LocalDefId, | |
39 | ) -> Result<(), ErrorGuaranteed> { | |
3c0e092e | 40 | let trait_def_id = trait_ref.def_id; |
1a4d82fc | 41 | |
2b03887a | 42 | let item = tcx.hir().expect_item(def_id); |
5e7ed085 FG |
43 | let hir::ItemKind::Impl(ref impl_) = item.kind else { |
44 | bug!("{:?} is not an impl: {:?}", def_id, item); | |
3c0e092e | 45 | }; |
064997fb | 46 | let sp = tcx.def_span(def_id); |
3c0e092e | 47 | let tr = impl_.of_trait.as_ref().unwrap(); |
04454e1e | 48 | |
2b03887a | 49 | match traits::orphan_check(tcx, item.owner_id.to_def_id()) { |
3c0e092e XL |
50 | Ok(()) => {} |
51 | Err(err) => emit_orphan_check_error( | |
52 | tcx, | |
53 | sp, | |
064997fb | 54 | item.span, |
3c0e092e | 55 | tr.path.span, |
04454e1e | 56 | trait_ref.self_ty(), |
3c0e092e XL |
57 | impl_.self_ty.span, |
58 | &impl_.generics, | |
59 | err, | |
60 | )?, | |
61 | } | |
62 | ||
63 | // In addition to the above rules, we restrict impls of auto traits | |
64 | // so that they can only be implemented on nominal types, such as structs, | |
65 | // enums or foreign types. To see why this restriction exists, consider the | |
66 | // following example (#22978). Imagine that crate A defines an auto trait | |
67 | // `Foo` and a fn that operates on pairs of types: | |
68 | // | |
69 | // ``` | |
70 | // // Crate A | |
71 | // auto trait Foo { } | |
72 | // fn two_foos<A:Foo,B:Foo>(..) { | |
73 | // one_foo::<(A,B)>(..) | |
74 | // } | |
75 | // fn one_foo<T:Foo>(..) { .. } | |
76 | // ``` | |
77 | // | |
78 | // This type-checks fine; in particular the fn | |
79 | // `two_foos` is able to conclude that `(A,B):Foo` | |
80 | // because `A:Foo` and `B:Foo`. | |
81 | // | |
82 | // Now imagine that crate B comes along and does the following: | |
83 | // | |
84 | // ``` | |
85 | // struct A { } | |
86 | // struct B { } | |
87 | // impl Foo for A { } | |
88 | // impl Foo for B { } | |
89 | // impl !Send for (A, B) { } | |
90 | // ``` | |
91 | // | |
92 | // This final impl is legal according to the orphan | |
93 | // rules, but it invalidates the reasoning from | |
94 | // `two_foos` above. | |
95 | debug!( | |
96 | "trait_ref={:?} trait_def_id={:?} trait_is_auto={}", | |
97 | trait_ref, | |
98 | trait_def_id, | |
99 | tcx.trait_is_auto(trait_def_id) | |
100 | ); | |
101 | ||
102 | if tcx.trait_is_auto(trait_def_id) && !trait_def_id.is_local() { | |
103 | let self_ty = trait_ref.self_ty(); | |
104 | let opt_self_def_id = match *self_ty.kind() { | |
5e7ed085 | 105 | ty::Adt(self_def, _) => Some(self_def.did()), |
3c0e092e XL |
106 | ty::Foreign(did) => Some(did), |
107 | _ => None, | |
108 | }; | |
60c5eb7d | 109 | |
3c0e092e XL |
110 | let msg = match opt_self_def_id { |
111 | // We only want to permit nominal types, but not *all* nominal types. | |
112 | // They must be local to the current crate, so that people | |
113 | // can't do `unsafe impl Send for Rc<SomethingLocal>` or | |
114 | // `impl !Send for Box<SomethingLocalAndSend>`. | |
115 | Some(self_def_id) => { | |
116 | if self_def_id.is_local() { | |
117 | None | |
118 | } else { | |
119 | Some(( | |
120 | format!( | |
121 | "cross-crate traits with a default impl, like `{}`, \ | |
122 | can only be implemented for a struct/enum type \ | |
123 | defined in the current crate", | |
124 | tcx.def_path_str(trait_def_id) | |
125 | ), | |
126 | "can't implement cross-crate trait for type in another crate", | |
127 | )) | |
c34b1796 | 128 | } |
0bf4aa26 | 129 | } |
3c0e092e XL |
130 | _ => Some(( |
131 | format!( | |
132 | "cross-crate traits with a default impl, like `{}`, can \ | |
133 | only be implemented for a struct/enum type, not `{}`", | |
134 | tcx.def_path_str(trait_def_id), | |
135 | self_ty | |
136 | ), | |
137 | "can't implement cross-crate trait with a default impl for \ | |
138 | non-struct/enum type", | |
139 | )), | |
140 | }; | |
c34b1796 | 141 | |
3c0e092e | 142 | if let Some((msg, label)) = msg { |
5e7ed085 FG |
143 | let reported = |
144 | struct_span_err!(tcx.sess, sp, E0321, "{}", msg).span_label(sp, label).emit(); | |
145 | return Err(reported); | |
3c0e092e XL |
146 | } |
147 | } | |
148 | ||
3c0e092e XL |
149 | Ok(()) |
150 | } | |
151 | ||
a2a8927a | 152 | fn emit_orphan_check_error<'tcx>( |
3c0e092e XL |
153 | tcx: TyCtxt<'tcx>, |
154 | sp: Span, | |
064997fb | 155 | full_impl_span: Span, |
3c0e092e | 156 | trait_span: Span, |
04454e1e | 157 | self_ty: Ty<'tcx>, |
3c0e092e XL |
158 | self_ty_span: Span, |
159 | generics: &hir::Generics<'tcx>, | |
160 | err: traits::OrphanCheckErr<'tcx>, | |
5e7ed085 FG |
161 | ) -> Result<!, ErrorGuaranteed> { |
162 | Err(match err { | |
3c0e092e | 163 | traits::OrphanCheckErr::NonLocalInputType(tys) => { |
04454e1e FG |
164 | let msg = match self_ty.kind() { |
165 | ty::Adt(..) => "can be implemented for types defined outside of the crate", | |
166 | _ if self_ty.is_primitive() => "can be implemented for primitive types", | |
167 | _ => "can be implemented for arbitrary types", | |
168 | }; | |
3c0e092e XL |
169 | let mut err = struct_span_err!( |
170 | tcx.sess, | |
171 | sp, | |
172 | E0117, | |
04454e1e | 173 | "only traits defined in the current crate {msg}" |
dfeec247 | 174 | ); |
3c0e092e | 175 | err.span_label(sp, "impl doesn't use only types from inside the current crate"); |
2b03887a FG |
176 | for &(mut ty, is_target_ty) in &tys { |
177 | ty = tcx.erase_regions(ty); | |
3c0e092e XL |
178 | ty = match ty.kind() { |
179 | // Remove the type arguments from the output, as they are not relevant. | |
180 | // You can think of this as the reverse of `resolve_vars_if_possible`. | |
181 | // That way if we had `Vec<MyType>`, we will properly attribute the | |
182 | // problem to `Vec<T>` and avoid confusing the user if they were to see | |
183 | // `MyType` in the error. | |
5e7ed085 | 184 | ty::Adt(def, _) => tcx.mk_adt(*def, ty::List::empty()), |
3c0e092e | 185 | _ => ty, |
0bf4aa26 | 186 | }; |
3c0e092e XL |
187 | let this = "this".to_string(); |
188 | let (ty, postfix) = match &ty.kind() { | |
189 | ty::Slice(_) => (this, " because slices are always foreign"), | |
190 | ty::Array(..) => (this, " because arrays are always foreign"), | |
191 | ty::Tuple(..) => (this, " because tuples are always foreign"), | |
064997fb FG |
192 | ty::RawPtr(ptr_ty) => { |
193 | emit_newtype_suggestion_for_raw_ptr( | |
194 | full_impl_span, | |
195 | self_ty, | |
196 | self_ty_span, | |
197 | ptr_ty, | |
198 | &mut err, | |
199 | ); | |
200 | ||
201 | (format!("`{}`", ty), " because raw pointers are always foreign") | |
202 | } | |
3c0e092e | 203 | _ => (format!("`{}`", ty), ""), |
0bf4aa26 | 204 | }; |
064997fb | 205 | |
3c0e092e | 206 | let msg = format!("{} is not defined in the current crate{}", ty, postfix); |
2b03887a | 207 | if is_target_ty { |
3c0e092e XL |
208 | // Point at `D<A>` in `impl<A, B> for C<B> in D<A>` |
209 | err.span_label(self_ty_span, &msg); | |
210 | } else { | |
211 | // Point at `C<B>` in `impl<A, B> for C<B> in D<A>` | |
212 | err.span_label(trait_span, &msg); | |
213 | } | |
214 | } | |
215 | err.note("define and implement a trait or new type instead"); | |
216 | err.emit() | |
217 | } | |
218 | traits::OrphanCheckErr::UncoveredTy(param_ty, local_type) => { | |
219 | let mut sp = sp; | |
220 | for param in generics.params { | |
221 | if param.name.ident().to_string() == param_ty.to_string() { | |
222 | sp = param.span; | |
1a4d82fc | 223 | } |
c34b1796 | 224 | } |
1b1a35ee | 225 | |
3c0e092e XL |
226 | match local_type { |
227 | Some(local_type) => struct_span_err!( | |
228 | tcx.sess, | |
229 | sp, | |
230 | E0210, | |
231 | "type parameter `{}` must be covered by another type \ | |
232 | when it appears before the first local type (`{}`)", | |
233 | param_ty, | |
234 | local_type | |
235 | ) | |
236 | .span_label( | |
237 | sp, | |
238 | format!( | |
239 | "type parameter `{}` must be covered by another type \ | |
240 | when it appears before the first local type (`{}`)", | |
241 | param_ty, local_type | |
242 | ), | |
243 | ) | |
244 | .note( | |
245 | "implementing a foreign trait is only possible if at \ | |
246 | least one of the types for which it is implemented is local, \ | |
247 | and no uncovered type parameters appear before that first \ | |
248 | local type", | |
249 | ) | |
250 | .note( | |
251 | "in this case, 'before' refers to the following order: \ | |
252 | `impl<..> ForeignTrait<T1, ..., Tn> for T0`, \ | |
253 | where `T0` is the first and `Tn` is the last", | |
254 | ) | |
255 | .emit(), | |
256 | None => struct_span_err!( | |
257 | tcx.sess, | |
258 | sp, | |
259 | E0210, | |
260 | "type parameter `{}` must be used as the type parameter for some \ | |
261 | local type (e.g., `MyStruct<{}>`)", | |
262 | param_ty, | |
263 | param_ty | |
264 | ) | |
265 | .span_label( | |
266 | sp, | |
267 | format!( | |
268 | "type parameter `{}` must be used as the type parameter for some \ | |
269 | local type", | |
270 | param_ty, | |
271 | ), | |
272 | ) | |
273 | .note( | |
274 | "implementing a foreign trait is only possible if at \ | |
275 | least one of the types for which it is implemented is local", | |
276 | ) | |
277 | .note( | |
278 | "only traits defined in the current crate can be \ | |
279 | implemented for a type parameter", | |
280 | ) | |
281 | .emit(), | |
1b1a35ee | 282 | } |
1a4d82fc | 283 | } |
5e7ed085 | 284 | }) |
1a4d82fc | 285 | } |
5099ac24 | 286 | |
064997fb FG |
287 | fn emit_newtype_suggestion_for_raw_ptr( |
288 | full_impl_span: Span, | |
289 | self_ty: Ty<'_>, | |
290 | self_ty_span: Span, | |
291 | ptr_ty: &ty::TypeAndMut<'_>, | |
292 | diag: &mut Diagnostic, | |
293 | ) { | |
294 | if !self_ty.needs_subst() { | |
487cf647 | 295 | let mut_key = ptr_ty.mutbl.prefix_str(); |
064997fb FG |
296 | let msg_sugg = "consider introducing a new wrapper type".to_owned(); |
297 | let sugg = vec![ | |
298 | ( | |
299 | full_impl_span.shrink_to_lo(), | |
300 | format!("struct WrapperType(*{}{});\n\n", mut_key, ptr_ty.ty), | |
301 | ), | |
302 | (self_ty_span, "WrapperType".to_owned()), | |
303 | ]; | |
304 | diag.multipart_suggestion(msg_sugg, sugg, rustc_errors::Applicability::MaybeIncorrect); | |
305 | } | |
306 | } | |
307 | ||
5099ac24 FG |
308 | /// Lint impls of auto traits if they are likely to have |
309 | /// unsound or surprising effects on auto impls. | |
064997fb FG |
310 | fn lint_auto_trait_impl<'tcx>( |
311 | tcx: TyCtxt<'tcx>, | |
312 | trait_ref: ty::TraitRef<'tcx>, | |
313 | impl_def_id: LocalDefId, | |
314 | ) { | |
315 | if tcx.impl_polarity(impl_def_id) != ImplPolarity::Positive { | |
316 | return; | |
317 | } | |
5099ac24 | 318 | |
064997fb FG |
319 | assert_eq!(trait_ref.substs.len(), 1); |
320 | let self_ty = trait_ref.self_ty(); | |
321 | let (self_type_did, substs) = match self_ty.kind() { | |
322 | ty::Adt(def, substs) => (def.did(), substs), | |
323 | _ => { | |
324 | // FIXME: should also lint for stuff like `&i32` but | |
325 | // considering that auto traits are unstable, that | |
326 | // isn't too important for now as this only affects | |
327 | // crates using `nightly`, and std. | |
5099ac24 FG |
328 | return; |
329 | } | |
064997fb | 330 | }; |
5099ac24 | 331 | |
064997fb FG |
332 | // Impls which completely cover a given root type are fine as they |
333 | // disable auto impls entirely. So only lint if the substs | |
334 | // are not a permutation of the identity substs. | |
335 | let Err(arg) = tcx.uses_unique_generic_params(substs, IgnoreRegions::Yes) else { | |
336 | // ok | |
337 | return; | |
338 | }; | |
5099ac24 | 339 | |
064997fb FG |
340 | // Ideally: |
341 | // | |
342 | // - compute the requirements for the auto impl candidate | |
343 | // - check whether these are implied by the non covering impls | |
344 | // - if not, emit the lint | |
345 | // | |
346 | // What we do here is a bit simpler: | |
347 | // | |
348 | // - badly check if an auto impl candidate definitely does not apply | |
349 | // for the given simplified type | |
350 | // - if so, do not lint | |
351 | if fast_reject_auto_impl(tcx, trait_ref.def_id, self_ty) { | |
352 | // ok | |
353 | return; | |
5099ac24 FG |
354 | } |
355 | ||
064997fb FG |
356 | tcx.struct_span_lint_hir( |
357 | lint::builtin::SUSPICIOUS_AUTO_TRAIT_IMPLS, | |
358 | tcx.hir().local_def_id_to_hir_id(impl_def_id), | |
359 | tcx.def_span(impl_def_id), | |
2b03887a FG |
360 | DelayDm(|| { |
361 | format!( | |
064997fb | 362 | "cross-crate traits with a default impl, like `{}`, \ |
5099ac24 | 363 | should not be specialized", |
064997fb | 364 | tcx.def_path_str(trait_ref.def_id), |
2b03887a FG |
365 | ) |
366 | }), | |
367 | |lint| { | |
368 | let item_span = tcx.def_span(self_type_did); | |
369 | let self_descr = tcx.def_kind(self_type_did).descr(self_type_did); | |
064997fb FG |
370 | match arg { |
371 | ty::util::NotUniqueParam::DuplicateParam(arg) => { | |
2b03887a | 372 | lint.note(&format!("`{}` is mentioned multiple times", arg)); |
923072b8 | 373 | } |
064997fb | 374 | ty::util::NotUniqueParam::NotParam(arg) => { |
2b03887a | 375 | lint.note(&format!("`{}` is not a generic parameter", arg)); |
064997fb FG |
376 | } |
377 | } | |
2b03887a | 378 | lint.span_note( |
064997fb FG |
379 | item_span, |
380 | &format!( | |
381 | "try using the same sequence of generic parameters as the {} definition", | |
382 | self_descr, | |
383 | ), | |
2b03887a | 384 | ) |
064997fb FG |
385 | }, |
386 | ); | |
5099ac24 FG |
387 | } |
388 | ||
389 | fn fast_reject_auto_impl<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, self_ty: Ty<'tcx>) -> bool { | |
390 | struct DisableAutoTraitVisitor<'tcx> { | |
391 | tcx: TyCtxt<'tcx>, | |
392 | trait_def_id: DefId, | |
393 | self_ty_root: Ty<'tcx>, | |
394 | seen: FxHashSet<DefId>, | |
395 | } | |
396 | ||
397 | impl<'tcx> TypeVisitor<'tcx> for DisableAutoTraitVisitor<'tcx> { | |
398 | type BreakTy = (); | |
399 | fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { | |
400 | let tcx = self.tcx; | |
401 | if t != self.self_ty_root { | |
402 | for impl_def_id in tcx.non_blanket_impls_for_ty(self.trait_def_id, t) { | |
403 | match tcx.impl_polarity(impl_def_id) { | |
404 | ImplPolarity::Negative => return ControlFlow::BREAK, | |
405 | ImplPolarity::Reservation => {} | |
406 | // FIXME(@lcnr): That's probably not good enough, idk | |
407 | // | |
408 | // We might just want to take the rustdoc code and somehow avoid | |
409 | // explicit impls for `Self`. | |
410 | ImplPolarity::Positive => return ControlFlow::CONTINUE, | |
411 | } | |
412 | } | |
413 | } | |
414 | ||
415 | match t.kind() { | |
923072b8 | 416 | ty::Adt(def, substs) if def.is_phantom_data() => substs.visit_with(self), |
5099ac24 FG |
417 | ty::Adt(def, substs) => { |
418 | // @lcnr: This is the only place where cycles can happen. We avoid this | |
419 | // by only visiting each `DefId` once. | |
420 | // | |
421 | // This will be is incorrect in subtle cases, but I don't care :) | |
5e7ed085 | 422 | if self.seen.insert(def.did()) { |
5099ac24 FG |
423 | for ty in def.all_fields().map(|field| field.ty(tcx, substs)) { |
424 | ty.visit_with(self)?; | |
425 | } | |
426 | } | |
427 | ||
428 | ControlFlow::CONTINUE | |
429 | } | |
430 | _ => t.super_visit_with(self), | |
431 | } | |
432 | } | |
433 | } | |
434 | ||
435 | let self_ty_root = match self_ty.kind() { | |
5e7ed085 | 436 | ty::Adt(def, _) => tcx.mk_adt(*def, InternalSubsts::identity_for_item(tcx, def.did())), |
5099ac24 FG |
437 | _ => unimplemented!("unexpected self ty {:?}", self_ty), |
438 | }; | |
439 | ||
440 | self_ty_root | |
441 | .visit_with(&mut DisableAutoTraitVisitor { | |
442 | tcx, | |
443 | self_ty_root, | |
444 | trait_def_id, | |
445 | seen: FxHashSet::default(), | |
446 | }) | |
447 | .is_break() | |
448 | } |