rust icon indicating copy to clipboard operation
rust copied to clipboard

Allow dropping `dyn Trait` principal

Open Jules-Bertholet opened this issue 1 year ago • 26 comments

Redux of #114679, fixes #126313

This allows the following examples to compile:

trait Trait: Send {}
fn foo(x: &dyn Trait) -> &dyn Send { x }
trait Trait {}
fn foo(x: &(dyn Trait + Send)) -> &dyn Send { x }

This makes the language more consistent, as we already allow:

trait Trait {}
fn foo(x: &(dyn Trait + Send)) -> &dyn Trait { x }

The PR includes a test case, in tests/ui/traits/dyn-drop-principal.rs.

Documentation

dyn upcasting coercions, of any kind, are currently entirely undocumented in the reference. A future reference PR should address this.

@rustbot label T-lang T-types needs-fcp A-coercions A-trait-objects

Jules-Bertholet avatar Jun 19 '24 03:06 Jules-Bertholet

r? @fmease

rustbot has assigned @fmease. They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

rustbot avatar Jun 19 '24 03:06 rustbot

Some changes occurred in compiler/rustc_codegen_cranelift

cc @bjorn3

rustbot avatar Jun 19 '24 03:06 rustbot

cc @compiler-errors r? @lcnr

fmease avatar Jun 20 '24 18:06 fmease

I'll let @lcnr do the judgement call of whether this needs a lang FCP or if types suffices. Thanks for picking this back up!

compiler-errors avatar Jun 20 '24 18:06 compiler-errors

sorry for the delay!

nominating for lang, to let them decide whether they mind delegating this to t-types. We simply reuse the vtable of the parent trait when dropping the principal. This relies on all vtables to have the same header used for dropping and size_of_val/align_of_val. I don't know whether that's currently guaranteed anywhere, cc @rust-lang/opsem i guess?

Also, please extend the test to also check size_of_val and align_of_val :grin:

lcnr avatar Jul 10 '24 14:07 lcnr

Is there a Miri test that does this? This may run afoul of Miri's vtable validity check, which is supposed to ensure that the vtable has the same principal as the dyn trait type indicates.

This relies on all vtables to have the same header which is used for dropping and size_of_val/align_of_val. I don't know whether that's currently guaranteed anywhere, cc @rust-lang/opsem i guess?

vtables are entirely an implementation detail. In the op.sem they don't even exist in-memory, just as abstract opaque concepts (similar to function pointers). So we're free to do whatever here.

But it does mean we have to define the validity of wide pointers such that it's okay for the vtable to have a principal even if the type does not.

RalfJung avatar Jul 10 '24 16:07 RalfJung

The Miri subtree was changed

cc @rust-lang/miri

rustbot avatar Jul 10 '24 17:07 rustbot

Also, please extend the test to also check size_of_val and align_of_val

Done

Jules-Bertholet avatar Jul 10 '24 17:07 Jules-Bertholet

I don't understand how this new test can pass, it seems like it should fail this check...

RalfJung avatar Jul 10 '24 17:07 RalfJung

The job x86_64-gnu-llvm-17 failed! Check out the build log: (web) (plain)

Click to see the possible cause of the failure (guessed by this bot)
 - LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format (line 46)
 - LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format (line 49)
 - LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format (line 61)
