rust/compiler/rustc_hir_analysis/src/coherence/orphan.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

464 lines
17 KiB
Rust
Raw Normal View History

//! Orphan checker: every impl either implements a trait defined in this
//! crate or pertains to a type defined in this crate.
use rustc_data_structures::fx::FxHashSet;
2022-09-16 11:01:02 +04:00
use rustc_errors::{struct_span_err, DelayDm};
use rustc_errors::{Diagnostic, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_middle::ty::subst::InternalSubsts;
2022-04-04 10:56:59 +02:00
use rustc_middle::ty::util::IgnoreRegions;
Folding revamp. This commit makes type folding more like the way chalk does it. Currently, `TypeFoldable` has `fold_with` and `super_fold_with` methods. - `fold_with` is the standard entry point, and defaults to calling `super_fold_with`. - `super_fold_with` does the actual work of traversing a type. - For a few types of interest (`Ty`, `Region`, etc.) `fold_with` instead calls into a `TypeFolder`, which can then call back into `super_fold_with`. With the new approach, `TypeFoldable` has `fold_with` and `TypeSuperFoldable` has `super_fold_with`. - `fold_with` is still the standard entry point, *and* it does the actual work of traversing a type, for all types except types of interest. - `super_fold_with` is only implemented for the types of interest. Benefits of the new model. - I find it easier to understand. The distinction between types of interest and other types is clearer, and `super_fold_with` doesn't exist for most types. - With the current model is easy to get confused and implement a `super_fold_with` method that should be left defaulted. (Some of the precursor commits fixed such cases.) - With the current model it's easy to call `super_fold_with` within `TypeFolder` impls where `fold_with` should be called. The new approach makes this mistake impossible, and this commit fixes a number of such cases. - It's potentially faster, because it avoids the `fold_with` -> `super_fold_with` call in all cases except types of interest. A lot of the time the compile would inline those away, but not necessarily always.
2022-06-02 11:38:15 +10:00
use rustc_middle::ty::{
2022-06-17 13:15:00 +01:00
self, ImplPolarity, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
Folding revamp. This commit makes type folding more like the way chalk does it. Currently, `TypeFoldable` has `fold_with` and `super_fold_with` methods. - `fold_with` is the standard entry point, and defaults to calling `super_fold_with`. - `super_fold_with` does the actual work of traversing a type. - For a few types of interest (`Ty`, `Region`, etc.) `fold_with` instead calls into a `TypeFolder`, which can then call back into `super_fold_with`. With the new approach, `TypeFoldable` has `fold_with` and `TypeSuperFoldable` has `super_fold_with`. - `fold_with` is still the standard entry point, *and* it does the actual work of traversing a type, for all types except types of interest. - `super_fold_with` is only implemented for the types of interest. Benefits of the new model. - I find it easier to understand. The distinction between types of interest and other types is clearer, and `super_fold_with` doesn't exist for most types. - With the current model is easy to get confused and implement a `super_fold_with` method that should be left defaulted. (Some of the precursor commits fixed such cases.) - With the current model it's easy to call `super_fold_with` within `TypeFolder` impls where `fold_with` should be called. The new approach makes this mistake impossible, and this commit fixes a number of such cases. - It's potentially faster, because it avoids the `fold_with` -> `super_fold_with` call in all cases except types of interest. A lot of the time the compile would inline those away, but not necessarily always.
2022-06-02 11:38:15 +10:00
};
use rustc_session::lint;
use rustc_span::def_id::{DefId, LocalDefId};
use rustc_span::Span;
2020-02-11 21:19:40 +01:00
use rustc_trait_selection::traits;
use std::ops::ControlFlow;
2021-05-09 20:53:13 +02:00
#[instrument(skip(tcx), level = "debug")]
pub(crate) fn orphan_check_impl(
tcx: TyCtxt<'_>,
impl_def_id: LocalDefId,
) -> Result<(), ErrorGuaranteed> {
let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().subst_identity();
trait_ref.error_reported()?;
2021-05-09 20:53:13 +02:00
let ret = do_orphan_check_impl(tcx, trait_ref, impl_def_id);
if tcx.trait_is_auto(trait_ref.def_id) {
2022-06-22 21:19:12 +02:00
lint_auto_trait_impl(tcx, trait_ref, impl_def_id);
}
2021-05-09 20:53:13 +02:00
ret
}
2021-05-09 20:53:13 +02:00
fn do_orphan_check_impl<'tcx>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
def_id: LocalDefId,
) -> Result<(), ErrorGuaranteed> {
let trait_def_id = trait_ref.def_id;
let item = tcx.hir().expect_item(def_id);
let hir::ItemKind::Impl(ref impl_) = item.kind else {
bug!("{:?} is not an impl: {:?}", def_id, item);
};
let sp = tcx.def_span(def_id);
let tr = impl_.of_trait.as_ref().unwrap();
match traits::orphan_check(tcx, item.owner_id.to_def_id()) {
Ok(()) => {}
Err(err) => emit_orphan_check_error(
tcx,
sp,
item.span,
tr.path.span,
2023-01-01 18:08:02 -08:00
trait_ref,
impl_.self_ty.span,
&impl_.generics,
err,
)?,
}
// 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,
tcx.trait_is_auto(trait_def_id)
);
if 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",
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 `{}`",
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 {
let reported =
struct_span_err!(tcx.sess, sp, E0321, "{}", msg).span_label(sp, label).emit();
return Err(reported);
}
}
Ok(())
}
2021-12-13 21:45:08 -04:00
fn emit_orphan_check_error<'tcx>(
tcx: TyCtxt<'tcx>,
sp: Span,
full_impl_span: Span,
trait_span: Span,
2023-01-01 18:08:02 -08:00
trait_ref: ty::TraitRef<'tcx>,
self_ty_span: Span,
generics: &hir::Generics<'tcx>,
err: traits::OrphanCheckErr<'tcx>,
) -> Result<!, ErrorGuaranteed> {
2023-01-01 18:08:02 -08:00
let self_ty = trait_ref.self_ty();
Err(match err {
traits::OrphanCheckErr::NonLocalInputType(tys) => {
2022-04-21 16:47:01 +09:00
let msg = match self_ty.kind() {
ty::Adt(..) => "can be implemented for types defined outside of the crate",
_ if self_ty.is_primitive() => "can be implemented for primitive types",
_ => "can be implemented for arbitrary types",
};
let mut err = struct_span_err!(
tcx.sess,
sp,
E0117,
2022-04-21 16:47:01 +09:00
"only traits defined in the current crate {msg}"
);
err.span_label(sp, "impl doesn't use only types from inside the current crate");
2022-09-19 17:00:38 +02:00
for &(mut ty, is_target_ty) in &tys {
ty = tcx.erase_regions(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, _) => tcx.mk_adt(*def, ty::List::empty()),
_ => ty,
};
let msg = |ty: &str, postfix: &str| {
format!("{ty} is not defined in the current crate{postfix}")
};
2023-01-01 18:08:02 -08:00
let this = |name: &str| {
if !trait_ref.def_id.is_local() && !is_target_ty {
msg("this", &format!(" because this is a foreign trait"))
} else {
msg("this", &format!(" because {name} are always foreign"))
}
};
let msg = match &ty.kind() {
ty::Slice(_) => this("slices"),
ty::Array(..) => this("arrays"),
ty::Tuple(..) => this("tuples"),
ty::Alias(ty::Opaque, ..) => {
"type alias impl trait is treated as if it were foreign, \
because its hidden type could be from a foreign crate"
.to_string()
}
ty::RawPtr(ptr_ty) => {
emit_newtype_suggestion_for_raw_ptr(
full_impl_span,
self_ty,
self_ty_span,
ptr_ty,
&mut err,
);
msg(&format!("`{ty}`"), " because raw pointers are always foreign")
}
_ => msg(&format!("`{ty}`"), ""),
};
2022-09-19 17:00:38 +02:00
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(trait_span, &msg);
}
}
err.note("define and implement a trait or new type instead");
err.emit()
}
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!(
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!(
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(),
}
}
})
}
fn emit_newtype_suggestion_for_raw_ptr(
full_impl_span: Span,
self_ty: Ty<'_>,
self_ty_span: Span,
ptr_ty: &ty::TypeAndMut<'_>,
diag: &mut Diagnostic,
) {
if !self_ty.needs_subst() {
2022-11-23 18:22:51 +00:00
let mut_key = ptr_ty.mutbl.prefix_str();
let msg_sugg = "consider introducing a new wrapper type".to_owned();
let sugg = vec![
(
full_impl_span.shrink_to_lo(),
format!("struct WrapperType(*{}{});\n\n", mut_key, ptr_ty.ty),
),
(self_ty_span, "WrapperType".to_owned()),
];
diag.multipart_suggestion(msg_sugg, sugg, rustc_errors::Applicability::MaybeIncorrect);
}
}
/// Lint impls of auto traits if they are likely to have
/// unsound or surprising effects on auto impls.
2022-06-22 21:19:12 +02:00
fn lint_auto_trait_impl<'tcx>(
2021-05-09 20:53:13 +02:00
tcx: TyCtxt<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
impl_def_id: LocalDefId,
) {
if tcx.impl_polarity(impl_def_id) != ImplPolarity::Positive {
return;
}
2021-05-09 20:53:13 +02:00
assert_eq!(trait_ref.substs.len(), 1);
let self_ty = trait_ref.self_ty();
let (self_type_did, substs) = match self_ty.kind() {
ty::Adt(def, substs) => (def.did(), substs),
_ => {
// FIXME: should also lint for stuff like `&i32` but
// considering that auto traits are unstable, that
// isn't too important for now as this only affects
// crates using `nightly`, and std.
return;
}
2021-05-09 20:53:13 +02:00
};
2021-05-09 20:53:13 +02:00
// Impls which completely cover a given root type are fine as they
// disable auto impls entirely. So only lint if the substs
// are not a permutation of the identity substs.
let Err(arg) = tcx.uses_unique_generic_params(substs, IgnoreRegions::Yes) else {
// ok
return;
};
2021-05-09 20:53:13 +02:00
// Ideally:
//
// - compute the requirements for the auto impl candidate
// - check whether these are implied by the non covering impls
// - if not, emit the lint
//
// What we do here is a bit simpler:
//
// - badly check if an auto impl candidate definitely does not apply
// for the given simplified type
// - if so, do not lint
if fast_reject_auto_impl(tcx, trait_ref.def_id, self_ty) {
// ok
return;
}
2021-05-09 20:53:13 +02:00
tcx.struct_span_lint_hir(
lint::builtin::SUSPICIOUS_AUTO_TRAIT_IMPLS,
tcx.hir().local_def_id_to_hir_id(impl_def_id),
tcx.def_span(impl_def_id),
2022-09-16 11:01:02 +04:00
DelayDm(|| {
format!(
2021-05-09 20:53:13 +02:00
"cross-crate traits with a default impl, like `{}`, \
should not be specialized",
2021-05-09 20:53:13 +02:00
tcx.def_path_str(trait_ref.def_id),
2022-09-16 11:01:02 +04:00
)
}),
|lint| {
let item_span = tcx.def_span(self_type_did);
let self_descr = tcx.def_kind(self_type_did).descr(self_type_did);
2021-05-09 20:53:13 +02:00
match arg {
ty::util::NotUniqueParam::DuplicateParam(arg) => {
2022-09-16 11:01:02 +04:00
lint.note(&format!("`{}` is mentioned multiple times", arg));
}
2021-05-09 20:53:13 +02:00
ty::util::NotUniqueParam::NotParam(arg) => {
2022-09-16 11:01:02 +04:00
lint.note(&format!("`{}` is not a generic parameter", arg));
2021-05-09 20:53:13 +02:00
}
}
2022-09-16 11:01:02 +04:00
lint.span_note(
2021-05-09 20:53:13 +02:00
item_span,
&format!(
"try using the same sequence of generic parameters as the {} definition",
self_descr,
),
2022-09-16 11:01:02 +04:00
)
2021-05-09 20:53:13 +02:00
},
);
}
fn fast_reject_auto_impl<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, self_ty: Ty<'tcx>) -> bool {
struct DisableAutoTraitVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
trait_def_id: DefId,
self_ty_root: Ty<'tcx>,
seen: FxHashSet<DefId>,
}
impl<'tcx> TypeVisitor<'tcx> for DisableAutoTraitVisitor<'tcx> {
type BreakTy = ();
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
let tcx = self.tcx;
if t != self.self_ty_root {
for impl_def_id in tcx.non_blanket_impls_for_ty(self.trait_def_id, t) {
match tcx.impl_polarity(impl_def_id) {
ImplPolarity::Negative => return ControlFlow::BREAK,
ImplPolarity::Reservation => {}
// FIXME(@lcnr): That's probably not good enough, idk
//
// We might just want to take the rustdoc code and somehow avoid
// explicit impls for `Self`.
ImplPolarity::Positive => return ControlFlow::CONTINUE,
}
}
}
match t.kind() {
Folding revamp. This commit makes type folding more like the way chalk does it. Currently, `TypeFoldable` has `fold_with` and `super_fold_with` methods. - `fold_with` is the standard entry point, and defaults to calling `super_fold_with`. - `super_fold_with` does the actual work of traversing a type. - For a few types of interest (`Ty`, `Region`, etc.) `fold_with` instead calls into a `TypeFolder`, which can then call back into `super_fold_with`. With the new approach, `TypeFoldable` has `fold_with` and `TypeSuperFoldable` has `super_fold_with`. - `fold_with` is still the standard entry point, *and* it does the actual work of traversing a type, for all types except types of interest. - `super_fold_with` is only implemented for the types of interest. Benefits of the new model. - I find it easier to understand. The distinction between types of interest and other types is clearer, and `super_fold_with` doesn't exist for most types. - With the current model is easy to get confused and implement a `super_fold_with` method that should be left defaulted. (Some of the precursor commits fixed such cases.) - With the current model it's easy to call `super_fold_with` within `TypeFolder` impls where `fold_with` should be called. The new approach makes this mistake impossible, and this commit fixes a number of such cases. - It's potentially faster, because it avoids the `fold_with` -> `super_fold_with` call in all cases except types of interest. A lot of the time the compile would inline those away, but not necessarily always.
2022-06-02 11:38:15 +10:00
ty::Adt(def, substs) if def.is_phantom_data() => substs.visit_with(self),
ty::Adt(def, substs) => {
// @lcnr: This is the only place where cycles can happen. We avoid this
// by only visiting each `DefId` once.
//
// This will be is incorrect in subtle cases, but I don't care :)
if self.seen.insert(def.did()) {
for ty in def.all_fields().map(|field| field.ty(tcx, substs)) {
ty.visit_with(self)?;
}
}
ControlFlow::CONTINUE
}
_ => t.super_visit_with(self),
}
}
}
let self_ty_root = match self_ty.kind() {
ty::Adt(def, _) => tcx.mk_adt(*def, InternalSubsts::identity_for_item(tcx, def.did())),
_ => unimplemented!("unexpected self ty {:?}", self_ty),
};
self_ty_root
.visit_with(&mut DisableAutoTraitVisitor {
tcx,
self_ty_root,
trait_def_id,
seen: FxHashSet::default(),
})
.is_break()
}