Do not deduplicate list of associated types provided by dyn principal

This commit is contained in:
Michael Goulet 2025-02-03 02:38:56 +00:00
parent 71e06b9c59
commit 72bd174c43
18 changed files with 265 additions and 117 deletions

View file

@ -1,4 +1,4 @@
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_errors::codes::*;
use rustc_errors::struct_span_code_err;
use rustc_hir as hir;
@ -58,9 +58,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
}
let (trait_bounds, mut projection_bounds) =
let (elaborated_trait_bounds, elaborated_projection_bounds) =
traits::expand_trait_aliases(tcx, user_written_bounds.iter().copied());
let (regular_traits, mut auto_traits): (Vec<_>, Vec<_>) = trait_bounds
let (regular_traits, mut auto_traits): (Vec<_>, Vec<_>) = elaborated_trait_bounds
.into_iter()
.partition(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id()));
@ -103,29 +103,81 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
}
// Map the projection bounds onto a key that makes it easy to remove redundant
// bounds that are constrained by supertraits of the principal def id.
//
// Also make sure we detect conflicting bounds from expanding a trait alias and
// also specifying it manually, like:
// ```
// type Alias = Trait<Assoc = i32>;
// let _: &dyn Alias<Assoc = u32> = /* ... */;
// ```
let mut projection_bounds = FxIndexMap::default();
for (proj, proj_span) in elaborated_projection_bounds {
let key = (
proj.skip_binder().projection_term.def_id,
tcx.anonymize_bound_vars(
proj.map_bound(|proj| proj.projection_term.trait_ref(tcx)),
),
);
if let Some((old_proj, old_proj_span)) =
projection_bounds.insert(key, (proj, proj_span))
&& tcx.anonymize_bound_vars(proj) != tcx.anonymize_bound_vars(old_proj)
{
let item = tcx.item_name(proj.item_def_id());
self.dcx()
.struct_span_err(
span,
format!(
"conflicting associated type bounds for `{item}` when \
expanding trait alias"
),
)
.with_span_label(
old_proj_span,
format!("`{item}` is specified to be `{}` here", old_proj.term()),
)
.with_span_label(
proj_span,
format!("`{item}` is specified to be `{}` here", proj.term()),
)
.emit();
}
}
let principal_trait = regular_traits.into_iter().next();
let mut needed_associated_types = FxIndexSet::default();
if let Some((principal_trait, spans)) = &principal_trait {
let pred: ty::Predicate<'tcx> = (*principal_trait).upcast(tcx);
for ClauseWithSupertraitSpan { pred, supertrait_span } in traits::elaborate(
let mut needed_associated_types = vec![];
if let Some((principal_trait, ref spans)) = principal_trait {
let principal_trait = principal_trait.map_bound(|trait_pred| {
assert_eq!(trait_pred.polarity, ty::PredicatePolarity::Positive);
trait_pred.trait_ref
});
for ClauseWithSupertraitSpan { clause, supertrait_span } in traits::elaborate(
tcx,
[ClauseWithSupertraitSpan::new(pred, *spans.last().unwrap())],
[ClauseWithSupertraitSpan::new(
ty::TraitRef::identity(tcx, principal_trait.def_id()).upcast(tcx),
*spans.last().unwrap(),
)],
)
.filter_only_self()
{
debug!("observing object predicate `{pred:?}`");
let clause = clause.instantiate_supertrait(tcx, principal_trait);
debug!("observing object predicate `{clause:?}`");
let bound_predicate = pred.kind();
let bound_predicate = clause.kind();
match bound_predicate.skip_binder() {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
ty::ClauseKind::Trait(pred) => {
// FIXME(negative_bounds): Handle this correctly...
let trait_ref =
tcx.anonymize_bound_vars(bound_predicate.rebind(pred.trait_ref));
needed_associated_types.extend(
tcx.associated_items(trait_ref.def_id())
tcx.associated_items(pred.trait_ref.def_id)
.in_definition_order()
// We only care about associated types.
.filter(|item| item.kind == ty::AssocKind::Type)
// No RPITITs -- even with `async_fn_in_dyn_trait`, they are implicit.
.filter(|item| !item.is_impl_trait_in_trait())
// If the associated type has a `where Self: Sized` bound,
// we do not need to constrain the associated type.
@ -133,7 +185,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.map(|item| (item.def_id, trait_ref)),
);
}
ty::PredicateKind::Clause(ty::ClauseKind::Projection(pred)) => {
ty::ClauseKind::Projection(pred) => {
let pred = bound_predicate.rebind(pred);
// A `Self` within the original bound will be instantiated with a
// `trait_object_dummy_self`, so check for that.
@ -161,8 +213,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// `dyn MyTrait<MyOutput = X, Output = X>`, which is uglier but works. See
// the discussion in #56288 for alternatives.
if !references_self {
// Include projections defined on supertraits.
projection_bounds.push((pred, supertrait_span));
let key = (
pred.skip_binder().projection_term.def_id,
tcx.anonymize_bound_vars(
pred.map_bound(|proj| proj.projection_term.trait_ref(tcx)),
),
);
if !projection_bounds.contains_key(&key) {
projection_bounds.insert(key, (pred, supertrait_span));
}
}
self.check_elaborated_projection_mentions_input_lifetimes(
@ -182,12 +241,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// types that we expect to be provided by the user, so the following loop
// removes all the associated types that have a corresponding `Projection`
// clause, either from expanding trait aliases or written by the user.
for &(projection_bound, span) in &projection_bounds {
for &(projection_bound, span) in projection_bounds.values() {
let def_id = projection_bound.item_def_id();
let trait_ref = tcx.anonymize_bound_vars(
projection_bound.map_bound(|p| p.projection_term.trait_ref(tcx)),
);
needed_associated_types.swap_remove(&(def_id, trait_ref));
if tcx.generics_require_sized_self(def_id) {
tcx.emit_node_span_lint(
UNUSED_ASSOCIATED_TYPE_BOUNDS,
@ -198,9 +253,22 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
}
let mut missing_assoc_types = FxIndexSet::default();
let projection_bounds: Vec<_> = needed_associated_types
.into_iter()
.filter_map(|key| {
if let Some(assoc) = projection_bounds.get(&key) {
Some(*assoc)
} else {
missing_assoc_types.insert(key);
None
}
})
.collect();
if let Err(guar) = self.check_for_required_assoc_tys(
principal_trait.as_ref().map_or(smallvec![], |(_, spans)| spans.clone()),
needed_associated_types,
missing_assoc_types,
potential_assoc_types,
hir_bounds,
) {
@ -266,7 +334,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
})
});
let existential_projections = projection_bounds.iter().map(|(bound, _)| {
let existential_projections = projection_bounds.into_iter().map(|(bound, _)| {
bound.map_bound(|mut b| {
assert_eq!(b.projection_term.self_ty(), dummy_self);
@ -291,12 +359,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
})
});
let auto_trait_predicates = auto_traits.into_iter().map(|(trait_pred, _)| {
assert_eq!(trait_pred.polarity(), ty::PredicatePolarity::Positive);
assert_eq!(trait_pred.self_ty().skip_binder(), dummy_self);
let mut auto_trait_predicates: Vec<_> = auto_traits
.into_iter()
.map(|(trait_pred, _)| {
assert_eq!(trait_pred.polarity(), ty::PredicatePolarity::Positive);
assert_eq!(trait_pred.self_ty().skip_binder(), dummy_self);
ty::Binder::dummy(ty::ExistentialPredicate::AutoTrait(trait_pred.def_id()))
});
ty::Binder::dummy(ty::ExistentialPredicate::AutoTrait(trait_pred.def_id()))
})
.collect();
auto_trait_predicates.dedup();
// N.b. principal, projections, auto traits
// FIXME: This is actually wrong with multiple principals in regards to symbol mangling
@ -306,7 +378,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.chain(auto_trait_predicates)
.collect::<SmallVec<[_; 8]>>();
v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
v.dedup();
let existential_predicates = tcx.mk_poly_existential_predicates(&v);
// Use explicitly-specified region bound, unless the bound is missing.

View file

@ -79,20 +79,11 @@ impl<'tcx> Relate<TyCtxt<'tcx>> for &'tcx ty::List<ty::PolyExistentialPredicate<
b: Self,
) -> RelateResult<'tcx, Self> {
let tcx = relation.cx();
// FIXME: this is wasteful, but want to do a perf run to see how slow it is.
// We need to perform this deduplication as we sometimes generate duplicate projections
// in `a`.
let mut a_v: Vec<_> = a.into_iter().collect();
let mut b_v: Vec<_> = b.into_iter().collect();
a_v.dedup();
b_v.dedup();
if a_v.len() != b_v.len() {
if a.len() != b.len() {
return Err(TypeError::ExistentialMismatch(ExpectedFound::new(a, b)));
}
let v = iter::zip(a_v, b_v).map(|(ep_a, ep_b)| {
match (ep_a.skip_binder(), ep_b.skip_binder()) {
let v =
iter::zip(a, b).map(|(ep_a, ep_b)| match (ep_a.skip_binder(), ep_b.skip_binder()) {
(ty::ExistentialPredicate::Trait(a), ty::ExistentialPredicate::Trait(b)) => {
Ok(ep_a.rebind(ty::ExistentialPredicate::Trait(
relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(),
@ -109,8 +100,7 @@ impl<'tcx> Relate<TyCtxt<'tcx>> for &'tcx ty::List<ty::PolyExistentialPredicate<
ty::ExistentialPredicate::AutoTrait(b),
) if a == b => Ok(ep_a.rebind(ty::ExistentialPredicate::AutoTrait(a))),
_ => Err(TypeError::ExistentialMismatch(ExpectedFound::new(a, b))),
}
});
});
tcx.mk_poly_existential_predicates_from_iter(v)
}
}

View file

@ -44,25 +44,22 @@ pub trait Elaboratable<I: Interner> {
}
pub struct ClauseWithSupertraitSpan<I: Interner> {
pub pred: I::Predicate,
pub clause: I::Clause,
// 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, supertrait_span: span }
pub fn new(clause: I::Clause, span: I::Span) -> Self {
ClauseWithSupertraitSpan { clause, supertrait_span: span }
}
}
impl<I: Interner> Elaboratable<I> for ClauseWithSupertraitSpan<I> {
fn predicate(&self) -> <I as Interner>::Predicate {
self.pred
self.clause.as_predicate()
}
fn child(&self, clause: <I as Interner>::Clause) -> Self {
ClauseWithSupertraitSpan {
pred: clause.as_predicate(),
supertrait_span: self.supertrait_span,
}
ClauseWithSupertraitSpan { clause, supertrait_span: self.supertrait_span }
}
fn child_with_derived_cause(
@ -72,7 +69,7 @@ impl<I: Interner> Elaboratable<I> for ClauseWithSupertraitSpan<I> {
_parent_trait_pred: crate::Binder<I, crate::TraitPredicate<I>>,
_index: usize,
) -> Self {
ClauseWithSupertraitSpan { pred: clause.as_predicate(), supertrait_span }
ClauseWithSupertraitSpan { clause, supertrait_span }
}
}

View file

@ -1,20 +0,0 @@
//@ known-bug: rust-lang/rust#125957
#![feature(generic_const_exprs)]
#![allow(incomplete_features)]
#![feature(associated_const_equality)]
pub struct Equal<const T: usize, const R: usize>();
pub enum ParseMode {
Raw,
}
pub trait Parse {
const PARSE_MODE: ParseMode;
}
pub trait RenderRaw: Parse<PARSE_MODE = { ParseMode::Raw }> {}
trait GenericVec<T> {
fn unwrap() -> dyn RenderRaw;
}
fn main() {}

View file

@ -1,28 +0,0 @@
//@ known-bug: #132330
//@compile-flags: -Znext-solver=globally
trait Service {
type S;
}
trait Framing {
type F;
}
impl Framing for () {
type F = ();
}
trait HttpService<F: Framing>: Service<S = F::F> {}
type BoxService = Box<dyn HttpService<(), S = ()>>;
fn build_server<F: FnOnce() -> BoxService>(_: F) {}
fn make_server<F: Framing>() -> Box<dyn HttpService<F, S = F::F>> {
unimplemented!()
}
fn main() {
build_server(|| make_server())
}

View file

@ -4,5 +4,5 @@ trait I32Iterator = Iterator<Item = i32>;
fn main() {
let _: &dyn I32Iterator<Item = u32> = &vec![42].into_iter();
//~^ ERROR expected `IntoIter<u32>` to be an iterator that yields `i32`, but it yields `u32`
//~^ ERROR conflicting associated type bounds
}

View file

@ -1,11 +1,13 @@
error[E0271]: expected `IntoIter<u32>` to be an iterator that yields `i32`, but it yields `u32`
--> $DIR/associated-types-overridden-binding-2.rs:6:43
error: conflicting associated type bounds for `Item` when expanding trait alias
--> $DIR/associated-types-overridden-binding-2.rs:6:13
|
LL | trait I32Iterator = Iterator<Item = i32>;
| ---------- `Item` is specified to be `i32` here
...
LL | let _: &dyn I32Iterator<Item = u32> = &vec![42].into_iter();
| ^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `u32`
|
= note: required for the cast from `&std::vec::IntoIter<u32>` to `&dyn Iterator<Item = u32, Item = i32>`
| ^^^^^^^^^^^^^^^^----------^
| |
| `Item` is specified to be `u32` here
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0271`.

View file

@ -8,4 +8,5 @@ trait U32Iterator = I32Iterator<Item = u32>; //~ ERROR type annotations needed
fn main() {
let _: &dyn I32Iterator<Item = u32>;
//~^ ERROR conflicting associated type bounds
}

View file

@ -22,6 +22,17 @@ note: required by a bound in `I32Iterator`
LL | trait I32Iterator = Iterator<Item = i32>;
| ^^^^^^^^^^ required by this bound in `I32Iterator`
error: aborting due to 2 previous errors
error: conflicting associated type bounds for `Item` when expanding trait alias
--> $DIR/associated-types-overridden-binding.rs:10:13
|
LL | trait I32Iterator = Iterator<Item = i32>;
| ---------- `Item` is specified to be `i32` here
...
LL | let _: &dyn I32Iterator<Item = u32>;
| ^^^^^^^^^^^^^^^^----------^
| |
| `Item` is specified to be `u32` here
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0284`.

View file

@ -0,0 +1,18 @@
//@ check-pass
// This test checks that we look at consider the super traits of trait objects
// when deducing closure signatures.
trait Foo: Fn(Bar) {}
impl<T> Foo for T where T: Fn(Bar) {}
struct Bar;
impl Bar {
fn bar(&self) {}
}
fn main() {
let x: &dyn Foo = &|x| {
x.bar();
};
}

View file

@ -0,0 +1,21 @@
//@ check-pass
// We previously incorrectly deduplicated the list of projection bounds
// of trait objects, causing us to incorrectly reject this code, cc #136458.
trait Sup<T> {
type Assoc;
}
impl<T> Sup<T> for () {
type Assoc = T;
}
trait Trait<A, B>: Sup<A, Assoc = A> + Sup<B, Assoc = B> {}
impl<T, U> Trait<T, U> for () {}
fn main() {
let x: &dyn Trait<(), _> = &();
let y: &dyn Trait<_, ()> = x;
}

View file

@ -1,9 +1,12 @@
//@ known-bug: rust-lang/rust#126944
//@ check-pass
// Regression test for #126944.
// Step 1: Create two names for a single type: `Thing` and `AlsoThing`
struct Thing;
struct Dummy;
pub trait DummyTrait {
trait DummyTrait {
type DummyType;
}
impl DummyTrait for Dummy {
@ -13,7 +16,7 @@ type AlsoThing = <Dummy as DummyTrait>::DummyType;
// Step 2: Create names for a single trait object type: `TraitObject` and `AlsoTraitObject`
pub trait SomeTrait {
trait SomeTrait {
type Item;
}
type TraitObject = dyn SomeTrait<Item = AlsoThing>;
@ -21,12 +24,12 @@ type AlsoTraitObject = dyn SomeTrait<Item = Thing>;
// Step 3: Force the compiler to check whether the two names are the same type
pub trait Supertrait {
trait Supertrait {
type Foo;
}
pub trait Subtrait: Supertrait<Foo = TraitObject> {}
trait Subtrait: Supertrait<Foo = TraitObject> {}
pub trait HasOutput<A: ?Sized> {
trait HasOutput<A: ?Sized> {
type Output;
}
@ -36,3 +39,5 @@ where
{
todo!()
}
fn main() {}

View file

@ -0,0 +1,32 @@
// Regression test for #133361.
trait Sup<T> {
type Assoc;
}
impl<T> Sup<T> for () {
type Assoc = T;
}
impl<T, U> Dyn<T, U> for () {}
trait Dyn<A, B>: Sup<A, Assoc = A> + Sup<B, Assoc = B> {}
trait Trait {
type Assoc;
}
impl Trait for dyn Dyn<(), ()> {
type Assoc = &'static str;
}
impl<A, B> Trait for dyn Dyn<A, B> {
//~^ ERROR conflicting implementations of trait `Trait` for type `(dyn Dyn<(), ()> + 'static)`
type Assoc = usize;
}
fn call<A, B>(x: usize) -> <dyn Dyn<A, B> as Trait>::Assoc {
x
}
fn main() {
let x: &'static str = call::<(), ()>(0xDEADBEEF);
println!("{x}");
}

View file

@ -0,0 +1,12 @@
error[E0119]: conflicting implementations of trait `Trait` for type `(dyn Dyn<(), ()> + 'static)`
--> $DIR/incomplete-multiple-super-projection.rs:20:1
|
LL | impl Trait for dyn Dyn<(), ()> {
| ------------------------------ first implementation here
...
LL | impl<A, B> Trait for dyn Dyn<A, B> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Dyn<(), ()> + 'static)`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0119`.

View file

@ -1,4 +1,4 @@
//@ known-bug: #79590
//@ check-pass
trait Database: Restriction<Inner = u32> {}

View file

@ -0,0 +1,24 @@
//@ check-pass
// Make sure that we still deduce outlives bounds from supertrait projections
// and require them for well-formedness.
trait Trait {
type Assoc;
}
trait Bar {
type Assoc;
}
trait Foo<'a, T: 'a>: Bar<Assoc = &'a T> {
}
fn outlives<'a, T: 'a>() {}
fn implied_outlives<'a, T: Trait>(x: &dyn Foo<'a, T::Assoc>) {
outlives::<'a, T::Assoc>();
}
fn main() {}

View file

@ -154,12 +154,12 @@ error[E0308]: mismatched types
--> $DIR/pretty.rs:41:56
|
LL | fn dyn_has_gat(x: &dyn HasGat<u8, Assoc<bool> = ()>) { x }
| - ^ expected `()`, found `&dyn HasGat<u8, Assoc<bool> = ()>`
| - ^ expected `()`, found `&dyn HasGat<u8>`
| |
| help: try adding a return type: `-> &dyn HasGat<u8, Assoc<bool> = ()>`
| help: try adding a return type: `-> &dyn HasGat<u8>`
|
= note: expected unit type `()`
found reference `&dyn HasGat<u8, Assoc<bool> = ()>`
found reference `&dyn HasGat<u8>`
error: aborting due to 14 previous errors; 1 warning emitted

View file

@ -0,0 +1,12 @@
//@ check-pass
trait Foo: Bar<Out = ()> {}
trait Bar {
type Out;
}
fn w(x: &dyn Foo<Out = ()>) {
let x: &dyn Foo = x;
}
fn main() {}