Skip to content

Commit

Permalink
Initial (incomplete) implementation of transmutability trait.
Browse files Browse the repository at this point in the history
This initial implementation handles transmutations between primitive types and some structs, enums and unions.

See: rust-lang/compiler-team#411
  • Loading branch information
jswrenn committed Jul 7, 2022
1 parent 3bebee7 commit 778696d
Show file tree
Hide file tree
Showing 37 changed files with 4,143 additions and 2 deletions.
14 changes: 14 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4533,6 +4533,7 @@ dependencies = [
"rustc_session",
"rustc_span",
"rustc_target",
"rustc_transmute",
"smallvec",
"tracing",
]
Expand All @@ -4557,6 +4558,19 @@ dependencies = [
"tracing",
]

[[package]]
name = "rustc_transmute"
version = "0.1.0"
dependencies = [
"rustc_data_structures",
"rustc_infer",
"rustc_macros",
"rustc_middle",
"rustc_span",
"rustc_target",
"tracing",
]

[[package]]
name = "rustc_ty_utils"
version = "0.0.0"
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@ language_item_table! {
CoerceUnsized, sym::coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1);
DispatchFromDyn, sym::dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1);

// language items relating to transmutability
TransmuteTrait, sym::transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(6);

Add(Op), sym::add, add_trait, Target::Trait, GenericRequirement::Exact(1);
Sub(Op), sym::sub, sub_trait, Target::Trait, GenericRequirement::Exact(1);
Mul(Op), sym::mul, mul_trait, Target::Trait, GenericRequirement::Exact(1);
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ pub enum SelectionCandidate<'tcx> {
/// `false` if there are no *further* obligations.
has_nested: bool,
},

/// Implementation of transmutability trait.
TransmutabilityCandidate,

ParamCandidate(ty::PolyTraitPredicate<'tcx>),
ImplCandidate(DefId),
AutoImplCandidate(DefId),
Expand Down
71 changes: 70 additions & 1 deletion compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ use rustc_target::abi::{Align, VariantIdx};
pub use subst::*;
pub use vtable::*;

use std::cmp::Ordering;
use std::fmt::Debug;
use std::hash::Hash;
use std::hash::{Hash, Hasher};
use std::ops::ControlFlow;
use std::{fmt, str};

Expand Down Expand Up @@ -1750,6 +1751,40 @@ impl VariantDef {
}
}

impl PartialOrd for VariantDef {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(&other))
}
}

/// There should be only one VariantDef for each `def_id`, therefore
/// it is fine to implement `Ord` only based on `def_id`.
impl Ord for VariantDef {
fn cmp(&self, other: &Self) -> Ordering {
self.def_id.cmp(&other.def_id)
}
}

/// There should be only one VariantDef for each `def_id`, therefore
/// it is fine to implement `PartialEq` only based on `def_id`.
impl PartialEq for VariantDef {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.def_id == other.def_id
}
}

impl Eq for VariantDef {}

/// There should be only one VariantDef for each `def_id`, therefore
/// it is fine to implement `Hash` only based on `def_id`.
impl Hash for VariantDef {
#[inline]
fn hash<H: Hasher>(&self, s: &mut H) {
self.def_id.hash(s)
}
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)]
pub enum VariantDiscr {
/// Explicit value for this variant, i.e., `X = 123`.
Expand All @@ -1770,6 +1805,40 @@ pub struct FieldDef {
pub vis: Visibility,
}

impl PartialOrd for FieldDef {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(&other))
}
}

/// There should be only one FieldDef for each `did`, therefore
/// it is fine to implement `Ord` only based on `did`.
impl Ord for FieldDef {
fn cmp(&self, other: &Self) -> Ordering {
self.did.cmp(&other.did)
}
}

/// There should be only one FieldDef for each `did`, therefore
/// it is fine to implement `PartialEq` only based on `did`.
impl PartialEq for FieldDef {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.did == other.did
}
}

impl Eq for FieldDef {}

/// There should be only one FieldDef for each `did`, therefore
/// it is fine to implement `Hash` only based on `did`.
impl Hash for FieldDef {
#[inline]
fn hash<H: Hasher>(&self, s: &mut H) {
self.did.hash(s)
}
}

