Skip to content

Commit e059b8b

Browse files
committed
transmutability: shift abstraction boundary
Previously, `rustc_transmute`'s layout representations were genericized over `R`, a reference. Now, it's instead genericized over representations of type and region. This allows us to move reference transmutability logic from `rustc_trait_selection` to `rustc_transmutability` (and thus unit test it independently of the compiler), and — in a follow-up PR — will make it possible to support analyzing function pointer transmutability with minimal surgery.
1 parent 28174fc commit e059b8b

File tree

14 files changed

+384
-357
lines changed

14 files changed

+384
-357
lines changed

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

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2553,32 +2553,31 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
25532553
rustc_transmute::Reason::SrcIsNotYetSupported => {
25542554
format!("analyzing the transmutability of `{src}` is not yet supported")
25552555
}
2556-
25572556
rustc_transmute::Reason::DstIsNotYetSupported => {
25582557
format!("analyzing the transmutability of `{dst}` is not yet supported")
25592558
}
2560-
25612559
rustc_transmute::Reason::DstIsBitIncompatible => {
25622560
format!(
25632561
"at least one value of `{src}` isn't a bit-valid value of `{dst}`"
25642562
)
25652563
}
2566-
25672564
rustc_transmute::Reason::DstUninhabited => {
25682565
format!("`{dst}` is uninhabited")
25692566
}
2570-
25712567
rustc_transmute::Reason::DstMayHaveSafetyInvariants => {
25722568
format!("`{dst}` may carry safety invariants")
25732569
}
25742570
rustc_transmute::Reason::DstIsTooBig => {
25752571
format!("the size of `{src}` is smaller than the size of `{dst}`")
25762572
}
2577-
rustc_transmute::Reason::DstRefIsTooBig { src, dst } => {
2578-
let src_size = src.size;
2579-
let dst_size = dst.size;
2573+
rustc_transmute::Reason::DstRefIsTooBig {
2574+
src,
2575+
src_size,
2576+
dst,
2577+
dst_size,
2578+
} => {
25802579
format!(
2581-
"the referent size of `{src}` ({src_size} bytes) \
2580+
"the size of `{src}` ({src_size} bytes) \
25822581
is smaller than that of `{dst}` ({dst_size} bytes)"
25832582
)
25842583
}

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

Lines changed: 46 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,12 @@
99
1010
use std::ops::ControlFlow;
1111

12-
use rustc_ast::Mutability;
1312
use rustc_data_structures::stack::ensure_sufficient_stack;
1413
use rustc_hir::lang_items::LangItem;
1514
use rustc_infer::infer::{DefineOpaqueTypes, HigherRankedType, InferOk};
1615
use rustc_infer::traits::ObligationCauseCode;
1716
use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData};
18-
use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, Upcast, elaborate};
17+
use rustc_middle::ty::{self, GenericArgsRef, Region, Ty, TyCtxt, Upcast, elaborate};
1918
use rustc_middle::{bug, span_bug};
2019
use rustc_span::def_id::DefId;
2120
use thin_vec::thin_vec;
@@ -291,99 +290,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
291290
) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> {
292291
use rustc_transmute::{Answer, Assume, Condition};
293292

294-
/// Generate sub-obligations for reference-to-reference transmutations.
295-
fn reference_obligations<'tcx>(
296-
tcx: TyCtxt<'tcx>,
297-
obligation: &PolyTraitObligation<'tcx>,
298-
(src_lifetime, src_ty, src_mut): (ty::Region<'tcx>, Ty<'tcx>, Mutability),
299-
(dst_lifetime, dst_ty, dst_mut): (ty::Region<'tcx>, Ty<'tcx>, Mutability),
300-
assume: Assume,
301-
) -> PredicateObligations<'tcx> {
302-
let make_transmute_obl = |src, dst| {
303-
let transmute_trait = obligation.predicate.def_id();
304-
let assume = obligation.predicate.skip_binder().trait_ref.args.const_at(2);
305-
let trait_ref = ty::TraitRef::new(
306-
tcx,
307-
transmute_trait,
308-
[
309-
ty::GenericArg::from(dst),
310-
ty::GenericArg::from(src),
311-
ty::GenericArg::from(assume),
312-
],
313-
);
314-
Obligation::with_depth(
315-
tcx,
316-
obligation.cause.clone(),
317-
obligation.recursion_depth + 1,
318-
obligation.param_env,
319-
trait_ref,
320-
)
321-
};
322-
323-
let make_freeze_obl = |ty| {
324-
let trait_ref = ty::TraitRef::new(
325-
tcx,
326-
tcx.require_lang_item(LangItem::Freeze, None),
327-
[ty::GenericArg::from(ty)],
328-
);
329-
Obligation::with_depth(
330-
tcx,
331-
obligation.cause.clone(),
332-
obligation.recursion_depth + 1,
333-
obligation.param_env,
334-
trait_ref,
335-
)
336-
};
337-
338-
let make_outlives_obl = |target, region| {
339-
let outlives = ty::OutlivesPredicate(target, region);
340-
Obligation::with_depth(
341-
tcx,
342-
obligation.cause.clone(),
343-
obligation.recursion_depth + 1,
344-
obligation.param_env,
345-
outlives,
346-
)
347-
};
348-
349-
// Given a transmutation from `&'a (mut) Src` and `&'dst (mut) Dst`,
350-
// it is always the case that `Src` must be transmutable into `Dst`,
351-
// and that that `'src` must outlive `'dst`.
352-
let mut obls = PredicateObligations::with_capacity(1);
353-
obls.push(make_transmute_obl(src_ty, dst_ty));
354-
if !assume.lifetimes {
355-
obls.push(make_outlives_obl(src_lifetime, dst_lifetime));
356-
}
357-
358-
// Given a transmutation from `&Src`, both `Src` and `Dst` must be
359-
// `Freeze`, otherwise, using the transmuted value could lead to
360-
// data races.
361-
if src_mut == Mutability::Not {
362-
obls.extend([make_freeze_obl(src_ty), make_freeze_obl(dst_ty)])
363-
}
364-
365-
// Given a transmutation into `&'dst mut Dst`, it also must be the
366-
// case that `Dst` is transmutable into `Src`. For example,
367-
// transmuting bool -> u8 is OK as long as you can't update that u8
368-
// to be > 1, because you could later transmute the u8 back to a
369-
// bool and get undefined behavior. It also must be the case that
370-
// `'dst` lives exactly as long as `'src`.
371-
if dst_mut == Mutability::Mut {
372-
obls.push(make_transmute_obl(dst_ty, src_ty));
373-
if !assume.lifetimes {
374-
obls.push(make_outlives_obl(dst_lifetime, src_lifetime));
375-
}
376-
}
377-
378-
obls
379-
}
380-
381293
/// Flatten the `Condition` tree into a conjunction of obligations.
382294
#[instrument(level = "debug", skip(tcx, obligation))]
383295
fn flatten_answer_tree<'tcx>(
384296
tcx: TyCtxt<'tcx>,
385297
obligation: &PolyTraitObligation<'tcx>,
386-
cond: Condition<rustc_transmute::layout::rustc::Ref<'tcx>>,
298+
cond: Condition<Region<'tcx>, Ty<'tcx>>,
387299
assume: Assume,
388300
) -> PredicateObligations<'tcx> {
389301
match cond {
@@ -393,13 +305,50 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
393305
.into_iter()
394306
.flat_map(|cond| flatten_answer_tree(tcx, obligation, cond, assume))
395307
.collect(),
396-
Condition::IfTransmutable { src, dst } => reference_obligations(
397-
tcx,
398-
obligation,
399-
(src.lifetime, src.ty, src.mutability),
400-
(dst.lifetime, dst.ty, dst.mutability),
401-
assume,
402-
),
308+
Condition::Immutable { ty } => {
309+
let trait_ref = ty::TraitRef::new(
310+
tcx,
311+
tcx.require_lang_item(LangItem::Freeze, None),
312+
[ty::GenericArg::from(ty)],
313+
);
314+
thin_vec![Obligation::with_depth(
315+
tcx,
316+
obligation.cause.clone(),
317+
obligation.recursion_depth + 1,
318+
obligation.param_env,
319+
trait_ref,
320+
)]
321+
}
322+
Condition::Outlives { long, short } => {
323+
let outlives = ty::OutlivesPredicate(long, short);
324+
thin_vec![Obligation::with_depth(
325+
tcx,
326+
obligation.cause.clone(),
327+
obligation.recursion_depth + 1,
328+
obligation.param_env,
329+
outlives,
330+
)]
331+
}
332+
Condition::Transmutable { src, dst } => {
333+
let transmute_trait = obligation.predicate.def_id();
334+
let assume = obligation.predicate.skip_binder().trait_ref.args.const_at(2);
335+
let trait_ref = ty::TraitRef::new(
336+
tcx,
337+
transmute_trait,
338+
[
339+
ty::GenericArg::from(dst),
340+
ty::GenericArg::from(src),
341+
ty::GenericArg::from(assume),
342+
],
343+
);
344+
thin_vec![Obligation::with_depth(
345+
tcx,
346+
obligation.cause.clone(),
347+
obligation.recursion_depth + 1,
348+
obligation.param_env,
349+
trait_ref,
350+
)]
351+
}
403352
}
404353
}
405354

