1
Fork 0

rustc_typeck to rustc_hir_analysis

This commit is contained in:
lcnr 2022-09-26 13:00:29 +02:00
parent de0b511daa
commit 1fc86a63f4
140 changed files with 101 additions and 102 deletions

View file

@ -0,0 +1,588 @@
//! Check properties that are required by built-in traits and set
//! up data structures required by type-checking/codegen.
use crate::errors::{CopyImplOnNonAdt, CopyImplOnTypeWithDtor, DropImplOnWrongItem};
use rustc_errors::{struct_span_err, MultiSpan};
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::lang_items::LangItem;
use rustc_hir::ItemKind;
use rustc_infer::infer;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::ty::adjustment::CoerceUnsizedInfo;
use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeVisitable};
use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
use rustc_trait_selection::traits::misc::{can_type_implement_copy, CopyImplementationError};
use rustc_trait_selection::traits::predicate_for_trait_def;
use rustc_trait_selection::traits::{self, ObligationCause};
use std::collections::BTreeMap;
pub fn check_trait(tcx: TyCtxt<'_>, trait_def_id: DefId) {
let lang_items = tcx.lang_items();
Checker { tcx, trait_def_id }
.check(lang_items.drop_trait(), visit_implementation_of_drop)
.check(lang_items.copy_trait(), visit_implementation_of_copy)
.check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized)
.check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn);
}
struct Checker<'tcx> {
tcx: TyCtxt<'tcx>,
trait_def_id: DefId,
}
impl<'tcx> Checker<'tcx> {
fn check<F>(&self, trait_def_id: Option<DefId>, mut f: F) -> &Self
where
F: FnMut(TyCtxt<'tcx>, LocalDefId),
{
if Some(self.trait_def_id) == trait_def_id {
for &impl_def_id in self.tcx.hir().trait_impls(self.trait_def_id) {
f(self.tcx, impl_def_id);
}
}
self
}
}
fn visit_implementation_of_drop(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
// Destructors only work on local ADT types.
match tcx.type_of(impl_did).kind() {
ty::Adt(def, _) if def.did().is_local() => return,
ty::Error(_) => return,
_ => {}
}
let sp = match tcx.hir().expect_item(impl_did).kind {
ItemKind::Impl(ref impl_) => impl_.self_ty.span,
_ => bug!("expected Drop impl item"),
};
tcx.sess.emit_err(DropImplOnWrongItem { span: sp });
}
fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
debug!("visit_implementation_of_copy: impl_did={:?}", impl_did);
let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did);
let self_type = tcx.type_of(impl_did);
debug!("visit_implementation_of_copy: self_type={:?} (bound)", self_type);
let param_env = tcx.param_env(impl_did);
assert!(!self_type.has_escaping_bound_vars());
debug!("visit_implementation_of_copy: self_type={:?} (free)", self_type);
let span = match tcx.hir().expect_item(impl_did).kind {
ItemKind::Impl(hir::Impl { polarity: hir::ImplPolarity::Negative(_), .. }) => return,
ItemKind::Impl(impl_) => impl_.self_ty.span,
_ => bug!("expected Copy impl item"),
};
let cause = traits::ObligationCause::misc(span, impl_hir_id);
match can_type_implement_copy(tcx, param_env, self_type, cause) {
Ok(()) => {}
Err(CopyImplementationError::InfrigingFields(fields)) => {
let mut err = struct_span_err!(
tcx.sess,
span,
E0204,
"the trait `Copy` may not be implemented for this type"
);
// We'll try to suggest constraining type parameters to fulfill the requirements of
// their `Copy` implementation.
let mut errors: BTreeMap<_, Vec<_>> = Default::default();
let mut bounds = vec![];
for (field, ty) in fields {
let field_span = tcx.def_span(field.did);
let field_ty_span = match tcx.hir().get_if_local(field.did) {
Some(hir::Node::Field(field_def)) => field_def.ty.span,
_ => field_span,
};
err.span_label(field_span, "this field does not implement `Copy`");
// Spin up a new FulfillmentContext, so we can get the _precise_ reason
// why this field does not implement Copy. This is useful because sometimes
// it is not immediately clear why Copy is not implemented for a field, since
// all we point at is the field itself.
tcx.infer_ctxt().ignoring_regions().enter(|infcx| {
for error in traits::fully_solve_bound(
&infcx,
traits::ObligationCause::dummy_with_span(field_ty_span),
param_env,
ty,
tcx.lang_items().copy_trait().unwrap(),
) {
let error_predicate = error.obligation.predicate;
// Only note if it's not the root obligation, otherwise it's trivial and
// should be self-explanatory (i.e. a field literally doesn't implement Copy).
// FIXME: This error could be more descriptive, especially if the error_predicate
// contains a foreign type or if it's a deeply nested type...
if error_predicate != error.root_obligation.predicate {
errors
.entry((ty.to_string(), error_predicate.to_string()))
.or_default()
.push(error.obligation.cause.span);
}
if let ty::PredicateKind::Trait(ty::TraitPredicate {
trait_ref,
polarity: ty::ImplPolarity::Positive,
..
}) = error_predicate.kind().skip_binder()
{
let ty = trait_ref.self_ty();
if let ty::Param(_) = ty.kind() {
bounds.push((
format!("{ty}"),
trait_ref.print_only_trait_path().to_string(),
Some(trait_ref.def_id),
));
}
}
}
});
}
for ((ty, error_predicate), spans) in errors {
let span: MultiSpan = spans.into();
err.span_note(
span,
&format!("the `Copy` impl for `{}` requires that `{}`", ty, error_predicate),
);
}
suggest_constraining_type_params(
tcx,
tcx.hir().get_generics(impl_did).expect("impls always have generics"),
&mut err,
bounds.iter().map(|(param, constraint, def_id)| {
(param.as_str(), constraint.as_str(), *def_id)
}),
);
err.emit();
}
Err(CopyImplementationError::NotAnAdt) => {
tcx.sess.emit_err(CopyImplOnNonAdt { span });
}
Err(CopyImplementationError::HasDestructor) => {
tcx.sess.emit_err(CopyImplOnTypeWithDtor { span });
}
}
}
fn visit_implementation_of_coerce_unsized<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) {
debug!("visit_implementation_of_coerce_unsized: impl_did={:?}", impl_did);
// Just compute this for the side-effects, in particular reporting
// errors; other parts of the code may demand it for the info of
// course.
let span = tcx.def_span(impl_did);
tcx.at(span).coerce_unsized_info(impl_did);
}
fn visit_implementation_of_dispatch_from_dyn<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) {
debug!("visit_implementation_of_dispatch_from_dyn: impl_did={:?}", impl_did);
let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did);
let span = tcx.hir().span(impl_hir_id);
let dispatch_from_dyn_trait = tcx.require_lang_item(LangItem::DispatchFromDyn, Some(span));
let source = tcx.type_of(impl_did);
assert!(!source.has_escaping_bound_vars());
let target = {
let trait_ref = tcx.impl_trait_ref(impl_did).unwrap();
assert_eq!(trait_ref.def_id, dispatch_from_dyn_trait);
trait_ref.substs.type_at(1)
};
debug!("visit_implementation_of_dispatch_from_dyn: {:?} -> {:?}", source, target);
let param_env = tcx.param_env(impl_did);
let create_err = |msg: &str| struct_span_err!(tcx.sess, span, E0378, "{}", msg);
tcx.infer_ctxt().enter(|infcx| {
let cause = ObligationCause::misc(span, impl_hir_id);
use rustc_type_ir::sty::TyKind::*;
match (source.kind(), target.kind()) {
(&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b))
if infcx.at(&cause, param_env).eq(r_a, *r_b).is_ok() && mutbl_a == *mutbl_b => {}
(&RawPtr(tm_a), &RawPtr(tm_b)) if tm_a.mutbl == tm_b.mutbl => (),
(&Adt(def_a, substs_a), &Adt(def_b, substs_b))
if def_a.is_struct() && def_b.is_struct() =>
{
if def_a != def_b {
let source_path = tcx.def_path_str(def_a.did());
let target_path = tcx.def_path_str(def_b.did());
create_err(&format!(
"the trait `DispatchFromDyn` may only be implemented \
for a coercion between structures with the same \
definition; expected `{}`, found `{}`",
source_path, target_path,
))
.emit();
return;
}
if def_a.repr().c() || def_a.repr().packed() {
create_err(
"structs implementing `DispatchFromDyn` may not have \
`#[repr(packed)]` or `#[repr(C)]`",
)
.emit();
}
let fields = &def_a.non_enum_variant().fields;
let coerced_fields = fields
.iter()
.filter(|field| {
let ty_a = field.ty(tcx, substs_a);
let ty_b = field.ty(tcx, substs_b);
if let Ok(layout) = tcx.layout_of(param_env.and(ty_a)) {
if layout.is_zst() && layout.align.abi.bytes() == 1 {
// ignore ZST fields with alignment of 1 byte
return false;
}
}
if let Ok(ok) = infcx.at(&cause, param_env).eq(ty_a, ty_b) {
if ok.obligations.is_empty() {
create_err(
"the trait `DispatchFromDyn` may only be implemented \
for structs containing the field being coerced, \
ZST fields with 1 byte alignment, and nothing else",
)
.note(&format!(
"extra field `{}` of type `{}` is not allowed",
field.name, ty_a,
))
.emit();
return false;
}
}
return true;
})
.collect::<Vec<_>>();
if coerced_fields.is_empty() {
create_err(
"the trait `DispatchFromDyn` may only be implemented \
for a coercion between structures with a single field \
being coerced, none found",
)
.emit();
} else if coerced_fields.len() > 1 {
create_err(
"implementing the `DispatchFromDyn` trait requires multiple coercions",
)
.note(
"the trait `DispatchFromDyn` may only be implemented \
for a coercion between structures with a single field \
being coerced",
)
.note(&format!(
"currently, {} fields need coercions: {}",
coerced_fields.len(),
coerced_fields
.iter()
.map(|field| {
format!(
"`{}` (`{}` to `{}`)",
field.name,
field.ty(tcx, substs_a),
field.ty(tcx, substs_b),
)
})
.collect::<Vec<_>>()
.join(", ")
))
.emit();
} else {
let errors = traits::fully_solve_obligations(
&infcx,
coerced_fields.into_iter().map(|field| {
predicate_for_trait_def(
tcx,
param_env,
cause.clone(),
dispatch_from_dyn_trait,
0,
field.ty(tcx, substs_a),
&[field.ty(tcx, substs_b).into()],
)
}),
);
if !errors.is_empty() {
infcx.report_fulfillment_errors(&errors, None, false);
}
// Finally, resolve all regions.
let outlives_env = OutlivesEnvironment::new(param_env);
infcx.check_region_obligations_and_report_errors(impl_did, &outlives_env);
}
}
_ => {
create_err(
"the trait `DispatchFromDyn` may only be implemented \
for a coercion between structures",
)
.emit();
}
}
})
}
pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedInfo {
debug!("compute_coerce_unsized_info(impl_did={:?})", impl_did);
// this provider should only get invoked for local def-ids
let impl_did = impl_did.expect_local();
let span = tcx.def_span(impl_did);
let coerce_unsized_trait = tcx.require_lang_item(LangItem::CoerceUnsized, Some(span));
let unsize_trait = tcx.lang_items().require(LangItem::Unsize).unwrap_or_else(|err| {
tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err.to_string()));
});
let source = tcx.type_of(impl_did);
let trait_ref = tcx.impl_trait_ref(impl_did).unwrap();
assert_eq!(trait_ref.def_id, coerce_unsized_trait);
let target = trait_ref.substs.type_at(1);
debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)", source, target);
let param_env = tcx.param_env(impl_did);
assert!(!source.has_escaping_bound_vars());
let err_info = CoerceUnsizedInfo { custom_kind: None };
debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)", source, target);
tcx.infer_ctxt().enter(|infcx| {
let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did);
let cause = ObligationCause::misc(span, impl_hir_id);
let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>,
mt_b: ty::TypeAndMut<'tcx>,
mk_ptr: &dyn Fn(Ty<'tcx>) -> Ty<'tcx>| {
if (mt_a.mutbl, mt_b.mutbl) == (hir::Mutability::Not, hir::Mutability::Mut) {
infcx
.report_mismatched_types(
&cause,
mk_ptr(mt_b.ty),
target,
ty::error::TypeError::Mutability,
)
.emit();
}
(mt_a.ty, mt_b.ty, unsize_trait, None)
};
let (source, target, trait_def_id, kind) = match (source.kind(), target.kind()) {
(&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => {
infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a);
let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };
let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b };
check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ref(r_b, ty))
}
(&ty::Ref(_, ty_a, mutbl_a), &ty::RawPtr(mt_b)) => {
let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };
check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty))
}
(&ty::RawPtr(mt_a), &ty::RawPtr(mt_b)) => {
check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty))
}
(&ty::Adt(def_a, substs_a), &ty::Adt(def_b, substs_b))
if def_a.is_struct() && def_b.is_struct() =>
{
if def_a != def_b {
let source_path = tcx.def_path_str(def_a.did());
let target_path = tcx.def_path_str(def_b.did());
struct_span_err!(
tcx.sess,
span,
E0377,
"the trait `CoerceUnsized` may only be implemented \
for a coercion between structures with the same \
definition; expected `{}`, found `{}`",
source_path,
target_path
)
.emit();
return err_info;
}
// Here we are considering a case of converting
// `S<P0...Pn>` to S<Q0...Qn>`. As an example, let's imagine a struct `Foo<T, U>`,
// which acts like a pointer to `U`, but carries along some extra data of type `T`:
//
// struct Foo<T, U> {
// extra: T,
// ptr: *mut U,
// }
//
// We might have an impl that allows (e.g.) `Foo<T, [i32; 3]>` to be unsized
// to `Foo<T, [i32]>`. That impl would look like:
//
// impl<T, U: Unsize<V>, V> CoerceUnsized<Foo<T, V>> for Foo<T, U> {}
//
// Here `U = [i32; 3]` and `V = [i32]`. At runtime,
// when this coercion occurs, we would be changing the
// field `ptr` from a thin pointer of type `*mut [i32;
// 3]` to a fat pointer of type `*mut [i32]` (with
// extra data `3`). **The purpose of this check is to
// make sure that we know how to do this conversion.**
//
// To check if this impl is legal, we would walk down
// the fields of `Foo` and consider their types with
// both substitutes. We are looking to find that
// exactly one (non-phantom) field has changed its
// type, which we will expect to be the pointer that
// is becoming fat (we could probably generalize this
// to multiple thin pointers of the same type becoming
// fat, but we don't). In this case:
//
// - `extra` has type `T` before and type `T` after
// - `ptr` has type `*mut U` before and type `*mut V` after
//
// Since just one field changed, we would then check
// that `*mut U: CoerceUnsized<*mut V>` is implemented
// (in other words, that we know how to do this
// conversion). This will work out because `U:
// Unsize<V>`, and we have a builtin rule that `*mut
// U` can be coerced to `*mut V` if `U: Unsize<V>`.
let fields = &def_a.non_enum_variant().fields;
let diff_fields = fields
.iter()
.enumerate()
.filter_map(|(i, f)| {
let (a, b) = (f.ty(tcx, substs_a), f.ty(tcx, substs_b));
if tcx.type_of(f.did).is_phantom_data() {
// Ignore PhantomData fields
return None;
}
// Ignore fields that aren't changed; it may
// be that we could get away with subtyping or
// something more accepting, but we use
// equality because we want to be able to
// perform this check without computing
// variance where possible. (This is because
// we may have to evaluate constraint
// expressions in the course of execution.)
// See e.g., #41936.
if let Ok(ok) = infcx.at(&cause, param_env).eq(a, b) {
if ok.obligations.is_empty() {
return None;
}
}
// Collect up all fields that were significantly changed
// i.e., those that contain T in coerce_unsized T -> U
Some((i, a, b))
})
.collect::<Vec<_>>();
if diff_fields.is_empty() {
struct_span_err!(
tcx.sess,
span,
E0374,
"the trait `CoerceUnsized` may only be implemented \
for a coercion between structures with one field \
being coerced, none found"
)
.emit();
return err_info;
} else if diff_fields.len() > 1 {
let item = tcx.hir().expect_item(impl_did);
let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(ref t), .. }) =
item.kind
{
t.path.span
} else {
tcx.def_span(impl_did)
};
struct_span_err!(
tcx.sess,
span,
E0375,
"implementing the trait \
`CoerceUnsized` requires multiple \
coercions"
)
.note(
"`CoerceUnsized` may only be implemented for \
a coercion between structures with one field being coerced",
)
.note(&format!(
"currently, {} fields need coercions: {}",
diff_fields.len(),
diff_fields
.iter()
.map(|&(i, a, b)| {
format!("`{}` (`{}` to `{}`)", fields[i].name, a, b)
})
.collect::<Vec<_>>()
.join(", ")
))
.span_label(span, "requires multiple coercions")
.emit();
return err_info;
}
let (i, a, b) = diff_fields[0];
let kind = ty::adjustment::CustomCoerceUnsized::Struct(i);
(a, b, coerce_unsized_trait, Some(kind))
}
_ => {
struct_span_err!(
tcx.sess,
span,
E0376,
"the trait `CoerceUnsized` may only be implemented \
for a coercion between structures"
)
.emit();
return err_info;
}
};
// Register an obligation for `A: Trait<B>`.
let cause = traits::ObligationCause::misc(span, impl_hir_id);
let predicate = predicate_for_trait_def(
tcx,
param_env,
cause,
trait_def_id,
0,
source,
&[target.into()],
);
let errors = traits::fully_solve_obligation(&infcx, predicate);
if !errors.is_empty() {
infcx.report_fulfillment_errors(&errors, None, false);
}
// Finally, resolve all regions.
let outlives_env = OutlivesEnvironment::new(param_env);
infcx.check_region_obligations_and_report_errors(impl_did, &outlives_env);
CoerceUnsizedInfo { custom_kind: kind }
})
}