bitflags! {
#[derive(TyEncodable, TyDecodable, Default, HashStable)]
pub struct ReprFlags: u8 {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1434,6 +1434,7 @@ symbols! {
trait_alias,
trait_upcasting,
transmute,
transmute_trait,
transparent,
transparent_enums,
transparent_unions,
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_target/src/abi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,7 @@ impl fmt::Debug for Align {

impl Align {
pub const ONE: Align = Align { pow2: 0 };
pub const MAX: Align = Align { pow2: 29 };

#[inline]
pub fn from_bits(bits: u64) -> Result<Align, String> {
Expand Down Expand Up @@ -540,7 +541,7 @@ impl Align {
if bytes != 1 {
return Err(not_power_of_2(align));
}
if pow2 > 29 {
if pow2 > Self::MAX.pow2 {
return Err(too_large(align));
}

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_trait_selection/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ rustc_query_system = { path = "../rustc_query_system" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }
rustc_transmute = { path = "../rustc_transmute", features = ["rustc"] }
smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
self.assemble_candidates_for_unsizing(obligation, &mut candidates);
} else if lang_items.destruct_trait() == Some(def_id) {
self.assemble_const_destruct_candidates(obligation, &mut candidates);
} else if lang_items.transmute_trait() == Some(def_id) {
// User-defined transmutability impls are permitted.
self.assemble_candidates_from_impls(obligation, &mut candidates);
self.assemble_candidates_for_transmutability(obligation, &mut candidates);
} else {
if lang_items.clone_trait() == Some(def_id) {
// Same builtin conditions as `Copy`, i.e., every type which has builtin support
Expand Down Expand Up @@ -873,6 +877,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
};
}

#[tracing::instrument(level = "debug", skip(self, obligation, candidates))]
fn assemble_candidates_for_transmutability(
&mut self,
obligation: &TraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
if obligation.has_param_types_or_consts() {
candidates.ambiguous = false;
return;
}

if obligation.has_infer_types_or_consts() {
candidates.ambiguous = true;
return;
}

candidates.vec.push(TransmutabilityCandidate);
}

#[tracing::instrument(level = "debug", skip(self, obligation, candidates))]
fn assemble_candidates_for_trait_alias(
&mut self,
Expand Down
51 changes: 51 additions & 0 deletions compiler/rustc_trait_selection/src/traits/select/confirmation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
Ok(ImplSource::Builtin(data))
}

TransmutabilityCandidate => {
self.confirm_transmutability_candidate(obligation).map(ImplSource::Builtin)
}

ParamCandidate(param) => {
let obligations =
self.confirm_param_candidate(obligation, param.map_bound(|t| t.trait_ref));
Expand Down Expand Up @@ -286,6 +290,53 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ImplSourceBuiltinData { nested: obligations }
}

fn confirm_transmutability_candidate(
&mut self,
obligation: &TraitObligation<'tcx>,
) -> Result<ImplSourceBuiltinData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
debug!(?obligation, "confirm_transmutability_candidate");

let predicate = obligation.predicate;

let type_at = |i| predicate.map_bound(|p| p.trait_ref.substs.type_at(i));
let bool_at = |i| {
predicate
.skip_binder()
.trait_ref
.substs
.const_at(i)
.try_eval_bool(self.tcx(), obligation.param_env)
.unwrap()
};

let src_and_dst = predicate.map_bound(|p| rustc_transmute::Types {
src: p.trait_ref.substs.type_at(1),
dst: p.trait_ref.substs.type_at(0),
});

let scope = type_at(2).skip_binder();

let assume = rustc_transmute::Assume {
alignment: bool_at(3),
lifetimes: bool_at(4),
validity: bool_at(5),
visibility: bool_at(6),
};

let cause = obligation.cause.clone();

let mut transmute_env = rustc_transmute::TransmuteTypeEnv::new(self.infcx);

let maybe_transmutable = transmute_env.is_transmutable(cause, src_and_dst, scope, assume);

use rustc_transmute::Answer;

match maybe_transmutable {
Answer::Yes => Ok(ImplSourceBuiltinData { nested: vec![] }),
_ => Err(Unimplemented),
}
}

/// This handles the case where an `auto trait Foo` impl is being used.
/// The idea is that the impl applies to `X : Foo` if the following conditions are met:
///
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_trait_selection/src/traits/select/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1551,6 +1551,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
);
}

// FIXME(@jswrenn): this should probably be more sophisticated
(TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => false,

// (*)
(
BuiltinCandidate { has_nested: false }
Expand Down
25 changes: 25 additions & 0 deletions compiler/rustc_transmute/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[package]
name = "rustc_transmute"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
tracing = "0.1"
rustc_data_structures = { path = "../rustc_data_structures", optional = true}
rustc_infer = { path = "../rustc_infer", optional = true}
rustc_macros = { path = "../rustc_macros", optional = true}
rustc_middle = { path = "../rustc_middle", optional = true}
rustc_span = { path = "../rustc_span", optional = true}
rustc_target = { path = "../rustc_target", optional = true}

[features]
rustc = [
"rustc_middle",
"rustc_data_structures",
"rustc_infer",
"rustc_macros",
"rustc_span",
"rustc_target",
]
Loading

0 comments on commit 778696d

Please sign in to comment.