compiler/rustc_transmute/src/layout/dfa.rs

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,35 @@ use std::fmt;
22
use std::iter::Peekable;
33
use std::sync::atomic::{AtomicU32, Ordering};
44

5-
use super::{Byte, Ref, Tree, Uninhabited};
5+
use super::{Byte, Reference, Region, Tree, Type, Uninhabited};
66
use crate::{Map, Set};
77

88
#[derive(PartialEq)]
99
#[cfg_attr(test, derive(Clone))]
10-
pub(crate) struct Dfa<R>
10+
pub(crate) struct Dfa<R, T>
1111
where
12-
R: Ref,
12+
R: Region,
13+
T: Type,
1314
{
14-
pub(crate) transitions: Map<State, Transitions<R>>,
15+
pub(crate) transitions: Map<State, Transitions<R, T>>,
1516
pub(crate) start: State,
1617
pub(crate) accept: State,
1718
}
1819

1920
#[derive(PartialEq, Clone, Debug)]
20-
pub(crate) struct Transitions<R>
21+
pub(crate) struct Transitions<R, T>
2122
where
22-
R: Ref,
23+
R: Region,
24+
T: Type,
2325
{
2426
byte_transitions: EdgeSet<State>,
25-
ref_transitions: Map<R, State>,
27+
ref_transitions: Map<Reference<R, T>, State>,
2628
}
2729

