Skip to content

Commit

Permalink
Report nicer lifetime errors for specialization
Browse files Browse the repository at this point in the history
Add an obligation cause for these error so that the error points to the
implementations that caused the error.
  • Loading branch information
matthewjasper committed May 5, 2023
1 parent fafe9e7 commit f46eabb
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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() {
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_trait_selection/src/traits/coherence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_trait_selection/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down
34 changes: 31 additions & 3 deletions compiler/rustc_trait_selection/src/traits/specialize/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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({:?}, {:?}, {:?}, {:?})",
Expand All @@ -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:?}, \
Expand Down Expand Up @@ -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
Expand All @@ -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<SubstsRef<'tcx>, ()> {
debug!(
"fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)",
Expand Down Expand Up @@ -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, .. }) =
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_trait_selection/src/traits/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Item = PredicateObligation<'tcx>>) {
let subject = selcx.tcx().impl_subject(impl_def_id);
let subject = subject.subst(selcx.tcx(), impl_substs);
Expand All @@ -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())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Regression test for #79457.

#![feature(min_specialization)]

use std::any::Any;

pub trait Tr {
fn method(self) -> Box<dyn Any + 'static>;
fn other(self);
}

impl<T: Any + 'static> Tr for T {
default fn method(self) -> Box<dyn Any + 'static> {
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);
}
Original file line number Diff line number Diff line change
@@ -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<T: Any + 'static> 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<T: Any + 'static> Tr for T {
| ^^^^^^^

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0477`.

0 comments on commit f46eabb

Please sign in to comment.