Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 74 additions & 62 deletions compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ use rustc_mir_dataflow::points::DenseLocationMap;
use rustc_span::def_id::CRATE_DEF_ID;
use rustc_span::source_map::Spanned;
use rustc_span::{Span, sym};
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints;
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
use tracing::{debug, instrument, trace};
Expand Down Expand Up @@ -1455,68 +1456,79 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}
CastKind::PtrToPtr => {
let ty_from = op.ty(self.body, 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(src)), Some(CastTy::Ptr(dst))) => {
let src_tail = self.struct_tail(src.ty, location);
let dst_tail = self.struct_tail(dst.ty, location);

// 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_lt, ty::Dyn) = *src_tail.kind()
&& let ty::Dynamic(dst_tty, dst_lt, ty::Dyn) = *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 = Ty::new_dynamic(
tcx,
tcx.mk_poly_existential_predicates(
&src_tty.without_auto_traits().collect::<Vec<_>>(),
),
// FIXME: Once we disallow casting `*const dyn Trait + 'short`
// to `*const dyn Trait + 'long`, then this can just be `src_lt`.
dst_lt,
ty::Dyn,
);
let dst_obj = Ty::new_dynamic(
tcx,
tcx.mk_poly_existential_predicates(
&dst_tty.without_auto_traits().collect::<Vec<_>>(),
),
dst_lt,
ty::Dyn,
);

debug!(?src_tty, ?dst_tty, ?src_obj, ?dst_obj);

self.sub_types(
src_obj,
dst_obj,
location.to_locations(),
ConstraintCategory::Cast {
is_implicit_coercion: false,
unsize_to: None,
},
)
.unwrap();
}
}
_ => {
span_mirbug!(
self,
rvalue,
"Invalid PtrToPtr cast {:?} -> {:?}",
ty_from,
ty
)
}
let Some(CastTy::Ptr(src)) = CastTy::from_ty(ty_from) else {
unreachable!();
};
let Some(CastTy::Ptr(dst)) = CastTy::from_ty(*ty) else {
unreachable!();
};

if self.infcx.type_is_sized_modulo_regions(self.infcx.param_env, dst.ty) {
// Wide to thin ptr cast. This may even occur in an env with
// impossible predicates, such as `where dyn Trait: Sized`.
// In this case, we don't want to fall into the case below,
// since the types may not actually be equatable, but it's
// fine to perform this operation in an impossible env.
let trait_ref = ty::TraitRef::new(
tcx,
tcx.require_lang_item(LangItem::Sized, self.last_span),
[dst.ty],
);
self.prove_trait_ref(
trait_ref,
location.to_locations(),
ConstraintCategory::Cast {
is_implicit_coercion: true,
unsize_to: None,
},
);
} else if let ty::Dynamic(src_tty, _src_lt, ty::Dyn) =
*self.struct_tail(src.ty, location).kind()
&& let ty::Dynamic(dst_tty, dst_lt, ty::Dyn) =
*self.struct_tail(dst.ty, location).kind()
&& src_tty.principal().is_some()
&& dst_tty.principal().is_some()
{
// 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`.

// Remove auto traits.
// Auto trait checks are handled in `rustc_hir_typeck` as FCW.
let src_obj = Ty::new_dynamic(
tcx,
tcx.mk_poly_existential_predicates(
&src_tty.without_auto_traits().collect::<Vec<_>>(),
),
// FIXME: Once we disallow casting `*const dyn Trait + 'short`
// to `*const dyn Trait + 'long`, then this can just be `src_lt`.
dst_lt,
ty::Dyn,
);
let dst_obj = Ty::new_dynamic(
tcx,
tcx.mk_poly_existential_predicates(
&dst_tty.without_auto_traits().collect::<Vec<_>>(),
),
dst_lt,
ty::Dyn,
);

debug!(?src_tty, ?dst_tty, ?src_obj, ?dst_obj);

self.sub_types(
src_obj,
dst_obj,
location.to_locations(),
ConstraintCategory::Cast {
is_implicit_coercion: false,
unsize_to: None,
},
)
.unwrap();
}
}
CastKind::Transmute => {
Expand Down
16 changes: 16 additions & 0 deletions tests/ui/coercion/fake-sized-ptr-cast.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Make sure borrowck doesn't ICE because it thinks a pointer cast is a metadata-preserving
// wide-to-wide ptr cast when it's actually (falsely) a wide-to-thin ptr cast due to an
// impossible dyn sized bound.

//@ check-pass

trait Trait<T> {}

fn func<'a>(x: *const (dyn Trait<()> + 'a))
where
dyn Trait<u8> + 'a: Sized,
{
let _x: *const dyn Trait<u8> = x as _;
}

fn main() {}
Loading