Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 63be0e2

Browse files
authoredMar 9, 2025
Unrolled build for rust-lang#136127
Rollup merge of rust-lang#136127 - WaffleLapkin:dyn_ptr_unwrap_cast, r=compiler-errors Allow `*const W<dyn A> -> *const dyn A` ptr cast Followup of rust-lang#120248 (comment). This PR allows casting pointers from something wrapping a trait object, to the trait object, i.e. `*const W<dyn A> -> *const dyn A` where `W` is `struct W<T: ?Sized>(T);`. r? compiler-errors Fixes rust-lang#128625
2 parents 4f52199 + 80157a5 commit 63be0e2

8 files changed

+437
-8
lines changed
 

‎compiler/rustc_hir_typeck/src/coercion.rs

+60-8
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ use rustc_abi::ExternAbi;
4141
use rustc_attr_parsing::InlineAttr;
4242
use rustc_errors::codes::*;
4343
use rustc_errors::{Applicability, Diag, struct_span_code_err};
44-
use rustc_hir as hir;
4544
use rustc_hir::def_id::{DefId, LocalDefId};
45+
use rustc_hir::{self as hir, LangItem};
4646
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
4747
use rustc_infer::infer::relate::RelateResult;
4848
use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult};
@@ -56,7 +56,7 @@ use rustc_middle::ty::adjustment::{
5656
};
5757
use rustc_middle::ty::error::TypeError;
5858
use rustc_middle::ty::visit::TypeVisitableExt;
59-
use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt};
59+
use rustc_middle::ty::{self, AliasTy, GenericArgsRef, Ty, TyCtxt};
6060
use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Span};
6161
use rustc_trait_selection::infer::InferCtxtExt as _;
6262
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
@@ -593,6 +593,63 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
593593

594594
// Create an obligation for `Source: CoerceUnsized<Target>`.
595595
let cause = self.cause(self.cause.span, ObligationCauseCode::Coercion { source, target });
596+
let root_obligation = Obligation::new(
597+
self.tcx,
598+
cause.clone(),
599+
self.fcx.param_env,
600+
ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target]),
601+
);
602+
603+
// If the root `Source: CoerceUnsized<Target>` obligation can't possibly hold,
604+
// we don't have to assume that this is unsizing coercion (it will always lead to an error)
605+
//
606+
// However, we don't want to bail early all the time, since the unholdable obligations
607+
// may be interesting for diagnostics (such as trying to coerce `&T` to `&dyn Id<This = U>`),
608+
// so we only bail if there (likely) is another way to convert the types.
609+
if !self.infcx.predicate_may_hold(&root_obligation) {
610+
if let Some(dyn_metadata_adt_def_id) = self.tcx.lang_items().get(LangItem::DynMetadata)
611+
&& let Some(metadata_type_def_id) = self.tcx.lang_items().get(LangItem::Metadata)
612+
{
613+
self.probe(|_| {
614+
let ocx = ObligationCtxt::new(&self.infcx);
615+
616+
// returns `true` if `<ty as Pointee>::Metadata` is `DynMetadata<_>`
617+
let has_dyn_trait_metadata = |ty| {
618+
let metadata_ty: Result<_, _> = ocx.structurally_normalize_ty(
619+
&ObligationCause::dummy(),
620+
self.fcx.param_env,
621+
Ty::new_alias(
622+
self.tcx,
623+
ty::AliasTyKind::Projection,
624+
AliasTy::new(self.tcx, metadata_type_def_id, [ty]),
625+
),
626+
);
627+
628+
metadata_ty.is_ok_and(|metadata_ty| {
629+
metadata_ty
630+
.ty_adt_def()
631+
.is_some_and(|d| d.did() == dyn_metadata_adt_def_id)
632+
})
633+
};
634+
635+
// If both types are raw pointers to a (wrapper over a) trait object,
636+
// this might be a cast like `*const W<dyn Trait> -> *const dyn Trait`.
637+
// So it's better to bail and try that. (even if the cast is not possible, for
638+
// example due to vtables not matching, cast diagnostic will likely still be better)
639+
//
640+
// N.B. use `target`, not `coerce_target` (the latter is a var)
641+
if let &ty::RawPtr(source_pointee, _) = coerce_source.kind()
642+
&& let &ty::RawPtr(target_pointee, _) = target.kind()
643+
&& has_dyn_trait_metadata(source_pointee)
644+
&& has_dyn_trait_metadata(target_pointee)
645+
{
646+
return Err(TypeError::Mismatch);
647+
}
648+
649+
Ok(())
650+
})?;
651+
}
652+
}
596653

