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

@ -38,6 +38,7 @@ use rustc_span::def_id::CRATE_DEF_ID;
use rustc_span::source_map::Spanned;
use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_span::DUMMY_SP;
use rustc_target::abi::{FieldIdx, FIRST_VARIANT};
use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints;
use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
@ -49,6 +50,7 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
use rustc_mir_dataflow::move_paths::MoveData;
use rustc_mir_dataflow::ResultsCursor;
use crate::renumber::RegionCtxt;
use crate::session_diagnostics::{MoveUnsized, SimdIntrinsicArgConst};
use crate::{
borrow_set::BorrowSet,
@ -2333,7 +2335,57 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
let cast_ty_from = CastTy::from_ty(ty_from);
let cast_ty_to = CastTy::from_ty(*ty);
match (cast_ty_from, cast_ty_to) {
(Some(CastTy::Ptr(_)), Some(CastTy::Ptr(_))) => (),
(Some(CastTy::Ptr(src)), Some(CastTy::Ptr(dst))) => {
let mut normalize = |t| self.normalize(t, location);
let src_tail =
tcx.struct_tail_with_normalize(src.ty, &mut normalize, || ());
let dst_tail =
tcx.struct_tail_with_normalize(dst.ty, &mut normalize, || ());
// This checks (lifetime part of) vtable validity for pointer casts,
// which is irrelevant when there are aren't principal traits on both sides (aka only auto traits).
//
// Note that other checks (such as denying `dyn Send` -> `dyn Debug`) are in `rustc_hir_typeck`.
if let ty::Dynamic(src_tty, ..) = src_tail.kind()
&& let ty::Dynamic(dst_tty, ..) = dst_tail.kind()
&& src_tty.principal().is_some()
&& dst_tty.principal().is_some()
{
// Remove auto traits.
// Auto trait checks are handled in `rustc_hir_typeck` as FCW.
let src_obj = tcx.mk_ty_from_kind(ty::Dynamic(
tcx.mk_poly_existential_predicates(
&src_tty.without_auto_traits().collect::<Vec<_>>(),
),
tcx.lifetimes.re_static,
ty::Dyn,
));
let dst_obj = tcx.mk_ty_from_kind(ty::Dynamic(
tcx.mk_poly_existential_predicates(
&dst_tty.without_auto_traits().collect::<Vec<_>>(),
),
tcx.lifetimes.re_static,
ty::Dyn,
));
// Replace trait object lifetimes with fresh vars, to allow casts like
// `*mut dyn FnOnce() + 'a` -> `*mut dyn FnOnce() + 'static`,
let src_obj =
freshen_single_trait_object_lifetime(self.infcx, src_obj);
let dst_obj =
freshen_single_trait_object_lifetime(self.infcx, dst_obj);
debug!(?src_tty, ?dst_tty, ?src_obj, ?dst_obj);
self.eq_types(
src_obj,
dst_obj,
location.to_locations(),
ConstraintCategory::Cast { unsize_to: None },
)
.unwrap();
}
}
_ => {
span_mirbug!(
self,
@ -2856,3 +2908,16 @@ impl<'tcx> TypeOp<'tcx> for InstantiateOpaqueType<'tcx> {
Ok(output)
}
}
fn freshen_single_trait_object_lifetime<'tcx>(
infcx: &BorrowckInferCtxt<'tcx>,
ty: Ty<'tcx>,
) -> Ty<'tcx> {
let &ty::Dynamic(tty, _, dyn_kind @ ty::Dyn) = ty.kind() else { bug!("expected trait object") };
let fresh = infcx
.next_region_var(rustc_infer::infer::RegionVariableOrigin::MiscVariable(DUMMY_SP), || {
RegionCtxt::Unknown
});
infcx.tcx.mk_ty_from_kind(ty::Dynamic(tty, fresh, dyn_kind))
}

View File