##[endgroup]
Setting extra environment values for docker:  --env ENABLE_GCC_CODEGEN=1 --env GCC_EXEC_PREFIX=/usr/lib/gcc/
[CI_JOB_NAME=x86_64-gnu-llvm-17]
---
sccache: Starting the server...
##[group]Configure the build
configure: processing command line
configure: 
configure: build.configure-args := ['--build=x86_64-unknown-linux-gnu', '--llvm-root=/usr/lib/llvm-17', '--enable-llvm-link-shared', '--set', 'rust.thin-lto-import-instr-limit=10', '--set', 'change-id=99999999', '--enable-verbose-configure', '--enable-sccache', '--disable-manage-submodules', '--enable-locked-deps', '--enable-cargo-native-static', '--set', 'rust.codegen-units-std=1', '--set', 'dist.compression-profile=balanced', '--dist-compression-formats=xz', '--set', 'rust.lld=false', '--disable-dist-src', '--release-channel=nightly', '--enable-debug-assertions', '--enable-overflow-checks', '--enable-llvm-assertions', '--set', 'rust.verify-llvm-ir', '--set', 'rust.codegen-backends=llvm,cranelift,gcc', '--set', 'llvm.static-libstdcpp', '--enable-new-symbol-mangling']
configure: target.x86_64-unknown-linux-gnu.llvm-config := /usr/lib/llvm-17/bin/llvm-config
configure: llvm.link-shared     := True
configure: rust.thin-lto-import-instr-limit := 10
configure: change-id            := 99999999
---
---- [ui] tests/ui/impl-trait/unsized_coercion5.rs#next stdout ----

error in revision `next`: ui test compiled successfully!
status: exit status: 0
command: env -u RUSTC_LOG_COLOR RUSTC_ICE="0" RUST_BACKTRACE="short" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2/bin/rustc" "/checkout/tests/ui/impl-trait/unsized_coercion5.rs" "-Zthreads=1" "-Zsimulate-remapped-rust-src-base=/rustc/FAKE_PREFIX" "-Ztranslate-remapped-path-to-local-path=no" "-Z" "ignore-directory-in-diagnostics-source-blocks=/cargo" "-Z" "ignore-directory-in-diagnostics-source-blocks=/checkout/vendor" "--sysroot" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2" "--target=x86_64-unknown-linux-gnu" "--cfg" "next" "--check-cfg" "cfg(FALSE,next,old)" "--error-format" "json" "--json" "future-incompat" "-Ccodegen-units=1" "-Zui-testing" "-Zdeduplicate-diagnostics=no" "-Zwrite-long-types-to-disk=no" "-Cstrip=debuginfo" "--emit" "metadata" "-C" "prefer-dynamic" "--out-dir" "/checkout/obj/build/x86_64-unknown-linux-gnu/test/ui/impl-trait/unsized_coercion5.next" "-A" "unused" "-A" "internal_features" "-Crpath" "-Cdebuginfo=0" "-Lnative=/checkout/obj/build/x86_64-unknown-linux-gnu/native/rust-test-helpers" "-L" "/checkout/obj/build/x86_64-unknown-linux-gnu/test/ui/impl-trait/unsized_coercion5.next/auxiliary" "-Znext-solver"
stderr: none


---- [ui] tests/ui/impl-trait/unsized_coercion5.rs#old stdout ----
---- [ui] tests/ui/impl-trait/unsized_coercion5.rs#old stdout ----
diff of stderr:

- error[E0308]: mismatched types
-   --> $DIR/unsized_coercion5.rs:16:32
-    |
- LL |         let y: Box<dyn Send> = x as Box<dyn Trait + Send>;
-    |                -------------   ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected trait `Send`, found trait `Trait + Send`
-    |                expected due to this
-    |
-    = note: expected struct `Box<dyn Send>`
-               found struct `Box<dyn Trait + Send>`
-               found struct `Box<dyn Trait + Send>`
- 
12 error: cannot check whether the hidden type of opaque type satisfies auto traits
13   --> $DIR/unsized_coercion5.rs:16:32
14    |

32    = help: the trait `Sized` is not implemented for `impl Trait + ?Sized`
33    = note: required for the cast from `Box<impl Trait + ?Sized>` to `Box<dyn Trait + Send>`
- error: aborting due to 3 previous errors
+ error: aborting due to 2 previous errors
36 
- Some errors have detailed explanations: E0277, E0308.
- Some errors have detailed explanations: E0277, E0308.
- For more information about an error, try `rustc --explain E0277`.
+ For more information about this error, try `rustc --explain E0277`.
39 


