Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit f46eabb

Browse files
committed
Report nicer lifetime errors for specialization
Add an obligation cause for these error so that the error points to the implementations that caused the error.
1 parent fafe9e7 commit f46eabb

File tree

7 files changed

+132
-10
lines changed

7 files changed

+132
-10
lines changed

compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
8080
use rustc_span::Span;
8181
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
8282
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
83-
use rustc_trait_selection::traits::{self, translate_substs, wf, ObligationCtxt};
83+
use rustc_trait_selection::traits::{self, translate_substs_with_cause, wf, ObligationCtxt};
8484

8585
pub(super) fn check_min_specialization(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) {
8686
if let Some(node) = parent_specialization_node(tcx, impl_def_id) {
@@ -180,8 +180,21 @@ fn get_impl_substs(
180180
ocx.assumed_wf_types(param_env, tcx.def_span(impl1_def_id), impl1_def_id);
181181

182182
let impl1_substs = InternalSubsts::identity_for_item(tcx, impl1_def_id);
183-
let impl2_substs =
184-
translate_substs(infcx, param_env, impl1_def_id.to_def_id(), impl1_substs, impl2_node);
183+
let impl1_span = tcx.def_span(impl1_def_id);
184+
let impl2_substs = translate_substs_with_cause(
185+
infcx,
186+
param_env,
187+
impl1_def_id.to_def_id(),
188+
impl1_substs,
189+
impl2_node,
190+
|_, span| {
191+
traits::ObligationCause::new(
192+
impl1_span,
193+
impl1_def_id,
194+
traits::ObligationCauseCode::BindingObligation(impl2_node.def_id(), span),
195+
)
196+
},
197+
);
185198

186199
let errors = ocx.select_all_or_error();
187200
if !errors.is_empty() {

compiler/rustc_trait_selection/src/traits/coherence.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,9 @@ fn negative_impl(tcx: TyCtxt<'_>, impl1_def_id: DefId, impl2_def_id: DefId) -> b
322322
let selcx = &mut SelectionContext::new(&infcx);
323323
let impl2_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl2_def_id);
324324
let (subject2, obligations) =
325-
impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_substs);
325+
impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_substs, |_, _| {
326+
ObligationCause::dummy()
327+
});
326328

327329
!equate(&infcx, impl_env, subject1, subject2, obligations, impl1_def_id)
328330
}

compiler/rustc_trait_selection/src/traits/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ pub use self::select::{EvaluationCache, SelectionCache, SelectionContext};
5454
pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError};
5555
pub use self::specialize::specialization_graph::FutureCompatOverlapError;
5656
pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind;
57-
pub use self::specialize::{specialization_graph, translate_substs, OverlapError};
57+
pub use self::specialize::{
58+
specialization_graph, translate_substs, translate_substs_with_cause, OverlapError,
59+
};
5860
pub use self::structural_match::{
5961
search_for_adt_const_param_violation, search_for_structural_match_violation,
6062
};

compiler/rustc_trait_selection/src/traits/specialize/mod.rs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,30 @@ pub fn translate_substs<'tcx>(
8282
source_impl: DefId,
8383
source_substs: SubstsRef<'tcx>,
8484
target_node: specialization_graph::Node,
85+
) -> SubstsRef<'tcx> {
86+
translate_substs_with_cause(
87+
infcx,
88+
param_env,
89+
source_impl,
90+
source_substs,
91+
target_node,
92+
|_, _| ObligationCause::dummy(),
93+
)
94+
}
95+
96+
/// Like [translate_substs], but obligations from the parent implementation
97+
/// are registered with the provided `ObligationCause`.
98+
///
99+
/// This is for reporting *region* errors from those bounds. Type errors should
100+
/// not happen because the specialization graph already checks for those, and
101+
/// will result in an ICE.
102+
pub fn translate_substs_with_cause<'tcx>(
103+
infcx: &InferCtxt<'tcx>,
104+
param_env: ty::ParamEnv<'tcx>,
105+
source_impl: DefId,
106+
source_substs: SubstsRef<'tcx>,
107+
target_node: specialization_graph::Node,
108+
cause: impl Fn(usize, Span) -> ObligationCause<'tcx>,
85109
) -> SubstsRef<'tcx> {
86110
debug!(
87111
"translate_substs({:?}, {:?}, {:?}, {:?})",
@@ -99,7 +123,7 @@ pub fn translate_substs<'tcx>(
99123
return source_substs;
100124
}
101125

