1
Fork 0

Rollup merge of #120248 - WaffleLapkin:bonk-ptr-object-casts, r=compiler-errors,oli-obk,lnicola

Make casts of pointers to trait objects stricter

This is an attempt to `fix` https://github.com/rust-lang/rust/issues/120222 and https://github.com/rust-lang/rust/issues/120217.

This is done by adding restrictions on casting pointers to trait objects.

Before this PR the rules were as follows:

> When casting `*const X<dyn A>` -> `*const Y<dyn B>`, principal traits in `A` and `B` must refer to the same trait definition (or no trait).

With this PR the rules are changed to

> When casting `*const X<dyn Src>` -> `*const Y<dyn Dst>`
> - if `Dst` has a principal trait `DstP`,
>   - `Src` must have a principal trait `SrcP`
>   - `dyn SrcP` and `dyn DstP` must be the same type (modulo the trait object lifetime, `dyn T+'a` -> `dyn T+'b` is allowed)
>   - Auto traits in `Dst` must be a subset of auto traits in `Src`
>     - Not adhering to this is currently a FCW (warn-by-default + `FutureReleaseErrorReportInDeps`), instead of an error
> - if `Src` has a principal trait `Dst` must as well
>   - this restriction will be removed in a follow up PR

This ensures that
1. Principal trait's generic arguments match (no `*const dyn Tr<A>` -> `*const dyn Tr<B>` casts, which are a problem for [#120222](https://github.com/rust-lang/rust/issues/120222))
2. Principal trait's lifetime arguments match (no `*const dyn Tr<'a>` -> `*const dyn Tr<'b>` casts, which are a problem for [#120217](https://github.com/rust-lang/rust/issues/120217))
3. No auto traits can be _added_ (this is a problem for arbitrary self types, see [this comment](https://github.com/rust-lang/rust/pull/120248#discussion_r1463835350))

Some notes:
 - We only care about the metadata/last field, so you can still cast `*const dyn T` to `*const WithHeader<dyn T>`, etc
- The lifetime of the trait object itself (`dyn A + 'lt`) is not checked, so you can still cast `*mut FnOnce() + '_` to `*mut FnOnce() + 'static`, etc
  - This feels fishy, but I couldn't come up with a reason it must be checked

The diagnostics are currently not great, to say the least, but as far as I can tell this correctly fixes the issues.

cc `@oli-obk` `@compiler-errors` `@lcnr`
This commit is contained in:
Matthias Krüger 2024-07-08 16:28:15 +02:00 committed by GitHub
commit c4ee2df539
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 758 additions and 144 deletions

View file

@ -80,6 +80,7 @@ declare_lint_pass! {
PRIVATE_BOUNDS,
PRIVATE_INTERFACES,
PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
PTR_CAST_ADD_AUTO_TO_OBJECT,
PUB_USE_OF_PRIVATE_EXTERN_CRATE,
REDUNDANT_LIFETIMES,
REFINING_IMPL_TRAIT_INTERNAL,
@ -4938,6 +4939,58 @@ declare_lint! {
};
}
declare_lint! {
/// The `ptr_cast_add_auto_to_object` lint detects casts of raw pointers to trait
/// objects, which add auto traits.
///
/// ### Example
///
/// ```rust,edition2021,compile_fail
/// let ptr: *const dyn core::any::Any = &();
/// _ = ptr as *const dyn core::any::Any + Send;
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Adding an auto trait can make the vtable invalid, potentially causing
/// UB in safe code afterwards. For example:
///
/// ```ignore (causes a warning)
/// #![feature(arbitrary_self_types)]
///
/// trait Trait {
/// fn f(self: *const Self)
/// where
/// Self: Send;
/// }
///
/// impl Trait for *const () {
/// fn f(self: *const Self) {
/// unreachable!()
/// }
/// }
///
/// fn main() {
/// let unsend: *const () = &();
/// let unsend: *const dyn Trait = &unsend;
/// let send_bad: *const (dyn Trait + Send) = unsend as _;
/// send_bad.f(); // this crashes, since vtable for `*const ()` does not have an entry for `f`
/// }
/// ```
///
/// Generally you must ensure that vtable is right for the pointer's type,
/// before passing the pointer to safe code.
pub PTR_CAST_ADD_AUTO_TO_OBJECT,
Warn,
"detects `as` casts from pointers to `dyn Trait` to pointers to `dyn Trait + Auto`",
@future_incompatible = FutureIncompatibleInfo {
reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps,
reference: "issue #127323 <https://github.com/rust-lang/rust/issues/127323>",
};
}
declare_lint! {
/// The `out_of_scope_macro_calls` lint detects `macro_rules` called when they are not in scope,
/// above their definition, which may happen in key-value attributes.