597654
// Use a FIFO queue for this custom fulfillment procedure.
598655
//
@@ -601,12 +658,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
601658
// and almost never more than 3. By using a SmallVec we avoid an
602659
// allocation, at the (very small) cost of (occasionally) having to
603660
// shift subsequent elements down when removing the front element.
604-
let mut queue: SmallVec<[PredicateObligation<'tcx>; 4]> = smallvec![Obligation::new(
605-
self.tcx,
606-
cause,
607-
self.fcx.param_env,
608-
ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target])
609-
)];
661+
let mut queue: SmallVec<[PredicateObligation<'tcx>; 4]> = smallvec![root_obligation];
610662

611663
// Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid
612664
// emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Combination of `ptr-to-trait-obj-wrap.rs` and `ptr-to-trait-obj-add-auto.rs`.
2+
//
3+
// Checks that you *can't* add auto traits to trait object in pointer casts involving wrapping said
4+
// traits structures.
5+
6+
trait A {}
7+
8+
struct W<T: ?Sized>(T);
9+
struct X<T: ?Sized>(T);
10+
11+
fn unwrap(a: *const W<dyn A>) -> *const (dyn A + Send) {
12+
a as _
13+
//~^ error: cannot add auto trait `Send` to dyn bound via pointer cast
14+
}
15+
16+
fn unwrap_nested(a: *const W<W<dyn A>>) -> *const W<dyn A + Send> {
17+
a as _
18+
//~^ error: cannot add auto trait `Send` to dyn bound via pointer cast
19+
}
20+
21+
fn rewrap(a: *const W<dyn A>) -> *const X<dyn A + Send> {
22+
a as _
23+
//~^ error: cannot add auto trait `Send` to dyn bound via pointer cast
24+
}
25+
26+
fn rewrap_nested(a: *const W<W<dyn A>>) -> *const W<X<dyn A + Send>> {
27+
a as _
28+
//~^ error: cannot add auto trait `Send` to dyn bound via pointer cast
29+
}
30+
31+
fn wrap(a: *const dyn A) -> *const W<dyn A + Send> {
32+
a as _
33+
//~^ error: cannot add auto trait `Send` to dyn bound via pointer cast
34+
}
35+
36+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
error[E0804]: cannot add auto trait `Send` to dyn bound via pointer cast
2+
--> $DIR/ptr-to-trait-obj-wrap-add-auto.rs:12:5
3+
|
4+
LL | a as _
5+
| ^^^^^^ unsupported cast
6+
|
7+
= note: this could allow UB elsewhere
8+
= help: use `transmute` if you're sure this is sound
9+
10+
error[E0804]: cannot add auto trait `Send` to dyn bound via pointer cast
11+
--> $DIR/ptr-to-trait-obj-wrap-add-auto.rs:17:5
12+
|
13+
LL | a as _
14+
| ^^^^^^ unsupported cast
15+
|
16+
= note: this could allow UB elsewhere
17+
= help: use `transmute` if you're sure this is sound
18+
19+
error[E0804]: cannot add auto trait `Send` to dyn bound via pointer cast
20+
--> $DIR/ptr-to-trait-obj-wrap-add-auto.rs:22:5
21+
|
22+
LL | a as _
23+
| ^^^^^^ unsupported cast
24+
|
25+
= note: this could allow UB elsewhere
26+
= help: use `transmute` if you're sure this is sound
27+
28+
error[E0804]: cannot add auto trait `Send` to dyn bound via pointer cast
29+
--> $DIR/ptr-to-trait-obj-wrap-add-auto.rs:27:5
30+
|
31+
LL | a as _
32+
| ^^^^^^ unsupported cast
33+
|
34+
= note: this could allow UB elsewhere
35+
= help: use `transmute` if you're sure this is sound
36+
37+
error[E0804]: cannot add auto trait `Send` to dyn bound via pointer cast
38+
--> $DIR/ptr-to-trait-obj-wrap-add-auto.rs:32:5
39+
|
40+
LL | a as _
41+
| ^^^^^^ unsupported cast
42+
|
43+
= note: this could allow UB elsewhere
44+
= help: use `transmute` if you're sure this is sound
45+
46+
error: aborting due to 5 previous errors
47+
48+
For more information about this error, try `rustc --explain E0804`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Combination of `ptr-to-trait-obj-different-args.rs` and `ptr-to-trait-obj-wrap.rs`.
2+
//
3+
// Checks that you *can't* change type arguments of trait objects in pointer casts involving
4+
// wrapping said traits structures.
5+
6+
trait A<T> {}
7+
8+
struct W<T: ?Sized>(T);
9+
struct X<T: ?Sized>(T);
10+
11+
fn unwrap<F, G>(a: *const W<dyn A<F>>) -> *const dyn A<G> {
12+
a as _
13+
//~^ error casting `*const W<(dyn A<F> + 'static)>` as `*const dyn A<G>` is invalid
14+
}
15+
16+
fn unwrap_nested<F, G>(a: *const W<W<dyn A<F>>>) -> *const W<dyn A<G>> {
17+
a as _
18+
//~^ error casting `*const W<W<(dyn A<F> + 'static)>>` as `*const W<dyn A<G>>` is invalid
19+
}
20+
21+
fn rewrap<F, G>(a: *const W<dyn A<F>>) -> *const X<dyn A<G>> {
22+
a as _
23+
//~^ error: casting `*const W<(dyn A<F> + 'static)>` as `*const X<dyn A<G>>` is invalid
24+
}
25+
26+
fn rewrap_nested<F, G>(a: *const W<W<dyn A<F>>>) -> *const W<X<dyn A<G>>> {
27+
a as _
28+
//~^ error: casting `*const W<W<(dyn A<F> + 'static)>>` as `*const W<X<dyn A<G>>>` is invalid
29+
}
30+
31+
fn wrap<F, G>(a: *const dyn A<F>) -> *const W<dyn A<G>> {
32+
a as _
33+
//~^ error: casting `*const (dyn A<F> + 'static)` as `*const W<dyn A<G>>` is invalid
34+
}
35+
36+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
error[E0606]: casting `*const W<(dyn A<F> + 'static)>` as `*const dyn A<G>` is invalid
2+
--> $DIR/ptr-to-trait-obj-wrap-different-args.rs:12:5
3+
|
4+
LL | a as _
5+
| ^^^^^^
6+
|
7+
= note: the trait objects may have different vtables
8+
9+
error[E0606]: casting `*const W<W<(dyn A<F> + 'static)>>` as `*const W<dyn A<G>>` is invalid
10+
--> $DIR/ptr-to-trait-obj-wrap-different-args.rs:17:5
11+
|
12+
LL | a as _
13+
| ^^^^^^
14+
|
15+
= note: the trait objects may have different vtables
16+
17+
error[E0606]: casting `*const W<(dyn A<F> + 'static)>` as `*const X<dyn A<G>>` is invalid
18+
--> $DIR/ptr-to-trait-obj-wrap-different-args.rs:22:5
19+
|
20+
LL | a as _
21+
| ^^^^^^
22+
|
23+
= note: the trait objects may have different vtables
24+
25+
error[E0606]: casting `*const W<W<(dyn A<F> + 'static)>>` as `*const W<X<dyn A<G>>>` is invalid
26+
--> $DIR/ptr-to-trait-obj-wrap-different-args.rs:27:5
27+
|
28+
LL | a as _
29+
| ^^^^^^
30+
|
31+
= note: the trait objects may have different vtables
32+
33+
error[E0606]: casting `*const (dyn A<F> + 'static)` as `*const W<dyn A<G>>` is invalid
34+
--> $DIR/ptr-to-trait-obj-wrap-different-args.rs:32:5
35+
|
36+
LL | a as _
37+
| ^^^^^^
38+
|
39+
= note: the trait objects may have different vtables
40+
41+
error: aborting due to 5 previous errors
42+
43+
For more information about this error, try `rustc --explain E0606`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Combination of `ptr-to-trait-obj-different-regions-misc.rs` and `ptr-to-trait-obj-wrap.rs`.
2+
//
3+
// Checks that you *can't* change lifetime arguments of trait objects in pointer casts involving
4+
// wrapping said traits structures.
5+
6+
trait A<'a> {}
7+
8+
struct W<T: ?Sized>(T);
9+
struct X<T: ?Sized>(T);
10+
11+
fn unwrap<'a, 'b>(a: *const W<dyn A<'a>>) -> *const dyn A<'b> {
12+
a as _
13+
//~^ error
14+
//~| error
15+
}
16+
17+
fn unwrap_nested<'a, 'b>(a: *const W<W<dyn A<'a>>>) -> *const W<dyn A<'b>> {
18+
a as _
19+
//~^ error
20+
//~| error
21+
}
22+
23+
fn rewrap<'a, 'b>(a: *const W<dyn A<'a>>) -> *const X<dyn A<'b>> {
24+
a as _
25+
//~^ error: lifetime may not live long enough
26+
//~| error: lifetime may not live long enough
27+
}
28+
29+
fn rewrap_nested<'a, 'b>(a: *const W<W<dyn A<'a>>>) -> *const W<X<dyn A<'b>>> {
30+
a as _
31+
//~^ error: lifetime may not live long enough
32+
//~| error: lifetime may not live long enough
33+
}
34+
35+
fn wrap<'a, 'b>(a: *const dyn A<'a>) -> *const W<dyn A<'b>> {
36+
a as _
37+
//~^ error: lifetime may not live long enough
38+
//~| error: lifetime may not live long enough
39+
}
40+
41+
fn main() {}
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Failed to load comments.