diff options
Diffstat (limited to 'compiler/rustc_trait_selection/src/traits/object_safety.rs')
-rw-r--r-- | compiler/rustc_trait_selection/src/traits/object_safety.rs | 202 |
1 files changed, 110 insertions, 92 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 612f51309..0bb25a74d 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -8,19 +8,20 @@ //! - not reference the erased type `Self` except for in this receiver; //! - not have generic type parameters. -use super::elaborate_predicates; +use super::{elaborate_predicates, elaborate_trait_ref}; use crate::infer::TyCtxtInferExt; use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::{self, Obligation, ObligationCause}; -use rustc_errors::{FatalError, MultiSpan}; +use hir::def::DefKind; +use rustc_errors::{DelayDm, FatalError, MultiSpan}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::ty::abstract_const::{walk_abstract_const, AbstractConst}; -use rustc_middle::ty::subst::{GenericArg, InternalSubsts, Subst}; use rustc_middle::ty::{ self, EarlyBinder, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, }; +use rustc_middle::ty::{GenericArg, InternalSubsts}; use rustc_middle::ty::{Predicate, ToPredicate}; use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY; use rustc_span::symbol::Symbol; @@ -163,37 +164,42 @@ fn lint_object_unsafe_trait( ) { // Using `CRATE_NODE_ID` is wrong, but it's hard to get a more precise id. // It's also hard to get a use site span, so we use the method definition span. - tcx.struct_span_lint_hir(WHERE_CLAUSES_OBJECT_SAFETY, hir::CRATE_HIR_ID, span, |lint| { - let mut err = lint.build(&format!( - "the trait `{}` cannot be made into an object", - tcx.def_path_str(trait_def_id) - )); - let node = tcx.hir().get_if_local(trait_def_id); - let mut spans = MultiSpan::from_span(span); - if let Some(hir::Node::Item(item)) = node { - spans.push_span_label(item.ident.span, "this trait cannot be made into an object..."); - spans.push_span_label(span, format!("...because {}", violation.error_msg())); - } else { - spans.push_span_label( - span, - format!( - "the trait cannot be made into an object because {}", - violation.error_msg() - ), + tcx.struct_span_lint_hir( + WHERE_CLAUSES_OBJECT_SAFETY, + hir::CRATE_HIR_ID, + span, + DelayDm(|| format!("the trait `{}` cannot be made into an object", tcx.def_path_str(trait_def_id))), + |err| { + let node = tcx.hir().get_if_local(trait_def_id); + let mut spans = MultiSpan::from_span(span); + if let Some(hir::Node::Item(item)) = node { + spans.push_span_label( + item.ident.span, + "this trait cannot be made into an object...", + ); + spans.push_span_label(span, format!("...because {}", violation.error_msg())); + } else { + spans.push_span_label( + span, + format!( + "the trait cannot be made into an object because {}", + violation.error_msg() + ), + ); + }; + err.span_note( + spans, + "for a trait to be \"object safe\" it needs to allow building a vtable to allow the \ + call to be resolvable dynamically; for more information visit \ + <https://doc.rust-lang.org/reference/items/traits.html#object-safety>", ); - }; - err.span_note( - spans, - "for a trait to be \"object safe\" it needs to allow building a vtable to allow the \ - call to be resolvable dynamically; for more information visit \ - <https://doc.rust-lang.org/reference/items/traits.html#object-safety>", - ); - if node.is_some() { - // Only provide the help if its a local trait, otherwise it's not - violation.solution(&mut err); - } - err.emit(); - }); + if node.is_some() { + // Only provide the help if its a local trait, otherwise it's not + violation.solution(err); + } + err + }, + ); } fn sized_trait_bound_spans<'tcx>( @@ -431,6 +437,9 @@ fn virtual_call_violation_for_method<'tcx>( if contains_illegal_self_type_reference(tcx, trait_def_id, sig.output()) { return Some(MethodViolationCode::ReferencesSelfOutput); } + if contains_illegal_impl_trait_in_trait(tcx, sig.output()) { + return Some(MethodViolationCode::ReferencesImplTraitInTrait); + } // We can't monomorphize things like `fn foo<A>(...)`. let own_counts = tcx.generics_of(method.def_id).own_counts(); @@ -438,19 +447,6 @@ fn virtual_call_violation_for_method<'tcx>( return Some(MethodViolationCode::Generic); } - if tcx - .predicates_of(method.def_id) - .predicates - .iter() - // A trait object can't claim to live more than the concrete type, - // so outlives predicates will always hold. - .cloned() - .filter(|(p, _)| p.to_opt_type_outlives().is_none()) - .any(|pred| contains_illegal_self_type_reference(tcx, trait_def_id, pred)) - { - return Some(MethodViolationCode::WhereClauseReferencesSelf); - } - let receiver_ty = tcx.liberate_late_bound_regions(method.def_id, sig.input(0)); // Until `unsized_locals` is fully implemented, `self: Self` can't be dispatched on. @@ -529,6 +525,21 @@ fn virtual_call_violation_for_method<'tcx>( } } + // NOTE: This check happens last, because it results in a lint, and not a + // hard error. + if tcx + .predicates_of(method.def_id) + .predicates + .iter() + // A trait object can't claim to live more than the concrete type, + // so outlives predicates will always hold. + .cloned() + .filter(|(p, _)| p.to_opt_type_outlives().is_none()) + .any(|pred| contains_illegal_self_type_reference(tcx, trait_def_id, pred)) + { + return Some(MethodViolationCode::WhereClauseReferencesSelf); + } + None } @@ -556,51 +567,44 @@ fn receiver_for_self_ty<'tcx>( /// Creates the object type for the current trait. For example, /// if the current trait is `Deref`, then this will be /// `dyn Deref<Target = Self::Target> + 'static`. +#[instrument(level = "trace", skip(tcx), ret)] fn object_ty_for_trait<'tcx>( tcx: TyCtxt<'tcx>, trait_def_id: DefId, lifetime: ty::Region<'tcx>, ) -> Ty<'tcx> { - debug!("object_ty_for_trait: trait_def_id={:?}", trait_def_id); - let trait_ref = ty::TraitRef::identity(tcx, trait_def_id); + debug!(?trait_ref); let trait_predicate = trait_ref.map_bound(|trait_ref| { ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)) }); - - let mut associated_types = traits::supertraits(tcx, trait_ref) - .flat_map(|super_trait_ref| { - tcx.associated_items(super_trait_ref.def_id()) - .in_definition_order() - .map(move |item| (super_trait_ref, item)) - }) - .filter(|(_, item)| item.kind == ty::AssocKind::Type) - .collect::<Vec<_>>(); - - // existential predicates need to be in a specific order - associated_types.sort_by_cached_key(|(_, item)| tcx.def_path_hash(item.def_id)); - - let projection_predicates = associated_types.into_iter().map(|(super_trait_ref, item)| { - // We *can* get bound lifetimes here in cases like - // `trait MyTrait: for<'s> OtherTrait<&'s T, Output=bool>`. - super_trait_ref.map_bound(|super_trait_ref| { - ty::ExistentialPredicate::Projection(ty::ExistentialProjection { - term: tcx.mk_projection(item.def_id, super_trait_ref.substs).into(), - item_def_id: item.def_id, - substs: super_trait_ref.substs, - }) + debug!(?trait_predicate); + + let mut elaborated_predicates: Vec<_> = elaborate_trait_ref(tcx, trait_ref) + .filter_map(|obligation| { + debug!(?obligation); + let pred = obligation.predicate.to_opt_poly_projection_pred()?; + Some(pred.map_bound(|p| { + ty::ExistentialPredicate::Projection(ty::ExistentialProjection { + item_def_id: p.projection_ty.item_def_id, + substs: p.projection_ty.substs, + term: p.term, + }) + })) }) - }); + .collect(); + // NOTE: Since #37965, the existential predicates list has depended on the + // list of predicates to be sorted. This is mostly to enforce that the primary + // predicate comes first. + elaborated_predicates.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); + elaborated_predicates.dedup(); let existential_predicates = tcx - .mk_poly_existential_predicates(iter::once(trait_predicate).chain(projection_predicates)); + .mk_poly_existential_predicates(iter::once(trait_predicate).chain(elaborated_predicates)); + debug!(?existential_predicates); - let object_ty = tcx.mk_dynamic(existential_predicates, lifetime); - - debug!("object_ty_for_trait: object_ty=`{}`", object_ty); - - object_ty + tcx.mk_dynamic(existential_predicates, lifetime, ty::Dyn) } /// Checks the method's receiver (the `self` argument) can be dispatched on when `Self` is a @@ -725,10 +729,9 @@ fn receiver_is_dispatchable<'tcx>( Obligation::new(ObligationCause::dummy(), param_env, predicate) }; - tcx.infer_ctxt().enter(|ref infcx| { - // the receiver is dispatchable iff the obligation holds - infcx.predicate_must_hold_modulo_regions(&obligation) - }) + let infcx = tcx.infer_ctxt().build(); + // the receiver is dispatchable iff the obligation holds + infcx.predicate_must_hold_modulo_regions(&obligation) } fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>( @@ -793,6 +796,12 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>( ControlFlow::CONTINUE } } + ty::Projection(ref data) + if self.tcx.def_kind(data.item_def_id) == DefKind::ImplTraitPlaceholder => + { + // We'll deny these later in their own pass + ControlFlow::CONTINUE + } ty::Projection(ref data) => { // This is a projected type `<Foo as SomeTrait>::X`. @@ -828,21 +837,14 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>( } } - fn visit_unevaluated(&mut self, uv: ty::Unevaluated<'tcx>) -> ControlFlow<Self::BreakTy> { + fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { // Constants can only influence object safety if they reference `Self`. // This is only possible for unevaluated constants, so we walk these here. // - // If `AbstractConst::new` returned an error we already failed compilation + // If `AbstractConst::from_const` returned an error we already failed compilation // so we don't have to emit an additional error here. - // - // We currently recurse into abstract consts here but do not recurse in - // `is_const_evaluatable`. This means that the object safety check is more - // liberal than the const eval check. - // - // This shouldn't really matter though as we can't really use any - // constants which are not considered const evaluatable. use rustc_middle::ty::abstract_const::Node; - if let Ok(Some(ct)) = AbstractConst::new(self.tcx, uv.shrink()) { + if let Ok(Some(ct)) = AbstractConst::from_const(self.tcx, ct) { walk_abstract_const(self.tcx, ct, |node| match node.root(self.tcx) { Node::Leaf(leaf) => self.visit_const(leaf), Node::Cast(_, _, ty) => self.visit_ty(ty), @@ -851,7 +853,7 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>( } }) } else { - ControlFlow::CONTINUE + ct.super_visit_with(self) } } } @@ -861,6 +863,22 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>( .is_break() } +pub fn contains_illegal_impl_trait_in_trait<'tcx>( + tcx: TyCtxt<'tcx>, + ty: ty::Binder<'tcx, Ty<'tcx>>, +) -> bool { + // FIXME(RPITIT): Perhaps we should use a visitor here? + ty.skip_binder().walk().any(|arg| { + if let ty::GenericArgKind::Type(ty) = arg.unpack() + && let ty::Projection(proj) = ty.kind() + { + tcx.def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder + } else { + false + } + }) +} + pub fn provide(providers: &mut ty::query::Providers) { *providers = ty::query::Providers { object_safety_violations, ..*providers }; } |