Skip to content

Commit b071fc0

Browse files
committed
Initial (incomplete) implementation of transmutability trait.
This initial implementation handles transmutations between primitive types and some structs. See: rust-lang/compiler-team#411
1 parent 256721e commit b071fc0

File tree

28 files changed

+3031
-1
lines changed

28 files changed

+3031
-1
lines changed

Cargo.lock

+22
Original file line numberDiff line numberDiff line change
@@ -4588,6 +4588,7 @@ dependencies = [
45884588
"rustc_session",
45894589
"rustc_span",
45904590
"rustc_target",
4591+
"rustc_transmute",
45914592
"smallvec",
45924593
"tracing",
45934594
]
@@ -4612,6 +4613,27 @@ dependencies = [
46124613
"tracing",
46134614
]
46144615

4616+
[[package]]
4617+
name = "rustc_transmute"
4618+
version = "0.1.0"
4619+
dependencies = [
4620+
"rustc_ast",
4621+
"rustc_attr",
4622+
"rustc_data_structures",
4623+
"rustc_errors",
4624+
"rustc_hir",
4625+
"rustc_index",
4626+
"rustc_infer",
4627+
"rustc_macros",
4628+
"rustc_middle",
4629+
"rustc_parse_format",
4630+
"rustc_session",
4631+
"rustc_span",
4632+
"rustc_target",
4633+
"smallvec",
4634+
"tracing",
4635+
]
4636+
46154637
[[package]]
46164638
name = "rustc_ty_utils"
46174639
version = "0.0.0"

compiler/rustc_hir/src/lang_items.rs

+3
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,9 @@ language_item_table! {
218218
CoerceUnsized, sym::coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1);
219219
DispatchFromDyn, sym::dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1);
220220

221+
// language items relating to transmutability
222+
TransmuteTrait, sym::transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(6);
223+
221224
Add(Op), sym::add, add_trait, Target::Trait, GenericRequirement::Exact(1);
222225
Sub(Op), sym::sub, sub_trait, Target::Trait, GenericRequirement::Exact(1);
223226
Mul(Op), sym::mul, mul_trait, Target::Trait, GenericRequirement::Exact(1);

compiler/rustc_middle/src/traits/select.rs

+4
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ pub enum SelectionCandidate<'tcx> {
101101
/// `false` if there are no *further* obligations.
102102
has_nested: bool,
103103
},
104+
105+
/// Implementation of transmutability trait.
106+
TransmutabilityCandidate,
107+
104108
ParamCandidate(ty::PolyTraitPredicate<'tcx>),
105109
ImplCandidate(DefId),
106110
AutoImplCandidate(DefId),

compiler/rustc_middle/src/ty/mod.rs

+76
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,10 @@ impl<'tcx> TraitPredicate<'tcx> {
752752
pub fn self_ty(self) -> Ty<'tcx> {
753753
self.trait_ref.self_ty()
754754
}
755+
756+
pub fn substs(self) -> SubstsRef<'tcx> {
757+
self.trait_ref.substs
758+
}
755759
}
756760

757761
impl<'tcx> PolyTraitPredicate<'tcx> {
@@ -763,6 +767,10 @@ impl<'tcx> PolyTraitPredicate<'tcx> {
763767
pub fn self_ty(self) -> ty::Binder<'tcx, Ty<'tcx>> {
764768
self.map_bound(|trait_ref| trait_ref.self_ty())
765769
}
770+
771+
pub fn substs(self) -> ty::Binder<'tcx, SubstsRef<'tcx>> {
772+
self.map_bound(|trait_ref| trait_ref.substs())
773+
}
766774
}
767775

768776
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)]
@@ -1588,6 +1596,40 @@ impl VariantDef {
15881596
}
15891597
}
15901598

