2014-11-25 21:17:11 -05:00
|
|
|
//! Orphan checker: every impl either implements a trait defined in this
|
|
|
|
//! crate or pertains to a type defined in this crate.
|
2014-09-12 10:54:08 -04:00
|
|
|
|
2022-01-27 10:49:52 +01:00
|
|
|
use rustc_data_structures::fx::FxHashSet;
|
2022-09-16 11:01:02 +04:00
|
|
|
use rustc_errors::{struct_span_err, DelayDm};
|
2022-07-24 17:45:21 +00:00
|
|
|
use rustc_errors::{Diagnostic, ErrorGuaranteed};
|
2020-01-05 02:37:57 +01:00
|
|
|
use rustc_hir as hir;
|
2022-03-23 09:41:31 +01:00
|
|
|
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
|
|
|
};
|
2022-01-27 10:49:52 +01:00
|
|
|
use rustc_session::lint;
|
|
|
|
use rustc_span::def_id::{DefId, LocalDefId};
|
2021-10-21 15:36:35 +02:00
|
|
|
use rustc_span::Span;
|
2020-02-11 21:19:40 +01:00
|
|
|
use rustc_trait_selection::traits;
|
2022-01-27 10:49:52 +01:00
|
|
|
use std::ops::ControlFlow;
|
2014-09-12 10:54:08 -04:00
|
|
|
|
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> {
|
2023-01-11 11:32:33 -07:00
|
|
|
let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().subst_identity();
|
2022-11-03 04:57:44 +08:00
|
|
|
trait_ref.error_reported()?;
|
2022-01-27 10:49:52 +01:00
|
|
|
|
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-10-21 15:36:35 +02:00
|
|
|
}
|
2021-05-09 20:53:13 +02:00
|
|
|
|
|
|
|
ret
|
2014-09-12 10:54:08 -04:00
|
|
|
}
|
|
|
|
|
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> {
|
2021-10-21 15:36:35 +02:00
|
|
|
let trait_def_id = trait_ref.def_id;
|
2014-09-12 10:54:08 -04:00
|
|
|
|
2022-09-20 14:11:23 +09:00
|
|
|
let item = tcx.hir().expect_item(def_id);
|
2022-02-19 00:44:45 +01:00
|
|
|
let hir::ItemKind::Impl(ref impl_) = item.kind else {
|
|
|
|
bug!("{:?} is not an impl: {:?}", def_id, item);
|
2021-10-21 15:36:35 +02:00
|
|
|
};
|
2022-07-04 17:23:24 +09:00
|
|
|
let sp = tcx.def_span(def_id);
|
2021-10-21 15:36:35 +02:00
|
|
|
let tr = impl_.of_trait.as_ref().unwrap();
|
2022-05-03 13:53:53 +00:00
|
|
|
|
2022-10-27 14:02:18 +11:00
|
|
|
match traits::orphan_check(tcx, item.owner_id.to_def_id()) {
|
2021-10-21 15:36:35 +02:00
|
|
|
Ok(()) => {}
|
|
|
|
Err(err) => emit_orphan_check_error(
|
|
|
|
tcx,
|
|
|
|
sp,
|
2022-07-24 17:45:21 +00:00
|
|
|
item.span,
|
2021-10-21 15:36:35 +02:00
|
|
|
tr.path.span,
|
2023-01-01 18:08:02 -08:00
|
|
|
trait_ref,
|
2021-10-21 15:36:35 +02:00
|
|
|
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() {
|
2022-03-05 07:28:41 +11:00
|
|
|
ty::Adt(self_def, _) => Some(self_def.did()),
|
2021-10-21 15:36:35 +02:00
|
|
|
ty::Foreign(did) => Some(did),
|
|
|
|
_ => None,
|
|
|
|
};
|
2019-11-09 17:32:15 +01:00
|
|
|
|
2021-10-21 15:36:35 +02:00
|
|
|
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",
|
|
|
|
))
|
2014-09-12 10:54:08 -04:00
|
|
|
}
|
2018-09-26 17:32:23 +02:00
|
|
|
}
|
2021-10-21 15:36:35 +02:00
|
|
|
_ => 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",
|
|
|
|
)),
|
|
|
|
};
|
2015-03-04 15:23:17 -05:00
|
|
|
|
2021-10-21 15:36:35 +02:00
|
|
|
if let Some((msg, label)) = msg {
|
2022-01-22 18:49:12 -06:00
|
|
|
let reported =
|
|
|
|
struct_span_err!(tcx.sess, sp, E0321, "{}", msg).span_label(sp, label).emit();
|
|
|
|
return Err(reported);
|
2021-10-21 15:36:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-12-13 21:45:08 -04:00
|
|
|
fn emit_orphan_check_error<'tcx>(
|
2021-10-21 15:36:35 +02:00
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
sp: Span,
|
2022-07-24 17:45:21 +00:00
|
|
|
full_impl_span: Span,
|
2021-10-21 15:36:35 +02:00
|
|
|
trait_span: Span,
|
2023-01-01 18:08:02 -08:00
|
|
|
trait_ref: ty::TraitRef<'tcx>,
|
2021-10-21 15:36:35 +02:00
|
|
|
self_ty_span: Span,
|
|
|
|
generics: &hir::Generics<'tcx>,
|
|
|
|
err: traits::OrphanCheckErr<'tcx>,
|
2022-01-23 12:34:26 -06:00
|
|
|
) -> Result<!, ErrorGuaranteed> {
|
2023-01-01 18:08:02 -08:00
|
|
|
let self_ty = trait_ref.self_ty();
|
2022-01-27 09:44:25 +00:00
|
|
|
Err(match err {
|
2021-10-21 15:36:35 +02:00
|
|
|
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",
|
|
|
|
};
|
2021-10-21 15:36:35 +02:00
|
|
|
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}"
|
2018-09-26 17:32:23 +02:00
|
|
|
);
|
2021-10-21 15:36:35 +02:00
|
|
|
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);
|
2021-10-21 15:36:35 +02:00
|
|
|
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.
|
2022-03-05 07:28:41 +11:00
|
|
|
ty::Adt(def, _) => tcx.mk_adt(*def, ty::List::empty()),
|
2021-10-21 15:36:35 +02:00
|
|
|
_ => ty,
|
2018-09-26 17:32:23 +02:00
|
|
|
};
|
2022-12-21 17:44:30 +00:00
|
|
|
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"))
|
|
|
|
}
|
|
|
|
};
|
2022-12-21 17:44:30 +00:00
|
|
|
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()
|
|
|
|
}
|
2022-07-24 17:45:21 +00:00
|
|
|
ty::RawPtr(ptr_ty) => {
|
|
|
|
emit_newtype_suggestion_for_raw_ptr(
|
|
|
|
full_impl_span,
|
|
|
|
self_ty,
|
|
|
|
self_ty_span,
|
|
|
|
ptr_ty,
|
|
|
|
&mut err,
|
|
|
|
);
|
|
|
|
|
2022-12-21 17:44:30 +00:00
|
|
|
msg(&format!("`{ty}`"), " because raw pointers are always foreign")
|
2022-07-24 17:45:21 +00:00
|
|
|
}
|
2022-12-21 17:44:30 +00:00
|
|
|
_ => msg(&format!("`{ty}`"), ""),
|
2018-09-26 17:32:23 +02:00
|
|
|
};
|
2022-07-24 17:45:21 +00:00
|
|
|
|
2022-09-19 17:00:38 +02:00
|
|
|
if is_target_ty {
|
2021-10-21 15:36:35 +02:00
|
|
|
// 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;
|
2015-03-04 15:23:17 -05:00
|
|
|
}
|
2014-09-12 10:54:08 -04:00
|
|
|
}
|
2020-09-19 16:25:50 -04:00
|
|
|
|
2021-10-21 15:36:35 +02:00
|
|
|
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(),
|
2020-09-19 16:25:50 -04:00
|
|
|
}
|
2014-09-12 10:54:08 -04:00
|
|
|
}
|
2022-01-27 09:44:25 +00:00
|
|
|
})
|
2014-09-12 10:54:08 -04:00
|
|
|
}
|
2022-01-27 10:49:52 +01:00
|
|
|
|
2022-07-24 17:45:21 +00:00
|
|
|
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();
|
2022-07-24 17:45:21 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-27 10:49:52 +01:00
|
|
|
/// 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;
|
|
|
|
}
|
2022-01-27 10:49:52 +01:00
|
|
|
|
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.
|
2022-01-27 10:49:52 +01:00
|
|
|
return;
|
|
|
|
}
|
2021-05-09 20:53:13 +02:00
|
|
|
};
|
2022-01-27 10:49:52 +01: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;
|
|
|
|
};
|
2022-01-27 10:49:52 +01:00
|
|
|
|
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;
|
2022-01-27 10:49:52 +01:00
|
|
|
}
|
|
|
|
|
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 `{}`, \
|
2022-01-27 10:49:52 +01:00
|
|
|
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));
|
2022-01-27 10:49:52 +01:00
|
|
|
}
|
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
|
|
|
},
|
|
|
|
);
|
2022-01-27 10:49:52 +01: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),
|
2022-01-27 10:49:52 +01:00
|
|
|
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 :)
|
2022-03-05 07:28:41 +11:00
|
|
|
if self.seen.insert(def.did()) {
|
2022-01-27 10:49:52 +01:00
|
|
|
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() {
|
2022-03-05 07:28:41 +11:00
|
|
|
ty::Adt(def, _) => tcx.mk_adt(*def, InternalSubsts::identity_for_item(tcx, def.did())),
|
2022-01-27 10:49:52 +01:00
|
|
|
_ => 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()
|
|
|
|
}
|