diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index 4f0df5c5677..e84da2519ae 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -80,7 +80,7 @@ use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use rustc_span::Span; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; -use rustc_trait_selection::traits::{self, translate_substs, wf, ObligationCtxt}; +use rustc_trait_selection::traits::{self, translate_substs_with_cause, wf, ObligationCtxt}; pub(super) fn check_min_specialization(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) { if let Some(node) = parent_specialization_node(tcx, impl_def_id) { @@ -180,8 +180,21 @@ fn get_impl_substs( ocx.assumed_wf_types(param_env, tcx.def_span(impl1_def_id), impl1_def_id); let impl1_substs = InternalSubsts::identity_for_item(tcx, impl1_def_id); - let impl2_substs = - translate_substs(infcx, param_env, impl1_def_id.to_def_id(), impl1_substs, impl2_node); + let impl1_span = tcx.def_span(impl1_def_id); + let impl2_substs = translate_substs_with_cause( + infcx, + param_env, + impl1_def_id.to_def_id(), + impl1_substs, + impl2_node, + |_, span| { + traits::ObligationCause::new( + impl1_span, + impl1_def_id, + traits::ObligationCauseCode::BindingObligation(impl2_node.def_id(), span), + ) + }, + ); let errors = ocx.select_all_or_error(); if !errors.is_empty() { diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index b7690f79933..9f405aaf1a8 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -322,7 +322,9 @@ fn negative_impl(tcx: TyCtxt<'_>, impl1_def_id: DefId, impl2_def_id: DefId) -> b let selcx = &mut SelectionContext::new(&infcx); let impl2_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl2_def_id); let (subject2, obligations) = - impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_substs); + impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_substs, |_, _| { + ObligationCause::dummy() + }); !equate(&infcx, impl_env, subject1, subject2, obligations, impl1_def_id) } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 8b8c50f6b83..138b0fb7432 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -54,7 +54,9 @@ pub use self::select::{EvaluationCache, SelectionCache, SelectionContext}; pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError}; pub use self::specialize::specialization_graph::FutureCompatOverlapError; pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind; -pub use self::specialize::{specialization_graph, translate_substs, OverlapError}; +pub use self::specialize::{ + specialization_graph, translate_substs, translate_substs_with_cause, OverlapError, +}; pub use self::structural_match::{ search_for_adt_const_param_violation, search_for_structural_match_violation, }; diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 8bbebadb22a..9a4b72013b8 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -82,6 +82,30 @@ pub fn translate_substs<'tcx>( source_impl: DefId, source_substs: SubstsRef<'tcx>, target_node: specialization_graph::Node, +) -> SubstsRef<'tcx> { + translate_substs_with_cause( + infcx, + param_env, + source_impl, + source_substs, + target_node, + |_, _| ObligationCause::dummy(), + ) +} + +/// Like [translate_substs], but obligations from the parent implementation +/// are registered with the provided `ObligationCause`. +/// +/// This is for reporting *region* errors from those bounds. Type errors should +/// not happen because the specialization graph already checks for those, and +/// will result in an ICE. +pub fn translate_substs_with_cause<'tcx>( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + source_impl: DefId, + source_substs: SubstsRef<'tcx>, + target_node: specialization_graph::Node, + cause: impl Fn(usize, Span) -> ObligationCause<'tcx>, ) -> SubstsRef<'tcx> { debug!( "translate_substs({:?}, {:?}, {:?}, {:?})", @@ -99,7 +123,7 @@ pub fn translate_substs<'tcx>( return source_substs; } - fulfill_implication(infcx, param_env, source_trait_ref, source_impl, target_impl) + fulfill_implication(infcx, param_env, source_trait_ref, source_impl, target_impl, cause) .unwrap_or_else(|()| { bug!( "When translating substitutions from {source_impl:?} to {target_impl:?}, \ @@ -154,7 +178,10 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, let infcx = tcx.infer_ctxt().build(); // Attempt to prove that impl2 applies, given all of the above. - fulfill_implication(&infcx, penv, impl1_trait_ref, impl1_def_id, impl2_def_id).is_ok() + fulfill_implication(&infcx, penv, impl1_trait_ref, impl1_def_id, impl2_def_id, |_, _| { + ObligationCause::dummy() + }) + .is_ok() } /// Attempt to fulfill all obligations of `target_impl` after unification with @@ -168,6 +195,7 @@ fn fulfill_implication<'tcx>( source_trait_ref: ty::TraitRef<'tcx>, source_impl: DefId, target_impl: DefId, + error_cause: impl Fn(usize, Span) -> ObligationCause<'tcx>, ) -> Result, ()> { debug!( "fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)", @@ -195,7 +223,7 @@ fn fulfill_implication<'tcx>( let selcx = &mut SelectionContext::new(&infcx); let target_substs = infcx.fresh_substs_for_item(DUMMY_SP, target_impl); let (target_trait, obligations) = - util::impl_subject_and_oblig(selcx, param_env, target_impl, target_substs); + util::impl_subject_and_oblig(selcx, param_env, target_impl, target_substs, error_cause); // do the impls unify? If not, no specialization. let Ok(InferOk { obligations: more_obligations, .. }) = diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 7b7e297c64b..82f3df40198 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -197,6 +197,7 @@ pub fn impl_subject_and_oblig<'a, 'tcx>( param_env: ty::ParamEnv<'tcx>, impl_def_id: DefId, impl_substs: SubstsRef<'tcx>, + cause: impl Fn(usize, Span) -> ObligationCause<'tcx>, ) -> (ImplSubject<'tcx>, impl Iterator>) { let subject = selcx.tcx().impl_subject(impl_def_id); let subject = subject.subst(selcx.tcx(), impl_substs); @@ -208,8 +209,7 @@ pub fn impl_subject_and_oblig<'a, 'tcx>( let predicates = predicates.instantiate(selcx.tcx(), impl_substs); let InferOk { value: predicates, obligations: normalization_obligations2 } = selcx.infcx.at(&ObligationCause::dummy(), param_env).normalize(predicates); - let impl_obligations = - super::predicates_for_generics(|_, _| ObligationCause::dummy(), param_env, predicates); + let impl_obligations = super::predicates_for_generics(cause, param_env, predicates); let impl_obligations = impl_obligations .chain(normalization_obligations1.into_iter()) diff --git a/tests/ui/specialization/min_specialization/specialize_with_generalize_lifetimes.rs b/tests/ui/specialization/min_specialization/specialize_with_generalize_lifetimes.rs new file mode 100644 index 00000000000..d90b81f717a --- /dev/null +++ b/tests/ui/specialization/min_specialization/specialize_with_generalize_lifetimes.rs @@ -0,0 +1,50 @@ +// Regression test for #79457. + +#![feature(min_specialization)] + +use std::any::Any; + +pub trait Tr { + fn method(self) -> Box; + fn other(self); +} + +impl Tr for T { + default fn method(self) -> Box { + Box::new(self) + } + + default fn other(self) {} +} + +impl<'a> Tr for &'a i32 { + //~^ ERROR does not fulfill the required lifetime + fn other(self) {} +} + +fn promote_to_static<'a>(i: &'a i32) -> &'static i32 { + *i.method().downcast().unwrap() +} + +struct Wrapper<'a>(&'a i32); + +impl<'a> Tr for Wrapper<'a> { + //~^ ERROR does not fulfill the required lifetime + fn other(self) {} +} + +fn promote_to_static_2<'a>(w: Wrapper<'a>) -> Wrapper<'static> { + *w.method().downcast().unwrap() +} + +fn main() { + let i = Box::new(100_i32); + let static_i: &'static i32 = promote_to_static(&*i); + drop(i); + println!("{}", *static_i); + + let j = Box::new(200_i32); + let static_w: Wrapper<'static> = promote_to_static_2(Wrapper(&*j)); + drop(j); + println!("{}", *static_w.0); +} diff --git a/tests/ui/specialization/min_specialization/specialize_with_generalize_lifetimes.stderr b/tests/ui/specialization/min_specialization/specialize_with_generalize_lifetimes.stderr new file mode 100644 index 00000000000..2af75876d5b --- /dev/null +++ b/tests/ui/specialization/min_specialization/specialize_with_generalize_lifetimes.stderr @@ -0,0 +1,27 @@ +error[E0477]: the type `&'a i32` does not fulfill the required lifetime + --> $DIR/specialize_with_generalize_lifetimes.rs:20:1 + | +LL | impl<'a> Tr for &'a i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: type must satisfy the static lifetime as required by this binding + --> $DIR/specialize_with_generalize_lifetimes.rs:12:15 + | +LL | impl Tr for T { + | ^^^^^^^ + +error[E0477]: the type `Wrapper<'a>` does not fulfill the required lifetime + --> $DIR/specialize_with_generalize_lifetimes.rs:31:1 + | +LL | impl<'a> Tr for Wrapper<'a> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: type must satisfy the static lifetime as required by this binding + --> $DIR/specialize_with_generalize_lifetimes.rs:12:15 + | +LL | impl Tr for T { + | ^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0477`.