Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 340d69b

Browse files
committed
Align the changes to the lang decision
1 parent c743557 commit 340d69b

File tree

8 files changed

+165
-21
lines changed

8 files changed

+165
-21
lines changed

compiler/rustc_borrowck/src/type_check/mod.rs

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2323,27 +2323,48 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
23232323
let src_tail = tcx.struct_tail_without_normalization(src.ty);
23242324
let dst_tail = tcx.struct_tail_without_normalization(dst.ty);
23252325

2326-
if let ty::Dynamic(..) = src_tail.kind()
2326+
if let ty::Dynamic(src_tty, ..) = src_tail.kind()
23272327
&& let ty::Dynamic(dst_tty, ..) = dst_tail.kind()
2328+
&& src_tty.principal().is_some()
23282329
&& dst_tty.principal().is_some()
23292330
{
2330-
// Erase trait object lifetimes, to allow casts like `*mut dyn FnOnce()` -> `*mut dyn FnOnce() + 'static`.
2331-
let src_tail =
2332-
erase_single_trait_object_lifetime(tcx, src_tail);
2333-
let dst_tail =
2334-
erase_single_trait_object_lifetime(tcx, dst_tail);
2331+
// Erase trait object lifetimes, to allow casts like `*mut dyn FnOnce()` -> `*mut dyn FnOnce() + 'static`
2332+
// and remove auto traits.
2333+
let src_obj = tcx.mk_ty_from_kind(ty::Dynamic(
2334+
tcx.mk_poly_existential_predicates(
2335+
&src_tty.without_auto_traits().collect::<Vec<_>>(),
2336+
),
2337+
tcx.lifetimes.re_erased,
2338+
ty::Dyn,
2339+
));
2340+
let dst_obj = tcx.mk_ty_from_kind(ty::Dynamic(
2341+
tcx.mk_poly_existential_predicates(
2342+
&dst_tty.without_auto_traits().collect::<Vec<_>>(),
2343+
),
2344+
tcx.lifetimes.re_erased,
2345+
ty::Dyn,
2346+
));
2347+
2348+
// FIXME:
2349+
// this currently does nothing, but once we make `ptr_cast_add_auto_to_object`
2350+
// into a hard error, we can remove the above removal of auto traits and only
2351+
// keep this.
2352+
let src_obj = erase_single_trait_object_lifetime(tcx, src_obj);
2353+
let dst_obj = erase_single_trait_object_lifetime(tcx, dst_obj);
23352354

23362355
let trait_ref = ty::TraitRef::new(
23372356
tcx,
23382357
tcx.require_lang_item(LangItem::Unsize, Some(span)),
2339-
[src_tail, dst_tail],
2358+
[src_obj, dst_obj],
23402359
);
23412360

2361+
debug!(?src_tty, ?dst_tty, ?src_obj, ?dst_obj);
2362+
23422363
self.prove_trait_ref(
23432364
trait_ref,
23442365
location.to_locations(),
23452366
ConstraintCategory::Cast {
2346-
unsize_to: Some(tcx.fold_regions(dst_tail, |r, _| {
2367+
unsize_to: Some(tcx.fold_regions(dst_obj, |r, _| {
23472368
if let ty::ReVar(_) = r.kind() {
23482369
tcx.lifetimes.re_erased
23492370
} else {

compiler/rustc_hir_typeck/messages.ftl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,11 @@ hir_typeck_option_result_asref = use `{$def_path}::as_ref` to convert `{$expecte
123123
hir_typeck_option_result_cloned = use `{$def_path}::cloned` to clone the value inside the `{$def_path}`
124124
hir_typeck_option_result_copied = use `{$def_path}::copied` to copy the value inside the `{$def_path}`
125125
126+
hir_typeck_ptr_cast_add_auto_to_object = adding an auto {$traits_len ->
127+
[1] trait {$traits}
128+
*[other] traits {$traits}
129+
} to a trait object in a pointer cast may cause UB later on
130+
126131
hir_typeck_remove_semi_for_coerce = you might have meant to return the `match` expression
127132
hir_typeck_remove_semi_for_coerce_expr = this could be implicitly returned but it is a statement, not a tail expression
128133
hir_typeck_remove_semi_for_coerce_ret = the `match` arms can conform to this return type

compiler/rustc_hir_typeck/src/cast.rs

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ use super::FnCtxt;
3232

3333
use crate::errors;
3434
use crate::type_error_struct;
35+
use itertools::Itertools;
36+
use rustc_data_structures::fx::FxHashSet;
3537
use rustc_errors::{codes::*, Applicability, Diag, ErrorGuaranteed};
3638
use rustc_hir::{self as hir, ExprKind, LangItem};
3739
use rustc_infer::traits::Obligation;
@@ -827,15 +829,16 @@ impl<'a, 'tcx> CastCheck<'tcx> {
827829
// trait object -> trait object? need to do additional checks
828830
(Some(PointerKind::VTable(src_tty)), Some(PointerKind::VTable(dst_tty))) => {
829831
match (src_tty.principal(), dst_tty.principal()) {
830-
// A<dyn Trait + Auto> -> B<dyn Trait' + Auto'>. need to make sure
831-
// - traits are the same
832+
// A<dyn Src<...> + SrcAuto> -> B<dyn Dst<...> + DstAuto>. need to make sure
833+
// - `Src` and `Dst` traits are the same
832834
// - traits have the same generic arguments
833-
// - Auto' is a subset of Auto
835+
// - `SrcAuto` is a superset of `DstAuto`
834836
(Some(src_principal), Some(dst_principal)) => {
835837
let tcx = fcx.tcx;
836838

837839
// Check that the traits are actually the same
838840
// (this is required as the `Unsize` check below would allow upcasting, etc)
841+
// N.B.: this is only correct as long as we don't support `trait A<T>: A<()>`.
839842
if src_principal.def_id() != dst_principal.def_id() {
840843
return Err(CastError::DifferingKinds);
841844
}
@@ -845,18 +848,24 @@ impl<'a, 'tcx> CastCheck<'tcx> {
845848
// contain wrappers, which we do not care about.
846849
//
847850
// e.g. we want to allow `dyn T -> (dyn T,)`, etc.
851+
//
852+
// We also need to skip auto traits to emit an FCW and not an error.
848853
let src_obj = tcx.mk_ty_from_kind(ty::Dynamic(
849-
src_tty,
854+
tcx.mk_poly_existential_predicates(
855+
&src_tty.without_auto_traits().collect::<Vec<_>>(),
856+
),
850857
tcx.lifetimes.re_erased,
851858
ty::Dyn,
852859
));
853860
let dst_obj = tcx.mk_ty_from_kind(ty::Dynamic(
854-
dst_tty,
861+
tcx.mk_poly_existential_predicates(
862+
&dst_tty.without_auto_traits().collect::<Vec<_>>(),
863+
),
855864
tcx.lifetimes.re_erased,
856865
ty::Dyn,
857866
));
858867

859-
// `dyn Src: Unsize<dyn Dst>`
868+
// `dyn Src: Unsize<dyn Dst>`, this checks for matching generics
860869
let cause = fcx.misc(self.span);
861870
let obligation = Obligation::new(
862871
tcx,
@@ -871,6 +880,31 @@ impl<'a, 'tcx> CastCheck<'tcx> {
871880

872881
fcx.register_predicate(obligation);
873882

883+
// Check that `SrcAuto` is a superset of `DstAuto`.
884+
// Emit an FCW otherwise.
885+
let src_auto = src_tty.auto_traits().collect::<FxHashSet<_>>();
886+
let added = dst_tty
887+
.auto_traits()
888+
.filter(|trait_did| !src_auto.contains(trait_did))
889+
.collect::<Vec<_>>();
890+
891+
if !added.is_empty() {
892+
tcx.emit_node_span_lint(
893+
lint::builtin::PTR_CAST_ADD_AUTO_TO_OBJECT,
894+
self.expr.hir_id,
895+
self.span,
896+
errors::PtrCastAddAutoToObject {
897+
traits_len: added.len(),
898+
traits: added
899+
.into_iter()
900+
.map(|trait_did| {
901+
format!("`{}`", tcx.def_path_str(trait_did))
902+
})
903+
.join(", "),
904+
},
905+
)
906+
}
907+
874908
// FIXME: ideally we'd maybe add a flag here, so that borrowck knows that
875909
// it needs to borrowck this ptr cast. this is made annoying by the
876910
// fact that `thir` does not have `CastKind` and mir restores it

compiler/rustc_hir_typeck/src/errors.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,14 @@ pub struct LossyProvenanceInt2Ptr<'tcx> {
253253
pub sugg: LossyProvenanceInt2PtrSuggestion,
254254
}
255255

256+
#[derive(LintDiagnostic)]
257+
#[diag(hir_typeck_ptr_cast_add_auto_to_object)]
258+
//#[help]
259+
pub struct PtrCastAddAutoToObject {
260+
pub traits_len: usize,
261+
pub traits: String,
262+
}
263+
256264
#[derive(Subdiagnostic)]
257265
#[multipart_suggestion(hir_typeck_suggestion, applicability = "has-placeholders")]
258266
pub struct LossyProvenanceInt2PtrSuggestion {

compiler/rustc_lint_defs/src/builtin.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ declare_lint_pass! {
8080
PRIVATE_BOUNDS,
8181
PRIVATE_INTERFACES,
8282
PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
83+
PTR_CAST_ADD_AUTO_TO_OBJECT,
8384
PUB_USE_OF_PRIVATE_EXTERN_CRATE,
8485
REDUNDANT_LIFETIMES,
8586
REFINING_IMPL_TRAIT_INTERNAL,
@@ -4937,6 +4938,59 @@ declare_lint! {
49374938
};
49384939
}
49394940

4941+
declare_lint! {
4942+
/// The `ptr_cast_add_auto_to_object` lint detects casts of raw pointers to trait
4943+
/// objects, which add auto traits.
4944+
///
4945+
/// ### Example
4946+
///
4947+
/// ```rust,edition2021,compile_fail
4948+
/// let ptr: *const dyn core::any::Any = &();
4949+
/// _ = ptr as *const dyn core::any::Any + Send;
4950+
/// ```
4951+
///
4952+
/// {{produces}}
4953+
///
4954+
/// ### Explanation
4955+
///
4956+
/// Adding an auto trait can make the vtable invalid, potentially causing
4957+
/// UB in safe code afterwards. For example:
4958+
///
4959+
/// ```ignore (causes a warning)
4960+
/// #![feature(arbitrary_self_types)]
4961+
///
4962+
/// trait Trait {
4963+
/// fn f(self: *const Self)
4964+
/// where
4965+
/// Self: Send;
4966+
/// }
4967+
///
4968+
/// impl Trait for *const () {
4969+
/// fn f(self: *const Self) {
4970+
/// unreachable!()
4971+
/// }
4972+
/// }
4973+
///
4974+
/// fn main() {
4975+
/// let unsend: *const () = &();
4976+
/// let unsend: *const dyn Trait = &unsend;
4977+
/// let send_bad: *const (dyn Trait + Send) = unsend as _;
4978+
/// send_bad.f(); // this crashes, since vtable for `*const ()` does not have an entry for `f`
4979+
/// }
4980+
/// ```
4981+
///
4982+
/// Generally you must ensure that vtable is right for the pointer's type,
4983+
/// before passing the pointer to safe code.
4984+
pub PTR_CAST_ADD_AUTO_TO_OBJECT,
4985+
Warn,
4986+
"detects `as` casts from pointers to `dyn Trait` to pointers to `dyn Trait + Auto`",
4987+
@future_incompatible = FutureIncompatibleInfo {
4988+
reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps,
4989+
// FIXME: actually write an issue with an explanation
4990+
reference: "issue #125289 <https://github.com/rust-lang/rust/issues/125289>",
4991+
};
4992+
}
4993+
49404994
declare_lint! {
49414995
/// The `out_of_scope_macro_calls` lint detects `macro_rules` called when they are not in scope,
49424996
/// above their definition, which may happen in key-value attributes.

compiler/rustc_middle/src/ty/predicate.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,14 @@ impl<'tcx> ty::List<ty::PolyExistentialPredicate<'tcx>> {
341341
_ => None,
342342
})
343343
}
344+
345+
pub fn without_auto_traits(
346+
&self,
347+
) -> impl Iterator<Item = ty::PolyExistentialPredicate<'tcx>> + '_ {
348+
self.iter().filter(|predicate| {
349+
!matches!(predicate.as_ref().skip_binder(), ExistentialPredicate::AutoTrait(_))
350+
})
351+
}
344352
}
345353

346354
pub type PolyTraitRef<'tcx> = ty::Binder<'tcx, TraitRef<'tcx>>;
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
//@ check-fail
1+
//@ check-pass
22

33
trait Trait<'a> {}
44

55
fn add_auto<'a>(x: *mut dyn Trait<'a>) -> *mut (dyn Trait<'a> + Send) {
6-
x as _ //~ error: the trait bound `dyn Trait<'_>: Unsize<dyn Trait<'_> + Send>` is not satisfied
6+
x as _
7+
//~^ warning: adding an auto trait `Send` to a trait object in a pointer cast may cause UB later on
8+
//~| warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
79
}
810

911
fn main() {}

tests/ui/cast/ptr-to-trait-obj-add-auto.stderr

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,23 @@
1-
error[E0277]: the trait bound `dyn Trait<'_>: Unsize<dyn Trait<'_> + Send>` is not satisfied
1+
warning: adding an auto trait `Send` to a trait object in a pointer cast may cause UB later on
22
--> $DIR/ptr-to-trait-obj-add-auto.rs:6:5
33
|
44
LL | x as _
5-
| ^^^^^^ the trait `Unsize<dyn Trait<'_> + Send>` is not implemented for `dyn Trait<'_>`
5+
| ^^^^^^
66
|
7-
= 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
7+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
8+
= note: for more information, see issue #125289 <https://github.com/rust-lang/rust/issues/125289>
9+
= note: `#[warn(ptr_cast_add_auto_to_object)]` on by default
810

9-
error: aborting due to 1 previous error
11+
warning: 1 warning emitted
12+
13+
Future incompatibility report: Future breakage diagnostic:
14+
warning: adding an auto trait `Send` to a trait object in a pointer cast may cause UB later on
15+
--> $DIR/ptr-to-trait-obj-add-auto.rs:6:5
16+
|
17+
LL | x as _
18+
| ^^^^^^
19+
|
20+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
21+
= note: for more information, see issue #125289 <https://github.com/rust-lang/rust/issues/125289>
22+
= note: `#[warn(ptr_cast_add_auto_to_object)]` on by default
1023

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

0 commit comments

Comments
 (0)