Align the changes to the lang decision
This commit is contained in:
parent
c7435571ad
commit
340d69be12
8 changed files with 165 additions and 21 deletions
|
@ -2323,27 +2323,48 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
let src_tail = tcx.struct_tail_without_normalization(src.ty);
|
||||
let dst_tail = tcx.struct_tail_without_normalization(dst.ty);
|
||||
|
||||
if let ty::Dynamic(..) = src_tail.kind()
|
||||
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()
|
||||
{
|
||||
// Erase trait object lifetimes, to allow casts like `*mut dyn FnOnce()` -> `*mut dyn FnOnce() + 'static`.
|
||||
let src_tail =
|
||||
erase_single_trait_object_lifetime(tcx, src_tail);
|
||||
let dst_tail =
|
||||
erase_single_trait_object_lifetime(tcx, dst_tail);
|
||||
// Erase trait object lifetimes, to allow casts like `*mut dyn FnOnce()` -> `*mut dyn FnOnce() + 'static`
|
||||
// and remove auto traits.
|
||||
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,
|
||||
));
|
||||
|
||||
// FIXME:
|
||||
// this currently does nothing, but once we make `ptr_cast_add_auto_to_object`
|
||||
// into a hard error, we can remove the above removal of auto traits and only
|
||||
// keep this.
|
||||
let src_obj = erase_single_trait_object_lifetime(tcx, src_obj);
|
||||
let dst_obj = erase_single_trait_object_lifetime(tcx, dst_obj);
|
||||
|
||||
let trait_ref = ty::TraitRef::new(
|
||||
tcx,
|
||||
tcx.require_lang_item(LangItem::Unsize, Some(span)),
|
||||
[src_tail, dst_tail],
|
||||
[src_obj, dst_obj],
|
||||
);
|
||||
|
||||
debug!(?src_tty, ?dst_tty, ?src_obj, ?dst_obj);
|
||||
|
||||
self.prove_trait_ref(
|
||||
trait_ref,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::Cast {
|
||||
unsize_to: Some(tcx.fold_regions(dst_tail, |r, _| {
|
||||
unsize_to: Some(tcx.fold_regions(dst_obj, |r, _| {
|
||||
if let ty::ReVar(_) = r.kind() {
|
||||
tcx.lifetimes.re_erased
|
||||
} else {
|
||||
|
|
|
@ -123,6 +123,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 an auto {$traits_len ->
|
||||
[1] trait {$traits}
|
||||
*[other] 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
|
||||
|
|
|
@ -32,6 +32,8 @@ use super::FnCtxt;
|
|||
|
||||
use crate::errors;
|
||||
use crate::type_error_struct;
|
||||
use itertools::Itertools;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::{codes::*, Applicability, Diag, ErrorGuaranteed};
|
||||
use rustc_hir::{self as hir, ExprKind, LangItem};
|
||||
use rustc_infer::traits::Obligation;
|
||||
|
@ -827,15 +829,16 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|
|||
// 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 Trait + Auto> -> B<dyn Trait' + Auto'>. need to make sure
|
||||
// - traits are the same
|
||||
// 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
|
||||
// - Auto' is a subset of Auto
|
||||
// - `SrcAuto` is a superset of `DstAuto`
|
||||
(Some(src_principal), Some(dst_principal)) => {
|
||||
let tcx = fcx.tcx;
|
||||
|
||||
// Check that the traits are actually the same
|
||||
// (this is required as the `Unsize` check below would allow upcasting, etc)
|
||||
// N.B.: this is only correct as long as we don't support `trait A<T>: A<()>`.
|
||||
if src_principal.def_id() != dst_principal.def_id() {
|
||||
return Err(CastError::DifferingKinds);
|
||||
}
|
||||
|
@ -845,18 +848,24 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|
|||
// 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(
|
||||
src_tty,
|
||||
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(
|
||||
dst_tty,
|
||||
tcx.mk_poly_existential_predicates(
|
||||
&dst_tty.without_auto_traits().collect::<Vec<_>>(),
|
||||
),
|
||||
tcx.lifetimes.re_erased,
|
||||
ty::Dyn,
|
||||
));
|
||||
|
||||
// `dyn Src: Unsize<dyn Dst>`
|
||||
// `dyn Src: Unsize<dyn Dst>`, this checks for matching generics
|
||||
let cause = fcx.misc(self.span);
|
||||
let obligation = Obligation::new(
|
||||
tcx,
|
||||
|
@ -871,6 +880,31 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|
|||
|
||||
fcx.register_predicate(obligation);
|
||||
|
||||
// Check that `SrcAuto` is a superset of `DstAuto`.
|
||||
// Emit an FCW otherwise.
|
||||
let src_auto = src_tty.auto_traits().collect::<FxHashSet<_>>();
|
||||
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: added
|
||||
.into_iter()
|
||||
.map(|trait_did| {
|
||||
format!("`{}`", tcx.def_path_str(trait_did))
|
||||
})
|
||||
.join(", "),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// FIXME: ideally we'd maybe add a flag here, so that borrowck knows that
|
||||
// it needs to borrowck this ptr cast. this is made annoying by the
|
||||
// fact that `thir` does not have `CastKind` and mir restores it
|
||||
|
|
|
@ -253,6 +253,14 @@ pub struct LossyProvenanceInt2Ptr<'tcx> {
|
|||
pub sugg: LossyProvenanceInt2PtrSuggestion,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(hir_typeck_ptr_cast_add_auto_to_object)]
|
||||
//#[help]
|
||||
pub struct PtrCastAddAutoToObject {
|
||||
pub traits_len: usize,
|
||||
pub traits: String,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[multipart_suggestion(hir_typeck_suggestion, applicability = "has-placeholders")]
|
||||
pub struct LossyProvenanceInt2PtrSuggestion {
|
||||
|
|
|
@ -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,
|
||||
|
@ -4937,6 +4938,59 @@ 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,
|
||||
// FIXME: actually write an issue with an explanation
|
||||
reference: "issue #125289 <https://github.com/rust-lang/rust/issues/125289>",
|
||||
};
|
||||
}
|
||||
|
||||
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.
|
||||
|
|
|
@ -341,6 +341,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>>;
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
//@ check-fail
|
||||
//@ check-pass
|
||||
|
||||
trait Trait<'a> {}
|
||||
|
||||
fn add_auto<'a>(x: *mut dyn Trait<'a>) -> *mut (dyn Trait<'a> + Send) {
|
||||
x as _ //~ error: the trait bound `dyn Trait<'_>: Unsize<dyn Trait<'_> + Send>` is not satisfied
|
||||
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!
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -1,11 +1,23 @@
|
|||
error[E0277]: the trait bound `dyn Trait<'_>: Unsize<dyn Trait<'_> + Send>` is not satisfied
|
||||
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 _
|
||||
| ^^^^^^ the trait `Unsize<dyn Trait<'_> + Send>` is not implemented for `dyn Trait<'_>`
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: all implementations of `Unsize` are provided automatically by the compiler, see <https://doc.rust-lang.org/stable/std/marker/trait.Unsize.html> for more information
|
||||
= 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 #125289 <https://github.com/rust-lang/rust/issues/125289>
|
||||
= note: `#[warn(ptr_cast_add_auto_to_object)]` on by default
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
warning: 1 warning 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 #125289 <https://github.com/rust-lang/rust/issues/125289>
|
||||
= note: `#[warn(ptr_cast_add_auto_to_object)]` on by default
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue