Skip to content

Commit a84dab3

Browse files
author
Lukas Markeffsky
committed
add MetadataCast built-in trait
1 parent a37053f commit a84dab3

File tree

13 files changed

+558
-7
lines changed

13 files changed

+558
-7
lines changed

compiler/rustc_hir/src/lang_items.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ language_item_table! {
155155
PointeeTrait, sym::pointee_trait, pointee_trait, Target::Trait, GenericRequirement::None;
156156
Metadata, sym::metadata_type, metadata_type, Target::AssocTy, GenericRequirement::None;
157157
DynMetadata, sym::dyn_metadata, dyn_metadata, Target::Struct, GenericRequirement::None;
158+
MetadataCast, sym::metadata_cast, metadata_cast_trait, Target::Trait, GenericRequirement::Minimum(1);
158159

159160
Freeze, sym::freeze, freeze_trait, Target::Trait, GenericRequirement::Exact(0);
160161

compiler/rustc_middle/src/traits/select.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,17 @@ pub enum SelectionCandidate<'tcx> {
108108
/// A builtin implementation for some specific traits, used in cases
109109
/// where we cannot rely an ordinary library implementations.
110110
///
111-
/// The most notable examples are `sized`, `Copy` and `Clone`. This is also
111+
/// The most notable examples are `Sized`, `Copy` and `Clone`. This is also
112112
/// used for the `DiscriminantKind` and `Pointee` trait, both of which have
113113
/// an associated type.
114114
BuiltinCandidate {
115115
/// `false` if there are no *further* obligations.
116116
has_nested: bool,
117117
},
118118

119+
/// Implementation of the `MetadataCast` trait.
120+
MetadataCastCandidate(MetadataCastKind<'tcx>),
121+
119122
/// Implementation of transmutability trait.
120123
TransmutabilityCandidate,
121124

@@ -176,6 +179,14 @@ pub enum SelectionCandidate<'tcx> {
176179
ConstDestructCandidate(Option<DefId>),
177180
}
178181

182+
#[derive(PartialEq, Eq, Debug, Clone, TypeVisitable)]
183+
pub enum MetadataCastKind {
184+
/// No further obligations.
185+
Unconditional,
186+
/// `T: MetadataCast<U>` if `T <: U`.
187+
Subtype,
188+
}
189+
179190
/// The result of trait evaluation. The order is important
180191
/// here as the evaluation of a list is the maximum of the
181192
/// evaluations.

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,6 +1017,7 @@ symbols! {
10171017
memtag,
10181018
message,
10191019
meta,
1020+
metadata_cast,
10201021
metadata_type,
10211022
min_align_of,
10221023
min_align_of_val,

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use hir::LangItem;
1111
use rustc_hir as hir;
1212
use rustc_infer::traits::ObligationCause;
1313
use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError};
14+
use rustc_middle::traits::select::MetadataCastKind;
1415
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
1516
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
1617

@@ -100,6 +101,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
100101
self.assemble_candidate_for_pointer_like(obligation, &mut candidates);
101102
} else if lang_items.fn_ptr_trait() == Some(def_id) {
102103
self.assemble_candidates_for_fn_ptr_trait(obligation, &mut candidates);
104+
} else if lang_items.metadata_cast_trait() == Some(def_id) {
105+
self.assemble_candidates_for_metadata_cast(obligation, &mut candidates);
103106
} else {
104107
if lang_items.clone_trait() == Some(def_id) {
105108
// Same builtin conditions as `Copy`, i.e., every type which has builtin support
@@ -1158,4 +1161,47 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
11581161
}
11591162
}
11601163
}
1164+
1165+
fn assemble_candidates_for_metadata_cast(
1166+
&mut self,
1167+
obligation: &PolyTraitObligation<'tcx>,
1168+
candidates: &mut SelectionCandidateSet<'tcx>,
1169+
) {
1170+
let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1);
1171+
let target = self.infcx.shallow_resolve(target);
1172+
1173+
if target.is_unit() {
1174+
candidates.vec.push(MetadataCastCandidate(MetadataCastKind::Unconditional));
1175+
return;
1176+
}
1177+
1178+
let source = obligation.self_ty().skip_binder();
1179+
let source = self.infcx.shallow_resolve(source);
1180+
1181+
if source.has_non_region_infer() || target.has_non_region_infer() {
1182+
candidates.ambiguous = true;
1183+
return;
1184+
}
1185+
1186+
if let ty::Adt(src_def, src_args) = source.kind()
1187+
&& let ty::Adt(tgt_def, tgt_args) = target.kind()
1188+
&& src_def == tgt_def
1189+
&& Some(src_def.did()) == self.tcx().lang_items().dyn_metadata()
1190+
{
1191+
let src_dyn = src_args.type_at(0);
1192+
let tgt_dyn = tgt_args.type_at(0);
1193+
1194+
// We could theoretically allow casting the principal away, but `as` casts
1195+
// don't allow that, so neither does `MetadataCast` for now.
1196+
if let ty::Dynamic(src_pred, _, ty::Dyn) = src_dyn.kind()
1197+
&& let ty::Dynamic(tgt_pred, _, ty::Dyn) = tgt_dyn.kind()
1198+
&& src_pred.principal_def_id() == tgt_pred.principal_def_id()
1199+
{
1200+
candidates.vec.push(MetadataCastCandidate(MetadataCastKind::Unconditional));
1201+
return;
1202+
}
1203+
}
1204+
1205+
candidates.vec.push(MetadataCastCandidate(MetadataCastKind::Subtype));
1206+
}
11611207
}

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
1111
use rustc_hir::lang_items::LangItem;
1212
use rustc_infer::infer::BoundRegionConversionTime::HigherRankedType;
1313
use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
14+
use rustc_middle::traits::select::MetadataCastKind;
1415
use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData};
1516
use rustc_middle::ty::{
1617
self, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, ToPredicate,
@@ -51,6 +52,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
5152
ImplSource::Builtin(BuiltinImplSource::Misc, data)
5253
}
5354

