1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
//! Orphan checker: every impl either implements a trait defined in this
//! crate or pertains to a type defined in this crate.

use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::ty::{self, TyCtxt};
use rustc_trait_selection::traits;

pub fn check(tcx: TyCtxt<'_>) {
    let mut orphan = OrphanChecker { tcx };
    tcx.hir().visit_all_item_likes(&mut orphan);
}

struct OrphanChecker<'tcx> {
    tcx: TyCtxt<'tcx>,
}

impl ItemLikeVisitor<'v> for OrphanChecker<'tcx> {
    /// Checks exactly one impl for orphan rules and other such
    /// restrictions. In this fn, it can happen that multiple errors
    /// apply to a specific impl, so just return after reporting one
    /// to prevent inundating the user with a bunch of similar error
    /// reports.
    fn visit_item(&mut self, item: &hir::Item<'_>) {
        // "Trait" impl
        if let hir::ItemKind::Impl(hir::Impl {
            generics, of_trait: Some(ref tr), self_ty, ..
        }) = &item.kind
        {
            debug!(
                "coherence2::orphan check: trait impl {}",
                self.tcx.hir().node_to_string(item.hir_id())
            );
            let trait_ref = self.tcx.impl_trait_ref(item.def_id).unwrap();
            let trait_def_id = trait_ref.def_id;
            let sm = self.tcx.sess.source_map();
            let sp = sm.guess_head_span(item.span);
            match traits::orphan_check(self.tcx, item.def_id.to_def_id()) {
                Ok(()) => {}
                Err(traits::OrphanCheckErr::NonLocalInputType(tys)) => {
                    let mut err = struct_span_err!(
                        self.tcx.sess,
                        sp,
                        E0117,
                        "only traits defined in the current crate can be implemented for \
                         arbitrary types"
                    );
                    err.span_label(sp, "impl doesn't use only types from inside the current crate");
                    for (ty, is_target_ty) in &tys {
                        let mut ty = *ty;
                        self.tcx.infer_ctxt().enter(|infcx| {
                            // Remove the lifetimes unnecessary for this error.
                            ty = infcx.freshen(ty);
                        });
                        ty = match ty.kind() {
                            // Remove the type arguments from the output, as they are not relevant.
                            // You can think of this as the reverse of `resolve_vars_if_possible`.
                            // That way if we had `Vec<MyType>`, we will properly attribute the
                            // problem to `Vec<T>` and avoid confusing the user if they were to see
                            // `MyType` in the error.
                            ty::Adt(def, _) => self.tcx.mk_adt(def, ty::List::empty()),
                            _ => ty,
                        };
                        let this = "this".to_string();
                        let (ty, postfix) = match &ty.kind() {
                            ty::Slice(_) => (this, " because slices are always foreign"),
                            ty::Array(..) => (this, " because arrays are always foreign"),
                            ty::Tuple(..) => (this, " because tuples are always foreign"),
                            _ => (format!("`{}`", ty), ""),
                        };
                        let msg = format!("{} is not defined in the current crate{}", ty, postfix);
                        if *is_target_ty {
                            // Point at `D<A>` in `impl<A, B> for C<B> in D<A>`
                            err.span_label(self_ty.span, &msg);
                        } else {
                            // Point at `C<B>` in `impl<A, B> for C<B> in D<A>`
                            err.span_label(tr.path.span, &msg);
                        }
                    }
                    err.note("define and implement a trait or new type instead");
                    err.emit();
                    return;
                }
                Err(traits::OrphanCheckErr::UncoveredTy(param_ty, local_type)) => {
                    let mut sp = sp;
                    for param in generics.params {
                        if param.name.ident().to_string() == param_ty.to_string() {
                            sp = param.span;
                        }
                    }

                    match local_type {
                        Some(local_type) => {
                            struct_span_err!(
                                self.tcx.sess,
                                sp,
                                E0210,
                                "type parameter `{}` must be covered by another type \
                                when it appears before the first local type (`{}`)",
                                param_ty,
                                local_type
                            )
                            .span_label(
                                sp,
                                format!(
                                    "type parameter `{}` must be covered by another type \
                                when it appears before the first local type (`{}`)",
                                    param_ty, local_type
                                ),
                            )
                            .note(
                                "implementing a foreign trait is only possible if at \
                                    least one of the types for which it is implemented is local, \
                                    and no uncovered type parameters appear before that first \
                                    local type",
                            )
                            .note(
                                "in this case, 'before' refers to the following order: \
                                    `impl<..> ForeignTrait<T1, ..., Tn> for T0`, \
                                    where `T0` is the first and `Tn` is the last",
                            )
                            .emit();
                        }
                        None => {
                            struct_span_err!(
                                self.tcx.sess,
                                sp,
                                E0210,
                                "type parameter `{}` must be used as the type parameter for some \
                                local type (e.g., `MyStruct<{}>`)",
                                param_ty,
                                param_ty
                            ).span_label(sp, format!(
                                "type parameter `{}` must be used as the type parameter for some \
                                local type",
                                param_ty,
                            )).note("implementing a foreign trait is only possible if at \
                                    least one of the types for which it is implemented is local"
                            ).note("only traits defined in the current crate can be \
                                    implemented for a type parameter"
                            ).emit();
                        }
                    };
                    return;
                }
            }

            // In addition to the above rules, we restrict impls of auto traits
            // so that they can only be implemented on nominal types, such as structs,
            // enums or foreign types. To see why this restriction exists, consider the
            // following example (#22978). Imagine that crate A defines an auto trait
            // `Foo` and a fn that operates on pairs of types:
            //
            // ```
            // // Crate A
            // auto trait Foo { }
            // fn two_foos<A:Foo,B:Foo>(..) {
            //     one_foo::<(A,B)>(..)
            // }
            // fn one_foo<T:Foo>(..) { .. }
            // ```
            //
            // This type-checks fine; in particular the fn
            // `two_foos` is able to conclude that `(A,B):Foo`
            // because `A:Foo` and `B:Foo`.
            //
            // Now imagine that crate B comes along and does the following:
            //
            // ```
            // struct A { }
            // struct B { }
            // impl Foo for A { }
            // impl Foo for B { }
            // impl !Send for (A, B) { }
            // ```
            //
            // This final impl is legal according to the orphan
            // rules, but it invalidates the reasoning from
            // `two_foos` above.
            debug!(
                "trait_ref={:?} trait_def_id={:?} trait_is_auto={}",
                trait_ref,
                trait_def_id,
                self.tcx.trait_is_auto(trait_def_id)
            );
            if self.tcx.trait_is_auto(trait_def_id) && !trait_def_id.is_local() {
                let self_ty = trait_ref.self_ty();
                let opt_self_def_id = match *self_ty.kind() {
                    ty::Adt(self_def, _) => Some(self_def.did),
                    ty::Foreign(did) => Some(did),
                    _ => None,
                };

                let msg = match opt_self_def_id {
                    // We only want to permit nominal types, but not *all* nominal types.
                    // They must be local to the current crate, so that people
                    // can't do `unsafe impl Send for Rc<SomethingLocal>` or
                    // `impl !Send for Box<SomethingLocalAndSend>`.
                    Some(self_def_id) => {
                        if self_def_id.is_local() {
                            None
                        } else {
                            Some((
                                format!(
                                    "cross-crate traits with a default impl, like `{}`, \
                                         can only be implemented for a struct/enum type \
                                         defined in the current crate",
                                    self.tcx.def_path_str(trait_def_id)
                                ),
                                "can't implement cross-crate trait for type in another crate",
                            ))
                        }
                    }
                    _ => Some((
                        format!(
                            "cross-crate traits with a default impl, like `{}`, can \
                                       only be implemented for a struct/enum type, not `{}`",
                            self.tcx.def_path_str(trait_def_id),
                            self_ty
                        ),
                        "can't implement cross-crate trait with a default impl for \
                               non-struct/enum type",
                    )),
                };

                if let Some((msg, label)) = msg {
                    struct_span_err!(self.tcx.sess, sp, E0321, "{}", msg)
                        .span_label(sp, label)
                        .emit();
                    return;
                }
            }

            if let ty::Opaque(def_id, _) = *trait_ref.self_ty().kind() {
                self.tcx
                    .sess
                    .struct_span_err(sp, "cannot implement trait on type alias impl trait")
                    .span_note(self.tcx.def_span(def_id), "type alias impl trait defined here")
                    .emit();
            }
        }
    }

    fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {}

    fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {}

    fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'_>) {}
}