Skip to content

Commit 9510c73

Browse files
Rollup merge of #130367 - compiler-errors:super-unconstrained, r=spastorino
Check elaborated projections from dyn don't mention unconstrained late bound lifetimes Check that the projections that are *not* explicitly written but which we deduce from elaborating the principal of a `dyn` *also* do not reference unconstrained late-bound lifetimes, just like the ones that the user writes by hand. That is to say, given: ``` trait Foo<T>: Bar<Assoc = T> {} trait Bar { type Assoc; } ``` The type `dyn for<'a> Foo<&'a T>` (basically) elaborates to `dyn for<'a> Foo<&'a T> + for<'a> Bar<Assoc = &'a T>`[^1]. However, the `Bar` projection predicate is not well-formed, since `'a` must show up in the trait's arguments to be referenced in the term of a projection. We must error in this situation[^well], or else `dyn for<'a> Foo<&'a T>` is unsound. We already detect this for user-written projections during HIR->rustc_middle conversion, so this largely replicates that logic using the helper functions that were already conveniently defined. --- I'm cratering this first to see the fallout; if it's minimal or zero, then let's land it as-is. If not, the way that this is implemented is very conducive to an FCW. --- Fixes#130347 [^1]: We don't actually elaborate it like that in rustc; we only keep the principal trait ref `Foo<&'a T>` and the projection part of `Bar<Assoc = ...>`, but it's useful to be a bit verbose here for the purpose of explaining the issue. [^well]: Well, we could also make `dyn for<'a> Foo<&'a T>` *not* implement `for<'a> Bar<Assoc = &'a T>`, but this is inconsistent with the case where the user writes `Assoc = ...` in the type itself, and it overly complicates the implementation of trait objects' built-in impls.
2 parents f09e5a7 + fd7ee48 commit 9510c73

8 files changed

Lines changed: 149 additions & 18 deletions

File tree

‎compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs‎

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use rustc_middle::ty::{
1313
use rustc_span::{ErrorGuaranteed,Span};
1414
use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility;
1515
use rustc_trait_selection::traits::{self, hir_ty_lowering_dyn_compatibility_violations};
16+
use rustc_type_ir::elaborate::ClauseWithSupertraitSpan;
1617
use smallvec::{SmallVec, smallvec};
1718
use tracing::{debug, instrument};
1819

@@ -124,16 +125,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
124125
.into_iter()
125126
.filter(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id()));
126127

