Skip to content

Commit 778696d

Browse files
committed
Initial (incomplete) implementation of transmutability trait.
This initial implementation handles transmutations between primitive types and some structs, enums and unions. See: rust-lang/compiler-team#411
1 parent 3bebee7 commit 778696d

File tree

37 files changed

+4143
-2
lines changed

37 files changed

+4143
-2
lines changed

Cargo.lock

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4533,6 +4533,7 @@ dependencies = [
45334533
"rustc_session",
45344534
"rustc_span",
45354535
"rustc_target",
4536+
"rustc_transmute",
45364537
"smallvec",
45374538
"tracing",
45384539
]
@@ -4557,6 +4558,19 @@ dependencies = [
45574558
"tracing",
45584559
]
45594560

4561+
[[package]]
4562+
name = "rustc_transmute"
4563+
version = "0.1.0"
4564+
dependencies = [
4565+
"rustc_data_structures",
4566+
"rustc_infer",
4567+
"rustc_macros",
4568+
"rustc_middle",
4569+
"rustc_span",
4570+
"rustc_target",
4571+
"tracing",
4572+
]
4573+
45604574
[[package]]
45614575
name = "rustc_ty_utils"
45624576
version = "0.0.0"

compiler/rustc_hir/src/lang_items.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,9 @@ language_item_table! {
191191
CoerceUnsized, sym::coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1);
192192
DispatchFromDyn, sym::dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1);
193193

194+
// language items relating to transmutability
195+
TransmuteTrait, sym::transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(6);
196+
194197
Add(Op), sym::add, add_trait, Target::Trait, GenericRequirement::Exact(1);
195198
Sub(Op), sym::sub, sub_trait, Target::Trait, GenericRequirement::Exact(1);
196199
Mul(Op), sym::mul, mul_trait, Target::Trait, GenericRequirement::Exact(1);

compiler/rustc_middle/src/traits/select.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ pub enum SelectionCandidate<'tcx> {
109109
/// `false` if there are no *further* obligations.
110110
has_nested: bool,
111111
},
112+
113+
/// Implementation of transmutability trait.
114+
TransmutabilityCandidate,
115+
112116
ParamCandidate(ty::PolyTraitPredicate<'tcx>),
113117
ImplCandidate(DefId),
114118
AutoImplCandidate(DefId),

compiler/rustc_middle/src/ty/mod.rs

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ use rustc_target::abi::{Align, VariantIdx};
4646
pub use subst::*;
4747
pub use vtable::*;
4848

49+
use std::cmp::Ordering;
4950
use std::fmt::Debug;
50-
use std::hash::Hash;
51+
use std::hash::{Hash, Hasher};
5152
use std::ops::ControlFlow;
5253
use std::{fmt, str};
5354

@@ -1750,6 +1751,40 @@ impl VariantDef {
17501751
}
17511752
}
17521753