@ -298,15 +298,21 @@ impl IntoDiagArg for hir::def::Namespace {
}
#[derive(Clone)]
pub struct DiagSymbolList(Vec<Symbol>);
pub struct DiagSymbolList<S = Symbol>(Vec<S>);
impl From<Vec<Symbol>> for DiagSymbolList {
fn from(v: Vec<Symbol>) -> Self {
impl<S> From<Vec<S>> for DiagSymbolList<S> {
fn from(v: Vec<S>) -> Self {
DiagSymbolList(v)
}
}
impl IntoDiagArg for DiagSymbolList {
impl<S> FromIterator<S> for DiagSymbolList<S> {
fn from_iter<T: IntoIterator<Item = S>>(iter: T) -> Self {
iter.into_iter().collect::<Vec<_>>().into()
}
}
impl<S: std::fmt::Display> IntoDiagArg for DiagSymbolList<S> {
fn into_diag_arg(self) -> DiagArgValue {
DiagArgValue::StrListSepByAnd(
self.0.into_iter().map(|sym| Cow::Owned(format!("`{sym}`"))).collect(),

View File

@ -138,6 +138,11 @@ hir_typeck_option_result_asref = use `{$def_path}::as_ref` to convert `{$expecte
hir_typeck_option_result_cloned = use `{$def_path}::cloned` to clone the value inside the `{$def_path}`
hir_typeck_option_result_copied = use `{$def_path}::copied` to copy the value inside the `{$def_path}`
hir_typeck_ptr_cast_add_auto_to_object = adding {$traits_len ->
[1] an auto trait {$traits}
*[other] auto traits {$traits}
} to a trait object in a pointer cast may cause UB later on
hir_typeck_remove_semi_for_coerce = you might have meant to return the `match` expression
hir_typeck_remove_semi_for_coerce_expr = this could be implicitly returned but it is a statement, not a tail expression
hir_typeck_remove_semi_for_coerce_ret = the `match` arms can conform to this return type

View File

@ -32,9 +32,9 @@ use super::FnCtxt;
use crate::errors;
use crate::type_error_struct;
use hir::ExprKind;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{codes::*, Applicability, Diag, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_hir::{self as hir, ExprKind};
use rustc_macros::{TypeFoldable, TypeVisitable};
use rustc_middle::bug;
use rustc_middle::mir::Mutability;
@ -44,7 +44,7 @@ use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::TyCtxt;
use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitableExt, VariantDef};
use rustc_session::lint;
use rustc_span::def_id::{DefId, LOCAL_CRATE};
use rustc_span::def_id::LOCAL_CRATE;
use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_span::DUMMY_SP;
@ -73,7 +73,7 @@ enum PointerKind<'tcx> {
/// No metadata attached, ie pointer to sized type or foreign type
Thin,
/// A trait object
VTable(Option<DefId>),
VTable(&'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>),
/// Slice
Length,
/// The unsize info of this projection or opaque type
@ -101,7 +101,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Ok(match *t.kind() {
ty::Slice(_) | ty::Str => Some(PointerKind::Length),
ty::Dynamic(tty, _, ty::Dyn) => Some(PointerKind::VTable(tty.principal_def_id())),
ty::Dynamic(tty, _, ty::Dyn) => Some(PointerKind::VTable(tty)),
ty::Adt(def, args) if def.is_struct() => match def.non_enum_variant().tail_opt() {
None => Some(PointerKind::Thin),
Some(f) => {
@ -755,7 +755,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
Err(CastError::IllegalCast)
}
// ptr -> *
// ptr -> ptr
(Ptr(m_e), Ptr(m_c)) => self.check_ptr_ptr_cast(fcx, m_e, m_c), // ptr-ptr-cast
// ptr-addr-cast
@ -799,40 +799,126 @@ impl<'a, 'tcx> CastCheck<'tcx> {
fn check_ptr_ptr_cast(
&self,
fcx: &FnCtxt<'a, 'tcx>,
m_expr: ty::TypeAndMut<'tcx>,
m_cast: ty::TypeAndMut<'tcx>,
m_src: ty::TypeAndMut<'tcx>,
m_dst: ty::TypeAndMut<'tcx>,
) -> Result<CastKind, CastError> {
debug!("check_ptr_ptr_cast m_expr={:?} m_cast={:?}", m_expr, m_cast);
debug!("check_ptr_ptr_cast m_src={m_src:?} m_dst={m_dst:?}");
// ptr-ptr cast. vtables must match.
let expr_kind = fcx.pointer_kind(m_expr.ty, self.span)?;
let cast_kind = fcx.pointer_kind(m_cast.ty, self.span)?;
let src_kind = fcx.tcx.erase_regions(fcx.pointer_kind(m_src.ty, self.span)?);
let dst_kind = fcx.tcx.erase_regions(fcx.pointer_kind(m_dst.ty, self.span)?);
let Some(cast_kind) = cast_kind else {
match (src_kind, dst_kind) {
// We can't cast if target pointer kind is unknown
return Err(CastError::UnknownCastPtrKind);
};
(_, None) => Err(CastError::UnknownCastPtrKind),
// Cast to thin pointer is OK
(_, Some(PointerKind::Thin)) => Ok(CastKind::PtrPtrCast),
// Cast to thin pointer is OK
if cast_kind == PointerKind::Thin {
return Ok(CastKind::PtrPtrCast);
}
let Some(expr_kind) = expr_kind else {
// We can't cast to fat pointer if source pointer kind is unknown
return Err(CastError::UnknownExprPtrKind);
};
(None, _) => Err(CastError::UnknownExprPtrKind),
// thin -> fat? report invalid cast (don't complain about vtable kinds)
if expr_kind == PointerKind::Thin {
return Err(CastError::SizedUnsizedCast);
}
// thin -> fat? report invalid cast (don't complain about vtable kinds)
(Some(PointerKind::Thin), _) => Err(CastError::SizedUnsizedCast),
// vtable kinds must match
if fcx.tcx.erase_regions(cast_kind) == fcx.tcx.erase_regions(expr_kind) {
Ok(CastKind::PtrPtrCast)
} else {
Err(CastError::DifferingKinds)
// trait object -> trait object? need to do additional checks
(Some(PointerKind::VTable(src_tty)), Some(PointerKind::VTable(dst_tty))) => {
match (src_tty.principal(), dst_tty.principal()) {
// A<dyn Src<...> + SrcAuto> -> B<dyn Dst<...> + DstAuto>. need to make sure
// - `Src` and `Dst` traits are the same
// - traits have the same generic arguments
// - `SrcAuto` is a superset of `DstAuto`
(Some(src_principal), Some(dst_principal)) => {
let tcx = fcx.tcx;
// Check that the traits are actually the same.
// The `dyn Src = dyn Dst` check below would suffice,
// but this may produce a better diagnostic.
//
// Note that trait upcasting goes through a different mechanism (`coerce_unsized`)
// and is unaffected by this check.
if src_principal.def_id() != dst_principal.def_id() {
return Err(CastError::DifferingKinds);
}
// We need to reconstruct trait object types.
// `m_src` and `m_dst` won't work for us here because they will potentially
// contain wrappers, which we do not care about.
//
// e.g. we want to allow `dyn T -> (dyn T,)`, etc.
//
// We also need to skip auto traits to emit an FCW and not an error.
let src_obj = tcx.mk_ty_from_kind(ty::Dynamic(
tcx.mk_poly_existential_predicates(
&src_tty.without_auto_traits().collect::<Vec<_>>(),
),
tcx.lifetimes.re_erased,
ty::Dyn,
));
let dst_obj = tcx.mk_ty_from_kind(ty::Dynamic(
tcx.mk_poly_existential_predicates(
&dst_tty.without_auto_traits().collect::<Vec<_>>(),
),
tcx.lifetimes.re_erased,
ty::Dyn,
));
// `dyn Src = dyn Dst`, this checks for matching traits/generics
fcx.demand_eqtype(self.span, src_obj, dst_obj);
// Check that `SrcAuto` (+auto traits implied by `Src`) is a superset of `DstAuto`.
// Emit an FCW otherwise.
let src_auto: FxHashSet<_> = src_tty
.auto_traits()
.chain(
tcx.supertrait_def_ids(src_principal.def_id())
.filter(|def_id| tcx.trait_is_auto(*def_id)),
)
.collect();
let added = dst_tty
.auto_traits()
.filter(|trait_did| !src_auto.contains(trait_did))
.collect::<Vec<_>>();
if !added.is_empty() {
tcx.emit_node_span_lint(
lint::builtin::PTR_CAST_ADD_AUTO_TO_OBJECT,
self.expr.hir_id,
self.span,
errors::PtrCastAddAutoToObject {
traits_len: added.len(),
traits: {
let mut traits: Vec<_> = added
.into_iter()
.map(|trait_did| tcx.def_path_str(trait_did))
.collect();
traits.sort();
traits.into()
},
},
)
}
Ok(CastKind::PtrPtrCast)
}
// dyn Auto -> dyn Auto'? ok.
(None, None) => Ok(CastKind::PtrPtrCast),
// dyn Trait -> dyn Auto? should be ok, but we used to not allow it.
// FIXME: allow this
(Some(_), None) => Err(CastError::DifferingKinds),
// dyn Auto -> dyn Trait? not ok.
(None, Some(_)) => Err(CastError::DifferingKinds),
}
}
// fat -> fat? metadata kinds must match
(Some(src_kind), Some(dst_kind)) if src_kind == dst_kind => Ok(CastKind::PtrPtrCast),
(_, _) => Err(CastError::DifferingKinds),
}
}

View File

@ -4,8 +4,8 @@ use std::borrow::Cow;
use crate::fluent_generated as fluent;
use rustc_errors::{
codes::*, Applicability, Diag, DiagArgValue, EmissionGuarantee, IntoDiagArg, MultiSpan,
SubdiagMessageOp, Subdiagnostic,
codes::*, Applicability, Diag, DiagArgValue, DiagSymbolList, EmissionGuarantee, IntoDiagArg,
MultiSpan, SubdiagMessageOp, Subdiagnostic,
};
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_middle::ty::{self, Ty};
@ -253,6 +253,13 @@ pub struct LossyProvenanceInt2Ptr<'tcx> {
pub sugg: LossyProvenanceInt2PtrSuggestion,
}
#[derive(LintDiagnostic)]
#[diag(hir_typeck_ptr_cast_add_auto_to_object)]
pub struct PtrCastAddAutoToObject {
pub traits_len: usize,
pub traits: DiagSymbolList<String>,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(hir_typeck_suggestion, applicability = "has-placeholders")]
pub struct LossyProvenanceInt2PtrSuggestion {

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.

View File

@ -349,6 +349,14 @@ impl<'tcx> ty::List<ty::PolyExistentialPredicate<'tcx>> {
_ => None,
})
}
pub fn without_auto_traits(
&self,
) -> impl Iterator<Item = ty::PolyExistentialPredicate<'tcx>> + '_ {
self.iter().filter(|predicate| {
!matches!(predicate.as_ref().skip_binder(), ExistentialPredicate::AutoTrait(_))
})
}
}
pub type PolyTraitRef<'tcx> = ty::Binder<'tcx, TraitRef<'tcx>>;

View File

@ -2374,7 +2374,7 @@ impl dyn Error + Send {
let err: Box<dyn Error> = self;
<dyn Error>::downcast(err).map_err(|s| unsafe {
// Reapply the `Send` marker.
Box::from_raw(Box::into_raw(s) as *mut (dyn Error + Send))
mem::transmute::<Box<dyn Error>, Box<dyn Error + Send>>(s)
})
}
}
@ -2387,8 +2387,8 @@ impl dyn Error + Send + Sync {
pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<Self>> {
let err: Box<dyn Error> = self;
<dyn Error>::downcast(err).map_err(|s| unsafe {
// Reapply the `Send + Sync` marker.
Box::from_raw(Box::into_raw(s) as *mut (dyn Error + Send + Sync))
// Reapply the `Send + Sync` markers.
mem::transmute::<Box<dyn Error>, Box<dyn Error + Send + Sync>>(s)
})
}
}

View File

@ -68,8 +68,6 @@ pub type RawMap<A> = hash_map::HashMap<TypeId, Box<A>, BuildHasherDefault<TypeId
/// The type parameter `A` allows you to use a different value type; normally you will want
/// it to be `core::any::Any` (also known as `std::any::Any`), but there are other choices:
///
/// - If you want the entire map to be cloneable, use `CloneAny` instead of `Any`; with
/// that, you can only add types that implement `Clone` to the map.
/// - You can add on `+ Send` or `+ Send + Sync` (e.g. `Map<dyn Any + Send>`) to add those
/// auto traits.
///
@ -79,9 +77,6 @@ pub type RawMap<A> = hash_map::HashMap<TypeId, Box<A>, BuildHasherDefault<TypeId
/// also spelled [`AnyMap`] for convenience.
/// - <code>[Map]&lt;dyn [core::any::Any] + Send&gt;</code>
/// - <code>[Map]&lt;dyn [core::any::Any] + Send + Sync&gt;</code>
/// - <code>[Map]&lt;dyn [CloneAny]&gt;</code>
/// - <code>[Map]&lt;dyn [CloneAny] + Send&gt;</code>
/// - <code>[Map]&lt;dyn [CloneAny] + Send + Sync&gt;</code>
///
/// ## Example
///
@ -205,12 +200,6 @@ mod tests {
assert_debug::<Map<dyn Any>>();
assert_debug::<Map<dyn Any + Send>>();
assert_debug::<Map<dyn Any + Send + Sync>>();
assert_send::<Map<dyn CloneAny + Send>>();
assert_send::<Map<dyn CloneAny + Send + Sync>>();
assert_sync::<Map<dyn CloneAny + Send + Sync>>();
assert_debug::<Map<dyn CloneAny>>();
assert_debug::<Map<dyn CloneAny + Send>>();
assert_debug::<Map<dyn CloneAny + Send + Sync>>();
}
#[test]
@ -232,53 +221,6 @@ mod tests {
}
}
// impl some traits for dyn Any
use core::fmt;
#[doc(hidden)]
pub trait CloneToAny {
/// Clone `self` into a new `Box<dyn CloneAny>` object.
fn clone_to_any(&self) -> Box<dyn CloneAny>;
}
impl<T: Any + Clone> CloneToAny for T {
#[inline]
fn clone_to_any(&self) -> Box<dyn CloneAny> {
Box::new(self.clone())
}
}
macro_rules! impl_clone {
($t:ty) => {
impl Clone for Box<$t> {
#[inline]
fn clone(&self) -> Box<$t> {
// SAFETY: this dance is to reapply any Send/Sync marker. Im not happy about this
// approach, given that I used to do it in safe code, but then came a dodgy
// future-compatibility warning where_clauses_object_safety, which is spurious for
// auto traits but still super annoying (future-compatibility lints seem to mean
// your bin crate needs a corresponding allow!). Although I explained my plight¹
// and it was all explained and agreed upon, no action has been taken. So I finally
// caved and worked around it by doing it this way, which matches whats done for
// core::any², so its probably not *too* bad.
//
// ¹ https://github.com/rust-lang/rust/issues/51443#issuecomment-421988013
// ² https://github.com/rust-lang/rust/blob/e7825f2b690c9a0d21b6f6d84c404bb53b151b38/library/alloc/src/boxed.rs#L1613-L1616
let clone: Box<dyn CloneAny> = (**self).clone_to_any();
let raw: *mut dyn CloneAny = Box::into_raw(clone);
unsafe { Box::from_raw(raw as *mut $t) }
}
}
impl fmt::Debug for $t {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad(stringify!($t))
}
}
};
}
/// Methods for downcasting from an `Any`-like trait object.
///
/// This should only be implemented on trait objects for subtraits of `Any`, though you can
@ -350,16 +292,3 @@ macro_rules! implement {
implement!(Any);
implement!(Any + Send);
implement!(Any + Send + Sync);
/// [`Any`], but with cloning.
///
/// Every type with no non-`'static` references that implements `Clone` implements `CloneAny`.
/// See [`core::any`] for more details on `Any` in general.
pub trait CloneAny: Any + CloneToAny {}
impl<T: Any + Clone> CloneAny for T {}
implement!(CloneAny);
implement!(CloneAny + Send);
implement!(CloneAny + Send + Sync);
impl_clone!(dyn CloneAny);
impl_clone!(dyn CloneAny + Send);
impl_clone!(dyn CloneAny + Send + Sync);

View File

@ -4,25 +4,12 @@
#![feature(unsized_tuple_coercion)]
trait Foo<T> {
fn foo(&self, _: T) -> u32 { 42 }
}
trait Bar { //~ WARN trait `Bar` is never used
fn bar(&self) { println!("Bar!"); }
}
impl<T> Foo<T> for () {}
impl Foo<u32> for u32 { fn foo(&self, _: u32) -> u32 { self+43 } }
impl Bar for () {}
unsafe fn round_trip_and_call<'a>(t: *const (dyn Foo<u32>+'a)) -> u32 {
let foo_e : *const dyn Foo<u32> = t as *const _;
let r_1 = foo_e as *mut dyn Foo<u32>;
(&*r_1).foo(0)
}
#[repr(C)]
struct FooS<T:?Sized>(T);
#[repr(C)]
@ -38,11 +25,6 @@ fn tuple_i32_to_u32<T:?Sized>(u: *const (i32, T)) -> *const (u32, T) {
fn main() {
let x = 4u32;
let y : &dyn Foo<u32> = &x;
let fl = unsafe { round_trip_and_call(y as *const dyn Foo<u32>) };
assert_eq!(fl, (43+4));
let s = FooS([0,1,2]);
let u: &FooS<[u32]> = &s;
let u: *const FooS<[u32]> = u;

View File

@ -1,5 +1,5 @@
warning: trait `Bar` is never used
--> $DIR/cast-rfc0401-vtable-kinds.rs:11:7
--> $DIR/cast-rfc0401-vtable-kinds.rs:7:7
|
LL | trait Bar {
| ^^^

View File

@ -0,0 +1,18 @@
//@ check-pass
trait Trait<'a> {}
fn add_auto<'a>(x: *mut dyn Trait<'a>) -> *mut (dyn Trait<'a> + Send) {
x as _
//~^ warning: adding an auto trait `Send` to a trait object in a pointer cast may cause UB later on
//~| warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
}
// (to test diagnostic list formatting)
fn add_multiple_auto<'a>(x: *mut dyn Trait<'a>) -> *mut (dyn Trait<'a> + Send + Sync + Unpin) {
x as _
//~^ warning: adding auto traits `Send`, `Sync`, and `Unpin` to a trait object in a pointer cast may cause UB later on
//~| warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
}
fn main() {}

View File

@ -0,0 +1,43 @@
warning: adding an auto trait `Send` to a trait object in a pointer cast may cause UB later on
--> $DIR/ptr-to-trait-obj-add-auto.rs:6:5
|
LL | x as _
| ^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #127323 <https://github.com/rust-lang/rust/issues/127323>
= note: `#[warn(ptr_cast_add_auto_to_object)]` on by default
warning: adding auto traits `Send`, `Sync`, and `Unpin` to a trait object in a pointer cast may cause UB later on
--> $DIR/ptr-to-trait-obj-add-auto.rs:13:5
|
LL | x as _
| ^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #127323 <https://github.com/rust-lang/rust/issues/127323>
warning: 2 warnings emitted
Future incompatibility report: Future breakage diagnostic:
warning: adding an auto trait `Send` to a trait object in a pointer cast may cause UB later on
--> $DIR/ptr-to-trait-obj-add-auto.rs:6:5
|
LL | x as _
| ^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #127323 <https://github.com/rust-lang/rust/issues/127323>
= note: `#[warn(ptr_cast_add_auto_to_object)]` on by default
Future breakage diagnostic:
warning: adding auto traits `Send`, `Sync`, and `Unpin` to a trait object in a pointer cast may cause UB later on
--> $DIR/ptr-to-trait-obj-add-auto.rs:13:5
|
LL | x as _
| ^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #127323 <https://github.com/rust-lang/rust/issues/127323>
= note: `#[warn(ptr_cast_add_auto_to_object)]` on by default

View File

@ -0,0 +1,9 @@
//@ check-pass
trait Trait: Send {}
impl Trait for () {}
fn main() {
// This is OK: `Trait` has `Send` super trait.
&() as *const dyn Trait as *const (dyn Trait + Send);
}

View File

@ -0,0 +1,37 @@
//@ check-fail
//
// issue: <https://github.com/rust-lang/rust/issues/120222>
trait A {}
impl<T> A for T {}
trait B {}
impl<T> B for T {}
trait Trait<G> {}
struct X;
impl<T> Trait<X> for T {}
struct Y;
impl<T> Trait<Y> for T {}
fn main() {
let a: *const dyn A = &();
let b: *const dyn B = a as _; //~ error: casting `*const dyn A` as `*const dyn B` is invalid
let x: *const dyn Trait<X> = &();
let y: *const dyn Trait<Y> = x as _; //~ error: mismatched types
_ = (b, y);
}
fn generic<T>(x: *const dyn Trait<X>, t: *const dyn Trait<T>) {
let _: *const dyn Trait<T> = x as _; //~ error: mismatched types
let _: *const dyn Trait<X> = t as _; //~ error: mismatched types
}
trait Assocked {
type Assoc: ?Sized;
}
fn change_assoc(x: *mut dyn Assocked<Assoc = u8>) -> *mut dyn Assocked<Assoc = u32> {
x as _ //~ error: mismatched types
}

View File

@ -0,0 +1,53 @@
error[E0606]: casting `*const dyn A` as `*const dyn B` is invalid
--> $DIR/ptr-to-trait-obj-different-args.rs:18:27
|
LL | let b: *const dyn B = a as _;
| ^^^^^^
|
= note: vtable kinds may not match
error[E0308]: mismatched types
--> $DIR/ptr-to-trait-obj-different-args.rs:21:34
|
LL | let y: *const dyn Trait<Y> = x as _;
| ^^^^^^ expected `X`, found `Y`
|
= note: expected trait object `dyn Trait<X>`
found trait object `dyn Trait<Y>`
error[E0308]: mismatched types
--> $DIR/ptr-to-trait-obj-different-args.rs:27:34
|
LL | fn generic<T>(x: *const dyn Trait<X>, t: *const dyn Trait<T>) {
| - found this type parameter
LL | let _: *const dyn Trait<T> = x as _;
| ^^^^^^ expected `X`, found type parameter `T`
|
= note: expected trait object `dyn Trait<X>`
found trait object `dyn Trait<T>`
error[E0308]: mismatched types
--> $DIR/ptr-to-trait-obj-different-args.rs:28:34
|
LL | fn generic<T>(x: *const dyn Trait<X>, t: *const dyn Trait<T>) {
| - expected this type parameter
LL | let _: *const dyn Trait<T> = x as _;
LL | let _: *const dyn Trait<X> = t as _;
| ^^^^^^ expected type parameter `T`, found `X`
|
= note: expected trait object `dyn Trait<T>`
found trait object `dyn Trait<X>`
error[E0308]: mismatched types
--> $DIR/ptr-to-trait-obj-different-args.rs:36:5
|
LL | x as _
| ^^^^^^ expected `u8`, found `u32`
|
= note: expected trait object `dyn Assocked<Assoc = u8>`
found trait object `dyn Assocked<Assoc = u32>`
error: aborting due to 5 previous errors
Some errors have detailed explanations: E0308, E0606.
For more information about an error, try `rustc --explain E0308`.

View File

@ -0,0 +1,27 @@
//@ check-fail
//
// Make sure we can't trick the compiler by using a projection.
trait Cat<'a> {}
impl Cat<'_> for () {}
trait Id {
type Id: ?Sized;
}
impl<T: ?Sized> Id for T {
type Id = T;
}
struct S<T: ?Sized> {
tail: <T as Id>::Id,
}
fn m<'a>() {
let unsend: *const dyn Cat<'a> = &();
let _send = unsend as *const S<dyn Cat<'static>>;
//~^ error: lifetime may not live long enough
}
fn main() {
m();
}

View File

@ -0,0 +1,15 @@
error: lifetime may not live long enough
--> $DIR/ptr-to-trait-obj-different-regions-id-trait.rs:21:17
|
LL | fn m<'a>() {
| -- lifetime `'a` defined here
LL | let unsend: *const dyn Cat<'a> = &();
LL | let _send = unsend as *const S<dyn Cat<'static>>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`
|
= note: requirement occurs because of the type `S<dyn Cat<'_>>`, which makes the generic argument `dyn Cat<'_>` invariant
= note: the struct `S<T>` is invariant over the parameter `T`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error: aborting due to 1 previous error

View File

@ -0,0 +1,31 @@
//@ check-fail
//
// issue: <https://github.com/rust-lang/rust/issues/120217>
#![feature(arbitrary_self_types)]
trait Static<'a> {
fn proof(self: *const Self, s: &'a str) -> &'static str;
}
fn bad_cast<'a>(x: *const dyn Static<'static>) -> *const dyn Static<'a> {
x as _ //~ error: lifetime may not live long enough
}
impl Static<'static> for () {
fn proof(self: *const Self, s: &'static str) -> &'static str {
s
}
}
fn extend_lifetime(s: &str) -> &'static str {
bad_cast(&()).proof(s)
}
fn main() {
let s = String::from("Hello World");
let slice = extend_lifetime(&s);
println!("Now it exists: {slice}");
drop(s);
println!("Now its gone: {slice}");
}

View File

@ -0,0 +1,10 @@
error: lifetime may not live long enough
--> $DIR/ptr-to-trait-obj-different-regions-lt-ext.rs:12:5
|
LL | fn bad_cast<'a>(x: *const dyn Static<'static>) -> *const dyn Static<'a> {
| -- lifetime `'a` defined here
LL | x as _
| ^^^^^^ returning this value requires that `'a` must outlive `'static`
error: aborting due to 1 previous error

View File

@ -0,0 +1,37 @@
//@ check-fail
trait Trait<'a> {}
fn change_lt<'a, 'b>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> {
x as _ //~ error: lifetime may not live long enough
//~| error: lifetime may not live long enough
}
fn change_lt_ab<'a: 'b, 'b>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> {
x as _ //~ error: lifetime may not live long enough
}
fn change_lt_ba<'a, 'b: 'a>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> {
x as _ //~ error: lifetime may not live long enough
}
trait Assocked {
type Assoc: ?Sized;
}
fn change_assoc_0<'a, 'b>(
x: *mut dyn Assocked<Assoc = dyn Send + 'a>,
) -> *mut dyn Assocked<Assoc = dyn Send + 'b> {
x as _ //~ error: lifetime may not live long enough
//~| error: lifetime may not live long enough
}
fn change_assoc_1<'a, 'b>(
x: *mut dyn Assocked<Assoc = dyn Trait<'a>>,
) -> *mut dyn Assocked<Assoc = dyn Trait<'b>> {
x as _ //~ error: lifetime may not live long enough
//~| error: lifetime may not live long enough
}
fn main() {}

