Skip to content

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

+16-3
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

+3-1
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

+3-1
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

+31-3
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

+2-2
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())
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+
}
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)