Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot upcast dyn Trait in a way that reduces the number of associated types #114035

Closed
compiler-errors opened this issue Jul 24, 2023 · 0 comments · Fixed by #114036
Closed

Cannot upcast dyn Trait in a way that reduces the number of associated types #114035

compiler-errors opened this issue Jul 24, 2023 · 0 comments · Fixed by #114036
Assignees
Labels
C-bug Category: This is a bug. F-trait_upcasting `#![feature(trait_upcasting)]` T-types Relevant to the types team, which will review and decide on the PR/issue.

Comments

@compiler-errors
Copy link
Member

I tried this code:

#![feature(trait_upcasting)]

trait A: B {
    type Assoc;
}

trait B {}

fn upcast(a: &dyn A<Assoc = i32>) -> &dyn B { a }

fn main() {}

I expected to see it compile.

Instead, this happened:

error[E0308]: mismatched types
 --> src/main.rs:9:47
  |
9 | fn upcast(a: &dyn A<Assoc = i32>) -> &dyn B { a }
  |                                      ------   ^ expected trait `B`, found trait `A<Assoc = i32>`
  |                                      |
  |                                      expected `&dyn B` because of return type
  |
  = note: expected reference `&dyn B`
             found reference `&dyn A<Assoc = i32>`

The algorithm we use to do trait upcasting is incorrect, since it simply copies the existential associated types over:

.chain(
data_a
.projection_bounds()
.map(|b| b.map_bound(ty::ExistentialPredicate::Projection)),
)

So if we upcast to a trait with fewer associated types (like B), then the subtyping we do here is wrong:

let InferOk { obligations, .. } = self
.infcx
.at(&obligation.cause, obligation.param_env)
.sup(DefineOpaqueTypes::No, target, source_trait)
.map_err(|_| Unimplemented)?;

Since we require the list of existential trait bounds to be structurally compatible:

let mut a_v: Vec<_> = a.into_iter().collect();
let mut b_v: Vec<_> = b.into_iter().collect();
// `skip_binder` here is okay because `stable_cmp` doesn't look at binders
a_v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
a_v.dedup();
b_v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
b_v.dedup();
if a_v.len() != b_v.len() {
return Err(TypeError::ExistentialMismatch(expected_found(relation, a, b)));
}

@compiler-errors compiler-errors added C-bug Category: This is a bug. F-trait_upcasting `#![feature(trait_upcasting)]` labels Jul 24, 2023
@compiler-errors compiler-errors self-assigned this Jul 24, 2023
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Jul 24, 2023
@compiler-errors compiler-errors added T-types Relevant to the types team, which will review and decide on the PR/issue. and removed needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels Jul 24, 2023
@bors bors closed this as completed in 4f7bb98 Aug 4, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: This is a bug. F-trait_upcasting `#![feature(trait_upcasting)]` T-types Relevant to the types team, which will review and decide on the PR/issue.
Projects
None yet
2 participants