127-
for(base_trait_ref,span)in regular_traits_refs_spans {
128+
for(base_trait_ref,original_span)in regular_traits_refs_spans {
128129
let base_pred: ty::Predicate<'tcx> = base_trait_ref.upcast(tcx);
129-
for pred in traits::elaborate(tcx,[base_pred]).filter_only_self(){
130+
forClauseWithSupertraitSpan{ pred, original_span, supertrait_span }in
131+
traits::elaborate(tcx,[ClauseWithSupertraitSpan::new(base_pred, original_span)])
132+
.filter_only_self()
133+
{
130134
debug!("observing object predicate `{pred:?}`");
131135

132136
let bound_predicate = pred.kind();
133137
match bound_predicate.skip_binder(){
134138
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
135139
let pred = bound_predicate.rebind(pred);
136-
associated_types.entry(span).or_default().extend(
140+
associated_types.entry(original_span).or_default().extend(
137141
tcx.associated_items(pred.def_id())
138142
.in_definition_order()
139143
.filter(|item| item.kind == ty::AssocKind::Type)
@@ -172,8 +176,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
172176
// the discussion in #56288 for alternatives.
173177
if !references_self {
174178
// Include projections defined on supertraits.
175-
projection_bounds.push((pred,span));
179+
projection_bounds.push((pred,original_span));
176180
}
181+
182+
self.check_elaborated_projection_mentions_input_lifetimes(
183+
pred,
184+
original_span,
185+
supertrait_span,
186+
);
177187
}
178188
_ => (),
179189
}
@@ -360,6 +370,56 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
360370

361371
Ty::new_dynamic(tcx, existential_predicates, region_bound, representation)
362372
}
373+
374+
/// Check that elaborating the principal of a trait ref doesn't lead to projections
375+
/// that are unconstrained. This can happen because an otherwise unconstrained
376+
/// *type variable* can be substituted with a type that has late-bound regions. See
377+
/// `elaborated-predicates-unconstrained-late-bound.rs` for a test.
378+
fncheck_elaborated_projection_mentions_input_lifetimes(
379+
&self,
380+
pred: ty::PolyProjectionPredicate<'tcx>,
381+
span:Span,
382+
supertrait_span:Span,
383+
){
384+
let tcx = self.tcx();
385+
386+
// Find any late-bound regions declared in `ty` that are not
387+
// declared in the trait-ref or assoc_item. These are not well-formed.
388+
//
389+
// Example:
390+
//
391+
// for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad
392+
// for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok
393+
let late_bound_in_projection_term =
394+
tcx.collect_constrained_late_bound_regions(pred.map_bound(|pred| pred.projection_term));
395+
let late_bound_in_term =
396+
tcx.collect_referenced_late_bound_regions(pred.map_bound(|pred| pred.term));
397+
debug!(?late_bound_in_projection_term);
398+
debug!(?late_bound_in_term);
399+
400+
// FIXME: point at the type params that don't have appropriate lifetimes:
401+
// struct S1<F: for<'a> Fn(&i32, &i32) -> &'a i32>(F);
402+
// ---- ---- ^^^^^^^
403+
// NOTE(associated_const_equality): This error should be impossible to trigger
404+
// with associated const equality constraints.
405+
self.validate_late_bound_regions(
406+
late_bound_in_projection_term,
407+
late_bound_in_term,
408+
|br_name| {
409+
let item_name = tcx.item_name(pred.projection_def_id());
410+
struct_span_code_err!(
411+
self.dcx(),
412+
span,
413+
E0582,
414+
"binding for associated type `{}` references {}, \
415+
which does not appear in the trait input types",
416+
item_name,
417+
br_name
418+
)
419+
.with_span_label(supertrait_span,"due to this supertrait")
420+
},
421+
);
422+
}
363423
}
364424

365425
fnreplace_dummy_self_with_error<'tcx,T:TypeFoldable<TyCtxt<'tcx>>>(

‎compiler/rustc_middle/src/ty/predicate.rs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,10 @@ pub struct Clause<'tcx>(
179179
);
180180

181181
impl<'tcx> rustc_type_ir::inherent::Clause<TyCtxt<'tcx>>forClause<'tcx>{
182+
fnas_predicate(self) -> Predicate<'tcx>{
183+
self.as_predicate()
184+
}
185+
182186
fninstantiate_supertrait(self,tcx:TyCtxt<'tcx>,trait_ref: ty::PolyTraitRef<'tcx>) -> Self{
183187
self.instantiate_supertrait(tcx, trait_ref)
184188
}

‎compiler/rustc_type_ir/src/elaborate.rs‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,46 @@ pub trait Elaboratable<I: Interner> {
4444
) -> Self;
4545
}
4646

47+
pubstructClauseWithSupertraitSpan<I:Interner>{
48+
pubpred:I::Predicate,
49+
// Span of the original elaborated predicate.
50+
puboriginal_span:I::Span,
51+
// Span of the supertrait predicatae that lead to this clause.
52+
pubsupertrait_span:I::Span,
53+
}
54+
impl<I:Interner>ClauseWithSupertraitSpan<I>{
55+
pubfnnew(pred:I::Predicate,span:I::Span) -> Self{
56+
ClauseWithSupertraitSpan{ pred,original_span: span,supertrait_span: span }
57+
}
58+
}
59+
impl<I:Interner>Elaboratable<I>forClauseWithSupertraitSpan<I>{
60+
fnpredicate(&self) -> <IasInterner>::Predicate{
61+
self.pred
62+
}
63+
64+
fnchild(&self,clause: <IasInterner>::Clause) -> Self{
65+
ClauseWithSupertraitSpan{
66+
pred: clause.as_predicate(),
67+
original_span:self.original_span,
68+
supertrait_span:self.supertrait_span,
69+
}
70+
}
71+
72+
fnchild_with_derived_cause(
73+
&self,
74+
clause: <IasInterner>::Clause,
75+
supertrait_span: <IasInterner>::Span,
76+
_parent_trait_pred:crate::Binder<I,crate::TraitPredicate<I>>,
77+
_index:usize,
78+
) -> Self{
79+
ClauseWithSupertraitSpan{
80+
pred: clause.as_predicate(),
81+
original_span:self.original_span,
82+
supertrait_span: supertrait_span,
83+
}
84+
}
85+
}
86+
4787
pubfnelaborate<I:Interner,O:Elaboratable<I>>(
4888
cx:I,
4989
obligations:implIntoIterator<Item = O>,

‎compiler/rustc_type_ir/src/inherent.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,8 @@ pub trait Clause<I: Interner<Clause = Self>>:
460460
+ IntoKind<Kind = ty::Binder<I, ty::ClauseKind<I>>>
461461
+ Elaboratable<I>
462462
{
463+
fnas_predicate(self) -> I::Predicate;
464+
463465
fnas_trait_clause(self) -> Option<ty::Binder<I, ty::TraitPredicate<I>>>{
464466
self.kind()
465467
.map_bound(|clause| iflet ty::ClauseKind::Trait(t) = clause {Some(t)}else{None})
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Make sure that when elaborating the principal of a dyn trait for projection predicates
2+
// we don't end up in a situation where we have an unconstrained late-bound lifetime in
3+
// the output of a projection.
4+
5+
// Fix for <https://github.com/rust-lang/rust/issues/130347>.
6+
7+
traitA<T>:B<T = T>{}
8+
9+
traitB{
10+
typeT;
11+
}
12+
13+
structErase<T: ?Sized + B>(T::T);
14+
15+
fnmain(){
16+
let x = {
17+
let x = String::from("hello");
18+
19+
Erase::<dynfor<'a>A<&'a_>>(x.as_str())
20+
//~^ ERROR binding for associated type `T` references lifetime `'a`, which does not appear in the trait input types
21+
};
22+
23+
dbg!(x.0);
24+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0582]: binding for associated type `T` references lifetime `'a`, which does not appear in the trait input types
2+
--> $DIR/elaborated-predicates-unconstrained-late-bound.rs:19:21
3+
|
4+
LL | trait A<T>: B<T = T> {}
5+
| ----- due to this supertrait
6+
...
7+
LL | Erase::<dyn for<'a> A<&'a _>>(x.as_str())
8+
| ^^^^^^^^^^^^^^^^
9+
10+
error: aborting due to 1 previous error
11+
12+
For more information about this error, try `rustc --explain E0582`.

‎tests/ui/traits/object/pretty.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ trait SuperGeneric<'a> {
1313
}
1414
traitAnyGeneric<'a>:SuperGeneric<'a>{}
1515
traitFixedGeneric1<'a>:SuperGeneric<'a,Assoc2 = &'au8>{}
16-
traitFixedGeneric2<'a>:Super<Assoc = &'au8>{}
16+
// trait FixedGeneric2<'a>: Super<Assoc = &'a u8> {} // Unsound!
1717
traitFixedHrtb:for<'a>SuperGeneric<'a,Assoc2 = &'au8>{}
1818
traitAnyDifferentBinders:for<'a>SuperGeneric<'a,Assoc2 = &'au8> + Super{}
1919
traitFixedDifferentBinders:for<'a>SuperGeneric<'a,Assoc2 = &'au8> + Super<Assoc = u8>{}
@@ -32,7 +32,7 @@ fn dyn_fixed_static(x: &dyn FixedStatic) { x } //~ERROR mismatched types
3232
fndyn_super_generic(x:&dynfor<'a>SuperGeneric<'a,Assoc2 = &'au8>){ x }//~ERROR mismatched types
3333
fndyn_any_generic(x:&dynfor<'a>AnyGeneric<'a,Assoc2 = &'au8>){ x }//~ERROR mismatched types
3434
fndyn_fixed_generic1(x:&dynfor<'a>FixedGeneric1<'a>){ x }//~ERROR mismatched types
35-
fndyn_fixed_generic2(x:&dynfor<'a>FixedGeneric2<'a>){ x }//~ERROR mismatched types
35+
// fn dyn_fixed_generic2(x: &dyn for<'a> FixedGeneric2<'a>) { x } // Unsound!
3636
fndyn_fixed_generic_multi(x:&dynfor<'a>FixedGeneric1<'a,Assoc2 = &u8>){ x }//~ERROR mismatched types
3737
fndyn_fixed_hrtb(x:&dynFixedHrtb){ x }//~ERROR mismatched types
3838
fndyn_any_different_binders(x:&dynAnyDifferentBinders<Assoc = u8>){ x }//~ERROR mismatched types

‎tests/ui/traits/object/pretty.stderr‎

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -106,17 +106,6 @@ LL | fn dyn_fixed_generic1(x: &dyn for<'a> FixedGeneric1<'a>) { x }
106106
= note: expected unit type `()`
107107
found reference `&dyn for<'a> FixedGeneric1<'a>`
108108

109-
error[E0308]: mismatched types
110-
--> $DIR/pretty.rs:35:60
111-
|
112-
LL | fn dyn_fixed_generic2(x: &dyn for<'a> FixedGeneric2<'a>) { x }
113-
| - ^ expected `()`, found `&dyn FixedGeneric2<'a>`
114-
| |
115-
| help: try adding a return type: `-> &dyn for<'a> FixedGeneric2<'a>`
116-
|
117-
= note: expected unit type `()`
118-
found reference `&dyn for<'a> FixedGeneric2<'a>`
119-
120109
error[E0308]: mismatched types
121110
--> $DIR/pretty.rs:36:79
122111
|
@@ -172,6 +161,6 @@ LL | fn dyn_has_gat(x: &dyn HasGat<u8, Assoc<bool> = ()>) { x }
172161
= note: expected unit type `()`
173162
found reference `&dyn HasGat<u8, Assoc<bool> = ()>`
174163

175-
error: aborting due to 15 previous errors; 1 warning emitted
164+
error: aborting due to 14 previous errors; 1 warning emitted
176165

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

0 commit comments

Comments
 (0)