]> git.lizzy.rs Git - rust.git/blob - src/librustc_typeck/coherence/orphan.rs
Rollup merge of #66633 - GuillaumeGomez:err-codes-cleanup, r=Dylan-DPC
[rust.git] / src / librustc_typeck / 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::traits;
5 use rustc::ty::{self, TyCtxt};
6 use rustc::hir::itemlikevisit::ItemLikeVisitor;
7 use rustc::hir;
8
9 use rustc_error_codes::*;
10
11 pub fn check(tcx: TyCtxt<'_>) {
12     let mut orphan = OrphanChecker { tcx };
13     tcx.hir().krate().visit_all_item_likes(&mut orphan);
14 }
15
16 struct OrphanChecker<'tcx> {
17     tcx: TyCtxt<'tcx>,
18 }
19
20 impl ItemLikeVisitor<'v> for OrphanChecker<'tcx> {
21     /// Checks exactly one impl for orphan rules and other such
22     /// restrictions. In this fn, it can happen that multiple errors
23     /// apply to a specific impl, so just return after reporting one
24     /// to prevent inundating the user with a bunch of similar error
25     /// reports.
26     fn visit_item(&mut self, item: &hir::Item) {
27         let def_id = self.tcx.hir().local_def_id(item.hir_id);
28         // "Trait" impl
29         if let hir::ItemKind::Impl(.., generics, Some(tr), impl_ty, _) = &item.kind {
30             debug!("coherence2::orphan check: trait impl {}",
31                    self.tcx.hir().node_to_string(item.hir_id));
32             let trait_ref = self.tcx.impl_trait_ref(def_id).unwrap();
33             let trait_def_id = trait_ref.def_id;
34             let cm = self.tcx.sess.source_map();
35             let sp = cm.def_span(item.span);
36             match traits::orphan_check(self.tcx, def_id) {
37                 Ok(()) => {}
38                 Err(traits::OrphanCheckErr::NonLocalInputType(tys)) => {
39                     let mut err = struct_span_err!(
40                         self.tcx.sess,
41                         sp,
42                         E0117,
43                         "only traits defined in the current crate can be implemented for \
44                          arbitrary types"
45                     );
46                     err.span_label(sp, "impl doesn't use only types from inside the current crate");
47                     for (ty, is_target_ty) in &tys {
48                         let mut ty = *ty;
49                         self.tcx.infer_ctxt().enter(|infcx| {
50                             // Remove the lifetimes unnecessary for this error.
51                             ty = infcx.freshen(ty);
52                         });
53                         ty = match ty.kind {
54                             // Remove the type arguments from the output, as they are not relevant.
55                             // You can think of this as the reverse of `resolve_vars_if_possible`.
56                             // That way if we had `Vec<MyType>`, we will properly attribute the
57                             // problem to `Vec<T>` and avoid confusing the user if they were to see
58                             // `MyType` in the error.
59                             ty::Adt(def, _) => self.tcx.mk_adt(def, ty::List::empty()),
60                             _ => ty,
61                         };
62                         let this = "this".to_string();
63                         let (ty, postfix) = match &ty.kind {
64                             ty::Slice(_) => (this, " because slices are always foreign"),
65                             ty::Array(..) => (this, " because arrays are always foreign"),
66                             ty::Tuple(..) => (this, " because tuples are always foreign"),
67                             _ => (format!("`{}`", ty), ""),
68                         };
69                         let msg = format!("{} is not defined in the current crate{}", ty, postfix);
70                         if *is_target_ty {
71                             // Point at `D<A>` in `impl<A, B> for C<B> in D<A>`
72                             err.span_label(impl_ty.span, &msg);
73                         } else {
74                             // Point at `C<B>` in `impl<A, B> for C<B> in D<A>`
75                             err.span_label(tr.path.span, &msg);
76                         }
77                     }
78                     err.note("define and implement a trait or new type instead");
79                     err.emit();
80                     return;
81                 }
82                 Err(traits::OrphanCheckErr::UncoveredTy(param_ty, local_type)) => {
83                     let mut sp = sp;
84                     for param in &generics.params {
85                         if param.name.ident().to_string() == param_ty.to_string() {
86                             sp = param.span;
87                         }
88                     }
89
90                     match local_type {
91                         Some(local_type) => {
92                             struct_span_err!(
93                                 self.tcx.sess,
94                                 sp,
95                                 E0210,
96                                 "type parameter `{}` must be covered by another type \
97                                 when it appears before the first local type (`{}`)",
98                                 param_ty,
99                                 local_type
100                             ).span_label(sp, format!(
101                                 "type parameter `{}` must be covered by another type \
102                                 when it appears before the first local type (`{}`)",
103                                 param_ty,
104                                 local_type
105                             )).note("implementing a foreign trait is only possible if at \
106                                     least one of the types for which is it implemented is local, \
107                                     and no uncovered type parameters appear before that first \
108                                     local type"
109                             ).note("in this case, 'before' refers to the following order: \
110                                     `impl<..> ForeignTrait<T1, ..., Tn> for T0`, \
111                                     where `T0` is the first and `Tn` is the last"
112                             ).emit();
113                         }
114                         None => {
115                             struct_span_err!(
116                                 self.tcx.sess,
117                                 sp,
118                                 E0210,
119                                 "type parameter `{}` must be used as the type parameter for some \
120                                 local type (e.g., `MyStruct<{}>`)",
121                                 param_ty,
122                                 param_ty
123                             ).span_label(sp, format!(
124                                 "type parameter `{}` must be used as the type parameter for some \
125                                 local type",
126                                 param_ty,
127                             )).note("implementing a foreign trait is only possible if at \
128                                     least one of the types for which is it implemented is local"
129                             ).note("only traits defined in the current crate can be \
130                                     implemented for a type parameter"
131                             ).emit();
132                         }
133                     };
134                     return;
135                 }
136             }
137
138             // In addition to the above rules, we restrict impls of auto traits
139             // so that they can only be implemented on nominal types, such as structs,
140             // enums or foreign types. To see why this restriction exists, consider the
141             // following example (#22978). Imagine that crate A defines an auto trait
142             // `Foo` and a fn that operates on pairs of types:
143             //
144             // ```
145             // // Crate A
146             // auto trait Foo { }
147             // fn two_foos<A:Foo,B:Foo>(..) {
148             //     one_foo::<(A,B)>(..)
149             // }
150             // fn one_foo<T:Foo>(..) { .. }
151             // ```
152             //
153             // This type-checks fine; in particular the fn
154             // `two_foos` is able to conclude that `(A,B):Foo`
155             // because `A:Foo` and `B:Foo`.
156             //
157             // Now imagine that crate B comes along and does the following:
158             //
159             // ```
160             // struct A { }
161             // struct B { }
162             // impl Foo for A { }
163             // impl Foo for B { }
164             // impl !Send for (A, B) { }
165             // ```
166             //
167             // This final impl is legal according to the orpan
168             // rules, but it invalidates the reasoning from
169             // `two_foos` above.
170             debug!("trait_ref={:?} trait_def_id={:?} trait_is_auto={}",
171                    trait_ref,
172                    trait_def_id,
173                    self.tcx.trait_is_auto(trait_def_id));
174             if self.tcx.trait_is_auto(trait_def_id) &&
175                !trait_def_id.is_local() {
176                 let self_ty = trait_ref.self_ty();
177                 let opt_self_def_id = match self_ty.kind {
178                     ty::Adt(self_def, _) => Some(self_def.did),
179                     ty::Foreign(did) => Some(did),
180                     _ => None,
181                 };
182
183                 let msg = match opt_self_def_id {
184                     // We only want to permit nominal types, but not *all* nominal types.
185                     // They must be local to the current crate, so that people
186                     // can't do `unsafe impl Send for Rc<SomethingLocal>` or
187                     // `impl !Send for Box<SomethingLocalAndSend>`.
188                     Some(self_def_id) => {
189                         if self_def_id.is_local() {
190                             None
191                         } else {
192                             Some((
193                                 format!("cross-crate traits with a default impl, like `{}`, \
194                                          can only be implemented for a struct/enum type \
195                                          defined in the current crate",
196                                         self.tcx.def_path_str(trait_def_id)),
197                                 "can't implement cross-crate trait for type in another crate"
198                             ))
199                         }
200                     }
201                     _ => {
202                         Some((format!("cross-crate traits with a default impl, like `{}`, can \
203                                        only be implemented for a struct/enum type, not `{}`",
204                                       self.tcx.def_path_str(trait_def_id),
205                                       self_ty),
206                               "can't implement cross-crate trait with a default impl for \
207                                non-struct/enum type"))
208                     }
209                 };
210
211                 if let Some((msg, label)) = msg {
212                     struct_span_err!(self.tcx.sess, sp, E0321, "{}", msg)
213                         .span_label(sp, label)
214                         .emit();
215                     return;
216                 }
217             }
218         }
219     }
220
221     fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem) {
222     }
223
224     fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem) {
225     }
226 }