View file

@ -0,0 +1,251 @@
//! The code in this module gathers up all of the inherent impls in
//! the current crate and organizes them in a map. It winds up
//! touching the whole crate and thus must be recomputed completely
//! for any change, but it is very cheap to compute. In practice, most
//! code in the compiler never *directly* requests this map. Instead,
//! it requests the inherent impls specific to some type (via
//! `tcx.inherent_impls(def_id)`). That value, however,
//! is computed by selecting an idea from this table.
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId};
use rustc_middle::ty::fast_reject::{simplify_type, SimplifiedType, TreatParams};
use rustc_middle::ty::{self, CrateInherentImpls, Ty, TyCtxt};
use rustc_span::symbol::sym;
use rustc_span::Span;
/// On-demand query: yields a map containing all types mapped to their inherent impls.
pub fn crate_inherent_impls(tcx: TyCtxt<'_>, (): ()) -> CrateInherentImpls {
let mut collect = InherentCollect { tcx, impls_map: Default::default() };
for id in tcx.hir().items() {
collect.check_item(id);
}
collect.impls_map
}
pub fn crate_incoherent_impls(tcx: TyCtxt<'_>, (_, simp): (CrateNum, SimplifiedType)) -> &[DefId] {
let crate_map = tcx.crate_inherent_impls(());
tcx.arena.alloc_from_iter(
crate_map.incoherent_impls.get(&simp).unwrap_or(&Vec::new()).iter().map(|d| d.to_def_id()),
)
}
/// On-demand query: yields a vector of the inherent impls for a specific type.
pub fn inherent_impls(tcx: TyCtxt<'_>, ty_def_id: DefId) -> &[DefId] {
let ty_def_id = ty_def_id.expect_local();
let crate_map = tcx.crate_inherent_impls(());
match crate_map.inherent_impls.get(&ty_def_id) {
Some(v) => &v[..],
None => &[],
}
}
struct InherentCollect<'tcx> {
tcx: TyCtxt<'tcx>,
impls_map: CrateInherentImpls,
}
const INTO_CORE: &str = "consider moving this inherent impl into `core` if possible";
const INTO_DEFINING_CRATE: &str =
"consider moving this inherent impl into the crate defining the type if possible";
const ADD_ATTR_TO_TY: &str = "alternatively add `#[rustc_has_incoherent_inherent_impls]` to the type \
and `#[rustc_allow_incoherent_impl]` to the relevant impl items";
const ADD_ATTR: &str =
"alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items";
impl<'tcx> InherentCollect<'tcx> {
fn check_def_id(&mut self, item: &hir::Item<'_>, self_ty: Ty<'tcx>, def_id: DefId) {
let impl_def_id = item.def_id;
if let Some(def_id) = def_id.as_local() {
// Add the implementation to the mapping from implementation to base
// type def ID, if there is a base type for this implementation and
// the implementation does not have any associated traits.
let vec = self.impls_map.inherent_impls.entry(def_id).or_default();
vec.push(impl_def_id.to_def_id());
return;
}
if self.tcx.features().rustc_attrs {
let hir::ItemKind::Impl(&hir::Impl { items, .. }) = item.kind else {
bug!("expected `impl` item: {:?}", item);
};
if !self.tcx.has_attr(def_id, sym::rustc_has_incoherent_inherent_impls) {
struct_span_err!(
self.tcx.sess,
item.span,
E0390,
"cannot define inherent `impl` for a type outside of the crate where the type is defined",
)
.help(INTO_DEFINING_CRATE)
.span_help(item.span, ADD_ATTR_TO_TY)
.emit();
return;
}
for impl_item in items {
if !self
.tcx
.has_attr(impl_item.id.def_id.to_def_id(), sym::rustc_allow_incoherent_impl)
{
struct_span_err!(
self.tcx.sess,
item.span,
E0390,
"cannot define inherent `impl` for a type outside of the crate where the type is defined",
)
.help(INTO_DEFINING_CRATE)
.span_help(impl_item.span, ADD_ATTR)
.emit();
return;
}
}
if let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsInfer) {
self.impls_map.incoherent_impls.entry(simp).or_default().push(impl_def_id.def_id);
} else {
bug!("unexpected self type: {:?}", self_ty);
}
} else {
struct_span_err!(
self.tcx.sess,
item.span,
E0116,
"cannot define inherent `impl` for a type outside of the crate \
where the type is defined"
)
.span_label(item.span, "impl for type defined outside of crate.")
.note("define and implement a trait or new type instead")
.emit();
}
}
fn check_primitive_impl(
&mut self,
impl_def_id: LocalDefId,
ty: Ty<'tcx>,
items: &[hir::ImplItemRef],
span: Span,
) {
if !self.tcx.hir().rustc_coherence_is_core() {
if self.tcx.features().rustc_attrs {
for item in items {
if !self
.tcx
.has_attr(item.id.def_id.to_def_id(), sym::rustc_allow_incoherent_impl)
{
struct_span_err!(
self.tcx.sess,
span,
E0390,
"cannot define inherent `impl` for primitive types outside of `core`",
)
.help(INTO_CORE)
.span_help(item.span, ADD_ATTR)
.emit();
return;
}
}
} else {
let mut err = struct_span_err!(
self.tcx.sess,
span,
E0390,
"cannot define inherent `impl` for primitive types",
);
err.help("consider using an extension trait instead");
if let ty::Ref(_, subty, _) = ty.kind() {
err.note(&format!(
"you could also try moving the reference to \
uses of `{}` (such as `self`) within the implementation",
subty
));
}
err.emit();
return;
}
}
if let Some(simp) = simplify_type(self.tcx, ty, TreatParams::AsInfer) {
self.impls_map.incoherent_impls.entry(simp).or_default().push(impl_def_id);
} else {
bug!("unexpected primitive type: {:?}", ty);
}
}
fn check_item(&mut self, id: hir::ItemId) {
if !matches!(self.tcx.def_kind(id.def_id), DefKind::Impl) {
return;
}
let item = self.tcx.hir().item(id);
let hir::ItemKind::Impl(hir::Impl { of_trait: None, self_ty: ty, ref items, .. }) = item.kind else {
return;
};
let self_ty = self.tcx.type_of(item.def_id);
match *self_ty.kind() {
ty::Adt(def, _) => {
self.check_def_id(item, self_ty, def.did());
}
ty::Foreign(did) => {
self.check_def_id(item, self_ty, did);
}
ty::Dynamic(data, ..) if data.principal_def_id().is_some() => {
self.check_def_id(item, self_ty, data.principal_def_id().unwrap());
}
ty::Dynamic(..) => {
struct_span_err!(
self.tcx.sess,
ty.span,
E0785,
"cannot define inherent `impl` for a dyn auto trait"
)
.span_label(ty.span, "impl requires at least one non-auto trait")
.note("define and implement a new trait or type instead")
.emit();
}
ty::Bool
| ty::Char
| ty::Int(_)
| ty::Uint(_)
| ty::Float(_)
| ty::Str
| ty::Array(..)
| ty::Slice(_)
| ty::RawPtr(_)
| ty::Ref(..)
| ty::Never
| ty::FnPtr(_)
| ty::Tuple(..) => {
self.check_primitive_impl(item.def_id.def_id, self_ty, items, ty.span)
}
ty::Projection(..) | ty::Opaque(..) | ty::Param(_) => {
let mut err = struct_span_err!(
self.tcx.sess,
ty.span,
E0118,
"no nominal type found for inherent implementation"
);
err.span_label(ty.span, "impl requires a nominal type")
.note("either implement a trait on it or create a newtype to wrap it instead");
err.emit();
}
ty::FnDef(..)
| ty::Closure(..)
| ty::Generator(..)
| ty::GeneratorWitness(..)
| ty::Bound(..)
| ty::Placeholder(_)
| ty::Infer(_) => {
bug!("unexpected impl self type of impl: {:?} {:?}", item.def_id, self_ty);
}
ty::Error(_) => {}
}
}
}

