Uh oh!
There was an error while loading. Please reload this page.
Allow instantiating object trait binder when upcasting - #130866
Conversation
lcnr
left a comment
There was a problem hiding this comment.
we previously incorrectly used sub there in the wrong order, resulting in an unsoundness fixed by #119849, cc https://github.com/rust-lang/rust/pull/119849/files#r1525200073
I fully agree that not requiring the binders to be equal is desirable here.
I would like to manually instantiate the binders here instead of relying on sub. This is easier to read imo and makes it far clearer what's going on.
r=me after FCP
lcnr
commented
Sep 26, 2024
Wait, why would this need an FCP? trait upcasting is still unstable, is it not? |
compiler-errors
commented
Sep 26, 2024
Oh yeah, I guess it's double unstable: new solver and/or feature gate. |
lcnr
commented
Sep 26, 2024
alright, so r=me after manually instantiating the binders :3 unless you're strongly opposed to doing that |
compiler-errors
commented
Sep 27, 2024
No, instantiating the binder is a bit more verbose but I agree it makes it much clearer who is the placeholders and who is the infer vars. |
8de3078 to
2ed14f3Comparecompiler-errors
commented
Sep 27, 2024
Could use another review. The implementation is less than beautiful in both the new and old solvers:
|
Uh oh!
There was an error while loading. Please reload this page.
| value: ty::Binder<I, T>, | ||
| f: impl FnOnce(&mut Self, T) -> U, | ||
| ) -> U { | ||
| self.delegate.enter_forall(value, |value| f(self, value)) |
There was a problem hiding this comment.
given that enter_forall is only used once, maybe it's easier to always use enter_forall_mut by modifying the signature of neter_forall.
While you're at it, change fn compute_goal to use self.enter_forall from self.delegate.enter_forall 😁
There was a problem hiding this comment.
I remember there being a specific problem where we had an infectious &mut self that ended up changing quite a lot if we did this
There was a problem hiding this comment.
nvm, just needed to move an arg lol
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
2ed14f3 to
d753abaComparecompiler-errors
commented
Sep 27, 2024
@bors r=lcnr |
bors
commented
Sep 27, 2024
…iaskrgr Rollup of 8 pull requests Successful merges: - rust-lang#125404 (Fix `read_buf` uses in `std`) - rust-lang#130866 (Allow instantiating object trait binder when upcasting) - rust-lang#130922 (Reference UNSPECIFIED instead of INADDR_ANY in join_multicast_v4) - rust-lang#130924 (Make clashing_extern_declarations considering generic args for ADT field) - rust-lang#130939 (rustdoc: update `ProcMacro` docs section on helper attributes) - rust-lang#130940 (Revert space-saving operations) - rust-lang#130944 (Allow instantiating trait object binder in ptr-to-ptr casts) - rust-lang#130953 (Rename a few tests to make tidy happier) r? `@ghost` `@rustbot` modify labels: rollup
Rollup merge of rust-lang#130866 - compiler-errors:dyn-instantiate-binder, r=lcnr Allow instantiating object trait binder when upcasting This PR fixes two bugs (that probably need an FCP). ### We use equality rather than subtyping for upcasting dyn conversions This code should be valid: ```rust #![feature(trait_upcasting)] trait Foo: for<'h> Bar<'h> {} trait Bar<'a> {} fn foo(x: &dyn Foo) { let y: &dyn Bar<'static> = x; } ``` But instead: ``` error[E0308]: mismatched types --> src/lib.rs:7:32 | 7 | let y: &dyn Bar<'static> = x; | ^ one type is more general than the other | = note: expected existential trait ref `for<'h> Bar<'h>` found existential trait ref `Bar<'_>` ``` And so should this: ```rust #![feature(trait_upcasting)] fn foo(x: &dyn for<'h> Fn(&'h ())) { let y: &dyn FnOnce(&'static ()) = x; } ``` But instead: ``` error[E0308]: mismatched types --> src/lib.rs:4:39 | 4 | let y: &dyn FnOnce(&'static ()) = x; | ^ one type is more general than the other | = note: expected existential trait ref `for<'h> FnOnce<(&'h (),)>` found existential trait ref `FnOnce<(&(),)>` ``` Specifically, both of these fail because we use *equality* when comparing the supertrait to the *target* of the unsize goal. For the first example, since our supertrait is `for<'h> Bar<'h>` but our target is `Bar<'static>`, there's a higher-ranked type mismatch even though we *should* be able to instantiate that supertrait binder when upcasting. Similarly for the second example. ### New solver uses equality rather than subtyping for no-op (i.e. non-upcasting) dyn conversions This code should be valid in the new solver, like it is with the old solver: ```rust // -Znext-solver fn foo<'a>(x: &mut for<'h> dyn Fn(&'h ())) { let _: &mut dyn Fn(&'a ()) = x; } ``` But instead: ``` error: lifetime may not live long enough --> <source>:2:11 | 1 | fn foo<'a>(x: &mut dyn for<'h> Fn(&'h ())) { | -- lifetime `'a` defined here 2 | let _: &mut dyn Fn(&'a ()) = x; | ^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` | = note: requirement occurs because of a mutable reference to `dyn Fn(&())` ``` Specifically, this fails because we try to coerce `&mut dyn for<'h> Fn(&'h ())` to `&mut dyn Fn(&'a ())`, which registers an `dyn for<'h> Fn(&'h ()): dyn Fn(&'a ())` goal. This fails because the new solver uses *equating* rather than *subtyping* in `Unsize` goals. This is *mostly* not a problem... You may wonder why the same code passes on the new solver for immutable references: ``` // -Znext-solver fn foo<'a>(x: &dyn Fn(&())) { let _: &dyn Fn(&'a ()) = x; // works } ``` That's because in this case, we first try to coerce via `Unsize`, but due to the leak check the goal fails. Then, later in coercion, we fall back to a simple subtyping operation, which *does* work. Since `&T` is covariant over `T`, but `&mut T` is invariant, that's where the discrepancy between these two examples crops up. --- r? lcnr or reassign :D
This PR fixes two bugs (that probably need an FCP).
We use equality rather than subtyping for upcasting dyn conversions
This code should be valid:
But instead:
And so should this:
But instead:
Specifically, both of these fail because we use equality when comparing the supertrait to the target of the unsize goal. For the first example, since our supertrait is
for<'h> Bar<'h>but our target isBar<'static>, there's a higher-ranked type mismatch even though we should be able to instantiate that supertrait binder when upcasting. Similarly for the second example.New solver uses equality rather than subtyping for no-op (i.e. non-upcasting) dyn conversions
This code should be valid in the new solver, like it is with the old solver:
But instead:
Specifically, this fails because we try to coerce
&mut dyn for<'h> Fn(&'h ())to&mut dyn Fn(&'a ()), which registers andyn for<'h> Fn(&'h ()): dyn Fn(&'a ())goal. This fails because the new solver uses equating rather than subtyping inUnsizegoals.This is mostly not a problem... You may wonder why the same code passes on the new solver for immutable references:
That's because in this case, we first try to coerce via
Unsize, but due to the leak check the goal fails. Then, later in coercion, we fall back to a simple subtyping operation, which does work.Since
&Tis covariant overT, but&mut Tis invariant, that's where the discrepancy between these two examples crops up.r? lcnr or reassign :D