The actual stderr differed from the expected stderr.
Actual stderr saved to /checkout/obj/build/x86_64-unknown-linux-gnu/test/ui/impl-trait/unsized_coercion5.old/unsized_coercion5.old.stderr
To only update this specific test, also pass `--test-args impl-trait/unsized_coercion5.rs`


error in revision `old`: 1 errors occurred comparing output.
status: exit status: 1
command: env -u RUSTC_LOG_COLOR RUSTC_ICE="0" RUST_BACKTRACE="short" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2/bin/rustc" "/checkout/tests/ui/impl-trait/unsized_coercion5.rs" "-Zthreads=1" "-Zsimulate-remapped-rust-src-base=/rustc/FAKE_PREFIX" "-Ztranslate-remapped-path-to-local-path=no" "-Z" "ignore-directory-in-diagnostics-source-blocks=/cargo" "-Z" "ignore-directory-in-diagnostics-source-blocks=/checkout/vendor" "--sysroot" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2" "--target=x86_64-unknown-linux-gnu" "--cfg" "old" "--check-cfg" "cfg(FALSE,next,old)" "--error-format" "json" "--json" "future-incompat" "-Ccodegen-units=1" "-Zui-testing" "-Zdeduplicate-diagnostics=no" "-Zwrite-long-types-to-disk=no" "-Cstrip=debuginfo" "--emit" "metadata" "-C" "prefer-dynamic" "--out-dir" "/checkout/obj/build/x86_64-unknown-linux-gnu/test/ui/impl-trait/unsized_coercion5.old" "-A" "unused" "-A" "internal_features" "-Crpath" "-Cdebuginfo=0" "-Lnative=/checkout/obj/build/x86_64-unknown-linux-gnu/native/rust-test-helpers" "-L" "/checkout/obj/build/x86_64-unknown-linux-gnu/test/ui/impl-trait/unsized_coercion5.old/auxiliary"
--- stderr -------------------------------
error: cannot check whether the hidden type of opaque type satisfies auto traits
##[error]  --> /checkout/tests/ui/impl-trait/unsized_coercion5.rs:16:32
   |
   |
LL |         let y: Box<dyn Send> = x as Box<dyn Trait + Send>;
   |
   |
   = note: fetching the hidden types of an opaque inside of the defining scope is not supported. You can try moving the opaque type and the item that actually registers a hidden type into a new submodule
note: opaque type is declared here
   |
   |