View file

@ -0,0 +1,307 @@
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
use rustc_index::vec::IndexVec;
use rustc_middle::traits::specialization_graph::OverlapMode;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::Symbol;
use rustc_trait_selection::traits::{self, SkipLeakCheck};
use smallvec::SmallVec;
use std::collections::hash_map::Entry;
pub fn crate_inherent_impls_overlap_check(tcx: TyCtxt<'_>, (): ()) {
let mut inherent_overlap_checker = InherentOverlapChecker { tcx };
for id in tcx.hir().items() {
inherent_overlap_checker.check_item(id);
}
}
struct InherentOverlapChecker<'tcx> {
tcx: TyCtxt<'tcx>,
}
impl<'tcx> InherentOverlapChecker<'tcx> {
/// Checks whether any associated items in impls 1 and 2 share the same identifier and
/// namespace.
fn impls_have_common_items(
&self,
impl_items1: &ty::AssocItems<'_>,
impl_items2: &ty::AssocItems<'_>,
) -> bool {
let mut impl_items1 = &impl_items1;
let mut impl_items2 = &impl_items2;
// Performance optimization: iterate over the smaller list
if impl_items1.len() > impl_items2.len() {
std::mem::swap(&mut impl_items1, &mut impl_items2);
}
for item1 in impl_items1.in_definition_order() {
let collision = impl_items2
.filter_by_name_unhygienic(item1.name)
.any(|item2| self.compare_hygienically(item1, item2));
if collision {
return true;
}
}
false
}
fn compare_hygienically(&self, item1: &ty::AssocItem, item2: &ty::AssocItem) -> bool {
// Symbols and namespace match, compare hygienically.
item1.kind.namespace() == item2.kind.namespace()
&& item1.ident(self.tcx).normalize_to_macros_2_0()
== item2.ident(self.tcx).normalize_to_macros_2_0()
}
fn check_for_common_items_in_impls(
&self,
impl1: DefId,
impl2: DefId,
overlap: traits::OverlapResult<'_>,
) {
let impl_items1 = self.tcx.associated_items(impl1);
let impl_items2 = self.tcx.associated_items(impl2);
for item1 in impl_items1.in_definition_order() {
let collision = impl_items2
.filter_by_name_unhygienic(item1.name)
.find(|item2| self.compare_hygienically(item1, item2));
if let Some(item2) = collision {
let name = item1.ident(self.tcx).normalize_to_macros_2_0();
let mut err = struct_span_err!(
self.tcx.sess,
self.tcx.def_span(item1.def_id),
E0592,
"duplicate definitions with name `{}`",
name
);
err.span_label(
self.tcx.def_span(item1.def_id),
format!("duplicate definitions for `{}`", name),
);
err.span_label(
self.tcx.def_span(item2.def_id),
format!("other definition for `{}`", name),
);
for cause in &overlap.intercrate_ambiguity_causes {
cause.add_intercrate_ambiguity_hint(&mut err);
}
if overlap.involves_placeholder {
traits::add_placeholder_note(&mut err);
}
err.emit();
}
}
}
fn check_for_overlapping_inherent_impls(
&self,
overlap_mode: OverlapMode,
impl1_def_id: DefId,
impl2_def_id: DefId,
) {
traits::overlapping_impls(
self.tcx,
impl1_def_id,
impl2_def_id,
// We go ahead and just skip the leak check for
// inherent impls without warning.
SkipLeakCheck::Yes,
overlap_mode,
|overlap| {
self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap);
false
},
|| true,
);
}
fn check_item(&mut self, id: hir::ItemId) {
let def_kind = self.tcx.def_kind(id.def_id);
if !matches!(def_kind, DefKind::Enum | DefKind::Struct | DefKind::Trait | DefKind::Union) {
return;
}
let impls = self.tcx.inherent_impls(id.def_id);
// If there is only one inherent impl block,
// there is nothing to overlap check it with
if impls.len() <= 1 {
return;
}
let overlap_mode = OverlapMode::get(self.tcx, id.def_id.to_def_id());
let impls_items = impls
.iter()
.map(|impl_def_id| (impl_def_id, self.tcx.associated_items(*impl_def_id)))
.collect::<SmallVec<[_; 8]>>();
// Perform a O(n^2) algorithm for small n,
// otherwise switch to an allocating algorithm with
// faster asymptotic runtime.
const ALLOCATING_ALGO_THRESHOLD: usize = 500;
if impls.len() < ALLOCATING_ALGO_THRESHOLD {
for (i, &(&impl1_def_id, impl_items1)) in impls_items.iter().enumerate() {
for &(&impl2_def_id, impl_items2) in &impls_items[(i + 1)..] {
if self.impls_have_common_items(impl_items1, impl_items2) {
self.check_for_overlapping_inherent_impls(
overlap_mode,
impl1_def_id,
impl2_def_id,
);
}
}
}
} else {
// Build a set of connected regions of impl blocks.
// Two impl blocks are regarded as connected if they share
// an item with the same unhygienic identifier.
// After we have assembled the connected regions,
// run the O(n^2) algorithm on each connected region.
// This is advantageous to running the algorithm over the
// entire graph when there are many connected regions.
rustc_index::newtype_index! {
pub struct RegionId {
ENCODABLE = custom
}
}
struct ConnectedRegion {
idents: SmallVec<[Symbol; 8]>,
impl_blocks: FxHashSet<usize>,
}
let mut connected_regions: IndexVec<RegionId, _> = Default::default();
// Reverse map from the Symbol to the connected region id.
let mut connected_region_ids = FxHashMap::default();
for (i, &(&_impl_def_id, impl_items)) in impls_items.iter().enumerate() {
if impl_items.len() == 0 {
continue;
}
// First obtain a list of existing connected region ids
let mut idents_to_add = SmallVec::<[Symbol; 8]>::new();
let mut ids = impl_items
.in_definition_order()
.filter_map(|item| {
let entry = connected_region_ids.entry(item.name);
if let Entry::Occupied(e) = &entry {
Some(*e.get())
} else {
idents_to_add.push(item.name);
None
}
})
.collect::<SmallVec<[RegionId; 8]>>();
// Sort the id list so that the algorithm is deterministic
ids.sort_unstable();
ids.dedup();
let ids = ids;
match &ids[..] {
// Create a new connected region
[] => {
let id_to_set = connected_regions.next_index();
// Update the connected region ids
for ident in &idents_to_add {
connected_region_ids.insert(*ident, id_to_set);
}
connected_regions.insert(
id_to_set,
ConnectedRegion {
idents: idents_to_add,
impl_blocks: std::iter::once(i).collect(),
},
);
}
// Take the only id inside the list
&[id_to_set] => {
let region = connected_regions[id_to_set].as_mut().unwrap();
region.impl_blocks.insert(i);
region.idents.extend_from_slice(&idents_to_add);
// Update the connected region ids
for ident in &idents_to_add {
connected_region_ids.insert(*ident, id_to_set);
}
}
// We have multiple connected regions to merge.
// In the worst case this might add impl blocks
// one by one and can thus be O(n^2) in the size
// of the resulting final connected region, but
// this is no issue as the final step to check
// for overlaps runs in O(n^2) as well.
&[id_to_set, ..] => {
let mut region = connected_regions.remove(id_to_set).unwrap();
region.impl_blocks.insert(i);
region.idents.extend_from_slice(&idents_to_add);
// Update the connected region ids
for ident in &idents_to_add {
connected_region_ids.insert(*ident, id_to_set);
}
// Remove other regions from ids.
for &id in ids.iter() {
if id == id_to_set {
continue;
}
let r = connected_regions.remove(id).unwrap();
for ident in r.idents.iter() {
connected_region_ids.insert(*ident, id_to_set);
}
region.idents.extend_from_slice(&r.idents);
region.impl_blocks.extend(r.impl_blocks);
}
connected_regions.insert(id_to_set, region);
}
}
}
debug!(
"churning through {} components (sum={}, avg={}, var={}, max={})",
connected_regions.len(),
impls.len(),
impls.len() / connected_regions.len(),
{
let avg = impls.len() / connected_regions.len();
let s = connected_regions
.iter()
.flatten()
.map(|r| r.impl_blocks.len() as isize - avg as isize)
.map(|v| v.abs() as usize)
.sum::<usize>();
s / connected_regions.len()
},
connected_regions.iter().flatten().map(|r| r.impl_blocks.len()).max().unwrap()
);
// List of connected regions is built. Now, run the overlap check
// for each pair of impl blocks in the same connected region.
for region in connected_regions.into_iter().flatten() {
let mut impl_blocks =
region.impl_blocks.into_iter().collect::<SmallVec<[usize; 8]>>();
impl_blocks.sort_unstable();
for (i, &impl1_items_idx) in impl_blocks.iter().enumerate() {
let &(&impl1_def_id, impl_items1) = &impls_items[impl1_items_idx];
for &impl2_items_idx in impl_blocks[(i + 1)..].iter() {
let &(&impl2_def_id, impl_items2) = &impls_items[impl2_items_idx];
if self.impls_have_common_items(impl_items1, impl_items2) {
self.check_for_overlapping_inherent_impls(
overlap_mode,
impl1_def_id,
impl2_def_id,
);
}
}
}
}
}
}
}

