Skip to content

Commit afb5668

Browse files
author
Lukas Markeffsky
committed
MetadataCast
1 parent 2457c02 commit afb5668

File tree

11 files changed

+443
-1
lines changed

11 files changed

+443
-1
lines changed

compiler/rustc_hir/src/lang_items.rs

+1
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

+7-1
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,20 @@ 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<T>` trait.
120+
MetadataCastCandidate {
121+
/// Whether we should check for `Self == T`.
122+
require_eq: bool,
123+
},
124+
119125
/// Implementation of transmutability trait.
120126
TransmutabilityCandidate,
121127

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,7 @@ symbols! {
10151015
memtag,
10161016
message,
10171017
meta,
1018+
metadata_cast,
10181019
metadata_type,
10191020
min_align_of,
10201021
min_align_of_val,

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

+46
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
9797
self.assemble_candidate_for_pointer_like(obligation, &mut candidates);
9898
} else if lang_items.fn_ptr_trait() == Some(def_id) {
9999
self.assemble_candidates_for_fn_ptr_trait(obligation, &mut candidates);
100+
} else if lang_items.metadata_cast_trait() == Some(def_id) {
101+
self.assemble_candidates_for_metadata_cast(obligation, &mut candidates);
100102
} else {
101103
if lang_items.clone_trait() == Some(def_id) {
102104
// Same builtin conditions as `Copy`, i.e., every type which has builtin support
@@ -1107,4 +1109,48 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
11071109
}
11081110
}
11091111
}
1112+
1113+
fn assemble_candidates_for_metadata_cast(
1114+
&mut self,
1115+
obligation: &PolyTraitObligation<'tcx>,
1116+
candidates: &mut SelectionCandidateSet<'tcx>,
1117+
) {
1118+
let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1);
1119+
let target = self.infcx.shallow_resolve(target);
1120+
1121+
if let ty::Tuple(tys) = target.kind()
1122+
&& tys.is_empty()
1123+
{
1124+
candidates.vec.push(MetadataCastCandidate { require_eq: false });
1125+
return;
1126+
}
1127+
1128+
let source = obligation.self_ty().skip_binder();
1129+
let source = self.infcx.shallow_resolve(source);
1130+
1131+
if let ty::Adt(src_def, src_args) = source.kind()
1132+
&& let ty::Adt(tgt_def, tgt_args) = target.kind()
1133+
&& src_def == tgt_def
1134+
&& Some(src_def.did()) == self.tcx().lang_items().dyn_metadata()
1135+
{
1136+
let src_dyn = src_args.type_at(0);
1137+
let tgt_dyn = tgt_args.type_at(0);
1138+
1139+
// We could theoretically allow casting the principal away, but `as` casts
1140+
// don't allow that, so neither does `MetadataCast` for now.
1141+
if let ty::Dynamic(src_pred, _, ty::Dyn) = src_dyn.kind()
1142+
&& let ty::Dynamic(tgt_pred, _, ty::Dyn) = tgt_dyn.kind()
1143+
&& src_pred.principal_def_id() == tgt_pred.principal_def_id()
1144+
{
1145+
candidates.vec.push(MetadataCastCandidate { require_eq: false });
1146+
return;
1147+
}
1148+
}
1149+
1150+
if source.has_non_region_infer() || target.has_non_region_infer() {
1151+
candidates.ambiguous = true;
1152+
} else if self.infcx.can_eq(obligation.param_env, source, target) {
1153+
candidates.vec.push(MetadataCastCandidate { require_eq: true });
1154+
}
1155+
}
11101156
}

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

+19
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
5151
ImplSource::Builtin(BuiltinImplSource::Misc, data)
5252
}
5353

54+
MetadataCastCandidate { require_eq } => {
55+
let data = if require_eq {
56+
let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1);
57+
let source = obligation.self_ty().skip_binder();
58+
59+
let InferOk { obligations, .. } = self
60+
.infcx
61+
.at(&obligation.cause, obligation.param_env)
62+
.eq(DefineOpaqueTypes::No, target, source)
63+
.map_err(|_| Unimplemented)?;
64+
65+
obligations
66+
} else {
67+
Vec::new()
68+
};
69+
70+
ImplSource::Builtin(BuiltinImplSource::Misc, data)
71+
}
72+
5473
TransmutabilityCandidate => {
5574
let data = self.confirm_transmutability_candidate(obligation)?;
5675
ImplSource::Builtin(BuiltinImplSource::Misc, data)

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

+9
Original file line numberDiff line numberDiff line change
@@ -1837,6 +1837,9 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
18371837
DropVictim::No
18381838
}
18391839

1840+
(MetadataCastCandidate { require_eq: false }, _) => DropVictim::Yes,
1841+
(_, MetadataCastCandidate { require_eq: false }) => DropVictim::No,
1842+
18401843
(ParamCandidate(other), ParamCandidate(victim)) => {
18411844
let same_except_bound_vars = other.skip_binder().trait_ref
18421845
== victim.skip_binder().trait_ref
@@ -1873,6 +1876,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
18731876
| BuiltinUnsizeCandidate
18741877
| TraitUpcastingUnsizeCandidate(_)
18751878
| BuiltinCandidate { .. }
1879+
| MetadataCastCandidate { .. }
18761880
| TraitAliasCandidate
18771881
| ObjectCandidate(_)
18781882
| ProjectionCandidate(_),
@@ -1903,6 +1907,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
19031907
| BuiltinUnsizeCandidate
19041908
| TraitUpcastingUnsizeCandidate(_)
19051909
| BuiltinCandidate { has_nested: true }
1910+
| MetadataCastCandidate { require_eq: true }
19061911
| TraitAliasCandidate,
19071912
ParamCandidate(ref victim_cand),
19081913
) => {
@@ -1939,6 +1944,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
19391944
| BuiltinUnsizeCandidate
19401945
| TraitUpcastingUnsizeCandidate(_)
19411946
| BuiltinCandidate { .. }
1947+
| MetadataCastCandidate { .. }
19421948
| TraitAliasCandidate,
19431949
) => DropVictim::Yes,
19441950

@@ -1955,6 +1961,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
19551961
| BuiltinUnsizeCandidate
19561962
| TraitUpcastingUnsizeCandidate(_)
19571963
| BuiltinCandidate { .. }
1964+
| MetadataCastCandidate { .. }
19581965
| TraitAliasCandidate,
19591966
ObjectCandidate(_) | ProjectionCandidate(_),
19601967
) => DropVictim::No,
@@ -2063,6 +2070,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
20632070
| BuiltinUnsizeCandidate
20642071
| TraitUpcastingUnsizeCandidate(_)
20652072
| BuiltinCandidate { has_nested: true }
2073+
| MetadataCastCandidate { require_eq: true }
20662074
| TraitAliasCandidate,
20672075
ImplCandidate(_)
20682076
| ClosureCandidate { .. }
@@ -2075,6 +2083,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
20752083
| BuiltinUnsizeCandidate
20762084
| TraitUpcastingUnsizeCandidate(_)
20772085
| BuiltinCandidate { has_nested: true }
2086+
| MetadataCastCandidate { require_eq: true }
20782087
| TraitAliasCandidate,
20792088
) => DropVictim::No,
20802089
}

library/core/src/ptr/metadata.rs

+9
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,15 @@ 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+
/// Provides information about legal pointer-to-pointer casts.
81+
///
82+
/// If a type `T` implements `MetadataCast<U>`, then a pointer with a metadata
83+
/// of `T` can be cast to a pointer with a metadata of `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+
8089
/// Extract the metadata component of a pointer.
8190
///
8291
/// Values of type `*mut T`, `&T`, or `&mut T` can be passed directly to this function

library/core/src/ptr/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,8 @@ pub use crate::intrinsics::copy;
404404
pub use crate::intrinsics::write_bytes;
405405

406406
mod metadata;
407+
#[unstable(feature = "ptr_metadata_cast", issue = "none")]
408+
pub use metadata::MetadataCast;
407409
#[unstable(feature = "ptr_metadata", issue = "81513")]
408410
pub use metadata::{from_raw_parts, from_raw_parts_mut, metadata, DynMetadata, Pointee, Thin};
409411

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
error: lifetime may not live long enough
2+
--> $DIR/metadata-cast.rs:63:5
3+
|
4+
LL | fn lifetimes_err1<'a, 'b>() {
5+
| -- -- lifetime `'b` defined here
6+
| |
7+
| lifetime `'a` defined here
8+
LL | check::<DynMetadata<&'a ()>, DynMetadata<&'b ()>>();
9+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'b`
10+
|
11+
= help: consider adding the following bound: `'a: 'b`
12+
13+
error: lifetime may not live long enough
14+
--> $DIR/metadata-cast.rs:63:5
15+
|
16+
LL | fn lifetimes_err1<'a, 'b>() {
17+
| -- -- lifetime `'b` defined here
18+
| |
19+
| lifetime `'a` defined here
20+
LL | check::<DynMetadata<&'a ()>, DynMetadata<&'b ()>>();
21+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'b` must outlive `'a`
22+
|
23+
= help: consider adding the following bound: `'b: 'a`
24+
25+
help: `'a` and `'b` must be the same: replace one with the other
26+
27+
error: lifetime may not live long enough
28+
--> $DIR/metadata-cast.rs:68:5
29+
|
30+
LL | fn lifetimes_err2<'a, 'b>() {
31+
| -- -- lifetime `'b` defined here
32+
| |
33+
| lifetime `'a` defined here
34+
LL | check::<&'a (), &'b ()>();
35+
| ^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'b`
36+
|
37+
= help: consider adding the following bound: `'a: 'b`
38+
39+
error: lifetime may not live long enough
40+
--> $DIR/metadata-cast.rs:68:5
41+
|
42+
LL | fn lifetimes_err2<'a, 'b>() {
43+
| -- -- lifetime `'b` defined here
44+
| |
45+
| lifetime `'a` defined here
46+
LL | check::<&'a (), &'b ()>();
47+
| ^^^^^^^^^^^^^^^^^^^^^^^ requires that `'b` must outlive `'a`
48+
|
49+
= help: consider adding the following bound: `'b: 'a`
50+
51+
help: `'a` and `'b` must be the same: replace one with the other
52+
|
53+
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
54+
55+
error: aborting due to 4 previous errors
56+

tests/ui/traits/metadata-cast.rs

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// revisions: traits lifetimes
2+
#![feature(ptr_metadata, ptr_metadata_cast)]
3+
4+
use std::ptr::{Pointee, MetadataCast, DynMetadata};
5+
6+
fn cast<T: ?Sized, U: ?Sized>(_: *mut T) -> *mut U
7+
where
8+
<T as Pointee>::Metadata: MetadataCast<<U as Pointee>::Metadata>,
9+
{
10+
todo!()
11+
}
12+
13+
fn check<T: ?Sized, U: ?Sized>() where T: MetadataCast<U> {}
14+
15+
trait Trait {}
16+
trait Trait2 {}
17+
18+
fn main() {
19+
// any to () is OK
20+
check::<(), ()>();
21+
check::<usize, ()>();
22+
check::<DynMetadata<dyn Trait>, ()>();
23+
24+
// same types are OK
25+
check::<usize, usize>();
26+
check::<i32, i32>();
27+
check::<DynMetadata<dyn Trait>, DynMetadata<dyn Trait>>();
28+
29+
// changing auto traits of trait object in DynMetadata is OK
30+
check::<DynMetadata<dyn Send>, DynMetadata<dyn Sync>>();
31+
check::<DynMetadata<dyn Trait + Send>, DynMetadata<dyn Trait + Sync>>();
32+
33+
#[cfg(traits)]
34+
{
35+
check::<(), usize>(); //[traits]~ ERROR not satisfied
36+
check::<(), DynMetadata<dyn Trait>>(); //[traits]~ ERROR not satisfied
37+
check::<usize, DynMetadata<dyn Trait>>(); //[traits]~ ERROR not satisfied
38+
check::<DynMetadata<dyn Trait>, usize>(); //[traits]~ ERROR not satisfied
39+
check::<DynMetadata<dyn Trait>, DynMetadata<dyn Trait2>>(); //[traits]~ ERROR not satisfied
40+
check::<dyn Trait + Send, dyn Trait + Sync>(); //[traits]~ ERROR not satisfied
41+
}
42+
43+
// `dyn MetadataCast<usize>` should not implement `MetadataCast<usize>`
44+
check::<dyn MetadataCast<usize>, ()>();
45+
check::<dyn MetadataCast<usize>, dyn MetadataCast<usize>>();
46+
#[cfg(traits)]
47+
check::<dyn MetadataCast<usize>, usize>(); //[traits]~ ERROR not satisfied
48+
49+
// this could pass in the future, but for now it matches the behavior of `as` casts
50+
#[cfg(traits)]
51+
check::<DynMetadata<dyn Trait>, DynMetadata<dyn Send>>(); //[traits]~ ERROR not satisfied
52+
}
53+
54+
fn lifetimes_ok<'a, 'b>() {
55+
// changing lifetimes of trait object in DynMetadata is OK
56+
check::<DynMetadata<dyn Trait + 'a>, DynMetadata<dyn Trait + 'b>>();
57+
check::<DynMetadata<dyn Trait + 'b>, DynMetadata<dyn Trait + 'a>>();
58+
59+
// otherwise, require equal lifetimes
60+
check::<&'a (), &'a ()>();
61+
}
62+
fn lifetimes_err1<'a, 'b>() {
63+
check::<DynMetadata<&'a ()>, DynMetadata<&'b ()>>();
64+
//[lifetimes]~^ ERROR may not live long enough
65+
//[lifetimes]~| ERROR may not live long enough
66+
}
67+
fn lifetimes_err2<'a, 'b>() {
68+
check::<&'a (), &'b ()>();
69+
//[lifetimes]~^ ERROR may not live long enough
70+
//[lifetimes]~| ERROR may not live long enough
71+
}
72+
73+
fn do_casts<'a>(thin: &mut i32, slice: &mut [i32], trait_object: &mut dyn Trait) {
74+
let _: *mut u8 = cast(thin);
75+
let _: *mut u8 = cast(slice);
76+
let _: *mut [u8] = cast(slice);
77+
let _: *mut u8 = cast(trait_object);
78+
let _: *mut (dyn Trait + Send + 'a) = cast(trait_object);
79+
80+
81+
#[cfg(traits)]
82+
{
83+
let _: *mut [u8] = cast(thin); //[traits]~ ERROR not satisfied
84+
let _: *mut dyn Trait = cast(thin); //[traits]~ ERROR not satisfied
85+
let _: *mut [u8] = cast(trait_object); //[traits]~ ERROR not satisfied
86+
let _: *mut dyn Trait = cast(slice); //[traits]~ ERROR not satisfied
87+
let _: *mut dyn Trait2 = cast(trait_object); //[traits]~ ERROR not satisfied
88+
89+
// may be allowed in the future
90+
let _: *mut dyn Send = cast(trait_object); //[traits]~ ERROR not satisfied
91+
}
92+
}

0 commit comments

Comments
 (0)