55+
MetadataCastCandidate(kind) => {
56+
let data = self.confirm_metadata_cast_candidate(obligation, kind)?;
57+
ImplSource::Builtin(BuiltinImplSource::Misc, data)
58+
}
59+
5460
TransmutabilityCandidate => {
5561
let data = self.confirm_transmutability_candidate(obligation)?;
5662
ImplSource::Builtin(BuiltinImplSource::Misc, data)
@@ -272,6 +278,28 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
272278
obligations
273279
}
274280

281+
fn confirm_metadata_cast_candidate(
282+
&mut self,
283+
obligation: &PolyTraitObligation<'tcx>,
284+
kind: MetadataCastKind,
285+
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
286+
match kind {
287+
MetadataCastKind::Unconditional => Ok(Vec::new()),
288+
MetadataCastKind::Subtype => {
289+
let source = obligation.self_ty().skip_binder();
290+
let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1);
291+
292+
let InferOk { obligations, .. } = self
293+
.infcx
294+
.at(&obligation.cause, obligation.param_env)
295+
.sub(DefineOpaqueTypes::No, source, target)
296+
.map_err(|_| Unimplemented)?;
297+
298+
Ok(obligations)
299+
}
300+
}
301+
}
302+
275303
#[instrument(level = "debug", skip(self))]
276304
fn confirm_transmutability_candidate(
277305
&mut self,

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

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1830,12 +1830,18 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
18301830
(TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => DropVictim::No,
18311831

18321832
// (*)
1833-
(BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_), _) => {
1834-
DropVictim::Yes
1835-
}
1836-
(_, BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_)) => {
1837-
DropVictim::No
1838-
}
1833+
(
1834+
BuiltinCandidate { has_nested: false }
1835+
| ConstDestructCandidate(_)
1836+
| MetadataCastCandidate(MetadataCastKind::Unconditional),
1837+
_,
1838+
) => DropVictim::Yes,
1839+
(
1840+
_,
1841+
BuiltinCandidate { has_nested: false }
1842+
| ConstDestructCandidate(_)
1843+
| MetadataCastCandidate(MetadataCastKind::Unconditional),
1844+
) => DropVictim::No,
18391845

18401846
(ParamCandidate(other), ParamCandidate(victim)) => {
18411847
let same_except_bound_vars = other.skip_binder().trait_ref
@@ -1873,6 +1879,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
18731879
| BuiltinUnsizeCandidate
18741880
| TraitUpcastingUnsizeCandidate(_)
18751881
| BuiltinCandidate { .. }
1882+
| MetadataCastCandidate(_)
18761883
| TraitAliasCandidate
18771884
| ObjectCandidate(_)
18781885
| ProjectionCandidate(_),
@@ -1903,6 +1910,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
19031910
| BuiltinUnsizeCandidate
19041911
| TraitUpcastingUnsizeCandidate(_)
19051912
| BuiltinCandidate { has_nested: true }
1913+
| MetadataCastCandidate(MetadataCastKind::Subtype)
19061914
| TraitAliasCandidate,
19071915
ParamCandidate(ref victim_cand),
19081916
) => {
@@ -1939,6 +1947,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
19391947
| BuiltinUnsizeCandidate
19401948
| TraitUpcastingUnsizeCandidate(_)
19411949
| BuiltinCandidate { .. }
1950+
| MetadataCastCandidate(_)
19421951
| TraitAliasCandidate,
19431952
) => DropVictim::Yes,
19441953

