Skip to content

Commit e4417cf

Browse files
committed
Auto merge of #92268 - jswrenn:transmute, r=oli-obk
Initial implementation of transmutability trait. *T'was the night before Christmas and all through the codebase, not a miri was stirring — no hint of `unsafe`!* This PR provides an initial, **incomplete** implementation of *[MCP 411: Lang Item for Transmutability](rust-lang/compiler-team#411. The `core::mem::BikeshedIntrinsicFrom` trait provided by this PR is implemented on-the-fly by the compiler for types `Src` and `Dst` when the bits of all possible values of type `Src` are safely reinterpretable as a value of type `Dst`. What this PR provides is: - [x] [support for transmutations involving primitives](https://github.com/jswrenn/rust/tree/transmute/src/test/ui/transmutability/primitives) - [x] [support for transmutations involving arrays](https://github.com/jswrenn/rust/tree/transmute/src/test/ui/transmutability/arrays) - [x] [support for transmutations involving structs](https://github.com/jswrenn/rust/tree/transmute/src/test/ui/transmutability/structs) - [x] [support for transmutations involving enums](https://github.com/jswrenn/rust/tree/transmute/src/test/ui/transmutability/enums) - [x] [support for transmutations involving unions](https://github.com/jswrenn/rust/tree/transmute/src/test/ui/transmutability/unions) - [x] [support for weaker validity checks](https://github.com/jswrenn/rust/blob/transmute/src/test/ui/transmutability/unions/should_permit_intersecting_if_validity_is_assumed.rs) (i.e., `Assume::VALIDITY`) - [x] visibility checking What isn't yet implemented: - [ ] transmutability options passed using the `Assume` struct - [ ] [support for references](https://github.com/jswrenn/rust/blob/transmute/src/test/ui/transmutability/references.rs) - [ ] smarter error messages These features will be implemented in future PRs.
2 parents 4493a0f + 965ffb0 commit e4417cf

File tree

96 files changed

+6026
-2
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

96 files changed

+6026
-2
lines changed

Diff for: Cargo.lock

+15
Original file line numberDiff line numberDiff line change
@@ -4553,6 +4553,7 @@ dependencies = [
45534553
"rustc_session",
45544554
"rustc_span",
45554555
"rustc_target",
4556+
"rustc_transmute",
45564557
"smallvec",
45574558
"tracing",
45584559
]
@@ -4577,6 +4578,20 @@ dependencies = [
45774578
"tracing",
45784579
]
45794580

4581+
[[package]]
4582+
name = "rustc_transmute"
4583+
version = "0.1.0"
4584+
dependencies = [
4585+
"itertools",
4586+
"rustc_data_structures",
4587+
"rustc_infer",
4588+
"rustc_macros",
4589+
"rustc_middle",
4590+
"rustc_span",
4591+
"rustc_target",
4592+
"tracing",
4593+
]
4594+
45804595
[[package]]
45814596
name = "rustc_ty_utils"
45824597
version = "0.0.0"

Diff for: compiler/rustc_hir/src/lang_items.rs

+3
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);

Diff for: compiler/rustc_middle/src/traits/select.rs

+4
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),

Diff for: compiler/rustc_middle/src/ty/mod.rs

+90-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ pub use subst::*;
4848
pub use vtable::*;
4949

5050
use std::fmt::Debug;
51-
use std::hash::Hash;
51+
use std::hash::{Hash, Hasher};
5252
use std::ops::ControlFlow;
5353
use std::{fmt, str};
5454

@@ -1704,6 +1704,59 @@ impl VariantDef {
17041704
}
17051705
}
17061706

