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