-
Notifications
You must be signed in to change notification settings - Fork 13.6k
Description
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:
rust/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
Lines 933 to 937 in fd56162
.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:
rust/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
Lines 949 to 953 in fd56162
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:
rust/compiler/rustc_middle/src/ty/relate.rs
Lines 689 to 698 in fd56162
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))); | |
} |