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 src_tail = tcx.struct_tail_without_normalization(src.ty);
|
||||||
let dst_tail = tcx.struct_tail_without_normalization(dst.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()
|
&& let ty::Dynamic(dst_tty, ..) = dst_tail.kind()
|
||||||
|
&& src_tty.principal().is_some()
|
||||||
&& dst_tty.principal().is_some()
|
&& dst_tty.principal().is_some()
|
||||||
{
|
{
|
||||||
// Erase trait object lifetimes, to allow casts like `*mut dyn FnOnce()` -> `*mut dyn FnOnce() + 'static`.
|
// Erase trait object lifetimes, to allow casts like `*mut dyn FnOnce()` -> `*mut dyn FnOnce() + 'static`
|
||||||
let src_tail =
|
// and remove auto traits.
|
||||||
erase_single_trait_object_lifetime(tcx, src_tail);
|
let src_obj = tcx.mk_ty_from_kind(ty::Dynamic(
|
||||||
let dst_tail =
|
tcx.mk_poly_existential_predicates(
|
||||||
erase_single_trait_object_lifetime(tcx, dst_tail);
|
&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(
|
let trait_ref = ty::TraitRef::new(
|
||||||
tcx,
|
tcx,
|
||||||
tcx.require_lang_item(LangItem::Unsize, Some(span)),
|
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(
|
self.prove_trait_ref(
|
||||||
trait_ref,
|
trait_ref,
|
||||||
location.to_locations(),
|
location.to_locations(),
|
||||||
ConstraintCategory::Cast {
|
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() {
|
if let ty::ReVar(_) = r.kind() {
|
||||||
tcx.lifetimes.re_erased
|
tcx.lifetimes.re_erased
|
||||||
} else {
|
} 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_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_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 = 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_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
|
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::errors;
|
||||||
use crate::type_error_struct;
|
use crate::type_error_struct;
|
||||||
|
use itertools::Itertools;
|
||||||
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_errors::{codes::*, Applicability, Diag, ErrorGuaranteed};
|
use rustc_errors::{codes::*, Applicability, Diag, ErrorGuaranteed};
|
||||||
use rustc_hir::{self as hir, ExprKind, LangItem};
|
use rustc_hir::{self as hir, ExprKind, LangItem};
|
||||||
use rustc_infer::traits::Obligation;
|
use rustc_infer::traits::Obligation;
|
||||||
|
@ -827,15 +829,16 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|
||||||
// trait object -> trait object? need to do additional checks
|
// trait object -> trait object? need to do additional checks
|
||||||
(Some(PointerKind::VTable(src_tty)), Some(PointerKind::VTable(dst_tty))) => {
|
(Some(PointerKind::VTable(src_tty)), Some(PointerKind::VTable(dst_tty))) => {
|
||||||
match (src_tty.principal(), dst_tty.principal()) {
|
match (src_tty.principal(), dst_tty.principal()) {
|
||||||
// A<dyn Trait + Auto> -> B<dyn Trait' + Auto'>. need to make sure
|
// A<dyn Src<...> + SrcAuto> -> B<dyn Dst<...> + DstAuto>. need to make sure
|
||||||
// - traits are the same
|
// - `Src` and `Dst` traits are the same
|
||||||
// - traits have the same generic arguments
|
// - traits have the same generic arguments
|
||||||
// - Auto' is a subset of Auto
|
// - `SrcAuto` is a superset of `DstAuto`
|
||||||
(Some(src_principal), Some(dst_principal)) => {
|
(Some(src_principal), Some(dst_principal)) => {
|
||||||
let tcx = fcx.tcx;
|
let tcx = fcx.tcx;
|
||||||
|
|
||||||
// Check that the traits are actually the same
|
// Check that the traits are actually the same
|
||||||
// (this is required as the `Unsize` check below would allow upcasting, etc)
|
// (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() {
|
if src_principal.def_id() != dst_principal.def_id() {
|
||||||
return Err(CastError::DifferingKinds);
|
return Err(CastError::DifferingKinds);
|
||||||
}
|
}
|
||||||
|
@ -845,18 +848,24 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|
||||||
// contain wrappers, which we do not care about.
|
// contain wrappers, which we do not care about.
|
||||||
//
|
//
|
||||||
// e.g. we want to allow `dyn T -> (dyn T,)`, etc.
|
// 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(
|
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,
|
tcx.lifetimes.re_erased,
|
||||||
ty::Dyn,
|
ty::Dyn,
|
||||||
));
|
));
|
||||||
let dst_obj = tcx.mk_ty_from_kind(ty::Dynamic(
|
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,
|
tcx.lifetimes.re_erased,
|
||||||
ty::Dyn,
|
ty::Dyn,
|
||||||
));
|
));
|
||||||
|
|
||||||
// `dyn Src: Unsize<dyn Dst>`
|
// `dyn Src: Unsize<dyn Dst>`, this checks for matching generics
|
||||||
let cause = fcx.misc(self.span);
|
let cause = fcx.misc(self.span);
|
||||||
let obligation = Obligation::new(
|
let obligation = Obligation::new(
|
||||||
tcx,
|
tcx,
|
||||||
|
@ -871,6 +880,31 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|
||||||
|
|
||||||
fcx.register_predicate(obligation);
|
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
|
// 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
|
// it needs to borrowck this ptr cast. this is made annoying by the
|
||||||
// fact that `thir` does not have `CastKind` and mir restores it
|
// fact that `thir` does not have `CastKind` and mir restores it
|
||||||
|
|
|
@ -253,6 +253,14 @@ pub struct LossyProvenanceInt2Ptr<'tcx> {
|
||||||
pub sugg: LossyProvenanceInt2PtrSuggestion,
|
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)]
|
#[derive(Subdiagnostic)]
|
||||||
#[multipart_suggestion(hir_typeck_suggestion, applicability = "has-placeholders")]
|
#[multipart_suggestion(hir_typeck_suggestion, applicability = "has-placeholders")]
|
||||||
pub struct LossyProvenanceInt2PtrSuggestion {
|
pub struct LossyProvenanceInt2PtrSuggestion {
|
||||||
|
|
|
@ -80,6 +80,7 @@ declare_lint_pass! {
|
||||||
PRIVATE_BOUNDS,
|
PRIVATE_BOUNDS,
|
||||||
PRIVATE_INTERFACES,
|
PRIVATE_INTERFACES,
|
||||||
PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
|
PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
|
||||||
|
PTR_CAST_ADD_AUTO_TO_OBJECT,
|
||||||
PUB_USE_OF_PRIVATE_EXTERN_CRATE,
|
PUB_USE_OF_PRIVATE_EXTERN_CRATE,
|
||||||
REDUNDANT_LIFETIMES,
|
REDUNDANT_LIFETIMES,
|
||||||
REFINING_IMPL_TRAIT_INTERNAL,
|
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! {
|
declare_lint! {
|
||||||
/// The `out_of_scope_macro_calls` lint detects `macro_rules` called when they are not in scope,
|
/// 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.
|
/// above their definition, which may happen in key-value attributes.
|
||||||
|
|
|
@ -341,6 +341,14 @@ impl<'tcx> ty::List<ty::PolyExistentialPredicate<'tcx>> {
|
||||||
_ => None,
|
_ => 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>>;
|
pub type PolyTraitRef<'tcx> = ty::Binder<'tcx, TraitRef<'tcx>>;
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
//@ check-fail
|
//@ check-pass
|
||||||
|
|
||||||
trait Trait<'a> {}
|
trait Trait<'a> {}
|
||||||
|
|
||||||
fn add_auto<'a>(x: *mut dyn Trait<'a>) -> *mut (dyn Trait<'a> + Send) {
|
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() {}
|
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
|
--> $DIR/ptr-to-trait-obj-add-auto.rs:6:5
|
||||||
|
|
|
|
||||||
LL | x as _
|
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