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.

See: rust-lang/compiler-team#411
  • Loading branch information
jswrenn committed Jan 13, 2022
1 parent 256721e commit b071fc0
Show file tree
Hide file tree
Showing 28 changed files with 3,031 additions and 1 deletion.
22 changes: 22 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4588,6 +4588,7 @@ dependencies = [
"rustc_session",
"rustc_span",
"rustc_target",
"rustc_transmute",
"smallvec",
"tracing",
]
Expand All @@ -4612,6 +4613,27 @@ dependencies = [
"tracing",
]

[[package]]
name = "rustc_transmute"
version = "0.1.0"
dependencies = [
"rustc_ast",
"rustc_attr",
"rustc_data_structures",
"rustc_errors",
"rustc_hir",
"rustc_index",
"rustc_infer",
"rustc_macros",
"rustc_middle",
"rustc_parse_format",
"rustc_session",
"rustc_span",
"rustc_target",
"smallvec",
"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 @@ -218,6 +218,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 @@ -101,6 +101,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
76 changes: 76 additions & 0 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,10 @@ impl<'tcx> TraitPredicate<'tcx> {
pub fn self_ty(self) -> Ty<'tcx> {
self.trait_ref.self_ty()
}

pub fn substs(self) -> SubstsRef<'tcx> {
self.trait_ref.substs
}
}

impl<'tcx> PolyTraitPredicate<'tcx> {
Expand All @@ -763,6 +767,10 @@ impl<'tcx> PolyTraitPredicate<'tcx> {
pub fn self_ty(self) -> ty::Binder<'tcx, Ty<'tcx>> {
self.map_bound(|trait_ref| trait_ref.self_ty())
}

pub fn substs(self) -> ty::Binder<'tcx, SubstsRef<'tcx>> {
self.map_bound(|trait_ref| trait_ref.substs())
}
}

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)]
Expand Down Expand Up @@ -1588,6 +1596,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 @@ -1608,6 +1650,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 @@ -1338,6 +1338,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 @@ -504,6 +504,7 @@ pub struct 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 @@ -536,7 +537,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" }
smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates);
} else if lang_items.unsize_trait() == Some(def_id) {
self.assemble_candidates_for_unsizing(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.drop_trait() == Some(def_id)
&& obligation.predicate.skip_binder().constness == ty::BoundConstness::ConstIfConst
{
Expand Down Expand Up @@ -876,6 +880,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
};
}

fn assemble_candidates_for_transmutability(
&mut self,
obligation: &TraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
if obligation.potentially_has_param_types_or_consts() {
return candidates.ambiguous = false;
}

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

candidates.vec.push(TransmutabilityCandidate);
}

fn assemble_candidates_for_trait_alias(
&mut self,
obligation: &TraitObligation<'tcx>,
Expand Down
52 changes: 52 additions & 0 deletions compiler/rustc_trait_selection/src/traits/select/confirmation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,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 @@ -294,6 +298,54 @@ 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 substs = obligation.predicate.substs();

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

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

let scope = type_at(2).skip_binder();
let assume_alignment: bool = bool_at(3);
let assume_lifetimes: bool = bool_at(4);
let assume_validity: bool = bool_at(5);
let assume_visibility: bool = 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_alignment,
assume_lifetimes,
assume_validity,
assume_visibility,
);

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
(TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => false,

// (*)
(
BuiltinCandidate { has_nested: false }
Expand Down
23 changes: 23 additions & 0 deletions compiler/rustc_transmute/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[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]
rustc_parse_format = { path = "../rustc_parse_format" }
tracing = "0.1"
rustc_attr = { path = "../rustc_attr" }
rustc_middle = { path = "../rustc_middle" }
rustc_ast = { path = "../rustc_ast" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_hir = { path = "../rustc_hir" }
rustc_index = { path = "../rustc_index" }
rustc_infer = { path = "../rustc_infer" }
rustc_macros = { path = "../rustc_macros" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }
smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }
Loading

0 comments on commit b071fc0

Please sign in to comment.