//! Orphan checker: every impl either implements a trait defined in this //! crate or pertains to a type defined in this crate. use crate::errors; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_middle::ty::{self, AliasKind, TyCtxt, TypeVisitableExt}; use rustc_span::def_id::LocalDefId; use rustc_span::Span; use rustc_trait_selection::traits::{self, IsFirstInputType}; #[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().instantiate_identity(); trait_ref.error_reported()?; let trait_def_id = trait_ref.def_id; match traits::orphan_check(tcx, impl_def_id.to_def_id()) { Ok(()) => {} Err(err) => { let item = tcx.hir().expect_item(impl_def_id); let hir::ItemKind::Impl(impl_) = item.kind else { bug!("{:?} is not an impl: {:?}", impl_def_id, item); }; let tr = impl_.of_trait.as_ref().unwrap(); let sp = tcx.def_span(impl_def_id); emit_orphan_check_error( tcx, sp, item.span, tr.path.span, 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(..) { // one_foo::<(A,B)>(..) // } // fn one_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 !Foo 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) { let self_ty = trait_ref.self_ty(); // If the impl is in the same crate as the auto-trait, almost anything // goes. // // impl MyAuto for Rc {} // okay // impl !MyAuto for *const T {} // okay // impl MyAuto for T {} // okay // // But there is one important exception: implementing for a trait object // is not allowed. // // impl MyAuto for dyn Trait {} // NOT OKAY // impl MyAuto for T {} // NOT OKAY // // With this restriction, it's guaranteed that an auto-trait is // implemented for a trait object if and only if the auto-trait is one // of the trait object's trait bounds (or a supertrait of a bound). In // other words `dyn Trait + AutoTrait` always implements AutoTrait, // while `dyn Trait` never implements AutoTrait. // // This is necessary in order for autotrait bounds on methods of trait // objects to be sound. // // auto trait AutoTrait {} // // trait ObjectSafeTrait { // fn f(&self) where Self: AutoTrait; // } // // We can allow f to be called on `dyn ObjectSafeTrait + AutoTrait`. // // If we didn't deny `impl AutoTrait for dyn Trait`, it would be unsound // for the ObjectSafeTrait shown above to be object safe because someone // could take some type implementing ObjectSafeTrait but not AutoTrait, // unsize it to `dyn ObjectSafeTrait`, and call .f() which has no // concrete implementation (issue #50781). enum LocalImpl { Allow, Disallow { problematic_kind: &'static str }, } // If the auto-trait is from a dependency, it must only be getting // implemented for a nominal type, and specifically one local to the // current crate. // // impl Sync for MyStruct {} // okay // // impl Sync for Rc {} // NOT OKAY enum NonlocalImpl { Allow, DisallowBecauseNonlocal, DisallowOther, } // Exhaustive match considering that this logic is essential for // soundness. let (local_impl, nonlocal_impl) = match self_ty.kind() { // struct Struct; // impl AutoTrait for Struct {} ty::Adt(self_def, _) => ( LocalImpl::Allow, if self_def.did().is_local() { NonlocalImpl::Allow } else { NonlocalImpl::DisallowBecauseNonlocal }, ), // extern { type OpaqueType; } // impl AutoTrait for OpaqueType {} ty::Foreign(did) => ( LocalImpl::Allow, if did.is_local() { NonlocalImpl::Allow } else { NonlocalImpl::DisallowBecauseNonlocal }, ), // impl AutoTrait for dyn Trait {} ty::Dynamic(..) => ( LocalImpl::Disallow { problematic_kind: "trait object" }, NonlocalImpl::DisallowOther, ), // impl AutoTrait for T {} // impl AutoTrait for T {} ty::Param(..) => ( if self_ty.is_sized(tcx, tcx.param_env(impl_def_id)) { LocalImpl::Allow } else { LocalImpl::Disallow { problematic_kind: "generic type" } }, NonlocalImpl::DisallowOther, ), ty::Alias(kind, _) => { let problematic_kind = match kind { // trait Id { type This: ?Sized; } // impl Id for T { // type This = T; // } // impl AutoTrait for ::This {} AliasKind::Projection => "associated type", // type Foo = (impl Sized, bool) // impl AutoTrait for Foo {} AliasKind::Weak => "type alias", // type Opaque = impl Trait; // impl AutoTrait for Opaque {} AliasKind::Opaque => "opaque type", // ``` // struct S(T); // impl S { // type This = T; // } // impl AutoTrait for S::This {} // ``` // FIXME(inherent_associated_types): The example code above currently leads to a cycle AliasKind::Inherent => "associated type", }; (LocalImpl::Disallow { problematic_kind }, NonlocalImpl::DisallowOther) } ty::Pat(..) => ( LocalImpl::Disallow { problematic_kind: "pattern type" }, NonlocalImpl::DisallowOther, ), ty::Bool | ty::Char | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Str | ty::Array(..) | ty::Slice(..) | ty::RawPtr(..) | ty::Ref(..) | ty::FnDef(..) | ty::FnPtr(..) | ty::Never | ty::Tuple(..) => (LocalImpl::Allow, NonlocalImpl::DisallowOther), ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) | ty::CoroutineWitness(..) | ty::Bound(..) | ty::Placeholder(..) | ty::Infer(..) => { let sp = tcx.def_span(impl_def_id); span_bug!(sp, "weird self type for autotrait impl") } ty::Error(..) => (LocalImpl::Allow, NonlocalImpl::Allow), }; if trait_def_id.is_local() { match local_impl { LocalImpl::Allow => {} LocalImpl::Disallow { problematic_kind } => { return Err(tcx.dcx().emit_err(errors::TraitsWithDefaultImpl { span: tcx.def_span(impl_def_id), traits: tcx.def_path_str(trait_def_id), problematic_kind, self_ty, })); } } } else { match nonlocal_impl { NonlocalImpl::Allow => {} NonlocalImpl::DisallowBecauseNonlocal => { return Err(tcx.dcx().emit_err(errors::CrossCrateTraitsDefined { span: tcx.def_span(impl_def_id), traits: tcx.def_path_str(trait_def_id), })); } NonlocalImpl::DisallowOther => { return Err(tcx.dcx().emit_err(errors::CrossCrateTraits { span: tcx.def_span(impl_def_id), traits: tcx.def_path_str(trait_def_id), self_ty, })); } } } } Ok(()) } fn emit_orphan_check_error<'tcx>( tcx: TyCtxt<'tcx>, sp: Span, full_impl_span: Span, trait_span: Span, trait_ref: ty::TraitRef<'tcx>, self_ty_span: Span, generics: &hir::Generics<'tcx>, err: traits::OrphanCheckErr<'tcx>, ) -> Result { let self_ty = trait_ref.self_ty(); Err(match err { traits::OrphanCheckErr::NonLocalInputType(tys) => { let mut diag = tcx.dcx().create_err(match self_ty.kind() { ty::Adt(..) => errors::OnlyCurrentTraits::Outside { span: sp, note: () }, _ if self_ty.is_primitive() => { errors::OnlyCurrentTraits::Primitive { span: sp, note: () } } _ => errors::OnlyCurrentTraits::Arbitrary { span: sp, note: () }, }); for &(mut ty, is_target_ty) in &tys { let span = if matches!(is_target_ty, IsFirstInputType::Yes) { // Point at `D` in `impl for C in D` self_ty_span } else { // Point at `C` in `impl for C in D` trait_span }; ty = tcx.erase_regions(ty); let is_foreign = !trait_ref.def_id.is_local() && matches!(is_target_ty, IsFirstInputType::No); match *ty.kind() { ty::Slice(_) => { if is_foreign { diag.subdiagnostic( tcx.dcx(), errors::OnlyCurrentTraitsForeign { span }, ); } else { diag.subdiagnostic( tcx.dcx(), errors::OnlyCurrentTraitsName { span, name: "slices" }, ); } } ty::Array(..) => { if is_foreign { diag.subdiagnostic( tcx.dcx(), errors::OnlyCurrentTraitsForeign { span }, ); } else { diag.subdiagnostic( tcx.dcx(), errors::OnlyCurrentTraitsName { span, name: "arrays" }, ); } } ty::Tuple(..) => { if is_foreign { diag.subdiagnostic( tcx.dcx(), errors::OnlyCurrentTraitsForeign { span }, ); } else { diag.subdiagnostic( tcx.dcx(), errors::OnlyCurrentTraitsName { span, name: "tuples" }, ); } } ty::Alias(ty::Opaque, ..) => { diag.subdiagnostic(tcx.dcx(), errors::OnlyCurrentTraitsOpaque { span }); } ty::RawPtr(ptr_ty, mutbl) => { if !self_ty.has_param() { diag.subdiagnostic( tcx.dcx(), errors::OnlyCurrentTraitsPointerSugg { wrapper_span: self_ty_span, struct_span: full_impl_span.shrink_to_lo(), mut_key: mutbl.prefix_str(), ptr_ty, }, ); } diag.subdiagnostic( tcx.dcx(), errors::OnlyCurrentTraitsPointer { span, pointer: ty }, ); } ty::Adt(adt_def, _) => { diag.subdiagnostic( tcx.dcx(), errors::OnlyCurrentTraitsAdt { span, name: tcx.def_path_str(adt_def.did()), }, ); } _ => { diag.subdiagnostic(tcx.dcx(), errors::OnlyCurrentTraitsTy { span, ty }); } } } diag.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) => tcx.dcx().emit_err(errors::TyParamFirstLocal { span: sp, note: (), param_ty, local_type, }), None => tcx.dcx().emit_err(errors::TyParamSome { span: sp, note: (), param_ty }), } } }) }