@@ -1955,6 +1964,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
19551964
| BuiltinUnsizeCandidate
19561965
| TraitUpcastingUnsizeCandidate(_)
19571966
| BuiltinCandidate { .. }
1967+
| MetadataCastCandidate(_)
19581968
| TraitAliasCandidate,
19591969
ObjectCandidate(_) | ProjectionCandidate(_),
19601970
) => DropVictim::No,
@@ -2063,6 +2073,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
20632073
| BuiltinUnsizeCandidate
20642074
| TraitUpcastingUnsizeCandidate(_)
20652075
| BuiltinCandidate { has_nested: true }
2076+
| MetadataCastCandidate(MetadataCastKind::Subtype)
20662077
| TraitAliasCandidate,
20672078
ImplCandidate(_)
20682079
| ClosureCandidate { .. }
@@ -2075,6 +2086,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
20752086
| BuiltinUnsizeCandidate
20762087
| TraitUpcastingUnsizeCandidate(_)
20772088
| BuiltinCandidate { has_nested: true }
2089+
| MetadataCastCandidate(MetadataCastKind::Subtype)
20782090
| TraitAliasCandidate,
20792091
) => DropVictim::No,
20802092
}

library/core/src/ptr/metadata.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,22 @@ pub trait Pointee {
7777
// NOTE: don’t stabilize this before trait aliases are stable in the language?
7878
pub trait Thin = Pointee<Metadata = ()>;
7979

80+
/// A marker for valid metadata conversions during pointer-to-pointer casts.
81+
///
82+
/// A type `T` implements `MetadataCast<U>` if and only if pointers with metadata `T`
83+
/// can be cast to pointers with metadata `U`.
84+
#[unstable(feature = "ptr_metadata_cast", issue = "none")]
85+
#[cfg_attr(not(bootstrap), lang = "metadata_cast")]
86+
#[rustc_deny_explicit_impl(implement_via_object = false)]
87+
pub trait MetadataCast<T: ?Sized> {}
88+
89+
/// A marker for valid pointer-to-pointer casts.
90+
///
91+
/// A type `T` implements `PointerCast<U>` if and only if a pointer to `T` can be
92+
/// cast into a pointer to `U`.
93+
#[unstable(feature = "ptr_metadata_cast", issue = "none")]
94+
pub trait PointerCast<T: ?Sized> = Pointee<Metadata: MetadataCast<<T as Pointee>::Metadata>>;
95+
8096
/// Extract the metadata component of a pointer.
8197
///
8298
/// Values of type `*mut T`, `&T`, or `&mut T` can be passed directly to this function

library/core/src/ptr/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,8 @@ pub use crate::intrinsics::write_bytes;
406406
mod metadata;
407407
#[unstable(feature = "ptr_metadata", issue = "81513")]
408408
pub use metadata::{from_raw_parts, from_raw_parts_mut, metadata, DynMetadata, Pointee, Thin};
409+
#[unstable(feature = "ptr_metadata_cast", issue = "none")]
410+
pub use metadata::{MetadataCast, PointerCast};
409411

410412
mod non_null;
411413
#[stable(feature = "nonnull", since = "1.25.0")]
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#![feature(ptr_metadata, ptr_metadata_cast)]
2+
3+
use std::ptr::{self, DynMetadata, MetadataCast, Pointee, PointerCast};
4+
5+
fn cast<T: ?Sized, U: ?Sized>(ptr: *mut T) -> *mut U
6+
where
7+
T: PointerCast<U>,
8+
{
9+
todo!()
10+
}
11+
12+
fn check<T: ?Sized, U: ?Sized>()
13+
where
14+
T: MetadataCast<U>,
15+
{
16+
}
17+
18+
fn main() {}
19+
20+
fn err1<'a, 'b>() {
21+
// changing lifetimes in `DynMetadata<T>` is only allowed if `T = dyn Trait`
22+
check::<DynMetadata<&'a ()>, DynMetadata<&'b ()>>();
23+
//~^ ERROR may not live long enough
24+
}
25+
26+
fn err2<'a, 'b>() {
27+
check::<&'a (), &'b ()>();
28+
//~^ ERROR may not live long enough
29+
}
30+
31+
fn subtype_fail<'short, 'long: 'short, T, U>(ptr: *mut T) -> *mut U
32+
where
33+
T: ?Sized + Pointee<Metadata = &'short ()>,
34+
U: ?Sized + Pointee<Metadata = &'long ()>,
35+
{
36+
// extending the lifetime of arbitrary metadata is not allowed
37+
cast(ptr)
38+
//~^ ERROR may not live long enough
39+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
error: lifetime may not live long enough
2+
--> $DIR/metadata-cast-fail-lifetimes.rs:22:5
3+
|
4+
LL | fn err1<'a, 'b>() {
5+
| -- -- lifetime `'b` defined here
6+
| |
7+
| lifetime `'a` defined here
8+
LL | // changing lifetimes in `DynMetadata<T>` is only allowed if `T = dyn Trait`
9+
LL | check::<DynMetadata<&'a ()>, DynMetadata<&'b ()>>();
10+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'b`
11+
|
12+
= help: consider adding the following bound: `'a: 'b`
13+
14+
error: lifetime may not live long enough
15+
--> $DIR/metadata-cast-fail-lifetimes.rs:27:5
16+
|
17+
LL | fn err2<'a, 'b>() {
18+
| -- -- lifetime `'b` defined here
19+
| |
20+
| lifetime `'a` defined here
21+
LL | check::<&'a (), &'b ()>();
22+
| ^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'b`
23+
|
24+
= help: consider adding the following bound: `'a: 'b`
25+
26+
error: lifetime may not live long enough
27+
--> $DIR/metadata-cast-fail-lifetimes.rs:37:5
28+
|
29+
LL | fn subtype_fail<'short, 'long: 'short, T, U>(ptr: *mut T) -> *mut U
30+
| ------ ----- lifetime `'long` defined here
31+
| |
32+
| lifetime `'short` defined here
33+
...
34+
LL | cast(ptr)
35+
| ^^^^^^^^^ requires that `'short` must outlive `'long`
36+
|
37+
= help: consider adding the following bound: `'short: 'long`
38+
39+
error: aborting due to 3 previous errors
40+
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#![feature(ptr_metadata, ptr_metadata_cast)]
2+
3+
use std::ptr::{DynMetadata, MetadataCast, PointerCast};
4+
5+
fn cast<T: ?Sized, U: ?Sized>(_: *mut T) -> *mut U
6+
where
7+
T: PointerCast<U>,
8+
{
9+
todo!()
10+
}
11+
12+
fn check<T: ?Sized, U: ?Sized>()
13+
where
14+
T: MetadataCast<U>,
15+
{
16+
}
17+
18+
trait Trait {}
19+
trait Trait2 {}
20+
21+
fn main() {
22+
check::<(), usize>(); //~ ERROR not satisfied
23+
check::<(), DynMetadata<dyn Trait>>(); //~ ERROR not satisfied
24+
check::<usize, DynMetadata<dyn Trait>>(); //~ ERROR not satisfied
25+
check::<DynMetadata<dyn Trait>, usize>(); //~ ERROR not satisfied
26+
check::<DynMetadata<dyn Trait>, DynMetadata<dyn Trait2>>(); //~ ERROR not satisfied
27+
check::<dyn Trait + Send, dyn Trait + Sync>(); //~ ERROR not satisfied
28+
29+
// `dyn MetadataCast<usize>` should not implement `MetadataCast<usize>`
30+
check::<dyn MetadataCast<usize>, usize>(); //~ ERROR not satisfied
31+
32+
// this could pass in the future, but for now it matches the behavior of `as` casts
33+
check::<DynMetadata<dyn Trait>, DynMetadata<dyn Send>>(); //~ ERROR not satisfied
34+
}
35+
36+
fn do_casts<'a>(thin: &mut i32, slice: &mut [i32], trait_object: &mut (dyn Trait + Send)) {
37+
let _: *mut [u8] = cast(thin); //~ ERROR not satisfied
38+
let _: *mut dyn Trait = cast(thin); //~ ERROR not satisfied
39+
let _: *mut [u8] = cast(trait_object); //~ ERROR not satisfied
40+
let _: *mut dyn Trait = cast(slice); //~ ERROR not satisfied
41+
let _: *mut dyn Trait2 = cast(trait_object); //~ ERROR not satisfied
42+
43+
// may be allowed in the future
44+
let _: *mut dyn Send = cast(trait_object); //~ ERROR not satisfied
45+
}

0 commit comments

Comments
 (0)