LL | fn hello() -> Box<impl Trait + ?Sized> {
   |                   ^^^^^^^^^^^^^^^^^^^
   = note: required for the cast from `Box<impl Trait + ?Sized>` to `Box<dyn Trait + Send>`
error[E0277]: the size for values of type `impl Trait + ?Sized` cannot be known at compilation time
##[error]  --> /checkout/tests/ui/impl-trait/unsized_coercion5.rs:16:32
   |
   |
LL |         let y: Box<dyn Send> = x as Box<dyn Trait + Send>;
   |
   = help: the trait `Sized` is not implemented for `impl Trait + ?Sized`
   = help: the trait `Sized` is not implemented for `impl Trait + ?Sized`
   = note: required for the cast from `Box<impl Trait + ?Sized>` to `Box<dyn Trait + Send>`
error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0277`.
------------------------------------------

rust-log-analyzer avatar Jul 10 '24 17:07 rust-log-analyzer

Miri test looks good, thanks!

RalfJung avatar Jul 10 '24 17:07 RalfJung

This relies on all vtables to have the same header which is used for dropping and size_of_val/align_of_val. I don't know whether that's currently guaranteed anywhere, cc @rust-lang/opsem i guess?

vtables are entirely an implementation detail. In the op.sem they don't even exist in-memory, just as abstract opaque concepts (similar to function pointers). So we're free to do whatever here.

But it does mean we have to define the validity of wide pointers such that it's okay for the vtable to have a principal even if the type does not.

does that mean this also requires an opsem FCP? I would expect that this sort of type pruning is definitely sound. I would expect that shrinking the size of the pointee is totally safe and we can't assume that all vtables for the same type are equal.

lcnr avatar Jul 16 '24 13:07 lcnr

Note that from an opsem perspective, vtables don't have a size -- they are opaque handles that just store a type-trait pair.

Given that we haven't really spec'ed out validity for vtables yet in opsem (and there are indeed some disagreements here), personally I'd be fine with just taking this as a further constraint that we have to take into account.

RalfJung avatar Jul 16 '24 16:07 RalfJung

:umbrella: The latest upstream changes (presumably #128155) made this pull request unmergeable. Please resolve the merge conflicts.

bors avatar Jul 25 '24 01:07 bors

cc @rust-lang/lang the nomination is for

nominating for lang, to let them decide whether they mind delegating this to t-types. We simply reuse the vtable of the parent trait when dropping the principal. This relies on all vtables to have the same header used for dropping and size_of_val/align_of_val.

lcnr avatar Aug 14 '24 06:08 lcnr

Also worth noting that keeping the same vtable is not a guarantee and in fact in Miri you will get a different vtable for the "None" principal. I assume this could be done at runtime as well.

Keeping the same vtable means runtime will see pointer equalities that are impossible to obtain in Miri - but we have that elsewhere, too, e.g. with deduplication of identical consts.

RalfJung avatar Aug 14 '24 08:08 RalfJung

@rustbot labels -I-lang-nominated @rfcbot fcp merge

We discussed this today in lang triage. This sounded right to us, in terms of making the language more consistent. We discussed whether we were closing any meaningful doors here and couldn't really find any. This seems in particular consistent with the direction that we set with dyn trait upcasting. We'll of course do this as a dual FCP with T-types.

traviscross avatar Aug 14 '24 17:08 traviscross

Team member @traviscross has proposed to merge this. The next step is review by the rest of the tagged team members:

  • [x] @BoxyUwU
  • [x] @compiler-errors
  • [x] @jackh726
  • [x] @joshtriplett
  • [x] @lcnr
  • [x] @nikomatsakis
  • [x] @oli-obk
  • [ ] @pnkfelix
  • [x] @scottmcm
  • [x] @spastorino
  • [x] @tmandry
  • [x] @traviscross

No concerns currently listed.

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

cc @rust-lang/lang-advisors: FCP proposed for lang, please feel free to register concerns. See this document for info about what commands tagged team members can give me.

rfcbot avatar Aug 14 '24 17:08 rfcbot

This seems entirely reasonable to me -- it's basically like every trait has the supertrait ε and you can go from dyn Foo + ε to just dyn ε.

@rfcbot reviewed

scottmcm avatar Aug 14 '24 17:08 scottmcm

:umbrella: The latest upstream changes (presumably #130473) made this pull request unmergeable. Please resolve the merge conflicts.

bors avatar Sep 17 '24 20:09 bors

Ping @nikomatsakis @pnkfelix @tmandry for remaining checkboxes.

joshtriplett avatar Sep 18 '24 14:09 joshtriplett

@rfcbot reviewed

tmandry avatar Sep 18 '24 16:09 tmandry

:bell: This is now entering its final comment period, as per the review above. :bell:

rfcbot avatar Sep 18 '24 16:09 rfcbot

@rfcbot reviewed

nikomatsakis avatar Sep 19 '24 12:09 nikomatsakis

The final comment period, with a disposition to merge, as per the review above, is now complete.

As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed.

This will be merged soon.

rfcbot avatar Sep 28 '24 16:09 rfcbot

Superseded by https://github.com/rust-lang/rust/pull/131857 -- should this PR be closed?

RalfJung avatar Oct 18 '24 05:10 RalfJung