View File

@ -0,0 +1,136 @@
error: lifetime may not live long enough
--> $DIR/ptr-to-trait-obj-different-regions-misc.rs:6:5
|
LL | fn change_lt<'a, 'b>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
LL | x as _
| ^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
|
= help: consider adding the following bound: `'b: 'a`
= note: requirement occurs because of a mutable pointer to `dyn Trait<'_>`
= note: mutable pointers are invariant over their type parameter
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error: lifetime may not live long enough
--> $DIR/ptr-to-trait-obj-different-regions-misc.rs:6:5
|
LL | fn change_lt<'a, 'b>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
LL | x as _
| ^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
|
= help: consider adding the following bound: `'a: 'b`
= note: requirement occurs because of a mutable pointer to `dyn Trait<'_>`
= note: mutable pointers are invariant over their type parameter
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
help: `'b` and `'a` must be the same: replace one with the other
error: lifetime may not live long enough
--> $DIR/ptr-to-trait-obj-different-regions-misc.rs:11:5
|
LL | fn change_lt_ab<'a: 'b, 'b>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
LL | x as _
| ^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
|
= help: consider adding the following bound: `'b: 'a`
= note: requirement occurs because of a mutable pointer to `dyn Trait<'_>`
= note: mutable pointers are invariant over their type parameter
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error: lifetime may not live long enough
--> $DIR/ptr-to-trait-obj-different-regions-misc.rs:15:5
|
LL | fn change_lt_ba<'a, 'b: 'a>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
LL | x as _
| ^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
|
= help: consider adding the following bound: `'a: 'b`
= note: requirement occurs because of a mutable pointer to `dyn Trait<'_>`
= note: mutable pointers are invariant over their type parameter
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error: lifetime may not live long enough
--> $DIR/ptr-to-trait-obj-different-regions-misc.rs:25:5
|
LL | fn change_assoc_0<'a, 'b>(
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
...
LL | x as _
| ^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
|
= help: consider adding the following bound: `'b: 'a`
= note: requirement occurs because of a mutable pointer to `dyn Assocked<Assoc = dyn Send>`
= note: mutable pointers are invariant over their type parameter
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error: lifetime may not live long enough
--> $DIR/ptr-to-trait-obj-different-regions-misc.rs:25:5
|
LL | fn change_assoc_0<'a, 'b>(
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
...
LL | x as _
| ^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
|
= help: consider adding the following bound: `'a: 'b`
= note: requirement occurs because of a mutable pointer to `dyn Assocked<Assoc = dyn Send>`
= note: mutable pointers are invariant over their type parameter
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
help: `'b` and `'a` must be the same: replace one with the other
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error: lifetime may not live long enough
--> $DIR/ptr-to-trait-obj-different-regions-misc.rs:32:5
|
LL | fn change_assoc_1<'a, 'b>(
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
...
LL | x as _
| ^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
|
= help: consider adding the following bound: `'b: 'a`
= note: requirement occurs because of a mutable pointer to `dyn Assocked<Assoc = dyn Trait<'_>>`
= note: mutable pointers are invariant over their type parameter
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error: lifetime may not live long enough
--> $DIR/ptr-to-trait-obj-different-regions-misc.rs:32:5
|
LL | fn change_assoc_1<'a, 'b>(
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
...
LL | x as _
| ^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
|
= help: consider adding the following bound: `'a: 'b`
= note: requirement occurs because of a mutable pointer to `dyn Assocked<Assoc = dyn Trait<'_>>`
= note: mutable pointers are invariant over their type parameter
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
help: `'b` and `'a` must be the same: replace one with the other
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error: aborting due to 8 previous errors

View File

@ -0,0 +1,17 @@
//@ check-pass
trait Trait<'a> {}
fn remove_auto<'a>(x: *mut (dyn Trait<'a> + Send)) -> *mut dyn Trait<'a> {
x as _
}
fn cast_inherent_lt<'a, 'b>(x: *mut (dyn Trait<'static> + 'a)) -> *mut (dyn Trait<'static> + 'b) {
x as _
}
fn unprincipled<'a, 'b>(x: *mut (dyn Send + 'a)) -> *mut (dyn Sync + 'b) {
x as _
}
fn main() {}

View File

@ -0,0 +1,14 @@
trait Super {}
trait Sub: Super {}
struct Wrapper<T: ?Sized>(T);
// This cast should not compile.
// Upcasting can't work here, because we are also changing the type (`Wrapper`),
// and reinterpreting would be confusing/surprising.
// See <https://github.com/rust-lang/rust/pull/120248#discussion_r1487739518>
fn cast(ptr: *const dyn Sub) -> *const Wrapper<dyn Super> {
ptr as _ //~ error: casting `*const (dyn Sub + 'static)` as `*const Wrapper<dyn Super>` is invalid
}
fn main() {}

View File

@ -0,0 +1,11 @@
error[E0606]: casting `*const (dyn Sub + 'static)` as `*const Wrapper<dyn Super>` is invalid
--> $DIR/ptr-to-trait-obj-wrap-upcast.rs:11:5
|
LL | ptr as _
| ^^^^^^^^
|
= note: vtable kinds may not match
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0606`.

View File

@ -66,7 +66,7 @@ fn main()
let cf: *const dyn Foo = &0;
let _ = cf as *const [u16]; //~ ERROR is invalid
let _ = cf as *const dyn Bar; //~ ERROR is invalid
let _ = cf as *const dyn Bar; //~ ERROR casting `*const dyn Foo` as `*const dyn Bar` is invalid
vec![0.0].iter().map(|s| s as f32).collect::<Vec<f32>>(); //~ ERROR is invalid
}

View File

@ -3,9 +3,9 @@
trait Trait {}
fn assert_send(ptr: *mut dyn Trait) -> *mut (dyn Trait + Send) {
fn assert_send() -> *mut (dyn Trait + Send) {
//~^ ERROR incorrect parentheses around trait bounds
ptr as _
loop {}
}
fn foo2(_: &(dyn Trait + Send)) {}

View File

@ -3,9 +3,9 @@
trait Trait {}
fn assert_send(ptr: *mut dyn Trait) -> *mut dyn (Trait + Send) {
fn assert_send() -> *mut dyn (Trait + Send) {
//~^ ERROR incorrect parentheses around trait bounds
ptr as _
loop {}
}
fn foo2(_: &dyn (Trait + Send)) {}

View File

@ -1,13 +1,13 @@
error: incorrect parentheses around trait bounds
--> $DIR/issue-114797-bad-parentheses-dyn-trait.rs:6:49
--> $DIR/issue-114797-bad-parentheses-dyn-trait.rs:6:30
|
LL | fn assert_send(ptr: *mut dyn Trait) -> *mut dyn (Trait + Send) {
| ^ ^
LL | fn assert_send() -> *mut dyn (Trait + Send) {
| ^ ^
|
help: fix the parentheses
|
LL - fn assert_send(ptr: *mut dyn Trait) -> *mut dyn (Trait + Send) {
LL + fn assert_send(ptr: *mut dyn Trait) -> *mut (dyn Trait + Send) {
LL - fn assert_send() -> *mut dyn (Trait + Send) {
LL + fn assert_send() -> *mut (dyn Trait + Send) {
|
error: incorrect parentheses around trait bounds

View File

@ -1,7 +1,8 @@
#![feature(trait_upcasting)]
//@ known-bug: #120222
//@ check-pass
//! This will segfault at runtime.
//@ check-fail
//
// issue: <https://github.com/rust-lang/rust/pull/120222>
//! This would segfault at runtime.
pub trait SupSupA {
fn method(&self) {}
@ -56,6 +57,7 @@ pub fn user2() -> &'static dyn Trait<u8, u16> {
fn main() {
let p: *const dyn Trait<u8, u8> = &();
let p = p as *const dyn Trait<u8, u16>; // <- this is bad!
//~^ error: mismatched types
let p = p as *const dyn Super<u16>; // <- this upcast accesses improper vtable entry
// accessing from L__unnamed_2 the position for the 'Super<u16> vtable (pointer)',
// thus reading 'null pointer for missing_method'

View File

@ -0,0 +1,13 @@
error[E0308]: mismatched types
--> $DIR/upcast_soundness_bug.rs:59:13
|
LL | let p = p as *const dyn Trait<u8, u16>; // <- this is bad!
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u8`, found `u16`
|
= note: expected trait object `dyn Trait<u8, u8>`
found trait object `dyn Trait<u8, u16>`
= help: `dyn Trait<u8, u16>` implements `Trait` so you could box the found value and coerce it to the trait object `Box<dyn Trait>`, you will have to change the expected type as well
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0308`.