Skip to content

Commit 6d0d4b8

Browse files
committed
Allow unsizing pattern types with pointer base
1 parent 39ed5c4 commit 6d0d4b8

File tree

25 files changed

+223
-25
lines changed

25 files changed

+223
-25
lines changed

compiler/rustc_codegen_cranelift/src/unsize.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,11 @@ pub(crate) fn coerce_unsized_into<'tcx>(
148148
dst.write_cvalue(fx, CValue::by_val_pair(base, info, dst.layout()));
149149
};
150150
match (&src_ty.kind(), &dst_ty.kind()) {
151+
(ty::Pat(a, _), ty::Pat(b, _)) => {
152+
let src = src.cast_pat_ty_to_base(fx.layout_of(*a));
153+
let dst = dst.place_transmute_type(fx, *b);
154+
return coerce_unsized_into(fx, src, dst);
155+
}
151156
(&ty::Ref(..), &ty::Ref(..))
152157
| (&ty::Ref(..), &ty::RawPtr(..))
153158
| (&ty::RawPtr(..), &ty::RawPtr(..)) => coerce_ptr(),

compiler/rustc_codegen_cranelift/src/value_and_place.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,14 @@ impl<'tcx> CValue<'tcx> {
379379
assert_eq!(self.layout().backend_repr, layout.backend_repr);
380380
CValue(self.0, layout)
381381
}
382+
383+
pub(crate) fn cast_pat_ty_to_base(self, layout: TyAndLayout<'tcx>) -> Self {
384+
let ty::Pat(base, _) = *self.layout().ty.kind() else {
385+
panic!("not a pattern type: {:#?}", self.layout())
386+
};
387+
assert_eq!(layout.ty, base);
388+
CValue(self.0, layout)
389+
}
382390
}
383391

384392
/// A place where you can write a value to or read a value from