1754+
impl PartialOrd for VariantDef {
1755+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1756+
Some(self.cmp(&other))
1757+
}
1758+
}
1759+
1760+
/// There should be only one VariantDef for each `def_id`, therefore
1761+
/// it is fine to implement `Ord` only based on `def_id`.
1762+
impl Ord for VariantDef {
1763+
fn cmp(&self, other: &Self) -> Ordering {
1764+
self.def_id.cmp(&other.def_id)
1765+
}
1766+
}
1767+
1768+
/// There should be only one VariantDef for each `def_id`, therefore
1769+
/// it is fine to implement `PartialEq` only based on `def_id`.
1770+
impl PartialEq for VariantDef {
1771+
#[inline]
1772+
fn eq(&self, other: &Self) -> bool {
1773+
self.def_id == other.def_id
1774+
}
1775+
}
1776+
1777+
impl Eq for VariantDef {}
1778+
1779+
/// There should be only one VariantDef for each `def_id`, therefore
1780+
/// it is fine to implement `Hash` only based on `def_id`.
1781+
impl Hash for VariantDef {
1782+
#[inline]
1783+
fn hash<H: Hasher>(&self, s: &mut H) {
1784+
self.def_id.hash(s)
1785+
}
1786+
}
1787+
17531788
#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)]
17541789
pub enum VariantDiscr {
17551790
/// Explicit value for this variant, i.e., `X = 123`.
@@ -1770,6 +1805,40 @@ pub struct FieldDef {
17701805
pub vis: Visibility,
17711806
}
17721807

1808+
impl PartialOrd for FieldDef {
1809+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1810+
Some(self.cmp(&other))
1811+
}
1812+
}
1813+
1814+
/// There should be only one FieldDef for each `did`, therefore
1815+
/// it is fine to implement `Ord` only based on `did`.
1816+
impl Ord for FieldDef {
1817+
fn cmp(&self, other: &Self) -> Ordering {
1818+
self.did.cmp(&other.did)
1819+
}
1820+
}
1821+
1822+
/// There should be only one FieldDef for each `did`, therefore
1823+
/// it is fine to implement `PartialEq` only based on `did`.
1824+
impl PartialEq for FieldDef {
1825+
#[inline]
1826+
fn eq(&self, other: &Self) -> bool {
1827+
self.did == other.did
1828+
}
1829+
}
1830+
1831+
impl Eq for FieldDef {}
1832+
1833+
/// There should be only one FieldDef for each `did`, therefore
1834+
/// it is fine to implement `Hash` only based on `did`.
1835+
impl Hash for FieldDef {
1836+
#[inline]
1837+
fn hash<H: Hasher>(&self, s: &mut H) {
1838+
self.did.hash(s)
1839+
}
1840+
}
1841+
17731842
bitflags! {
17741843
#[derive(TyEncodable, TyDecodable, Default, HashStable)]
17751844
pub struct ReprFlags: u8 {

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1434,6 +1434,7 @@ symbols! {
14341434
trait_alias,
14351435
trait_upcasting,
14361436
transmute,
1437+
transmute_trait,
14371438
transparent,
14381439
transparent_enums,
14391440
transparent_unions,

compiler/rustc_target/src/abi/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,7 @@ impl fmt::Debug for Align {
508508

509509
impl Align {
510510
pub const ONE: Align = Align { pow2: 0 };
511+
pub const MAX: Align = Align { pow2: 29 };
511512

512513
#[inline]
513514
pub fn from_bits(bits: u64) -> Result<Align, String> {
@@ -540,7 +541,7 @@ impl Align {
540541
if bytes != 1 {
541542
return Err(not_power_of_2(align));
542543
}
543-
if pow2 > 29 {
544+
if pow2 > Self::MAX.pow2 {
544545
return Err(too_large(align));
545546
}
546547

compiler/rustc_trait_selection/Cargo.toml

Lines changed: 1 addition & 0 deletions
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", features = ["rustc"] }
2627
smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
305305
self.assemble_candidates_for_unsizing(obligation, &mut candidates);
306306
} else if lang_items.destruct_trait() == Some(def_id) {
307307
self.assemble_const_destruct_candidates(obligation, &mut candidates);
308+
} else if lang_items.transmute_trait() == Some(def_id) {
309+
// User-defined transmutability impls are permitted.
310+
self.assemble_candidates_from_impls(obligation, &mut candidates);
311+
self.assemble_candidates_for_transmutability(obligation, &mut candidates);
308312
} else {
309313
if lang_items.clone_trait() == Some(def_id) {
310314
// Same builtin conditions as `Copy`, i.e., every type which has builtin support
@@ -873,6 +877,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
873877
};
874878
}
875879

880+
#[tracing::instrument(level = "debug", skip(self, obligation, candidates))]
881+
fn assemble_candidates_for_transmutability(
882+
&mut self,
883+
obligation: &TraitObligation<'tcx>,
884+
candidates: &mut SelectionCandidateSet<'tcx>,
885+
) {
886+
if obligation.has_param_types_or_consts() {
887+
candidates.ambiguous = false;
888+
return;
889+
}
890+
891+
if obligation.has_infer_types_or_consts() {
892+
candidates.ambiguous = true;
893+
return;
894+
}
895+
896+
candidates.vec.push(TransmutabilityCandidate);
897+
}
898+
876899
#[tracing::instrument(level = "debug", skip(self, obligation, candidates))]
877900
fn assemble_candidates_for_trait_alias(
878901
&mut self,

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

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
7777
Ok(ImplSource::Builtin(data))
7878
}
7979

80+
TransmutabilityCandidate => {
81+
self.confirm_transmutability_candidate(obligation).map(ImplSource::Builtin)
82+
}
83+
8084
ParamCandidate(param) => {
8185
let obligations =
8286
self.confirm_param_candidate(obligation, param.map_bound(|t| t.trait_ref));
@@ -286,6 +290,53 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
286290
ImplSourceBuiltinData { nested: obligations }
287291
}
288292

293+
fn confirm_transmutability_candidate(
294+
&mut self,
295+
obligation: &TraitObligation<'tcx>,
296+
) -> Result<ImplSourceBuiltinData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
297+
debug!(?obligation, "confirm_transmutability_candidate");
298+
299+
let predicate = obligation.predicate;
300+
301+
let type_at = |i| predicate.map_bound(|p| p.trait_ref.substs.type_at(i));
302+
let bool_at = |i| {
303+
predicate
304+
.skip_binder()
305+
.trait_ref
306+
.substs
307+
.const_at(i)
308+
.try_eval_bool(self.tcx(), obligation.param_env)
309+
.unwrap()
310+
};
311+
312+
let src_and_dst = predicate.map_bound(|p| rustc_transmute::Types {
313+
src: p.trait_ref.substs.type_at(1),
314+
dst: p.trait_ref.substs.type_at(0),
315+
});
316+
317+
let scope = type_at(2).skip_binder();
318+
319+
let assume = rustc_transmute::Assume {
320+
alignment: bool_at(3),
321+
lifetimes: bool_at(4),
322+
validity: bool_at(5),
323+
visibility: bool_at(6),
324+
};
325+
326+
let cause = obligation.cause.clone();
327+
328+
let mut transmute_env = rustc_transmute::TransmuteTypeEnv::new(self.infcx);
329+
330+
let maybe_transmutable = transmute_env.is_transmutable(cause, src_and_dst, scope, assume);
331+
332+
use rustc_transmute::Answer;
333+
334+
match maybe_transmutable {
335+
Answer::Yes => Ok(ImplSourceBuiltinData { nested: vec![] }),
336+
_ => Err(Unimplemented),
337+
}
338+
}
339+
289340
/// This handles the case where an `auto trait Foo` impl is being used.
290341
/// The idea is that the impl applies to `X : Foo` if the following conditions are met:
291342
///

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1551,6 +1551,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
15511551
);
15521552
}
15531553

1554+
// FIXME(@jswrenn): this should probably be more sophisticated
1555+
(TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => false,
1556+
15541557
// (*)
15551558
(
15561559
BuiltinCandidate { has_nested: false }

compiler/rustc_transmute/Cargo.toml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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+
tracing = "0.1"
10+
rustc_data_structures = { path = "../rustc_data_structures", optional = true}
11+
rustc_infer = { path = "../rustc_infer", optional = true}
12+
rustc_macros = { path = "../rustc_macros", optional = true}
13+
rustc_middle = { path = "../rustc_middle", optional = true}
14+
rustc_span = { path = "../rustc_span", optional = true}
15+
rustc_target = { path = "../rustc_target", optional = true}
16+
17+
[features]
18+
rustc = [
19+
"rustc_middle",
20+
"rustc_data_structures",
21+
"rustc_infer",
22+
"rustc_macros",
23+
"rustc_span",
24+
"rustc_target",
25+
]

0 commit comments

Comments
 (0)