Skip to content
/ rust Public
forked from rust-lang/rust

Commit c944090

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 9afdb8d commit c944090

File tree

124 files changed

+1366
-1925
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

+1366
-1925
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
@@ -874,15 +874,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
874874
pub(super) fn is_transmutable(
875875
&self,
876876
src_and_dst: rustc_transmute::Types<'tcx>,
877-
scope: Ty<'tcx>,
878877
assume: rustc_transmute::Assume,
879878
) -> Result<Certainty, NoSolution> {
880879
use rustc_transmute::Answer;
881880
// FIXME(transmutability): This really should be returning nested goals for `Answer::If*`
882881
match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable(
883882
ObligationCause::dummy(),
884883
src_and_dst,
885-
scope,
886884
assume,
887885
) {
888886
Answer::Yes => Ok(Certainty::Yes),

compiler/rustc_trait_selection/src/solve/trait_goals.rs

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

545545
let Some(assume) =
546-
rustc_transmute::Assume::from_const(ecx.tcx(), goal.param_env, args.const_at(3))
546+
rustc_transmute::Assume::from_const(ecx.tcx(), goal.param_env, args.const_at(2))
547547
else {
548548
return Err(NoSolution);
549549
};
550550

551551
let certainty = ecx.is_transmutable(
552552
rustc_transmute::Types { dst: args.type_at(0), src: args.type_at(1) },
553-
args.type_at(2),
554553
assume,
555554
)?;
556555
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
@@ -2970,11 +2970,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
29702970
dst: trait_ref.args.type_at(0),
29712971
src: trait_ref.args.type_at(1),
29722972
};
2973-
let scope = trait_ref.args.type_at(2);
29742973
let Some(assume) = rustc_transmute::Assume::from_const(
29752974
self.infcx.tcx,
29762975
obligation.param_env,
2977-
trait_ref.args.const_at(3),
2976+
trait_ref.args.const_at(2),
29782977
) else {
29792978
self.dcx().span_delayed_bug(
29802979
span,
@@ -2986,15 +2985,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
29862985
match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable(
29872986
obligation.cause,
29882987
src_and_dst,
2989-
scope,
29902988
assume,
29912989
) {
29922990
Answer::No(reason) => {
29932991
let dst = trait_ref.args.type_at(0);
29942992
let src = trait_ref.args.type_at(1);
2995-
let err_msg = format!(
2996-
"`{src}` cannot be safely transmuted into `{dst}` in the defining scope of `{scope}`"
2997-
);
2993+
let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`");
29982994
let safe_transmute_explanation = match reason {
29992995
rustc_transmute::Reason::SrcIsUnspecified => {
30002996
format!("`{src}` does not have a well-specified layout")
@@ -3008,9 +3004,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
30083004
format!("At least one value of `{src}` isn't a bit-valid value of `{dst}`")
30093005
}
30103006

3011-
rustc_transmute::Reason::DstIsPrivate => format!(
3012-
"`{dst}` is or contains a type or field that is not visible in that scope"
3013-
),
3007+
rustc_transmute::Reason::DstMayHaveSafetyInvariants => {
3008+
format!("`{dst}` may carry safety invariants")
3009+
}
30143010
rustc_transmute::Reason::DstIsTooBig => {
30153011
format!("The size of `{src}` is smaller than the size of `{dst}`")
30163012
}

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

+2-5
Original file line numberDiff line numberDiff line change
@@ -310,16 +310,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
310310
.collect(),
311311
Condition::IfTransmutable { src, dst } => {
312312
let trait_def_id = obligation.predicate.def_id();
313-
let scope = predicate.trait_ref.args.type_at(2);
314-
let assume_const = predicate.trait_ref.args.const_at(3);
313+
let assume_const = predicate.trait_ref.args.const_at(2);
315314
let make_obl = |from_ty, to_ty| {
316315
let trait_ref1 = ty::TraitRef::new(
317316
tcx,
318317
trait_def_id,
319318
[
320319
ty::GenericArg::from(to_ty),
321320
ty::GenericArg::from(from_ty),
322-
ty::GenericArg::from(scope),
323321
ty::GenericArg::from(assume_const),
324322
],
325323
);
@@ -355,7 +353,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
355353
let Some(assume) = rustc_transmute::Assume::from_const(
356354
self.infcx.tcx,
357355
obligation.param_env,
358-
predicate.trait_ref.args.const_at(3),
356+
predicate.trait_ref.args.const_at(2),
359357
) else {
360358
return Err(Unimplemented);
361359
};
@@ -367,7 +365,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
367365
let maybe_transmutable = transmute_env.is_transmutable(
368366
obligation.cause.clone(),
369367
rustc_transmute::Types { dst, src },
370-
predicate.trait_ref.args.type_at(2),
371368
assume,
372369
);
373370

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
@@ -49,8 +49,8 @@ pub enum Reason {
4949
DstIsUnspecified,
5050
/// The layout of the destination type is bit-incompatible with the source type.
5151
DstIsBitIncompatible,
52-
/// There aren't any public constructors for `Dst`.
53-
DstIsPrivate,
52+
/// The destination type may carry safety invariants.
53+
DstMayHaveSafetyInvariants,
5454
/// `Dst` is larger than `Src`, and the excess bytes were not exclusively uninitialized.
5555
DstIsTooBig,
5656
/// Src should have a stricter alignment than Dst, but it does not.
@@ -106,13 +106,11 @@ mod rustc {
106106
&mut self,
107107
cause: ObligationCause<'tcx>,
108108
types: Types<'tcx>,
109-
scope: Ty<'tcx>,
110109
assume: crate::Assume,
111110
) -> crate::Answer<crate::layout::rustc::Ref<'tcx>> {
112111
crate::maybe_transmutable::MaybeTransmutableQuery::new(
113112
types.src,
114113
types.dst,
115-
scope,
116114
assume,
117115
self.infcx.tcx,
118116
)

0 commit comments

Comments
 (0)