102-
fulfill_implication(infcx, param_env, source_trait_ref, source_impl, target_impl)
126+
fulfill_implication(infcx, param_env, source_trait_ref, source_impl, target_impl, cause)
103127
.unwrap_or_else(|()| {
104128
bug!(
105129
"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,
154178
let infcx = tcx.infer_ctxt().build();
155179

156180
// Attempt to prove that impl2 applies, given all of the above.
157-
fulfill_implication(&infcx, penv, impl1_trait_ref, impl1_def_id, impl2_def_id).is_ok()
181+
fulfill_implication(&infcx, penv, impl1_trait_ref, impl1_def_id, impl2_def_id, |_, _| {
182+
ObligationCause::dummy()
183+
})
184+
.is_ok()
158185
}
159186

160187
/// Attempt to fulfill all obligations of `target_impl` after unification with
@@ -168,6 +195,7 @@ fn fulfill_implication<'tcx>(
168195
source_trait_ref: ty::TraitRef<'tcx>,
169196
source_impl: DefId,
170197
target_impl: DefId,
198+
error_cause: impl Fn(usize, Span) -> ObligationCause<'tcx>,
171199
) -> Result<SubstsRef<'tcx>, ()> {
172200
debug!(
173201
"fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)",
@@ -195,7 +223,7 @@ fn fulfill_implication<'tcx>(
195223
let selcx = &mut SelectionContext::new(&infcx);
196224
let target_substs = infcx.fresh_substs_for_item(DUMMY_SP, target_impl);
197225
let (target_trait, obligations) =
198-
util::impl_subject_and_oblig(selcx, param_env, target_impl, target_substs);
226+
util::impl_subject_and_oblig(selcx, param_env, target_impl, target_substs, error_cause);
199227

200228
// do the impls unify? If not, no specialization.
201229
let Ok(InferOk { obligations: more_obligations, .. }) =

compiler/rustc_trait_selection/src/traits/util.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ pub fn impl_subject_and_oblig<'a, 'tcx>(
197197
param_env: ty::ParamEnv<'tcx>,
198198
impl_def_id: DefId,
199199
impl_substs: SubstsRef<'tcx>,
200+
cause: impl Fn(usize, Span) -> ObligationCause<'tcx>,
200201
) -> (ImplSubject<'tcx>, impl Iterator<Item = PredicateObligation<'tcx>>) {
201202
let subject = selcx.tcx().impl_subject(impl_def_id);
202203
let subject = subject.subst(selcx.tcx(), impl_substs);
@@ -208,8 +209,7 @@ pub fn impl_subject_and_oblig<'a, 'tcx>(
208209
let predicates = predicates.instantiate(selcx.tcx(), impl_substs);
209210
let InferOk { value: predicates, obligations: normalization_obligations2 } =
210211
selcx.infcx.at(&ObligationCause::dummy(), param_env).normalize(predicates);
211-
let impl_obligations =
212-
super::predicates_for_generics(|_, _| ObligationCause::dummy(), param_env, predicates);
212+
let impl_obligations = super::predicates_for_generics(cause, param_env, predicates);
213213

214214
let impl_obligations = impl_obligations
215215
.chain(normalization_obligations1.into_iter())
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Regression test for #79457.
2+
3+
#![feature(min_specialization)]
4+
5+
use std::any::Any;
6+
7+
pub trait Tr {
8+
fn method(self) -> Box<dyn Any + 'static>;
9+
fn other(self);
10+
}
11+
12+
impl<T: Any + 'static> Tr for T {
13+
default fn method(self) -> Box<dyn Any + 'static> {
14+
Box::new(self)
15+
}
16+
17+
default fn other(self) {}
18+
}
19+
20+
impl<'a> Tr for &'a i32 {
21+
//~^ ERROR does not fulfill the required lifetime
22+
fn other(self) {}
23+
}
24+
25+
fn promote_to_static<'a>(i: &'a i32) -> &'static i32 {
26+
*i.method().downcast().unwrap()
27+
}
28+
29+
struct Wrapper<'a>(&'a i32);
30+
31+
impl<'a> Tr for Wrapper<'a> {
32+
//~^ ERROR does not fulfill the required lifetime
33+
fn other(self) {}
34+
}
35+
36+
fn promote_to_static_2<'a>(w: Wrapper<'a>) -> Wrapper<'static> {
37+
*w.method().downcast().unwrap()
38+
}
39+
40+
fn main() {
41+
let i = Box::new(100_i32);
42+
let static_i: &'static i32 = promote_to_static(&*i);
43+
drop(i);
44+
println!("{}", *static_i);
45+
46+
let j = Box::new(200_i32);
47+
let static_w: Wrapper<'static> = promote_to_static_2(Wrapper(&*j));
48+
drop(j);
49+
println!("{}", *static_w.0);
50+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error[E0477]: the type `&'a i32` does not fulfill the required lifetime
2+
--> $DIR/specialize_with_generalize_lifetimes.rs:20:1
3+
|
4+
LL | impl<'a> Tr for &'a i32 {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
note: type must satisfy the static lifetime as required by this binding
8+
--> $DIR/specialize_with_generalize_lifetimes.rs:12:15
9+
|
10+
LL | impl<T: Any + 'static> Tr for T {
11+
| ^^^^^^^
12+
13+
error[E0477]: the type `Wrapper<'a>` does not fulfill the required lifetime
14+
--> $DIR/specialize_with_generalize_lifetimes.rs:31:1
15+
|
16+
LL | impl<'a> Tr for Wrapper<'a> {
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
18+
|
19+
note: type must satisfy the static lifetime as required by this binding
20+
--> $DIR/specialize_with_generalize_lifetimes.rs:12:15
21+
|
22+
LL | impl<T: Any + 'static> Tr for T {
23+
| ^^^^^^^
24+
25+
error: aborting due to 2 previous errors
26+
27+
For more information about this error, try `rustc --explain E0477`.

0 commit comments

Comments
 (0)