1707+
impl PartialEq for VariantDef {
1708+
#[inline]
1709+
fn eq(&self, other: &Self) -> bool {
1710+
// There should be only one `VariantDef` for each `def_id`, therefore
1711+
// it is fine to implement `PartialEq` only based on `def_id`.
1712+
//
1713+
// Below, we exhaustively destructure `self` and `other` so that if the
1714+
// definition of `VariantDef` changes, a compile-error will be produced,
1715+
// reminding us to revisit this assumption.
1716+
1717+
let Self {
1718+
def_id: lhs_def_id,
1719+
ctor_def_id: _,
1720+
name: _,
1721+
discr: _,
1722+
fields: _,
1723+
ctor_kind: _,
1724+
flags: _,
1725+
} = &self;
1726+
1727+
let Self {
1728+
def_id: rhs_def_id,
1729+
ctor_def_id: _,
1730+
name: _,
1731+
discr: _,
1732+
fields: _,
1733+
ctor_kind: _,
1734+
flags: _,
1735+
} = other;
1736+
1737+
lhs_def_id == rhs_def_id
1738+
}
1739+
}
1740+
1741+
impl Eq for VariantDef {}
1742+
1743+
impl Hash for VariantDef {
1744+
#[inline]
1745+
fn hash<H: Hasher>(&self, s: &mut H) {
1746+
// There should be only one `VariantDef` for each `def_id`, therefore
1747+
// it is fine to implement `Hash` only based on `def_id`.
1748+
//
1749+
// Below, we exhaustively destructure `self` so that if the definition
1750+
// of `VariantDef` changes, a compile-error will be produced, reminding
1751+
// us to revisit this assumption.
1752+
1753+
let Self { def_id, ctor_def_id: _, name: _, discr: _, fields: _, ctor_kind: _, flags: _ } =
1754+
&self;
1755+
1756+
def_id.hash(s)
1757+
}
1758+
}
1759+
17071760
#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)]
17081761
pub enum VariantDiscr {
17091762
/// Explicit value for this variant, i.e., `X = 123`.
@@ -1724,6 +1777,42 @@ pub struct FieldDef {
17241777
pub vis: Visibility,
17251778
}
17261779

1780+
impl PartialEq for FieldDef {
1781+
#[inline]
1782+
fn eq(&self, other: &Self) -> bool {
1783+
// There should be only one `FieldDef` for each `did`, therefore it is
1784+
// fine to implement `PartialEq` only based on `did`.
1785+
//
1786+
// Below, we exhaustively destructure `self` so that if the definition
1787+
// of `FieldDef` changes, a compile-error will be produced, reminding
1788+
// us to revisit this assumption.
1789+
1790+
let Self { did: lhs_did, name: _, vis: _ } = &self;
1791+
1792+
let Self { did: rhs_did, name: _, vis: _ } = other;
1793+
1794+
lhs_did == rhs_did
1795+
}
1796+
}
1797+
1798+
impl Eq for FieldDef {}
1799+
1800+
impl Hash for FieldDef {
1801+
#[inline]
1802+
fn hash<H: Hasher>(&self, s: &mut H) {
1803+
// There should be only one `FieldDef` for each `did`, therefore it is
1804+
// fine to implement `Hash` only based on `did`.
1805+
//
1806+
// Below, we exhaustively destructure `self` so that if the definition
1807+
// of `FieldDef` changes, a compile-error will be produced, reminding
1808+
// us to revisit this assumption.
1809+
1810+
let Self { did, name: _, vis: _ } = &self;
1811+
1812+
did.hash(s)
1813+
}
1814+
}
1815+
17271816
bitflags! {
17281817
#[derive(TyEncodable, TyDecodable, Default, HashStable)]
17291818
pub struct ReprFlags: u8 {

Diff for: compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1464,6 +1464,7 @@ symbols! {
14641464
trait_alias,
14651465
trait_upcasting,
14661466
transmute,
1467+
transmute_trait,
14671468
transparent,
14681469
transparent_enums,
14691470
transparent_unions,

Diff for: compiler/rustc_target/src/abi/mod.rs

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

Diff for: 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", features = ["rustc"] }
2627
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }

Diff for: compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

+22
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,24 @@ 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+
return;
888+
}
889+
890+
if obligation.has_infer_types_or_consts() {
891+
candidates.ambiguous = true;
892+
return;
893+
}
894+
895+
candidates.vec.push(TransmutabilityCandidate);
896+
}
897+
876898
#[tracing::instrument(level = "debug", skip(self, obligation, candidates))]
877899
fn assemble_candidates_for_trait_alias(
878900
&mut self,

Diff for: compiler/rustc_trait_selection/src/traits/select/confirmation.rs

+52
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
4848
ImplSource::Builtin(data)
4949
}
5050

51+
TransmutabilityCandidate => {
52+
let data = self.confirm_transmutability_candidate(obligation)?;
53+
ImplSource::Builtin(data)
54+
}
55+
5156
ParamCandidate(param) => {
5257
let obligations =
5358
self.confirm_param_candidate(obligation, param.map_bound(|t| t.trait_ref));
@@ -267,6 +272,53 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
267272
ImplSourceBuiltinData { nested: obligations }
268273
}
269274

275+
fn confirm_transmutability_candidate(
276+
&mut self,
277+
obligation: &TraitObligation<'tcx>,
278+
) -> Result<ImplSourceBuiltinData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
279+
debug!(?obligation, "confirm_transmutability_candidate");
280+
281+
let predicate = obligation.predicate;
282+
283+
let type_at = |i| predicate.map_bound(|p| p.trait_ref.substs.type_at(i));
284+
let bool_at = |i| {
285+
predicate
286+
.skip_binder()
287+
.trait_ref
288+
.substs
289+
.const_at(i)
290+
.try_eval_bool(self.tcx(), obligation.param_env)
291+
.unwrap_or(true)
292+
};
293+
294+
let src_and_dst = predicate.map_bound(|p| rustc_transmute::Types {
295+
src: p.trait_ref.substs.type_at(1),
296+
dst: p.trait_ref.substs.type_at(0),
297+
});
298+
299+
let scope = type_at(2).skip_binder();
300+
301+
let assume = rustc_transmute::Assume {
302+
alignment: bool_at(3),
303+
lifetimes: bool_at(4),
304+
validity: bool_at(5),
305+
visibility: bool_at(6),
306+
};
307+
308+
let cause = obligation.cause.clone();
309+
310+
let mut transmute_env = rustc_transmute::TransmuteTypeEnv::new(self.infcx);
311+
312+
let maybe_transmutable = transmute_env.is_transmutable(cause, src_and_dst, scope, assume);
313+
314+
use rustc_transmute::Answer;
315+
316+
match maybe_transmutable {
317+
Answer::Yes => Ok(ImplSourceBuiltinData { nested: vec![] }),
318+
_ => Err(Unimplemented),
319+
}
320+
}
321+
270322
/// This handles the case where an `auto trait Foo` impl is being used.
271323
/// The idea is that the impl applies to `X : Foo` if the following conditions are met:
272324
///

Diff for: compiler/rustc_trait_selection/src/traits/select/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1630,6 +1630,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
16301630
);
16311631
}
16321632

1633+
// FIXME(@jswrenn): this should probably be more sophisticated
1634+
(TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => false,
1635+
16331636
// (*)
16341637
(
16351638
BuiltinCandidate { has_nested: false }

Diff for: compiler/rustc_transmute/Cargo.toml

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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+
]
26+
27+
[dev-dependencies]
28+
itertools = "0.10.1"

0 commit comments

Comments
 (0)