Skip to content

Commit 96da85c

Browse files
committed
Allow unsizing pattern types with pointer base
1 parent 6221b16 commit 96da85c

File tree

17 files changed

+162
-11
lines changed

17 files changed

+162
-11
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_const_eval/src/interpret/cast.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,12 @@ 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_pat), &ty::Pat(cast_ty, c_pat)) if s_pat == c_pat => {
477+
let src = self.project_field(src, 0)?;
478+
let dest = self.project_field(dest, 0)?;
479+
let cast_ty = self.layout_of(cast_ty)?;
480+
self.unsize_into(&src, cast_ty, &dest)
481+
}
476482
(&ty::Ref(_, s, _), &ty::Ref(_, c, _) | &ty::RawPtr(c, _))
477483
| (&ty::RawPtr(s, _), &ty::RawPtr(c, _)) => self.unsize_into_ptr(src, dest, s, c),
478484
(&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {

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_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
}
@@ -501,6 +504,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
501504
}
502505
}
503506

507+
fn assemble_pointer_like_candidates(
508+
&mut self,
509+
obligation: &PolyTraitObligation<'tcx>,
510+
candidates: &mut SelectionCandidateSet<'tcx>,
511+
) {
512+
match obligation.self_ty().skip_binder().kind() {
513+
ty::Pat(..) => candidates.vec.push(PointerLikeCandidate),
514+
_ => {}
515+
}
516+
}
517+
504518
fn assemble_async_fn_kind_helper_candidates(
505519
&mut self,
506520
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.

library/core/src/pat.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
//! Helper module for exporting the `pattern_type` macro
22
3+
use crate::marker::{Freeze, Unsize};
4+
use crate::ops::{CoerceUnsized, DispatchFromDyn};
5+
36
/// Creates a pattern type.
47
/// ```ignore (cannot test this from within core yet)
58
/// type Positive = std::pat::pattern_type!(i32 is 1..);
@@ -74,3 +77,16 @@ impl const RangePattern for char {
7477
}
7578
}
7679
}
80+
81+
impl<T: ?Sized, U: ?Sized> CoerceUnsized<pattern_type!(*const U is !null)> for pattern_type!(*const T is !null) where
82+
T: Unsize<U>
83+
{
84+
}
85+
86+
impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<pattern_type!(U is !null)> for pattern_type!(T is !null) {}
87+
88+
impl<T: ?Sized> Unpin for pattern_type!(*const T is !null) {}
89+
90+
unsafe impl<T: ?Sized> Freeze for pattern_type!(*const T is !null) {}
91+
92+
unsafe impl<T: ?Sized> Freeze for pattern_type!(*mut T is !null) {}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#![feature(pattern_types, pattern_type_macro)]
2+
#![allow(dead_code)]
3+
4+
use std::mem::transmute;
5+
6+
pub struct NonNull<T: ?Sized> {
7+
pointer: std::pat::pattern_type!(*const T is !null),
8+
}
9+
10+
trait Trait {}
11+
impl Trait for () {}
12+
13+
fn main() {
14+
unsafe {
15+
let _: NonNull<dyn Trait> = NonNull { pointer: transmute(&mut () as *mut dyn Trait) };
16+
}
17+
}

tests/ui/type/pattern_types/unsize.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//! Show that pattern-types with pointer base types can be part of unsizing coercions
2+
3+
//@ check-pass
4+
5+
#![feature(pattern_type_macro, pattern_types)]
6+
7+
use std::pat::pattern_type;
8+
9+
type NonNull<T> = pattern_type!(*const T is !null);
10+
11+
trait Trait {}
12+
impl Trait for u32 {}
13+
impl Trait for i32 {}
14+
15+
fn main() {
16+
let x: NonNull<u32> = unsafe { std::mem::transmute(std::ptr::dangling::<u32>()) };
17+
let x: NonNull<dyn Trait> = x;
18+
}

0 commit comments

Comments
 (0)