View file

@ -0,0 +1,237 @@
// Coherence phase
//
// The job of the coherence phase of typechecking is to ensure that
// each trait has at most one implementation for each type. This is
// done by the orphan and overlap modules. Then we build up various
// mappings. That mapping code resides here.
use rustc_errors::struct_span_err;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
use rustc_trait_selection::traits;
mod builtin;
mod inherent_impls;
mod inherent_impls_overlap;
mod orphan;
mod unsafety;
fn check_impl(tcx: TyCtxt<'_>, impl_def_id: LocalDefId, trait_ref: ty::TraitRef<'_>) {
debug!(
"(checking implementation) adding impl for trait '{:?}', item '{}'",
trait_ref,
tcx.def_path_str(impl_def_id.to_def_id())
);
// Skip impls where one of the self type is an error type.
// This occurs with e.g., resolve failures (#30589).
if trait_ref.references_error() {
return;
}
enforce_trait_manually_implementable(tcx, impl_def_id, trait_ref.def_id);
enforce_empty_impls_for_marker_traits(tcx, impl_def_id, trait_ref.def_id);
}
fn enforce_trait_manually_implementable(
tcx: TyCtxt<'_>,
impl_def_id: LocalDefId,
trait_def_id: DefId,
) {
let did = Some(trait_def_id);
let li = tcx.lang_items();
let impl_header_span = tcx.def_span(impl_def_id);
// Disallow *all* explicit impls of `Pointee`, `DiscriminantKind`, `Sized` and `Unsize` for now.
if did == li.pointee_trait() {
struct_span_err!(
tcx.sess,
impl_header_span,
E0322,
"explicit impls for the `Pointee` trait are not permitted"
)
.span_label(impl_header_span, "impl of `Pointee` not allowed")
.emit();
return;
}
if did == li.discriminant_kind_trait() {
struct_span_err!(
tcx.sess,
impl_header_span,
E0322,
"explicit impls for the `DiscriminantKind` trait are not permitted"
)
.span_label(impl_header_span, "impl of `DiscriminantKind` not allowed")
.emit();
return;
}
if did == li.sized_trait() {
struct_span_err!(
tcx.sess,
impl_header_span,
E0322,
"explicit impls for the `Sized` trait are not permitted"
)
.span_label(impl_header_span, "impl of `Sized` not allowed")
.emit();
return;
}
if did == li.unsize_trait() {
struct_span_err!(
tcx.sess,
impl_header_span,
E0328,
"explicit impls for the `Unsize` trait are not permitted"
)
.span_label(impl_header_span, "impl of `Unsize` not allowed")
.emit();
return;
}
if tcx.features().unboxed_closures {
// the feature gate allows all Fn traits
return;
}
if let ty::trait_def::TraitSpecializationKind::AlwaysApplicable =
tcx.trait_def(trait_def_id).specialization_kind
{
if !tcx.features().specialization && !tcx.features().min_specialization {
tcx.sess
.struct_span_err(
impl_header_span,
"implementing `rustc_specialization_trait` traits is unstable",
)
.help("add `#![feature(min_specialization)]` to the crate attributes to enable")
.emit();
return;
}
}
}
/// We allow impls of marker traits to overlap, so they can't override impls
/// as that could make it ambiguous which associated item to use.
fn enforce_empty_impls_for_marker_traits(
tcx: TyCtxt<'_>,
impl_def_id: LocalDefId,
trait_def_id: DefId,
) {
if !tcx.trait_def(trait_def_id).is_marker {
return;
}
if tcx.associated_item_def_ids(trait_def_id).is_empty() {
return;
}
struct_span_err!(
tcx.sess,
tcx.def_span(impl_def_id),
E0715,
"impls for marker traits cannot contain items"
)
.emit();
}
pub fn provide(providers: &mut Providers) {
use self::builtin::coerce_unsized_info;
use self::inherent_impls::{crate_incoherent_impls, crate_inherent_impls, inherent_impls};
use self::inherent_impls_overlap::crate_inherent_impls_overlap_check;
use self::orphan::orphan_check_impl;
*providers = Providers {
coherent_trait,
crate_inherent_impls,
crate_incoherent_impls,
inherent_impls,
crate_inherent_impls_overlap_check,
coerce_unsized_info,
orphan_check_impl,
..*providers
};
}
fn coherent_trait(tcx: TyCtxt<'_>, def_id: DefId) {
// Trigger building the specialization graph for the trait. This will detect and report any
// overlap errors.
tcx.ensure().specialization_graph_of(def_id);
let impls = tcx.hir().trait_impls(def_id);
for &impl_def_id in impls {
let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
check_impl(tcx, impl_def_id, trait_ref);
check_object_overlap(tcx, impl_def_id, trait_ref);
tcx.sess.time("unsafety_checking", || unsafety::check_item(tcx, impl_def_id));
tcx.sess.time("orphan_checking", || tcx.ensure().orphan_check_impl(impl_def_id));
}
builtin::check_trait(tcx, def_id);
}
/// Checks whether an impl overlaps with the automatic `impl Trait for dyn Trait`.
fn check_object_overlap<'tcx>(
tcx: TyCtxt<'tcx>,
impl_def_id: LocalDefId,
trait_ref: ty::TraitRef<'tcx>,
) {
let trait_def_id = trait_ref.def_id;
if trait_ref.references_error() {
debug!("coherence: skipping impl {:?} with error {:?}", impl_def_id, trait_ref);
return;
}
// check for overlap with the automatic `impl Trait for dyn Trait`
if let ty::Dynamic(data, ..) = trait_ref.self_ty().kind() {
// This is something like impl Trait1 for Trait2. Illegal
// if Trait1 is a supertrait of Trait2 or Trait2 is not object safe.
let component_def_ids = data.iter().flat_map(|predicate| {
match predicate.skip_binder() {
ty::ExistentialPredicate::Trait(tr) => Some(tr.def_id),
ty::ExistentialPredicate::AutoTrait(def_id) => Some(def_id),
// An associated type projection necessarily comes with
// an additional `Trait` requirement.
ty::ExistentialPredicate::Projection(..) => None,
}
});
for component_def_id in component_def_ids {
if !tcx.is_object_safe(component_def_id) {
// Without the 'object_safe_for_dispatch' feature this is an error
// which will be reported by wfcheck. Ignore it here.
// This is tested by `coherence-impl-trait-for-trait-object-safe.rs`.
// With the feature enabled, the trait is not implemented automatically,
// so this is valid.
} else {
let mut supertrait_def_ids = traits::supertrait_def_ids(tcx, component_def_id);
if supertrait_def_ids.any(|d| d == trait_def_id) {
let span = tcx.def_span(impl_def_id);
struct_span_err!(
tcx.sess,
span,
E0371,
"the object type `{}` automatically implements the trait `{}`",
trait_ref.self_ty(),
tcx.def_path_str(trait_def_id)
)
.span_label(
span,
format!(
"`{}` automatically implements trait `{}`",
trait_ref.self_ty(),
tcx.def_path_str(trait_def_id)
),
)
.emit();
}
}
}
}
}