28-
impl<R> Default for Transitions<R>
30+
impl<R, T> Default for Transitions<R, T>
2931
where
30-
R: Ref,
32+
R: Region,
33+
T: Type,
3134
{
3235
fn default() -> Self {
3336
Self { byte_transitions: EdgeSet::empty(), ref_transitions: Map::default() }
@@ -51,9 +54,10 @@ impl fmt::Debug for State {
5154
}
5255
}
5356

54-
impl<R> Dfa<R>
57+
impl<R, T> Dfa<R, T>
5558
where
56-
R: Ref,
59+
R: Region,
60+
T: Type,
5761
{
5862
#[cfg(test)]
5963
pub(crate) fn bool() -> Self {
@@ -64,7 +68,7 @@ where
6468
}
6569

6670
pub(crate) fn unit() -> Self {
67-
let transitions: Map<State, Transitions<R>> = Map::default();
71+
let transitions: Map<State, Transitions<R, T>> = Map::default();
6872
let start = State::new();
6973
let accept = start;
7074

@@ -78,21 +82,21 @@ where
7882
})
7983
}
8084

81-
pub(crate) fn from_ref(r: R) -> Self {
85+
pub(crate) fn from_ref(r: Reference<R, T>) -> Self {
8286
Self::from_transitions(|accept| Transitions {
8387
byte_transitions: EdgeSet::empty(),
8488
ref_transitions: [(r, accept)].into_iter().collect(),
8589
})
8690
}
8791

88-
fn from_transitions(f: impl FnOnce(State) -> Transitions<R>) -> Self {
92+
fn from_transitions(f: impl FnOnce(State) -> Transitions<R, T>) -> Self {
8993
let start = State::new();
9094
let accept = State::new();
9195

9296
Self { transitions: [(start, f(accept))].into_iter().collect(), start, accept }
9397
}
9498

95-
pub(crate) fn from_tree(tree: Tree<!, R>) -> Result<Self, Uninhabited> {
99+
pub(crate) fn from_tree(tree: Tree<!, R, T>) -> Result<Self, Uninhabited> {
96100
Ok(match tree {
97101
Tree::Byte(b) => Self::from_byte(b),
98102
Tree::Ref(r) => Self::from_ref(r),
@@ -125,7 +129,7 @@ where
125129
let start = self.start;
126130
let accept = other.accept;
127131

128-
let mut transitions: Map<State, Transitions<R>> = self.transitions;
132+
let mut transitions: Map<State, Transitions<R, T>> = self.transitions;
129133

130134
for (source, transition) in other.transitions {
131135
let fix_state = |state| if state == other.start { self.accept } else { state };
@@ -169,7 +173,7 @@ where
169173
};
170174

171175
let start = mapped((Some(a.start), Some(b.start)));
172-
let mut transitions: Map<State, Transitions<R>> = Map::default();
176+
let mut transitions: Map<State, Transitions<R, T>> = Map::default();
173177
let empty_transitions = Transitions::default();
174178

175179
struct WorkQueue {
@@ -257,7 +261,7 @@ where
257261
.flat_map(|transitions| transitions.byte_transitions.iter())
258262
}
259263

260-
pub(crate) fn refs_from(&self, start: State) -> impl Iterator<Item = (R, State)> {
264+
pub(crate) fn refs_from(&self, start: State) -> impl Iterator<Item = (Reference<R, T>, State)> {
261265
self.transitions
262266
.get(&start)
263267
.into_iter()
@@ -297,9 +301,10 @@ where
297301
}
298302

299303
/// Serialize the DFA using the Graphviz DOT format.
300-
impl<R> fmt::Debug for Dfa<R>
304+
impl<R, T> fmt::Debug for Dfa<R, T>
301305
where
302-
R: Ref,
306+
R: Region,
307+
T: Type,
303308
{
304309
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
305310
writeln!(f, "digraph {{")?;

0 commit comments

Comments
 (0)