1599+
impl PartialOrd for VariantDef {
1600+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1601+
Some(self.cmp(&other))
1602+
}
1603+
}
1604+
1605+
/// There should be only one VariantDef for each `def_id`, therefore
1606+
/// it is fine to implement `Ord` only based on `def_id`.
1607+
impl Ord for VariantDef {
1608+
fn cmp(&self, other: &Self) -> Ordering {
1609+
self.def_id.cmp(&other.def_id)
1610+
}
1611+
}
1612+
1613+
/// There should be only one VariantDef for each `def_id`, therefore
1614+
/// it is fine to implement `PartialEq` only based on `def_id`.
1615+
impl PartialEq for VariantDef {
1616+
#[inline]
1617+
fn eq(&self, other: &Self) -> bool {
1618+
self.def_id == other.def_id
1619+
}
1620+
}
1621+
1622+
impl Eq for VariantDef {}
1623+
1624+
/// There should be only one VariantDef for each `def_id`, therefore
1625+
/// it is fine to implement `Hash` only based on `def_id`.
1626+
impl Hash for VariantDef {
1627+
#[inline]
1628+
fn hash<H: Hasher>(&self, s: &mut H) {
1629+
self.def_id.hash(s)
1630+
}
1631+
}
1632+
15911633
#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)]
15921634
pub enum VariantDiscr {
15931635
/// Explicit value for this variant, i.e., `X = 123`.
@@ -1608,6 +1650,40 @@ pub struct FieldDef {
16081650
pub vis: Visibility,
16091651
}
16101652

1653+
impl PartialOrd for FieldDef {
1654+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1655+
Some(self.cmp(&other))
1656+
}
1657+
}
1658+
1659+
/// There should be only one FieldDef for each `did`, therefore
1660+
/// it is fine to implement `Ord` only based on `did`.
1661+
impl Ord for FieldDef {
1662+
fn cmp(&self, other: &Self) -> Ordering {
1663+
self.did.cmp(&other.did)
1664+
}
1665+
}
1666+
1667+
/// There should be only one FieldDef for each `did`, therefore
1668+
/// it is fine to implement `PartialEq` only based on `did`.
1669+
impl PartialEq for FieldDef {
1670+
#[inline]
1671+
fn eq(&self, other: &Self) -> bool {
1672+
self.did == other.did
1673+
}
1674+
}
1675+
1676+
impl Eq for FieldDef {}
1677+
1678+
/// There should be only one FieldDef for each `did`, therefore
1679+
/// it is fine to implement `Hash` only based on `did`.
1680+
impl Hash for FieldDef {
1681+
#[inline]
1682+
fn hash<H: Hasher>(&self, s: &mut H) {
1683+
self.did.hash(s)
1684+
}
1685+
}
1686+
16111687
bitflags! {
16121688
#[derive(TyEncodable, TyDecodable, Default, HashStable)]
16131689
pub struct ReprFlags: u8 {

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1338,6 +1338,7 @@ symbols! {
13381338
trait_alias,
13391339
trait_upcasting,
13401340
transmute,
1341+
transmute_trait,
13411342
transparent,
13421343
transparent_enums,
13431344
transparent_unions,

compiler/rustc_target/src/abi/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,7 @@ pub struct Align {
504504

505505
impl Align {
506506
pub const ONE: Align = Align { pow2: 0 };
507+
pub const MAX: Align = Align { pow2: 29 };
507508

508509
#[inline]
509510
pub fn from_bits(bits: u64) -> Result<Align, String> {
@@ -536,7 +537,7 @@ impl Align {
536537
if bytes != 1 {
537538
return Err(not_power_of_2(align));
538539
}
539-
if pow2 > 29 {
540+
if pow2 > Self::MAX.pow2 {
540541
return Err(too_large(align));
541542
}
542543

compiler/rustc_trait_selection/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,5 @@ rustc_query_system = { path = "../rustc_query_system" }
2323
rustc_session = { path = "../rustc_session" }
2424
rustc_span = { path = "../rustc_span" }
2525
rustc_target = { path = "../rustc_target" }
26+
rustc_transmute = { path = "../rustc_transmute" }
2627
smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }

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

+20
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
300300
self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates);
301301
} else if lang_items.unsize_trait() == Some(def_id) {
302302
self.assemble_candidates_for_unsizing(obligation, &mut candidates);
303+
} else if lang_items.transmute_trait() == Some(def_id) {
304+
// User-defined transmutability impls are permitted.
305+
self.assemble_candidates_from_impls(obligation, &mut candidates);
306+
self.assemble_candidates_for_transmutability(obligation, &mut candidates);
303307
} else if lang_items.drop_trait() == Some(def_id)
304308
&& obligation.predicate.skip_binder().constness == ty::BoundConstness::ConstIfConst
305309
{
@@ -876,6 +880,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
876880
};
877881
}
878882

883+
fn assemble_candidates_for_transmutability(
884+
&mut self,
885+
obligation: &TraitObligation<'tcx>,
886+
candidates: &mut SelectionCandidateSet<'tcx>,
887+
) {
888+
if obligation.potentially_has_param_types_or_consts() {
889+
return candidates.ambiguous = false;
890+
}
891+
892+
if obligation.has_infer_types_or_consts() {
893+
return candidates.ambiguous = true;
894+
}
895+
896+
candidates.vec.push(TransmutabilityCandidate);
897+
}
898+
879899
fn assemble_candidates_for_trait_alias(
880900
&mut self,
881901
obligation: &TraitObligation<'tcx>,

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

+52
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
9090
Ok(ImplSource::Builtin(data))
9191
}
9292

93+
TransmutabilityCandidate => {
94+
self.confirm_transmutability_candidate(obligation).map(ImplSource::Builtin)
95+
}
96+
9397
ParamCandidate(param) => {
9498
let obligations =
9599
self.confirm_param_candidate(obligation, param.map_bound(|t| t.trait_ref));
@@ -294,6 +298,54 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
294298
ImplSourceBuiltinData { nested: obligations }
295299
}
296300

301+
fn confirm_transmutability_candidate(
302+
&mut self,
303+
obligation: &TraitObligation<'tcx>,
304+
) -> Result<ImplSourceBuiltinData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
305+
debug!(?obligation, "confirm_transmutability_candidate");
306+
307+
let substs = obligation.predicate.substs();
308+
309+
let type_at = |i| substs.map_bound(|s| s.type_at(i));
310+
let bool_at = |i| {
311+
substs
312+
.skip_binder()
313+
.const_at(i)
314+
.try_eval_bool(self.tcx(), obligation.param_env)
315+
.unwrap()
316+
};
317+
318+
let src_and_dst =
319+
substs.map_bound(|s| rustc_transmute::Types { src: s.type_at(1), dst: s.type_at(0) });
320+
321+
let scope = type_at(2).skip_binder();
322+
let assume_alignment: bool = bool_at(3);
323+
let assume_lifetimes: bool = bool_at(4);
324+
let assume_validity: bool = bool_at(5);
325+
let assume_visibility: bool = bool_at(6);
326+
327+
let cause = obligation.cause.clone();
328+
329+
let mut transmute_env = rustc_transmute::TransmuteTypeEnv::new(self.infcx);
330+
331+
let maybe_transmutable = transmute_env.is_transmutable(
332+
cause,
333+
src_and_dst,
334+
scope,
335+
assume_alignment,
336+
assume_lifetimes,
337+
assume_validity,
338+
assume_visibility,
339+
);
340+
341+
use rustc_transmute::Answer;
342+
343+
match maybe_transmutable {
344+
Answer::Yes => Ok(ImplSourceBuiltinData { nested: vec![] }),
345+
_ => Err(Unimplemented),
346+
}
347+
}
348+
297349
/// This handles the case where an `auto trait Foo` impl is being used.
298350
/// The idea is that the impl applies to `X : Foo` if the following conditions are met:
299351
///

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

+3
Original file line numberDiff line numberDiff line change
@@ -1551,6 +1551,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
15511551
);
15521552
}
15531553

1554+
// FIXME
1555+
(TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => false,
1556+
15541557
// (*)
15551558
(
15561559
BuiltinCandidate { has_nested: false }

compiler/rustc_transmute/Cargo.toml

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
[package]
2+
name = "rustc_transmute"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]
9+
rustc_parse_format = { path = "../rustc_parse_format" }
10+
tracing = "0.1"
11+
rustc_attr = { path = "../rustc_attr" }
12+
rustc_middle = { path = "../rustc_middle" }
13+
rustc_ast = { path = "../rustc_ast" }
14+
rustc_data_structures = { path = "../rustc_data_structures" }
15+
rustc_errors = { path = "../rustc_errors" }
16+
rustc_hir = { path = "../rustc_hir" }
17+
rustc_index = { path = "../rustc_index" }
18+
rustc_infer = { path = "../rustc_infer" }
19+
rustc_macros = { path = "../rustc_macros" }
20+
rustc_session = { path = "../rustc_session" }
21+
rustc_span = { path = "../rustc_span" }
22+
rustc_target = { path = "../rustc_target" }
23+
smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }

0 commit comments

Comments
 (0)