diff options
Diffstat (limited to '')
-rw-r--r-- | compiler/rustc_hir_analysis/src/coherence/builtin.rs | 572 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs (renamed from compiler/rustc_typeck/src/coherence/inherent_impls.rs) | 18 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs (renamed from compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs) | 58 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/coherence/mod.rs (renamed from compiler/rustc_typeck/src/coherence/mod.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/coherence/orphan.rs (renamed from compiler/rustc_typeck/src/coherence/orphan.rs) | 38 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/coherence/unsafety.rs (renamed from compiler/rustc_typeck/src/coherence/unsafety.rs) | 32 |
6 files changed, 673 insertions, 45 deletions
diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs new file mode 100644 index 000000000..b6c91d425 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -0,0 +1,572 @@ +//! 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::TypeErrCtxtExt; +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. + let infcx = tcx.infer_ctxt().ignoring_regions().build(); + 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); + + let infcx = tcx.infer_ctxt().build(); + 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.err_ctxt().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); + + let infcx = tcx.infer_ctxt().build(); + 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 + .err_ctxt() + .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.err_ctxt().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 } +} diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index 52aad636f..2890c149b 100644 --- a/compiler/rustc_typeck/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -58,7 +58,7 @@ const ADD_ATTR: &str = 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; + let impl_def_id = item.owner_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 @@ -89,7 +89,7 @@ impl<'tcx> InherentCollect<'tcx> { for impl_item in items { if !self .tcx - .has_attr(impl_item.id.def_id.to_def_id(), sym::rustc_allow_incoherent_impl) + .has_attr(impl_item.id.owner_id.to_def_id(), sym::rustc_allow_incoherent_impl) { struct_span_err!( self.tcx.sess, @@ -105,7 +105,7 @@ impl<'tcx> InherentCollect<'tcx> { } 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); + self.impls_map.incoherent_impls.entry(simp).or_default().push(impl_def_id.def_id); } else { bug!("unexpected self type: {:?}", self_ty); } @@ -135,7 +135,7 @@ impl<'tcx> InherentCollect<'tcx> { for item in items { if !self .tcx - .has_attr(item.id.def_id.to_def_id(), sym::rustc_allow_incoherent_impl) + .has_attr(item.id.owner_id.to_def_id(), sym::rustc_allow_incoherent_impl) { struct_span_err!( self.tcx.sess, @@ -177,7 +177,7 @@ impl<'tcx> InherentCollect<'tcx> { } fn check_item(&mut self, id: hir::ItemId) { - if !matches!(self.tcx.def_kind(id.def_id), DefKind::Impl) { + if !matches!(self.tcx.def_kind(id.owner_id), DefKind::Impl) { return; } @@ -186,7 +186,7 @@ impl<'tcx> InherentCollect<'tcx> { return; }; - let self_ty = self.tcx.type_of(item.def_id); + let self_ty = self.tcx.type_of(item.owner_id); match *self_ty.kind() { ty::Adt(def, _) => { self.check_def_id(item, self_ty, def.did()); @@ -220,7 +220,9 @@ impl<'tcx> InherentCollect<'tcx> { | ty::Ref(..) | ty::Never | ty::FnPtr(_) - | ty::Tuple(..) => self.check_primitive_impl(item.def_id, self_ty, items, ty.span), + | ty::Tuple(..) => { + self.check_primitive_impl(item.owner_id.def_id, self_ty, items, ty.span) + } ty::Projection(..) | ty::Opaque(..) | ty::Param(_) => { let mut err = struct_span_err!( self.tcx.sess, @@ -241,7 +243,7 @@ impl<'tcx> InherentCollect<'tcx> { | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) => { - bug!("unexpected impl self type of impl: {:?} {:?}", item.def_id, self_ty); + bug!("unexpected impl self type of impl: {:?} {:?}", item.owner_id, self_ty); } ty::Error(_) => {} } diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs index 03e076bf5..972769eb1 100644 --- a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs @@ -58,6 +58,37 @@ impl<'tcx> InherentOverlapChecker<'tcx> { == item2.ident(self.tcx).normalize_to_macros_2_0() } + fn check_for_duplicate_items_in_impl(&self, impl_: DefId) { + let impl_items = self.tcx.associated_items(impl_); + + let mut seen_items = FxHashMap::default(); + for impl_item in impl_items.in_definition_order() { + let span = self.tcx.def_span(impl_item.def_id); + let ident = impl_item.ident(self.tcx); + + let norm_ident = ident.normalize_to_macros_2_0(); + match seen_items.entry(norm_ident) { + Entry::Occupied(entry) => { + let former = entry.get(); + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0592, + "duplicate definitions with name `{}`", + ident, + ); + err.span_label(span, format!("duplicate definitions for `{}`", ident)); + err.span_label(*former, format!("other definition for `{}`", ident)); + + err.emit(); + } + Entry::Vacant(entry) => { + entry.insert(span); + } + } + } + } + fn check_for_common_items_in_impls( &self, impl1: DefId, @@ -117,29 +148,22 @@ impl<'tcx> InherentOverlapChecker<'tcx> { // 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, - ); + ) + .map_or(true, |overlap| { + self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap); + false + }); } fn check_item(&mut self, id: hir::ItemId) { - let def_kind = self.tcx.def_kind(id.def_id); + let def_kind = self.tcx.def_kind(id.owner_id); if !matches!(def_kind, DefKind::Enum | DefKind::Struct | DefKind::Trait | DefKind::Union) { return; } - let impls = self.tcx.inherent_impls(id.def_id); + let impls = self.tcx.inherent_impls(id.owner_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 overlap_mode = OverlapMode::get(self.tcx, id.owner_id.to_def_id()); let impls_items = impls .iter() @@ -152,6 +176,8 @@ impl<'tcx> InherentOverlapChecker<'tcx> { const ALLOCATING_ALGO_THRESHOLD: usize = 500; if impls.len() < ALLOCATING_ALGO_THRESHOLD { for (i, &(&impl1_def_id, impl_items1)) in impls_items.iter().enumerate() { + self.check_for_duplicate_items_in_impl(impl1_def_id); + 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( @@ -290,6 +316,8 @@ impl<'tcx> InherentOverlapChecker<'tcx> { 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]; + self.check_for_duplicate_items_in_impl(impl1_def_id); + 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) { diff --git a/compiler/rustc_typeck/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index ae9ebe590..ae9ebe590 100644 --- a/compiler/rustc_typeck/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index 1608550aa..bb45c3823 100644 --- a/compiler/rustc_typeck/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -2,10 +2,9 @@ //! 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::{struct_span_err, DelayDm}; use rustc_errors::{Diagnostic, ErrorGuaranteed}; use rustc_hir as hir; -use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::util::IgnoreRegions; @@ -43,7 +42,7 @@ fn do_orphan_check_impl<'tcx>( ) -> Result<(), ErrorGuaranteed> { let trait_def_id = trait_ref.def_id; - let item = tcx.hir().item(hir::ItemId { 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); }; @@ -102,7 +101,7 @@ fn do_orphan_check_impl<'tcx>( span_bug!(sp, "opaque type not found, but `has_opaque_types` is set") } - match traits::orphan_check(tcx, item.def_id.to_def_id()) { + match traits::orphan_check(tcx, item.owner_id.to_def_id()) { Ok(()) => {} Err(err) => emit_orphan_check_error( tcx, @@ -229,12 +228,8 @@ fn emit_orphan_check_error<'tcx>( "only traits defined in the current crate {msg}" ); err.span_label(sp, "impl doesn't use only types from inside the current crate"); - for (ty, is_target_ty) in &tys { - let mut ty = *ty; - tcx.infer_ctxt().enter(|infcx| { - // Remove the lifetimes unnecessary for this error. - ty = infcx.freshen(ty); - }); + 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`. @@ -264,7 +259,7 @@ fn emit_orphan_check_error<'tcx>( }; let msg = format!("{} is not defined in the current crate{}", ty, postfix); - if *is_target_ty { + 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 { @@ -417,30 +412,31 @@ fn lint_auto_trait_impl<'tcx>( 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!( + DelayDm(|| { + format!( "cross-crate traits with a default impl, like `{}`, \ should not be specialized", tcx.def_path_str(trait_ref.def_id), - )); + ) + }), + |lint| { + let item_span = tcx.def_span(self_type_did); + let self_descr = tcx.def_kind(self_type_did).descr(self_type_did); match arg { ty::util::NotUniqueParam::DuplicateParam(arg) => { - err.note(&format!("`{}` is mentioned multiple times", arg)); + lint.note(&format!("`{}` is mentioned multiple times", arg)); } ty::util::NotUniqueParam::NotParam(arg) => { - err.note(&format!("`{}` is not a generic parameter", arg)); + lint.note(&format!("`{}` is not a generic parameter", arg)); } } - err.span_note( + lint.span_note( item_span, &format!( "try using the same sequence of generic parameters as the {} definition", self_descr, ), - ); - err.emit(); + ) }, ); } diff --git a/compiler/rustc_typeck/src/coherence/unsafety.rs b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs index e45fb5fe4..a34815b45 100644 --- a/compiler/rustc_typeck/src/coherence/unsafety.rs +++ b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs @@ -13,7 +13,7 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { 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) { + if let Some(trait_ref) = tcx.impl_trait_ref(item.owner_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"); @@ -26,6 +26,12 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { "implementing the trait `{}` is not unsafe", trait_ref.print_only_trait_path() ) + .span_suggestion_verbose( + item.span.with_hi(item.span.lo() + rustc_span::BytePos(7)), + "remove `unsafe` from this trait implementation", + "", + rustc_errors::Applicability::MachineApplicable, + ) .emit(); } @@ -37,6 +43,18 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { "the trait `{}` requires an `unsafe impl` declaration", trait_ref.print_only_trait_path() ) + .note(format!( + "the trait `{}` enforces invariants that the compiler can't check. \ + Review the trait documentation and make sure this implementation \ + upholds those invariants before adding the `unsafe` keyword", + trait_ref.print_only_trait_path() + )) + .span_suggestion_verbose( + item.span.shrink_to_lo(), + "add `unsafe` to this trait implementation", + "unsafe ", + rustc_errors::Applicability::MaybeIncorrect, + ) .emit(); } @@ -48,6 +66,18 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { "requires an `unsafe impl` declaration due to `#[{}]` attribute", attr_name ) + .note(format!( + "the trait `{}` enforces invariants that the compiler can't check. \ + Review the trait documentation and make sure this implementation \ + upholds those invariants before adding the `unsafe` keyword", + trait_ref.print_only_trait_path() + )) + .span_suggestion_verbose( + item.span.shrink_to_lo(), + "add `unsafe` to this trait implementation", + "unsafe ", + rustc_errors::Applicability::MaybeIncorrect, + ) .emit(); } |