View file

@ -0,0 +1,502 @@
//! 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;
use rustc_errors::struct_span_err;
use rustc_errors::{Diagnostic, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::util::IgnoreRegions;
use rustc_middle::ty::{
self, ImplPolarity, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
};
use rustc_session::lint;
use rustc_span::def_id::{DefId, LocalDefId};
use rustc_span::Span;
use rustc_trait_selection::traits;
use std::ops::ControlFlow;
#[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();
if let Some(err) = trait_ref.error_reported() {
return Err(err);
}
let ret = do_orphan_check_impl(tcx, trait_ref, impl_def_id);
if tcx.trait_is_auto(trait_ref.def_id) {
lint_auto_trait_impl(tcx, trait_ref, impl_def_id);
}
ret
}
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();
// Ensure no opaque types are present in this impl header. See issues #76202 and #86411 for examples,
// and #84660 where it would otherwise allow unsoundness.
if trait_ref.has_opaque_types() {
trace!("{:#?}", item);
// First we find the opaque type in question.
for ty in trait_ref.substs {
for ty in ty.walk() {
let ty::subst::GenericArgKind::Type(ty) = ty.unpack() else { continue };
let ty::Opaque(def_id, _) = *ty.kind() else { continue };
trace!(?def_id);
// Then we search for mentions of the opaque type's type alias in the HIR
struct SpanFinder<'tcx> {
sp: Span,
def_id: DefId,
tcx: TyCtxt<'tcx>,
}
impl<'v, 'tcx> hir::intravisit::Visitor<'v> for SpanFinder<'tcx> {
#[instrument(level = "trace", skip(self, _id))]
fn visit_path(&mut self, path: &'v hir::Path<'v>, _id: hir::HirId) {
// You can't mention an opaque type directly, so we look for type aliases
if let hir::def::Res::Def(hir::def::DefKind::TyAlias, def_id) = path.res {
// And check if that type alias's type contains the opaque type we're looking for
for arg in self.tcx.type_of(def_id).walk() {
if let GenericArgKind::Type(ty) = arg.unpack() {
if let ty::Opaque(def_id, _) = *ty.kind() {
if def_id == self.def_id {
// Finally we update the span to the mention of the type alias
self.sp = path.span;
return;
}
}
}
}
}
hir::intravisit::walk_path(self, path)
}
}
let mut visitor = SpanFinder { sp, def_id, tcx };
hir::intravisit::walk_item(&mut visitor, item);
let reported = tcx
.sess
.struct_span_err(visitor.sp, "cannot implement trait on type alias impl trait")
.span_note(tcx.def_span(def_id), "type alias impl trait defined here")
.emit();
return Err(reported);
}
}
span_bug!(sp, "opaque type not found, but `has_opaque_types` is set")
}
match traits::orphan_check(tcx, item.def_id.to_def_id()) {
Ok(()) => {}
Err(err) => emit_orphan_check_error(
tcx,
sp,
item.span,
tr.path.span,
trait_ref.self_ty(),
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(())
}
fn emit_orphan_check_error<'tcx>(
tcx: TyCtxt<'tcx>,
sp: Span,
full_impl_span: Span,
trait_span: Span,
self_ty: Ty<'tcx>,
self_ty_span: Span,
generics: &hir::Generics<'tcx>,
err: traits::OrphanCheckErr<'tcx>,
) -> Result<!, ErrorGuaranteed> {
Err(match err {
traits::OrphanCheckErr::NonLocalInputType(tys) => {
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,
"only traits defined in the current crate {msg}"
);
err.span_label(sp, "impl doesn't use only types from inside the current crate");
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 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"),
ty::RawPtr(ptr_ty) => {
emit_newtype_suggestion_for_raw_ptr(
full_impl_span,
self_ty,
self_ty_span,
ptr_ty,
&mut err,
);
(format!("`{}`", ty), " because raw pointers 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(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() {
let mut_key = if ptr_ty.mutbl == rustc_middle::mir::Mutability::Mut { "mut " } else { "" };
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.
fn lint_auto_trait_impl<'tcx>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
impl_def_id: LocalDefId,
) {
if tcx.impl_polarity(impl_def_id) != ImplPolarity::Positive {
return;
}
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;
}
};
// 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;
};
// 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;
}
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),
|err| {
let item_span = tcx.def_span(self_type_did);
let self_descr = tcx.def_kind(self_type_did).descr(self_type_did);
let mut err = err.build(&format!(
"cross-crate traits with a default impl, like `{}`, \
should not be specialized",
tcx.def_path_str(trait_ref.def_id),
));
match arg {
ty::util::NotUniqueParam::DuplicateParam(arg) => {
err.note(&format!("`{}` is mentioned multiple times", arg));
}
ty::util::NotUniqueParam::NotParam(arg) => {
err.note(&format!("`{}` is not a generic parameter", arg));
}
}
err.span_note(
item_span,
&format!(
"try using the same sequence of generic parameters as the {} definition",
self_descr,
),
);
err.emit();
},
);
}
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() {
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()
}

