Skip to content

Commit 13675b8

Browse files
committed
safe transmute: revise safety analysis
Migrate to a simplified safety analysis that does not use visibility. Closes rust-lang/project-safe-transmute#15
1 parent cfb42e5 commit 13675b8

File tree

124 files changed

+1362
-1921
lines changed

Some content is hidden

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

124 files changed

+1362
-1921
lines changed

compiler/rustc_hir/src/lang_items.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ language_item_table! {
167167

168168
// language items relating to transmutability
169169
TransmuteOpts, sym::transmute_opts, transmute_opts, Target::Struct, GenericRequirement::Exact(0);
170-
TransmuteTrait, sym::transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(3);
170+
TransmuteTrait, sym::transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(2);
171171

172172
Add, sym::add, add_trait, Target::Trait, GenericRequirement::Exact(1);
173173
Sub, sym::sub, sub_trait, Target::Trait, GenericRequirement::Exact(1);

compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs

-2
Original file line numberDiff line numberDiff line change
@@ -856,15 +856,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
856856
pub(super) fn is_transmutable(
857857
&self,
858858
src_and_dst: rustc_transmute::Types<'tcx>,
859-
scope: Ty<'tcx>,
860859
assume: rustc_transmute::Assume,
861860
) -> Result<Certainty, NoSolution> {
862861
use rustc_transmute::Answer;
863862
// FIXME(transmutability): This really should be returning nested goals for `Answer::If*`
864863
match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable(
865864
ObligationCause::dummy(),
866865
src_and_dst,
867-
scope,
868866
assume,
869867
) {
870868
Answer::Yes => Ok(Certainty::Yes),

compiler/rustc_trait_selection/src/solve/trait_goals.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -537,14 +537,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
537537
let args = ecx.tcx().erase_regions(goal.predicate.trait_ref.args);
538538

539539
let Some(assume) =
540-
rustc_transmute::Assume::from_const(ecx.tcx(), goal.param_env, args.const_at(3))
540+
rustc_transmute::Assume::from_const(ecx.tcx(), goal.param_env, args.const_at(2))
541541
else {
542542
return Err(NoSolution);
543543
};
544544

545545
let certainty = ecx.is_transmutable(
546546
rustc_transmute::Types { dst: args.type_at(0), src: args.type_at(1) },
547-
args.type_at(2),
548547
assume,
549548
)?;
550549
ecx.evaluate_added_goals_and_make_canonical_response(certainty)

compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs

+5-9
Original file line numberDiff line numberDiff line change
@@ -3088,11 +3088,10 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
30883088
dst: trait_ref.args.type_at(0),
30893089
src: trait_ref.args.type_at(1),
30903090
};
3091-
let scope = trait_ref.args.type_at(2);
30923091
let Some(assume) = rustc_transmute::Assume::from_const(
30933092
self.infcx.tcx,
30943093
obligation.param_env,
3095-
trait_ref.args.const_at(3),
3094+
trait_ref.args.const_at(2),
30963095
) else {
30973096
span_bug!(
30983097
span,
@@ -3103,15 +3102,12 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
31033102
match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable(
31043103
obligation.cause,
31053104
src_and_dst,
3106-
scope,
31073105
assume,
31083106
) {
31093107
Answer::No(reason) => {
31103108
let dst = trait_ref.args.type_at(0);
31113109
let src = trait_ref.args.type_at(1);
3112-
let err_msg = format!(
3113-
"`{src}` cannot be safely transmuted into `{dst}` in the defining scope of `{scope}`"
3114-
);
3110+
let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`");
31153111
let safe_transmute_explanation = match reason {
31163112
rustc_transmute::Reason::SrcIsUnspecified => {
31173113
format!("`{src}` does not have a well-specified layout")
@@ -3125,9 +3121,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
31253121
format!("At least one value of `{src}` isn't a bit-valid value of `{dst}`")
31263122
}
31273123

3128-
rustc_transmute::Reason::DstIsPrivate => format!(
3129-
"`{dst}` is or contains a type or field that is not visible in that scope"
3130-
),
3124+
rustc_transmute::Reason::DstMayHaveSafetyInvariants => {
3125+
format!("`{dst}` may carry safety invariants")
3126+
}
31313127
rustc_transmute::Reason::DstIsTooBig => {
31323128
format!("The size of `{src}` is smaller than the size of `{dst}`")
31333129
}

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

+2-5
Original file line numberDiff line numberDiff line change
@@ -303,16 +303,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
303303
.collect(),
304304
Condition::IfTransmutable { src, dst } => {
305305
let trait_def_id = obligation.predicate.def_id();
306-
let scope = predicate.trait_ref.args.type_at(2);
307-
let assume_const = predicate.trait_ref.args.const_at(3);
306+
let assume_const = predicate.trait_ref.args.const_at(2);
308307
let make_obl = |from_ty, to_ty| {
309308
let trait_ref1 = ty::TraitRef::new(
310309
tcx,
311310
trait_def_id,
312311
[
313312
ty::GenericArg::from(to_ty),
314313
ty::GenericArg::from(from_ty),
315-
ty::GenericArg::from(scope),
316314
ty::GenericArg::from(assume_const),
317315
],
318316
);
@@ -348,7 +346,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
348346
let Some(assume) = rustc_transmute::Assume::from_const(
349347
self.infcx.tcx,
350348
obligation.param_env,
351-
predicate.trait_ref.args.const_at(3),
349+
predicate.trait_ref.args.const_at(2),
352350
) else {
353351
return Err(Unimplemented);
354352
};
@@ -360,7 +358,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
360358
let maybe_transmutable = transmute_env.is_transmutable(
361359
obligation.cause.clone(),
362360
rustc_transmute::Types { dst, src },
363-
predicate.trait_ref.args.type_at(2),
364361
assume,
365362
);
366363

compiler/rustc_transmute/src/layout/mod.rs

+17-3
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,21 @@ impl fmt::Debug for Byte {
2929
}
3030
}
3131

32-
pub(crate) trait Def: Debug + Hash + Eq + PartialEq + Copy + Clone {}
32+
pub(crate) trait Def: Debug + Hash + Eq + PartialEq + Copy + Clone {
33+
fn has_safety_invariants(&self) -> bool;
34+
}
3335
pub trait Ref: Debug + Hash + Eq + PartialEq + Copy + Clone {
3436
fn min_align(&self) -> usize;
3537

3638
fn is_mutable(&self) -> bool;
3739
}
3840

39-
impl Def for ! {}
41+
impl Def for ! {
42+
fn has_safety_invariants(&self) -> bool {
43+
unreachable!()
44+
}
45+
}
46+
4047
impl Ref for ! {
4148
fn min_align(&self) -> usize {
4249
unreachable!()
@@ -83,5 +90,12 @@ pub mod rustc {
8390
Primitive,
8491
}
8592

86-
impl<'tcx> super::Def for Def<'tcx> {}
93+
impl<'tcx> super::Def for Def<'tcx> {
94+
fn has_safety_invariants(&self) -> bool {
95+
// Rust presently has no notion of 'unsafe fields', so for now we
96+
// make the conservative assumption that everything besides
97+
// primitive types carry safety invariants.
98+
self != &Self::Primitive
99+
}
100+
}
87101
}

compiler/rustc_transmute/src/layout/tree.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ where
8181
Self::Seq(vec![Self::uninit(); width_in_bytes])
8282
}
8383

84-
/// Remove all `Def` nodes, and all branches of the layout for which `f` produces false.
84+
/// Remove all `Def` nodes, and all branches of the layout for which `f`
85+
/// produces `true`.
8586
pub(crate) fn prune<F>(self, f: &F) -> Tree<!, R>
8687
where
8788
F: Fn(D) -> bool,
@@ -106,7 +107,7 @@ where
106107
Self::Byte(b) => Tree::Byte(b),
107108
Self::Ref(r) => Tree::Ref(r),
108109
Self::Def(d) => {
109-
if !f(d) {
110+
if f(d) {
110111
Tree::uninhabited()
111112
} else {
112113
Tree::unit()

compiler/rustc_transmute/src/layout/tree/tests.rs

+47-22
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@ use super::Tree;
22

33
#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
44
pub enum Def {
5-
Visible,
6-
Invisible,
5+
NoSafetyInvariants,
6+
HasSafetyInvariants,
77
}
88

9-
impl super::Def for Def {}
9+
impl super::Def for Def {
10+
fn has_safety_invariants(&self) -> bool {
11+
self == &Self::HasSafetyInvariants
12+
}
13+
}
1014

1115
mod prune {
1216
use super::*;
@@ -16,17 +20,22 @@ mod prune {
1620

1721
#[test]
1822
fn seq_1() {
19-
let layout: Tree<Def, !> = Tree::def(Def::Visible).then(Tree::from_bits(0x00));
20-
assert_eq!(layout.prune(&|d| matches!(d, Def::Visible)), Tree::from_bits(0x00));
23+
let layout: Tree<Def, !> =
24+
Tree::def(Def::NoSafetyInvariants).then(Tree::from_bits(0x00));
25+
assert_eq!(
26+
layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)),
27+
Tree::from_bits(0x00)
28+
);
2129
}
2230

2331
#[test]
2432
fn seq_2() {
25-
let layout: Tree<Def, !> =
26-
Tree::from_bits(0x00).then(Tree::def(Def::Visible)).then(Tree::from_bits(0x01));
33+
let layout: Tree<Def, !> = Tree::from_bits(0x00)
34+
.then(Tree::def(Def::NoSafetyInvariants))
35+
.then(Tree::from_bits(0x01));
2736

2837
assert_eq!(
29-
layout.prune(&|d| matches!(d, Def::Visible)),
38+
layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)),
3039
Tree::from_bits(0x00).then(Tree::from_bits(0x01))
3140
);
3241
}
@@ -37,21 +46,32 @@ mod prune {
3746

3847
#[test]
3948
fn invisible_def() {
40-
let layout: Tree<Def, !> = Tree::def(Def::Invisible);
41-
assert_eq!(layout.prune(&|d| matches!(d, Def::Visible)), Tree::uninhabited());
49+
let layout: Tree<Def, !> = Tree::def(Def::HasSafetyInvariants);
50+
assert_eq!(
51+
layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)),
52+
Tree::uninhabited()
53+
);
4254
}
4355

4456
#[test]
4557
fn invisible_def_in_seq_len_2() {
46-
let layout: Tree<Def, !> = Tree::def(Def::Visible).then(Tree::def(Def::Invisible));
47-
assert_eq!(layout.prune(&|d| matches!(d, Def::Visible)), Tree::uninhabited());
58+
let layout: Tree<Def, !> =
59+
Tree::def(Def::NoSafetyInvariants).then(Tree::def(Def::HasSafetyInvariants));
60+
assert_eq!(
61+
layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)),
62+
Tree::uninhabited()
63+
);
4864
}
4965

5066
#[test]
5167
fn invisible_def_in_seq_len_3() {
52-
let layout: Tree<Def, !> =
53-
Tree::def(Def::Visible).then(Tree::from_bits(0x00)).then(Tree::def(Def::Invisible));
54-
assert_eq!(layout.prune(&|d| matches!(d, Def::Visible)), Tree::uninhabited());
68+
let layout: Tree<Def, !> = Tree::def(Def::NoSafetyInvariants)
69+
.then(Tree::from_bits(0x00))
70+
.then(Tree::def(Def::HasSafetyInvariants));
71+
assert_eq!(
72+
layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)),
73+
Tree::uninhabited()
74+
);
5575
}
5676
}
5777

@@ -60,21 +80,26 @@ mod prune {
6080

6181
#[test]
6282
fn visible_def() {
63-
let layout: Tree<Def, !> = Tree::def(Def::Visible);
64-
assert_eq!(layout.prune(&|d| matches!(d, Def::Visible)), Tree::unit());
83+
let layout: Tree<Def, !> = Tree::def(Def::NoSafetyInvariants);
84+
assert_eq!(layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), Tree::unit());
6585
}
6686

6787
#[test]
6888
fn visible_def_in_seq_len_2() {
69-
let layout: Tree<Def, !> = Tree::def(Def::Visible).then(Tree::def(Def::Visible));
70-
assert_eq!(layout.prune(&|d| matches!(d, Def::Visible)), Tree::unit());
89+
let layout: Tree<Def, !> =
90+
Tree::def(Def::NoSafetyInvariants).then(Tree::def(Def::NoSafetyInvariants));
91+
assert_eq!(layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), Tree::unit());
7192
}
7293

7394
#[test]
7495
fn visible_def_in_seq_len_3() {
75-
let layout: Tree<Def, !> =
76-
Tree::def(Def::Visible).then(Tree::from_bits(0x00)).then(Tree::def(Def::Visible));
77-
assert_eq!(layout.prune(&|d| matches!(d, Def::Visible)), Tree::from_bits(0x00));
96+
let layout: Tree<Def, !> = Tree::def(Def::NoSafetyInvariants)
97+
.then(Tree::from_bits(0x00))
98+
.then(Tree::def(Def::NoSafetyInvariants));
99+
assert_eq!(
100+
layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)),
101+
Tree::from_bits(0x00)
102+
);
78103
}
79104
}
80105
}

compiler/rustc_transmute/src/lib.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ pub enum Reason {
5151
DstIsUnspecified,
5252
/// The layout of the destination type is bit-incompatible with the source type.
5353
DstIsBitIncompatible,
54-
/// There aren't any public constructors for `Dst`.
55-
DstIsPrivate,
54+
/// The destination type may carry safety invariants.
55+
DstMayHaveSafetyInvariants,
5656
/// `Dst` is larger than `Src`, and the excess bytes were not exclusively uninitialized.
5757
DstIsTooBig,
5858
/// Src should have a stricter alignment than Dst, but it does not.
@@ -108,13 +108,11 @@ mod rustc {
108108
&mut self,
109109
cause: ObligationCause<'tcx>,
110110
types: Types<'tcx>,
111-
scope: Ty<'tcx>,
112111
assume: crate::Assume,
113112
) -> crate::Answer<crate::layout::rustc::Ref<'tcx>> {
114113
crate::maybe_transmutable::MaybeTransmutableQuery::new(
115114
types.src,
116115
types.dst,
117-
scope,
118116
assume,
119117
self.infcx.tcx,
120118
)

0 commit comments

Comments
 (0)