compiler/rustc_codegen_ssa/src/base.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ pub(crate) fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
230230
) -> (Bx::Value, Bx::Value) {
231231
debug!("unsize_ptr: {:?} => {:?}", src_ty, dst_ty);
232232
match (src_ty.kind(), dst_ty.kind()) {
233+
(&ty::Pat(a, _), &ty::Pat(b, _)) => unsize_ptr(bx, src, a, b, old_info),
233234
(&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(b, _))
234235
| (&ty::RawPtr(a, _), &ty::RawPtr(b, _)) => {
235236
assert_eq!(bx.cx().type_is_sized(a), old_info.is_none());

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,6 +1073,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
10731073
//
10741074
// This is also relevant for `Pin<&mut Self>`, where we need to peel the
10751075
// `Pin`.
1076+
1077+
loop {
1078+
match *op.layout.ty.kind() {
1079+
ty::Ref(..) | ty::RawPtr(..) => break,
1080+
ty::Pat(inner, _) => op.layout = bx.layout_of(inner),
1081+
_ => {
1082+
let (idx, _) = op.layout.non_1zst_field(bx).expect(
1083+
"not exactly one non-1-ZST field in a `DispatchFromDyn` type",
1084+
);
1085+
op = op.extract_field(self, bx, idx);
1086+
}
1087+
}
1088+
}
10761089
while !op.layout.ty.is_raw_ptr() && !op.layout.ty.is_ref() {
10771090
let (idx, _) = op.layout.non_1zst_field(bx).expect(
10781091
"not exactly one non-1-ZST field in a `DispatchFromDyn` type",

compiler/rustc_const_eval/src/interpret/call.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -637,7 +637,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
637637
let mut receiver = self.copy_fn_arg(&args[0]);
638638
let receiver_place = loop {
639639
match receiver.layout.ty.kind() {
640-
ty::Ref(..) | ty::RawPtr(..) => {
640+
ty::Pat(..) | ty::Ref(..) | ty::RawPtr(..) => {
641641
// We do *not* use `deref_pointer` here: we don't want to conceptually
642642
// create a place that must be dereferenceable, since the receiver might
643643
// be a raw pointer and (for `*const dyn Trait`) we don't need to

compiler/rustc_const_eval/src/interpret/cast.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
473473
) -> InterpResult<'tcx> {
474474
trace!("Unsizing {:?} of type {} into {}", *src, src.layout.ty, cast_ty.ty);
475475
match (src.layout.ty.kind(), cast_ty.ty.kind()) {
476+
(&ty::Pat(s, s_pat), &ty::Pat(c, c_pat)) if s_pat == c_pat => {
477+
let mut src = src.clone();
478+
src.layout = self.layout_of(s)?;
479+
let mut dest = dest.clone();
480+
dest.layout = self.layout_of(c)?;
481+
let cast_ty = match *cast_ty.ty.kind() {
482+
ty::Pat(base, pat) if pat == c_pat => self.layout_of(base)?,
483+
_ => span_bug!(
484+
self.cur_span(),
485+
"unsize_into: invalid cast ty for pattern type: {cast_ty:#?}"
486+
),
487+
};
488+
self.unsize_into(&src, cast_ty, &dest)
489+
}
476490
(&ty::Ref(_, s, _), &ty::Ref(_, c, _) | &ty::RawPtr(c, _))
477491
| (&ty::RawPtr(s, _), &ty::RawPtr(c, _)) => self.unsize_into_ptr(src, dest, s, c),
478492
(&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {

compiler/rustc_const_eval/src/interpret/place.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use std::assert_matches::assert_matches;
66

77
use either::{Either, Left, Right};
88
use rustc_abi::{BackendRepr, HasDataLayout, Size};
9-
use rustc_middle::ty::Ty;
109
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
10+
use rustc_middle::ty::{self, Ty};
1111
use rustc_middle::{bug, mir, span_bug};
1212
use tracing::{instrument, trace};
1313

@@ -419,8 +419,12 @@ where
419419
&self,
420420
val: &ImmTy<'tcx, M::Provenance>,
421421
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
422+
let pointer_type = match val.layout.ty.kind() {
423+
ty::Pat(base, _) => *base,
424+
_ => val.layout.ty,
425+
};
422426
let pointee_type =
423-
val.layout.ty.builtin_deref(true).expect("`ref_to_mplace` called on non-ptr type");
427+
pointer_type.builtin_deref(true).expect("`ref_to_mplace` called on non-ptr type");
424428
let layout = self.layout_of(pointee_type)?;
425429
let (ptr, meta) = val.to_scalar_and_meta();
426430

compiler/rustc_hir_analysis/messages.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ hir_analysis_coerce_pointee_not_struct = `derive(CoercePointee)` is only applica
9999
100100
hir_analysis_coerce_pointee_not_transparent = `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout
101101
102+
hir_analysis_coerce_same_pat_kind = only pattern types with the same pattern can be coerced between each other
103+
102104
hir_analysis_coerce_unsized_field_validity = for `{$ty}` to have a valid implementation of `{$trait_name}`, it must be possible to coerce the field of type `{$field_ty}`
103105
.label = `{$field_ty}` must be a pointer, reference, or smart pointer that is allowed to be unsized
104106

compiler/rustc_hir_analysis/src/coherence/builtin.rs

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,18 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
249249
// in the compiler (in particular, all the call ABI logic) will treat them as repr(transparent)
250250
// even if they do not carry that attribute.
251251
match (source.kind(), target.kind()) {
252+
(&ty::Pat(_, pat_a), &ty::Pat(_, pat_b)) => {
253+
if pat_a != pat_b {
254+
return Err(tcx.dcx().emit_err(errors::CoerceSamePatKind {
255+
span,
256+
trait_name,
257+
pat_a: pat_a.to_string(),
258+
pat_b: pat_b.to_string(),
259+
}));
260+
}
261+
Ok(())
262+
}
263+
252264
(&ty::Ref(r_a, _, mutbl_a), ty::Ref(r_b, _, mutbl_b))
253265
if r_a == *r_b && mutbl_a == *mutbl_b =>
254266
{
@@ -414,6 +426,18 @@ pub(crate) fn coerce_unsized_info<'tcx>(
414426
(mt_a.ty, mt_b.ty, unsize_trait, None, span)
415427
};
416428
let (source, target, trait_def_id, kind, field_span) = match (source.kind(), target.kind()) {
429+
(&ty::Pat(ty_a, pat_a), &ty::Pat(ty_b, pat_b)) => {
430+
if pat_a != pat_b {
431+
return Err(tcx.dcx().emit_err(errors::CoerceSamePatKind {
432+
span,
433+
trait_name,
434+
pat_a: pat_a.to_string(),
435+
pat_b: pat_b.to_string(),
436+
}));
437+
}
438+
(ty_a, ty_b, coerce_unsized_trait, None, span)
439+
}
440+
417441
(&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => {
418442
infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a);
419443
let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };
@@ -713,13 +737,16 @@ fn visit_implementation_of_pointer_like(checker: &Checker<'_>) -> Result<(), Err
713737
let impl_span = tcx.def_span(checker.impl_def_id);
714738
let self_ty = tcx.impl_trait_ref(checker.impl_def_id).unwrap().instantiate_identity().self_ty();
715739

716-
let is_permitted_primitive = match *self_ty.kind() {
717-
ty::Adt(def, _) => def.is_box(),
718-
ty::Uint(..) | ty::Int(..) | ty::RawPtr(..) | ty::Ref(..) | ty::FnPtr(..) => true,
719-
_ => false,
720-
};
740+
fn is_permitted_primitive(self_ty: Ty<'_>) -> bool {
741+
match *self_ty.kind() {
742+
ty::Adt(def, _) => def.is_box(),
743+
ty::Uint(..) | ty::Int(..) | ty::RawPtr(..) | ty::Ref(..) | ty::FnPtr(..) => true,
744+
ty::Pat(base, _) => is_permitted_primitive(base),
745+
_ => false,
746+
}
747+
}
721748

722-
if is_permitted_primitive
749+
if is_permitted_primitive(self_ty)
723750
&& let Ok(layout) = tcx.layout_of(typing_env.as_query_input(self_ty))
724751
&& layout.layout.is_pointer_like(&tcx.data_layout)
725752
{

compiler/rustc_hir_analysis/src/coherence/orphan.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -206,12 +206,8 @@ pub(crate) fn orphan_check_impl(
206206
(LocalImpl::Disallow { problematic_kind }, NonlocalImpl::DisallowOther)
207207
}
208208

209-
ty::Pat(..) => (
210-
LocalImpl::Disallow { problematic_kind: "pattern type" },
211-
NonlocalImpl::DisallowOther,
212-
),
213-
214209
ty::Bool
210+
| ty::Pat(..)
215211
| ty::Char
216212
| ty::Int(..)
217213
| ty::Uint(..)

compiler/rustc_hir_analysis/src/errors.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1280,6 +1280,16 @@ pub(crate) struct CoerceUnsizedNonStruct {
12801280
pub trait_name: &'static str,
12811281
}
12821282

1283+
#[derive(Diagnostic)]
1284+
#[diag(hir_analysis_coerce_same_pat_kind)]
1285+
pub(crate) struct CoerceSamePatKind {
1286+
#[primary_span]
1287+
pub span: Span,
1288+
pub trait_name: &'static str,
1289+
pub pat_a: String,
1290+
pub pat_b: String,
1291+
}
1292+
12831293
#[derive(Diagnostic)]
12841294
#[diag(hir_analysis_coerce_unsized_may, code = E0377)]
12851295
pub(crate) struct CoerceSameStruct {

compiler/rustc_middle/src/traits/select.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,9 @@ pub enum SelectionCandidate<'tcx> {
159159
/// types generated for a fn pointer type (e.g., `fn(int) -> int`)
160160
FnPointerCandidate,
161161

162+
/// Builtin impl of the `PointerLike` trait.
163+
PointerLikeCandidate,
164+
162165
TraitAliasCandidate,
163166

164167
/// Matching `dyn Trait` with a supertrait of `Trait`. The index is the

compiler/rustc_middle/src/ty/layout.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1009,13 +1009,17 @@ where
10091009
/// Compute the information for the pointer stored at the given offset inside this type.
10101010
/// This will recurse into fields of ADTs to find the inner pointer.
10111011
fn ty_and_layout_pointee_info_at(
1012-
this: TyAndLayout<'tcx>,
1012+
mut this: TyAndLayout<'tcx>,
10131013
cx: &C,
10141014
offset: Size,
10151015
) -> Option<PointeeInfo> {
10161016
let tcx = cx.tcx();
10171017
let typing_env = cx.typing_env();
10181018

1019+
if let ty::Pat(base, _) = *this.ty.kind() {
1020+
this.ty = base;
1021+
}
1022+
10191023
let pointee_info = match *this.ty.kind() {
10201024
ty::RawPtr(p_ty, _) if offset.bytes() == 0 => {
10211025
tcx.layout_of(typing_env.as_query_input(p_ty)).ok().map(|layout| PointeeInfo {

compiler/rustc_mir_transform/src/validate.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
707707
};
708708
check_equal(self, location, *f_ty);
709709
}
710+
// Debug info is allowed to project into pattern types
711+
ty::Pat(base, _) => check_equal(self, location, *base),
710712
ty::Adt(adt_def, args) => {
711713
// see <https://github.com/rust-lang/rust/blob/7601adcc764d42c9f2984082b49948af652df986/compiler/rustc_middle/src/ty/layout.rs#L861-L864>
712714
if self.tcx.is_lang_item(adt_def.did(), LangItem::DynMetadata) {

compiler/rustc_monomorphize/src/collector.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,7 @@ fn find_tails_for_unsizing<'tcx>(
10671067
debug_assert!(!target_ty.has_param(), "{target_ty} should be fully monomorphic");
10681068

10691069
match (source_ty.kind(), target_ty.kind()) {
1070+
(&ty::Pat(source, _), &ty::Pat(target, _)) => find_tails_for_unsizing(tcx, source, target),
10701071
(
10711072
&ty::Ref(_, source_pointee, _),
10721073
&ty::Ref(_, target_pointee, _) | &ty::RawPtr(target_pointee, _),

compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
138138
&mut candidates,
139139
);
140140
}
141+
Some(LangItem::PointerLike) => {
142+
self.assemble_pointer_like_candidates(obligation, &mut candidates);
143+
}
141144
Some(LangItem::AsyncFn | LangItem::AsyncFnMut | LangItem::AsyncFnOnce) => {
142145
self.assemble_async_closure_candidates(obligation, &mut candidates);
143146
}
@@ -485,6 +488,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
485488
}
486489
}
487490

491+
fn assemble_pointer_like_candidates(
492+
&mut self,
493+
obligation: &PolyTraitObligation<'tcx>,
494+
candidates: &mut SelectionCandidateSet<'tcx>,
495+
) {
496+
match obligation.self_ty().skip_binder().kind() {
497+
ty::Pat(..) => candidates.vec.push(PointerLikeCandidate),
498+
_ => {}
499+
}
500+
}
501+
488502
fn assemble_async_fn_kind_helper_candidates(
489503
&mut self,
490504
obligation: &PolyTraitObligation<'tcx>,

compiler/rustc_trait_selection/src/traits/select/confirmation.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
117117
ImplSource::Builtin(BuiltinImplSource::Misc, data)
118118
}
119119

120+
PointerLikeCandidate => {
121+
let data = self.confirm_pointer_like_candidate(obligation);
122+
ImplSource::Builtin(BuiltinImplSource::Misc, data)
123+
}
124+
120125
TraitAliasCandidate => {
121126
let data = self.confirm_trait_alias_candidate(obligation);
122127
ImplSource::Builtin(BuiltinImplSource::Misc, data)
@@ -665,6 +670,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
665670
Ok(nested)
666671
}
667672

673+
fn confirm_pointer_like_candidate(
674+
&mut self,
675+
obligation: &PolyTraitObligation<'tcx>,
676+
) -> PredicateObligations<'tcx> {
677+
debug!(?obligation, "confirm_fn_pointer_candidate");
678+
let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
679+
let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
680+
let ty::Pat(base, _) = *self_ty.kind() else { bug!() };
681+
let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived);
682+
683+
self.collect_predicates_for_types(
684+
obligation.param_env,
685+
cause,
686+
obligation.recursion_depth + 1,
687+
placeholder_predicate.def_id(),
688+
vec![base],
689+
)
690+
}
691+
668692
fn confirm_trait_alias_candidate(
669693
&mut self,
670694
obligation: &PolyTraitObligation<'tcx>,

compiler/rustc_trait_selection/src/traits/select/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1983,6 +1983,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
19831983
| TraitUpcastingUnsizeCandidate(_)
19841984
| BuiltinObjectCandidate
19851985
| BuiltinUnsizeCandidate
1986+
| PointerLikeCandidate
19861987
| BikeshedGuaranteedNoDropCandidate => false,
19871988
// Non-global param candidates have already been handled, global
19881989
// where-bounds get ignored.

compiler/rustc_ty_utils/src/abi.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -724,14 +724,19 @@ fn make_thin_self_ptr<'tcx>(
724724
// To get the type `*mut RcInner<Self>`, we just keep unwrapping newtypes until we
725725
// get a built-in pointer type
726726
let mut wide_pointer_layout = layout;
727-
while !wide_pointer_layout.ty.is_raw_ptr() && !wide_pointer_layout.ty.is_ref() {
728-
wide_pointer_layout = wide_pointer_layout
729-
.non_1zst_field(cx)
730-
.expect("not exactly one non-1-ZST field in a `DispatchFromDyn` type")
731-
.1
727+
loop {
728+
match *wide_pointer_layout.ty.kind() {
729+
ty::Ref(..) | ty::RawPtr(..) => break wide_pointer_layout.ty,
730+
ty::Pat(inner, _) => match inner.kind() {
731+
ty::RawPtr(..) => break inner,
732+
_ => panic!("only raw pointers are allowed in unsized pattern types"),
733+
},
734+
_ => wide_pointer_layout = wide_pointer_layout
735+
.non_1zst_field(cx)
736+
.unwrap_or_else(|| panic!("not exactly one non-1-ZST field in a `DispatchFromDyn` type: {wide_pointer_layout:#?}"))
737+
.1,
738+
}
732739
}
733-
734-
wide_pointer_layout.ty
735740
};
736741

737742
// we now have a type like `*mut RcInner<dyn Trait>`

library/core/src/pat.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
//! Helper module for exporting the `pattern_type` macro
22
3+
#[cfg(not(bootstrap))]
4+
use crate::marker::{Freeze, Unsize};
5+
#[cfg(not(bootstrap))]
6+
use crate::ops::{CoerceUnsized, DispatchFromDyn};
7+
38
/// Creates a pattern type.
49
/// ```ignore (cannot test this from within core yet)
510
/// type Positive = std::pat::pattern_type!(i32 is 1..);
@@ -74,3 +79,21 @@ impl const RangePattern for char {
7479
}
7580
}
7681
}
82+
83+
#[cfg(not(bootstrap))]
84+
impl<T: ?Sized, U: ?Sized> CoerceUnsized<pattern_type!(*const U is !null)> for pattern_type!(*const T is !null) where
85+
T: Unsize<U>
86+
{
87+
}
88+
89+
#[cfg(not(bootstrap))]
90+
impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<pattern_type!(U is !null)> for pattern_type!(T is !null) {}
91+
92+
#[cfg(not(bootstrap))]
93+
impl<T: ?Sized> Unpin for pattern_type!(*const T is !null) {}
94+
95+
#[cfg(not(bootstrap))]
96+
unsafe impl<T: ?Sized> Freeze for pattern_type!(*const T is !null) {}
97+
98+
#[cfg(not(bootstrap))]
99+
unsafe impl<T: ?Sized> Freeze for pattern_type!(*mut T is !null) {}

0 commit comments

Comments
 (0)