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](https://github.com/rust-lang/compiler-team/issues/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.
This commit is contained in:
bors 2022-08-02 21:17:31 +00:00
commit e4417cf020
96 changed files with 6026 additions and 2 deletions

View file

@ -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
@ -873,6 +877,24 @@ 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() {
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,

View file

@ -48,6 +48,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ImplSource::Builtin(data)
}
TransmutabilityCandidate => {
let data = self.confirm_transmutability_candidate(obligation)?;
ImplSource::Builtin(data)
}
ParamCandidate(param) => {
let obligations =
self.confirm_param_candidate(obligation, param.map_bound(|t| t.trait_ref));
@ -267,6 +272,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_or(true)
};
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:
///

View file

@ -1630,6 +1630,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
);
}
// FIXME(@jswrenn): this should probably be more sophisticated
(TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => false,
// (*)
(
BuiltinCandidate { has_nested: false }