blob: 71469770f2a33f642f058986d36ef540620fb2f9 [file] [log] [blame]
//! 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().krate().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<'_>) {
let def_id = self.tcx.hir().local_def_id(item.hir_id);
// "Trait" impl
if let hir::ItemKind::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(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, 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 is it 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 is it 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;
}
}
}
}
fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) {}
fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) {}
}