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.
This commit is contained in:
commit
9510c7366d
8 changed files with 149 additions and 18 deletions
|
@ -13,6 +13,7 @@ use rustc_middle::ty::{
|
||||||
use rustc_span::{ErrorGuaranteed, Span};
|
use rustc_span::{ErrorGuaranteed, Span};
|
||||||
use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility;
|
use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility;
|
||||||
use rustc_trait_selection::traits::{self, hir_ty_lowering_dyn_compatibility_violations};
|
use rustc_trait_selection::traits::{self, hir_ty_lowering_dyn_compatibility_violations};
|
||||||
|
use rustc_type_ir::elaborate::ClauseWithSupertraitSpan;
|
||||||
use smallvec::{SmallVec, smallvec};
|
use smallvec::{SmallVec, smallvec};
|
||||||
use tracing::{debug, instrument};
|
use tracing::{debug, instrument};
|
||||||
|
|
||||||
|
@ -124,16 +125,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id()));
|
.filter(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id()));
|
||||||
|
|
||||||
for (base_trait_ref, span) in regular_traits_refs_spans {
|
for (base_trait_ref, original_span) in regular_traits_refs_spans {
|
||||||
let base_pred: ty::Predicate<'tcx> = base_trait_ref.upcast(tcx);
|
let base_pred: ty::Predicate<'tcx> = base_trait_ref.upcast(tcx);
|
||||||
for pred in traits::elaborate(tcx, [base_pred]).filter_only_self() {
|
for ClauseWithSupertraitSpan { pred, original_span, supertrait_span } in
|
||||||
|
traits::elaborate(tcx, [ClauseWithSupertraitSpan::new(base_pred, original_span)])
|
||||||
|
.filter_only_self()
|
||||||
|
{
|
||||||
debug!("observing object predicate `{pred:?}`");
|
debug!("observing object predicate `{pred:?}`");
|
||||||
|
|
||||||
let bound_predicate = pred.kind();
|
let bound_predicate = pred.kind();
|
||||||
match bound_predicate.skip_binder() {
|
match bound_predicate.skip_binder() {
|
||||||
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
|
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
|
||||||
let pred = bound_predicate.rebind(pred);
|
let pred = bound_predicate.rebind(pred);
|
||||||
associated_types.entry(span).or_default().extend(
|
associated_types.entry(original_span).or_default().extend(
|
||||||
tcx.associated_items(pred.def_id())
|
tcx.associated_items(pred.def_id())
|
||||||
.in_definition_order()
|
.in_definition_order()
|
||||||
.filter(|item| item.kind == ty::AssocKind::Type)
|
.filter(|item| item.kind == ty::AssocKind::Type)
|
||||||
|
@ -172,8 +176,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
||||||
// the discussion in #56288 for alternatives.
|
// the discussion in #56288 for alternatives.
|
||||||
if !references_self {
|
if !references_self {
|
||||||
// Include projections defined on supertraits.
|
// Include projections defined on supertraits.
|
||||||
projection_bounds.push((pred, span));
|
projection_bounds.push((pred, original_span));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.check_elaborated_projection_mentions_input_lifetimes(
|
||||||
|
pred,
|
||||||
|
original_span,
|
||||||
|
supertrait_span,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
@ -360,6 +370,56 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
|
||||||
|
|
||||||
Ty::new_dynamic(tcx, existential_predicates, region_bound, representation)
|
Ty::new_dynamic(tcx, existential_predicates, region_bound, representation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check that elaborating the principal of a trait ref doesn't lead to projections
|
||||||
|
/// that are unconstrained. This can happen because an otherwise unconstrained
|
||||||
|
/// *type variable* can be substituted with a type that has late-bound regions. See
|
||||||
|
/// `elaborated-predicates-unconstrained-late-bound.rs` for a test.
|
||||||
|
fn check_elaborated_projection_mentions_input_lifetimes(
|
||||||
|
&self,
|
||||||
|
pred: ty::PolyProjectionPredicate<'tcx>,
|
||||||
|
span: Span,
|
||||||
|
supertrait_span: Span,
|
||||||
|
) {
|
||||||
|
let tcx = self.tcx();
|
||||||
|
|
||||||
|
// Find any late-bound regions declared in `ty` that are not
|
||||||
|
// declared in the trait-ref or assoc_item. These are not well-formed.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad
|
||||||
|
// for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok
|
||||||
|
let late_bound_in_projection_term =
|
||||||
|
tcx.collect_constrained_late_bound_regions(pred.map_bound(|pred| pred.projection_term));
|
||||||
|
let late_bound_in_term =
|
||||||
|
tcx.collect_referenced_late_bound_regions(pred.map_bound(|pred| pred.term));
|
||||||
|
debug!(?late_bound_in_projection_term);
|
||||||
|
debug!(?late_bound_in_term);
|
||||||
|
|
||||||
|
// FIXME: point at the type params that don't have appropriate lifetimes:
|
||||||
|
// struct S1<F: for<'a> Fn(&i32, &i32) -> &'a i32>(F);
|
||||||
|
// ---- ---- ^^^^^^^
|
||||||
|
// NOTE(associated_const_equality): This error should be impossible to trigger
|
||||||
|
// with associated const equality constraints.
|
||||||
|
self.validate_late_bound_regions(
|
||||||
|
late_bound_in_projection_term,
|
||||||
|
late_bound_in_term,
|
||||||
|
|br_name| {
|
||||||
|
let item_name = tcx.item_name(pred.projection_def_id());
|
||||||
|
struct_span_code_err!(
|
||||||
|
self.dcx(),
|
||||||
|
span,
|
||||||
|
E0582,
|
||||||
|
"binding for associated type `{}` references {}, \
|
||||||
|
which does not appear in the trait input types",
|
||||||
|
item_name,
|
||||||
|
br_name
|
||||||
|
)
|
||||||
|
.with_span_label(supertrait_span, "due to this supertrait")
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace_dummy_self_with_error<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
|
fn replace_dummy_self_with_error<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
|
||||||
|
|
|
@ -179,6 +179,10 @@ pub struct Clause<'tcx>(
|
||||||
);
|
);
|
||||||
|
|
||||||
impl<'tcx> rustc_type_ir::inherent::Clause<TyCtxt<'tcx>> for Clause<'tcx> {
|
impl<'tcx> rustc_type_ir::inherent::Clause<TyCtxt<'tcx>> for Clause<'tcx> {
|
||||||
|
fn as_predicate(self) -> Predicate<'tcx> {
|
||||||
|
self.as_predicate()
|
||||||
|
}
|
||||||
|
|
||||||
fn instantiate_supertrait(self, tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) -> Self {
|
fn instantiate_supertrait(self, tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) -> Self {
|
||||||
self.instantiate_supertrait(tcx, trait_ref)
|
self.instantiate_supertrait(tcx, trait_ref)
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,46 @@ pub trait Elaboratable<I: Interner> {
|
||||||
) -> Self;
|
) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ClauseWithSupertraitSpan<I: Interner> {
|
||||||
|
pub pred: I::Predicate,
|
||||||
|
// Span of the original elaborated predicate.
|
||||||
|
pub original_span: I::Span,
|
||||||
|
// Span of the supertrait predicatae that lead to this clause.
|
||||||
|
pub supertrait_span: I::Span,
|
||||||
|
}
|
||||||
|
impl<I: Interner> ClauseWithSupertraitSpan<I> {
|
||||||
|
pub fn new(pred: I::Predicate, span: I::Span) -> Self {
|
||||||
|
ClauseWithSupertraitSpan { pred, original_span: span, supertrait_span: span }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<I: Interner> Elaboratable<I> for ClauseWithSupertraitSpan<I> {
|
||||||
|
fn predicate(&self) -> <I as Interner>::Predicate {
|
||||||
|
self.pred
|
||||||
|
}
|
||||||
|
|
||||||
|
fn child(&self, clause: <I as Interner>::Clause) -> Self {
|
||||||
|
ClauseWithSupertraitSpan {
|
||||||
|
pred: clause.as_predicate(),
|
||||||
|
original_span: self.original_span,
|
||||||
|
supertrait_span: self.supertrait_span,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn child_with_derived_cause(
|
||||||
|
&self,
|
||||||
|
clause: <I as Interner>::Clause,
|
||||||
|
supertrait_span: <I as Interner>::Span,
|
||||||
|
_parent_trait_pred: crate::Binder<I, crate::TraitPredicate<I>>,
|
||||||
|
_index: usize,
|
||||||
|
) -> Self {
|
||||||
|
ClauseWithSupertraitSpan {
|
||||||
|
pred: clause.as_predicate(),
|
||||||
|
original_span: self.original_span,
|
||||||
|
supertrait_span: supertrait_span,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn elaborate<I: Interner, O: Elaboratable<I>>(
|
pub fn elaborate<I: Interner, O: Elaboratable<I>>(
|
||||||
cx: I,
|
cx: I,
|
||||||
obligations: impl IntoIterator<Item = O>,
|
obligations: impl IntoIterator<Item = O>,
|
||||||
|
|
|
@ -460,6 +460,8 @@ pub trait Clause<I: Interner<Clause = Self>>:
|
||||||
+ IntoKind<Kind = ty::Binder<I, ty::ClauseKind<I>>>
|
+ IntoKind<Kind = ty::Binder<I, ty::ClauseKind<I>>>
|
||||||
+ Elaboratable<I>
|
+ Elaboratable<I>
|
||||||
{
|
{
|
||||||
|
fn as_predicate(self) -> I::Predicate;
|
||||||
|
|
||||||
fn as_trait_clause(self) -> Option<ty::Binder<I, ty::TraitPredicate<I>>> {
|
fn as_trait_clause(self) -> Option<ty::Binder<I, ty::TraitPredicate<I>>> {
|
||||||
self.kind()
|
self.kind()
|
||||||
.map_bound(|clause| if let ty::ClauseKind::Trait(t) = clause { Some(t) } else { None })
|
.map_bound(|clause| if let ty::ClauseKind::Trait(t) = clause { Some(t) } else { None })
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
// Make sure that when elaborating the principal of a dyn trait for projection predicates
|
||||||
|
// we don't end up in a situation where we have an unconstrained late-bound lifetime in
|
||||||
|
// the output of a projection.
|
||||||
|
|
||||||
|
// Fix for <https://github.com/rust-lang/rust/issues/130347>.
|
||||||
|
|
||||||
|
trait A<T>: B<T = T> {}
|
||||||
|
|
||||||
|
trait B {
|
||||||
|
type T;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Erase<T: ?Sized + B>(T::T);
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let x = {
|
||||||
|
let x = String::from("hello");
|
||||||
|
|
||||||
|
Erase::<dyn for<'a> A<&'a _>>(x.as_str())
|
||||||
|
//~^ ERROR binding for associated type `T` references lifetime `'a`, which does not appear in the trait input types
|
||||||
|
};
|
||||||
|
|
||||||
|
dbg!(x.0);
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
error[E0582]: binding for associated type `T` references lifetime `'a`, which does not appear in the trait input types
|
||||||
|
--> $DIR/elaborated-predicates-unconstrained-late-bound.rs:19:21
|
||||||
|
|
|
||||||
|
LL | trait A<T>: B<T = T> {}
|
||||||
|
| ----- due to this supertrait
|
||||||
|
...
|
||||||
|
LL | Erase::<dyn for<'a> A<&'a _>>(x.as_str())
|
||||||
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0582`.
|
|
@ -13,7 +13,7 @@ trait SuperGeneric<'a> {
|
||||||
}
|
}
|
||||||
trait AnyGeneric<'a>: SuperGeneric<'a> {}
|
trait AnyGeneric<'a>: SuperGeneric<'a> {}
|
||||||
trait FixedGeneric1<'a>: SuperGeneric<'a, Assoc2 = &'a u8> {}
|
trait FixedGeneric1<'a>: SuperGeneric<'a, Assoc2 = &'a u8> {}
|
||||||
trait FixedGeneric2<'a>: Super<Assoc = &'a u8> {}
|
// trait FixedGeneric2<'a>: Super<Assoc = &'a u8> {} // Unsound!
|
||||||
trait FixedHrtb: for<'a> SuperGeneric<'a, Assoc2 = &'a u8> {}
|
trait FixedHrtb: for<'a> SuperGeneric<'a, Assoc2 = &'a u8> {}
|
||||||
trait AnyDifferentBinders: for<'a> SuperGeneric<'a, Assoc2 = &'a u8> + Super {}
|
trait AnyDifferentBinders: for<'a> SuperGeneric<'a, Assoc2 = &'a u8> + Super {}
|
||||||
trait FixedDifferentBinders: for<'a> SuperGeneric<'a, Assoc2 = &'a u8> + Super<Assoc = u8> {}
|
trait FixedDifferentBinders: for<'a> SuperGeneric<'a, Assoc2 = &'a u8> + Super<Assoc = u8> {}
|
||||||
|
@ -32,7 +32,7 @@ fn dyn_fixed_static(x: &dyn FixedStatic) { x } //~ERROR mismatched types
|
||||||
fn dyn_super_generic(x: &dyn for<'a> SuperGeneric<'a, Assoc2 = &'a u8>) { x } //~ERROR mismatched types
|
fn dyn_super_generic(x: &dyn for<'a> SuperGeneric<'a, Assoc2 = &'a u8>) { x } //~ERROR mismatched types
|
||||||
fn dyn_any_generic(x: &dyn for<'a> AnyGeneric<'a, Assoc2 = &'a u8>) { x } //~ERROR mismatched types
|
fn dyn_any_generic(x: &dyn for<'a> AnyGeneric<'a, Assoc2 = &'a u8>) { x } //~ERROR mismatched types
|
||||||
fn dyn_fixed_generic1(x: &dyn for<'a> FixedGeneric1<'a>) { x } //~ERROR mismatched types
|
fn dyn_fixed_generic1(x: &dyn for<'a> FixedGeneric1<'a>) { x } //~ERROR mismatched types
|
||||||
fn dyn_fixed_generic2(x: &dyn for<'a> FixedGeneric2<'a>) { x } //~ERROR mismatched types
|
// fn dyn_fixed_generic2(x: &dyn for<'a> FixedGeneric2<'a>) { x } // Unsound!
|
||||||
fn dyn_fixed_generic_multi(x: &dyn for<'a> FixedGeneric1<'a, Assoc2 = &u8>) { x } //~ERROR mismatched types
|
fn dyn_fixed_generic_multi(x: &dyn for<'a> FixedGeneric1<'a, Assoc2 = &u8>) { x } //~ERROR mismatched types
|
||||||
fn dyn_fixed_hrtb(x: &dyn FixedHrtb) { x } //~ERROR mismatched types
|
fn dyn_fixed_hrtb(x: &dyn FixedHrtb) { x } //~ERROR mismatched types
|
||||||
fn dyn_any_different_binders(x: &dyn AnyDifferentBinders<Assoc = u8>) { x } //~ERROR mismatched types
|
fn dyn_any_different_binders(x: &dyn AnyDifferentBinders<Assoc = u8>) { x } //~ERROR mismatched types
|
||||||
|
|
|
@ -106,17 +106,6 @@ LL | fn dyn_fixed_generic1(x: &dyn for<'a> FixedGeneric1<'a>) { x }
|
||||||
= note: expected unit type `()`
|
= note: expected unit type `()`
|
||||||
found reference `&dyn for<'a> FixedGeneric1<'a>`
|
found reference `&dyn for<'a> FixedGeneric1<'a>`
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
|
||||||
--> $DIR/pretty.rs:35:60
|
|
||||||
|
|
|
||||||
LL | fn dyn_fixed_generic2(x: &dyn for<'a> FixedGeneric2<'a>) { x }
|
|
||||||
| - ^ expected `()`, found `&dyn FixedGeneric2<'a>`
|
|
||||||
| |
|
|
||||||
| help: try adding a return type: `-> &dyn for<'a> FixedGeneric2<'a>`
|
|
||||||
|
|
|
||||||
= note: expected unit type `()`
|
|
||||||
found reference `&dyn for<'a> FixedGeneric2<'a>`
|
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/pretty.rs:36:79
|
--> $DIR/pretty.rs:36:79
|
||||||
|
|
|
|
||||||
|
@ -172,6 +161,6 @@ LL | fn dyn_has_gat(x: &dyn HasGat<u8, Assoc<bool> = ()>) { x }
|
||||||
= note: expected unit type `()`
|
= note: expected unit type `()`
|
||||||
found reference `&dyn HasGat<u8, Assoc<bool> = ()>`
|
found reference `&dyn HasGat<u8, Assoc<bool> = ()>`
|
||||||
|
|
||||||
error: aborting due to 15 previous errors; 1 warning emitted
|
error: aborting due to 14 previous errors; 1 warning emitted
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0308`.
|
For more information about this error, try `rustc --explain E0308`.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue