Skip to content

Commit 96205ed

Browse files
committed
Allow unsizing pattern types with pointer base
1 parent e5c8b26 commit 96205ed

File tree

17 files changed

+142
-5
lines changed

17 files changed

+142
-5
lines changed

compiler/rustc_codegen_cranelift/src/unsize.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,11 @@ pub(crate) fn coerce_unsized_into<'tcx>(
133133
dst.write_cvalue(fx, CValue::by_val_pair(base, info, dst.layout()));
134134
};
135135
match (&src_ty.kind(), &dst_ty.kind()) {
136+
(ty::Pat(a, _), ty::Pat(b, _)) => {
137+
let src = src.cast_pat_ty_to_base(fx.layout_of(*a));
138+
let dst = dst.place_transmute_type(fx, *b);
139+
return coerce_unsized_into(fx, src, dst);
140+
}
136141
(&ty::Ref(..), &ty::Ref(..))
137142
| (&ty::Ref(..), &ty::RawPtr(..))
138143
| (&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
@@ -342,6 +342,14 @@ impl<'tcx> CValue<'tcx> {
342342
assert_eq!(self.layout().backend_repr, layout.backend_repr);
343343
CValue(self.0, layout)
344344
}
345+
346+
pub(crate) fn cast_pat_ty_to_base(self, layout: TyAndLayout<'tcx>) -> Self {
347+
let ty::Pat(base, _) = *self.layout().ty.kind() else {
348+
panic!("not a pattern type: {:#?}", self.layout())
349+
};
350+
assert_eq!(layout.ty, base);
351+
CValue(self.0, layout)
352+
}
345353
}
346354

347355
/// 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
@@ -227,6 +227,7 @@ pub(crate) fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
227227
) -> (Bx::Value, Bx::Value) {
228228
debug!("unsize_ptr: {:?} => {:?}", src_ty, dst_ty);
229229
match (src_ty.kind(), dst_ty.kind()) {
230+
(&ty::Pat(a, _), &ty::Pat(b, _)) => unsize_ptr(bx, src, a, b, old_info),
230231
(&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(b, _))
231232
| (&ty::RawPtr(a, _), &ty::RawPtr(b, _)) => {
232233
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
@@ -459,6 +459,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
459459
) -> InterpResult<'tcx> {
460460
trace!("Unsizing {:?} of type {} into {}", *src, src.layout.ty, cast_ty.ty);
461461
match (src.layout.ty.kind(), cast_ty.ty.kind()) {
462+
(&ty::Pat(_, s_pat), &ty::Pat(cast_ty, c_pat)) if s_pat == c_pat => {
463+
let src = self.project_field(src, FieldIdx::ZERO)?;
464+
let dest = self.project_field(dest, FieldIdx::ZERO)?;
465+
let cast_ty = self.layout_of(cast_ty)?;
466+
self.unsize_into(&src, cast_ty, &dest)
467+
}
462468
(&ty::Ref(_, s, _), &ty::Ref(_, c, _) | &ty::RawPtr(c, _))
463469
| (&ty::RawPtr(s, _), &ty::RawPtr(c, _)) => self.unsize_into_ptr(src, dest, s, c),
464470
(&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
@@ -106,6 +106,8 @@ hir_analysis_coerce_pointee_not_struct = `derive(CoercePointee)` is only applica
106106
107107
hir_analysis_coerce_pointee_not_transparent = `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout
108108
109+
hir_analysis_coerce_same_pat_kind = only pattern types with the same pattern can be coerced between each other
110+
109111
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}`
110112
.label = `{$field_ty}` must be a pointer, reference, or smart pointer that is allowed to be unsized
111113

compiler/rustc_hir_analysis/src/coherence/builtin.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,18 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
248248
// in the compiler (in particular, all the call ABI logic) will treat them as repr(transparent)
249249
// even if they do not carry that attribute.
250250
match (source.kind(), target.kind()) {
251+
(&ty::Pat(_, pat_a), &ty::Pat(_, pat_b)) => {
252+
if pat_a != pat_b {
253+
return Err(tcx.dcx().emit_err(errors::CoerceSamePatKind {
254+
span,
255+
trait_name,
256+
pat_a: pat_a.to_string(),
257+
pat_b: pat_b.to_string(),
258+
}));
259+
}
260+
Ok(())
261+
}
262+
251263
(&ty::Ref(r_a, _, mutbl_a), ty::Ref(r_b, _, mutbl_b))
252264
if r_a == *r_b && mutbl_a == *mutbl_b =>
253265
{
@@ -413,6 +425,18 @@ pub(crate) fn coerce_unsized_info<'tcx>(
413425
(mt_a.ty, mt_b.ty, unsize_trait, None, span)
414426
};
415427
let (source, target, trait_def_id, kind, field_span) = match (source.kind(), target.kind()) {
428+
(&ty::Pat(ty_a, pat_a), &ty::Pat(ty_b, pat_b)) => {
429+
if pat_a != pat_b {
430+
return Err(tcx.dcx().emit_err(errors::CoerceSamePatKind {
431+
span,
432+
trait_name,
433+
pat_a: pat_a.to_string(),
434+
pat_b: pat_b.to_string(),
435+
}));
436+
}
437+
(ty_a, ty_b, coerce_unsized_trait, None, span)
438+
}
439+
416440
(&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => {
417441
infcx.sub_regions(SubregionOrigin::RelateObjectBound(span), r_b, r_a);
418442
let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };

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
@@ -1279,6 +1279,16 @@ pub(crate) struct CoerceUnsizedNonStruct {
12791279
pub trait_name: &'static str,
12801280
}
12811281

1282+
#[derive(Diagnostic)]
1283+
#[diag(hir_analysis_coerce_same_pat_kind)]
1284+
pub(crate) struct CoerceSamePatKind {
1285+
#[primary_span]
1286+
pub span: Span,
1287+
pub trait_name: &'static str,
1288+
pub pat_a: String,
1289+
pub pat_b: String,
1290+
}
1291+
12821292
#[derive(Diagnostic)]
12831293
#[diag(hir_analysis_coerce_unsized_may, code = E0377)]
12841294
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
@@ -1070,6 +1070,7 @@ fn find_tails_for_unsizing<'tcx>(
10701070
debug_assert!(!target_ty.has_param(), "{target_ty} should be fully monomorphic");
10711071

10721072
match (source_ty.kind(), target_ty.kind()) {
1073+
(&ty::Pat(source, _), &ty::Pat(target, _)) => find_tails_for_unsizing(tcx, source, target),
10731074
(
10741075
&ty::Ref(_, source_pointee, _),
10751076
&ty::Ref(_, target_pointee, _) | &ty::RawPtr(target_pointee, _),

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

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

118+
PointerLikeCandidate => {
119+
let data = self.confirm_pointer_like_candidate(obligation);
120+
ImplSource::Builtin(BuiltinImplSource::Misc, data)
121+
}
122+
118123
TraitAliasCandidate => {
119124
let data = self.confirm_trait_alias_candidate(obligation);
120125
ImplSource::Builtin(BuiltinImplSource::Misc, data)
@@ -631,6 +636,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
631636
Ok(nested)
632637
}
633638

639+
fn confirm_pointer_like_candidate(
640+
&mut self,
641+
obligation: &PolyTraitObligation<'tcx>,
642+
) -> PredicateObligations<'tcx> {
643+
debug!(?obligation, "confirm_pointer_like_candidate");
644+
let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
645+
let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
646+
let ty::Pat(base, _) = *self_ty.kind() else { bug!() };
647+
let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived);
648+
649+
self.collect_predicates_for_types(
650+
obligation.param_env,
651+
cause,
652+
obligation.recursion_depth + 1,
653+
placeholder_predicate.def_id(),
654+
vec![base],
655+
)
656+
}
657+
634658
fn confirm_trait_alias_candidate(
635659
&mut self,
636660
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
@@ -2002,6 +2002,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
20022002
| TraitUpcastingUnsizeCandidate(_)
20032003
| BuiltinObjectCandidate
20042004
| BuiltinUnsizeCandidate
2005+
| PointerLikeCandidate
20052006
| BikeshedGuaranteedNoDropCandidate => false,
20062007
// Non-global param candidates have already been handled, global
20072008
// where-bounds get ignored.

library/core/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@
115115
#![feature(link_cfg)]
116116
#![feature(offset_of_enum)]
117117
#![feature(panic_internals)]
118+
#![feature(pattern_type_macro)]
118119
#![feature(ptr_alignment_type)]
119120
#![feature(ptr_metadata)]
120121
#![feature(set_ptr_value)]
@@ -168,6 +169,7 @@
168169
#![feature(never_type)]
169170
#![feature(no_core)]
170171
#![feature(optimize_attribute)]
172+
#![feature(pattern_types)]
171173
#![feature(prelude_import)]
172174
#![feature(repr_simd)]
173175
#![feature(rustc_allow_const_fn_unstable)]

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, PointeeSized, 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: PointeeSized, U: PointeeSized> 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: PointeeSized> Unpin for pattern_type!(*const T is !null) {}
89+
90+
unsafe impl<T: PointeeSized> Freeze for pattern_type!(*const T is !null) {}
91+
92+
unsafe impl<T: PointeeSized> Freeze for pattern_type!(*mut T is !null) {}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#![feature(pattern_types, pattern_type_macro, sized_hierarchy)]
2+
#![allow(dead_code)]
3+
4+
use std::marker::PointeeSized;
5+
use std::mem::transmute;
6+
7+
pub struct NonNull<T: PointeeSized> {
8+
pointer: std::pat::pattern_type!(*const T is !null),
9+
}
10+
11+
trait Trait {}
12+
impl Trait for () {}
13+
14+
fn main() {
15+
unsafe {
16+
let _: NonNull<dyn Trait> = NonNull { pointer: transmute(&mut () as *mut dyn Trait) };
17+
}
18+
}

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)