View file

@ -0,0 +1,66 @@
//! Unsafety 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::def::DefKind;
use rustc_hir::Unsafety;
use rustc_middle::ty::TyCtxt;
use rustc_span::def_id::LocalDefId;
pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) {
debug_assert!(matches!(tcx.def_kind(def_id), DefKind::Impl));
let item = tcx.hir().expect_item(def_id);
let hir::ItemKind::Impl(ref impl_) = item.kind else { bug!() };
if let Some(trait_ref) = tcx.impl_trait_ref(item.def_id) {
let trait_def = tcx.trait_def(trait_ref.def_id);
let unsafe_attr =
impl_.generics.params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle");
match (trait_def.unsafety, unsafe_attr, impl_.unsafety, impl_.polarity) {
(Unsafety::Normal, None, Unsafety::Unsafe, hir::ImplPolarity::Positive) => {
struct_span_err!(
tcx.sess,
item.span,
E0199,
"implementing the trait `{}` is not unsafe",
trait_ref.print_only_trait_path()
)
.emit();
}
(Unsafety::Unsafe, _, Unsafety::Normal, hir::ImplPolarity::Positive) => {
struct_span_err!(
tcx.sess,
item.span,
E0200,
"the trait `{}` requires an `unsafe impl` declaration",
trait_ref.print_only_trait_path()
)
.emit();
}
(Unsafety::Normal, Some(attr_name), Unsafety::Normal, hir::ImplPolarity::Positive) => {
struct_span_err!(
tcx.sess,
item.span,
E0569,
"requires an `unsafe impl` declaration due to `#[{}]` attribute",
attr_name
)
.emit();
}
(_, _, Unsafety::Unsafe, hir::ImplPolarity::Negative(_)) => {
// Reported in AST validation
tcx.sess.delay_span_bug(item.span, "unsafe negative impl");
}
(_, _, Unsafety::Normal, hir::ImplPolarity::Negative(_))
| (Unsafety::Unsafe, _, Unsafety::Unsafe, hir::ImplPolarity::Positive)
| (Unsafety::Normal, Some(_), Unsafety::Unsafe, hir::ImplPolarity::Positive)
| (Unsafety::Normal, None, Unsafety::Normal, _) => {
// OK
}
}
}
}