Skip to content

Commit 8521a8c

Browse files
committed
Auto merge of #100726 - jswrenn:transmute, r=oli-obk
safe transmute: use `Assume` struct to provide analysis options This task was left as a TODO in #92268; resolving it brings [`BikeshedIntrinsicFrom`](https://doc.rust-lang.org/nightly/core/mem/trait.BikeshedIntrinsicFrom.html) more in line with the API defined in [MCP411](rust-lang/compiler-team#411). **Before:** ```rust pub unsafe trait BikeshedIntrinsicFrom< Src, Context, const ASSUME_ALIGNMENT: bool, const ASSUME_LIFETIMES: bool, const ASSUME_VALIDITY: bool, const ASSUME_VISIBILITY: bool, > where Src: ?Sized, {} ``` **After:** ```rust pub unsafe trait BikeshedIntrinsicFrom<Src, Context, const ASSUME: Assume = { Assume::NOTHING }> where Src: ?Sized, {} ``` `Assume::visibility` has also been renamed to `Assume::safety`, as library safety invariants are what's actually being assumed; visibility is just the mechanism by which it is currently checked (and that may change). r? `@oli-obk` --- Related: - rust-lang/compiler-team#411 - #99571
2 parents c2d140b + fbcc038 commit 8521a8c

File tree

79 files changed

+1319
-734
lines changed

Some content is hidden

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

79 files changed

+1319
-734
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -4234,6 +4234,7 @@ version = "0.1.0"
42344234
dependencies = [
42354235
"itertools",
42364236
"rustc_data_structures",
4237+
"rustc_hir",
42374238
"rustc_infer",
42384239
"rustc_macros",
42394240
"rustc_middle",

compiler/rustc_hir/src/lang_items.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,8 @@ language_item_table! {
193193
DispatchFromDyn, sym::dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1);
194194

195195
// language items relating to transmutability
196-
TransmuteTrait, sym::transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(6);
196+
TransmuteOpts, sym::transmute_opts, transmute_opts, Target::Struct, GenericRequirement::Exact(0);
197+
TransmuteTrait, sym::transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(3);
197198

198199
Add(Op), sym::add, add_trait, Target::Trait, GenericRequirement::Exact(1);
199200
Sub(Op), sym::sub, sub_trait, Target::Trait, GenericRequirement::Exact(1);

compiler/rustc_span/src/symbol.rs

+5
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@ symbols! {
339339
alias,
340340
align,
341341
align_offset,
342+
alignment,
342343
alignstack,
343344
all,
344345
alloc,
@@ -866,6 +867,7 @@ symbols! {
866867
lib,
867868
libc,
868869
lifetime,
870+
lifetimes,
869871
likely,
870872
line,
871873
line_macro,
@@ -1294,6 +1296,7 @@ symbols! {
12941296
rustfmt,
12951297
rvalue_static_promotion,
12961298
s,
1299+
safety,
12971300
sanitize,
12981301
sanitizer_runtime,
12991302
saturating_add,
@@ -1478,6 +1481,7 @@ symbols! {
14781481
trait_alias,
14791482
trait_upcasting,
14801483
transmute,
1484+
transmute_opts,
14811485
transmute_trait,
14821486
transparent,
14831487
transparent_enums,
@@ -1573,6 +1577,7 @@ symbols! {
15731577
va_list,
15741578
va_start,
15751579
val,
1580+
validity,
15761581
values,
15771582
var,
15781583
variant_count,

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

+4-16
Original file line numberDiff line numberDiff line change
@@ -279,29 +279,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
279279
let predicate = obligation.predicate;
280280

281281
let type_at = |i| predicate.map_bound(|p| p.trait_ref.substs.type_at(i));
282-
let bool_at = |i| {
283-
predicate
284-
.skip_binder()
285-
.trait_ref
286-
.substs
287-
.const_at(i)
288-
.try_eval_bool(self.tcx(), obligation.param_env)
289-
.unwrap_or(true)
290-
};
282+
let const_at = |i| predicate.skip_binder().trait_ref.substs.const_at(i);
291283

292284
let src_and_dst = predicate.map_bound(|p| rustc_transmute::Types {
293-
src: p.trait_ref.substs.type_at(1),
294285
dst: p.trait_ref.substs.type_at(0),
286+
src: p.trait_ref.substs.type_at(1),
295287
});
296288

297289
let scope = type_at(2).skip_binder();
298290

299-
let assume = rustc_transmute::Assume {
300-
alignment: bool_at(3),
301-
lifetimes: bool_at(4),
302-
validity: bool_at(5),
303-
visibility: bool_at(6),
304-
};
291+
let assume =
292+
rustc_transmute::Assume::from_const(self.infcx.tcx, obligation.param_env, const_at(3));
305293

306294
let cause = obligation.cause.clone();
307295

compiler/rustc_transmute/Cargo.toml

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ edition = "2021"
77

88
[dependencies]
99
tracing = "0.1"
10-
rustc_data_structures = { path = "../rustc_data_structures", optional = true}
10+
rustc_data_structures = { path = "../rustc_data_structures"}
11+
rustc_hir = { path = "../rustc_hir", optional = true}
1112
rustc_infer = { path = "../rustc_infer", optional = true}
1213
rustc_macros = { path = "../rustc_macros", optional = true}
1314
rustc_middle = { path = "../rustc_middle", optional = true}
@@ -17,7 +18,7 @@ rustc_target = { path = "../rustc_target", optional = true}
1718
[features]
1819
rustc = [
1920
"rustc_middle",
20-
"rustc_data_structures",
21+
"rustc_hir",
2122
"rustc_infer",
2223
"rustc_macros",
2324
"rustc_span",

compiler/rustc_transmute/src/layout/dfa.rs

-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ where
104104
}
105105

106106
#[instrument(level = "debug")]
107-
#[cfg_attr(feature = "rustc", allow(rustc::potential_query_instability))]
108107
pub(crate) fn from_nfa(nfa: Nfa<R>) -> Self {
109108
let Nfa { transitions: nfa_transitions, start: nfa_start, accepting: nfa_accepting } = nfa;
110109

compiler/rustc_transmute/src/layout/nfa.rs

-6
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,6 @@ where
119119

120120
let mut transitions: Map<State, Map<Transition<R>, Set<State>>> = self.transitions;
121121

122-
// the iteration order doesn't matter
123-
#[cfg_attr(feature = "rustc", allow(rustc::potential_query_instability))]
124122
for (source, transition) in other.transitions {
125123
let fix_state = |state| if state == other.start { self.accepting } else { state };
126124
let entry = transitions.entry(fix_state(source)).or_default();
@@ -142,8 +140,6 @@ where
142140

143141
let mut transitions: Map<State, Map<Transition<R>, Set<State>>> = self.transitions.clone();
144142

145-
// the iteration order doesn't matter
146-
#[cfg_attr(feature = "rustc", allow(rustc::potential_query_instability))]
147143
for (&(mut source), transition) in other.transitions.iter() {
148144
// if source is starting state of `other`, replace with starting state of `self`
149145
if source == other.start {
@@ -152,8 +148,6 @@ where
152148
let entry = transitions.entry(source).or_default();
153149
for (edge, destinations) in transition {
154150
let entry = entry.entry(edge.clone()).or_default();
155-
// the iteration order doesn't matter
156-
#[cfg_attr(feature = "rustc", allow(rustc::potential_query_instability))]
157151
for &(mut destination) in destinations {
158152
// if dest is accepting state of `other`, replace with accepting state of `self`
159153
if destination == other.accepting {

compiler/rustc_transmute/src/lib.rs

+56-6
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,7 @@
66
#[macro_use]
77
extern crate tracing;
88

9-
#[cfg(feature = "rustc")]
10-
pub(crate) use rustc_data_structures::fx::{FxHashMap as Map, FxHashSet as Set};
11-
12-
#[cfg(not(feature = "rustc"))]
13-
pub(crate) use std::collections::{HashMap as Map, HashSet as Set};
9+
pub(crate) use rustc_data_structures::fx::{FxIndexMap as Map, FxIndexSet as Set};
1410

1511
pub(crate) mod layout;
1612
pub(crate) mod maybe_transmutable;
@@ -19,8 +15,8 @@ pub(crate) mod maybe_transmutable;
1915
pub struct Assume {
2016
pub alignment: bool,
2117
pub lifetimes: bool,
18+
pub safety: bool,
2219
pub validity: bool,
23-
pub visibility: bool,
2420
}
2521

2622
/// The type encodes answers to the question: "Are these types transmutable?"
@@ -62,11 +58,17 @@ pub enum Reason {
6258

6359
#[cfg(feature = "rustc")]
6460
mod rustc {
61+
use super::*;
62+
63+
use rustc_hir::lang_items::LangItem;
6564
use rustc_infer::infer::InferCtxt;
6665
use rustc_macros::{TypeFoldable, TypeVisitable};
6766
use rustc_middle::traits::ObligationCause;
6867
use rustc_middle::ty::Binder;
68+
use rustc_middle::ty::Const;
69+
use rustc_middle::ty::ParamEnv;
6970
use rustc_middle::ty::Ty;
71+
use rustc_middle::ty::TyCtxt;
7072

7173
/// The source and destination types of a transmutation.
7274
#[derive(TypeFoldable, TypeVisitable, Debug, Clone, Copy)]
@@ -106,6 +108,54 @@ mod rustc {
106108
.answer()
107109
}
108110
}
111+
112+
impl Assume {
113+
/// Constructs an `Assume` from a given const-`Assume`.
114+
pub fn from_const<'tcx>(
115+
tcx: TyCtxt<'tcx>,
116+
param_env: ParamEnv<'tcx>,
117+
c: Const<'tcx>,
118+
) -> Self {
119+
use rustc_middle::ty::ScalarInt;
120+
use rustc_middle::ty::TypeVisitable;
121+
use rustc_span::symbol::sym;
122+
123+
let c = c.eval(tcx, param_env);
124+
125+
if let Some(err) = c.error_reported() {
126+
return Self { alignment: true, lifetimes: true, safety: true, validity: true };
127+
}
128+
129+
let adt_def = c.ty().ty_adt_def().expect("The given `Const` must be an ADT.");
130+
131+
assert_eq!(
132+
tcx.require_lang_item(LangItem::TransmuteOpts, None),
133+
adt_def.did(),
134+
"The given `Const` was not marked with the `{}` lang item.",
135+
LangItem::TransmuteOpts.name(),
136+
);
137+
138+
let variant = adt_def.non_enum_variant();
139+
let fields = c.to_valtree().unwrap_branch();
140+
141+
let get_field = |name| {
142+
let (field_idx, _) = variant
143+
.fields
144+
.iter()
145+
.enumerate()
146+
.find(|(_, field_def)| name == field_def.name)
147+
.expect(&format!("There were no fields named `{name}`."));
148+
fields[field_idx].unwrap_leaf() == ScalarInt::TRUE
149+
};
150+
151+
Self {
152+
alignment: get_field(sym::alignment),
153+
lifetimes: get_field(sym::lifetimes),
154+
safety: get_field(sym::safety),
155+
validity: get_field(sym::validity),
156+
}
157+
}
158+
}
109159
}
110160

111161
#[cfg(feature = "rustc")]

compiler/rustc_transmute/src/maybe_transmutable/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ where
105105
#[inline(always)]
106106
#[instrument(level = "debug", skip(self), fields(src = ?self.src, dst = ?self.dst))]
107107
pub(crate) fn answer(self) -> Answer<<C as QueryContext>::Ref> {
108-
let assume_visibility = self.assume.visibility;
108+
let assume_visibility = self.assume.safety;
109109
let query_or_answer = self.map_layouts(|src, dst, scope, context| {
110110
// Remove all `Def` nodes from `src`, without checking their visibility.
111111
let src = src.prune(&|def| true);

compiler/rustc_transmute/src/maybe_transmutable/tests.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ mod bool {
1313
layout::Tree::<Def, !>::bool(),
1414
layout::Tree::<Def, !>::bool(),
1515
(),
16-
crate::Assume { alignment: false, lifetimes: false, validity: true, visibility: false },
16+
crate::Assume { alignment: false, lifetimes: false, validity: true, safety: false },
1717
UltraMinimal,
1818
)
1919
.answer();
@@ -26,7 +26,7 @@ mod bool {
2626
layout::Dfa::<!>::bool(),
2727
layout::Dfa::<!>::bool(),
2828
(),
29-
crate::Assume { alignment: false, lifetimes: false, validity: true, visibility: false },
29+
crate::Assume { alignment: false, lifetimes: false, validity: true, safety: false },
3030
UltraMinimal,
3131
)
3232
.answer();

library/core/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
#![warn(missing_debug_implementations)]
9494
#![warn(missing_docs)]
9595
#![allow(explicit_outlives_requirements)]
96+
#![allow(incomplete_features)]
9697
//
9798
// Library features:
9899
#![feature(const_align_offset)]
@@ -160,6 +161,7 @@
160161
//
161162
// Language features:
162163
#![feature(abi_unadjusted)]
164+
#![feature(adt_const_params)]
163165
#![feature(allow_internal_unsafe)]
164166
#![feature(allow_internal_unstable)]
165167
#![feature(associated_type_bounds)]

library/core/src/mem/transmutability.rs

+76-12
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,20 @@
44
/// any value of type `Self` are safely transmutable into a value of type `Dst`, in a given `Context`,
55
/// notwithstanding whatever safety checks you have asked the compiler to [`Assume`] are satisfied.
66
#[unstable(feature = "transmutability", issue = "99571")]
7-
#[lang = "transmute_trait"]
7+
#[cfg_attr(not(bootstrap), lang = "transmute_trait")]
88
#[rustc_on_unimplemented(
99
message = "`{Src}` cannot be safely transmuted into `{Self}` in the defining scope of `{Context}`.",
1010
label = "`{Src}` cannot be safely transmuted into `{Self}` in the defining scope of `{Context}`."
1111
)]
12-
pub unsafe trait BikeshedIntrinsicFrom<
13-
Src,
14-
Context,
15-
const ASSUME_ALIGNMENT: bool,
16-
const ASSUME_LIFETIMES: bool,
17-
const ASSUME_VALIDITY: bool,
18-
const ASSUME_VISIBILITY: bool,
19-
> where
12+
pub unsafe trait BikeshedIntrinsicFrom<Src, Context, const ASSUME: Assume = { Assume::NOTHING }>
13+
where
2014
Src: ?Sized,
2115
{
2216
}
2317

2418
/// What transmutation safety conditions shall the compiler assume that *you* are checking?
2519
#[unstable(feature = "transmutability", issue = "99571")]
20+
#[cfg_attr(not(bootstrap), lang = "transmute_opts")]
2621
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
2722
pub struct Assume {
2823
/// When `true`, the compiler assumes that *you* are ensuring (either dynamically or statically) that
@@ -33,11 +28,80 @@ pub struct Assume {
3328
/// that violates Rust's memory model.
3429
pub lifetimes: bool,
3530

31+
/// When `true`, the compiler assumes that *you* have ensured that it is safe for you to violate the
32+
/// type and field privacy of the destination type (and sometimes of the source type, too).
33+
pub safety: bool,
34+
3635
/// When `true`, the compiler assumes that *you* are ensuring that the source type is actually a valid
3736
/// instance of the destination type.
3837
pub validity: bool,
38+
}
3939

40-
/// When `true`, the compiler assumes that *you* have ensured that it is safe for you to violate the
41-
/// type and field privacy of the destination type (and sometimes of the source type, too).
42-
pub visibility: bool,
40+
impl Assume {
41+
/// Do not assume that *you* have ensured any safety properties are met.
42+
#[unstable(feature = "transmutability", issue = "99571")]
43+
pub const NOTHING: Self =
44+
Self { alignment: false, lifetimes: false, safety: false, validity: false };
45+
46+
/// Assume only that alignment conditions are met.
47+
#[unstable(feature = "transmutability", issue = "99571")]
48+
pub const ALIGNMENT: Self = Self { alignment: true, ..Self::NOTHING };
49+
50+
/// Assume only that lifetime conditions are met.
51+
#[unstable(feature = "transmutability", issue = "99571")]
52+
pub const LIFETIMES: Self = Self { lifetimes: true, ..Self::NOTHING };
53+
54+
/// Assume only that safety conditions are met.
55+
#[unstable(feature = "transmutability", issue = "99571")]
56+
pub const SAFETY: Self = Self { safety: true, ..Self::NOTHING };
57+
58+
/// Assume only that dynamically-satisfiable validity conditions are met.
59+
#[unstable(feature = "transmutability", issue = "99571")]
60+
pub const VALIDITY: Self = Self { validity: true, ..Self::NOTHING };
61+
62+
/// Assume both `self` and `other_assumptions`.
63+
#[unstable(feature = "transmutability", issue = "99571")]
64+
pub const fn and(self, other_assumptions: Self) -> Self {
65+
Self {
66+
alignment: self.alignment || other_assumptions.alignment,
67+
lifetimes: self.lifetimes || other_assumptions.lifetimes,
68+
safety: self.safety || other_assumptions.safety,
69+
validity: self.validity || other_assumptions.validity,
70+
}
71+
}
72+
73+
/// Assume `self`, excepting `other_assumptions`.
74+
#[unstable(feature = "transmutability", issue = "99571")]
75+
pub const fn but_not(self, other_assumptions: Self) -> Self {
76+
Self {
77+
alignment: self.alignment && !other_assumptions.alignment,
78+
lifetimes: self.lifetimes && !other_assumptions.lifetimes,
79+
safety: self.safety && !other_assumptions.safety,
80+
validity: self.validity && !other_assumptions.validity,
81+
}
82+
}
83+
}
84+
85+
// FIXME(jswrenn): This const op is not actually usable. Why?
86+
// https://github.com/rust-lang/rust/pull/100726#issuecomment-1219928926
87+
#[unstable(feature = "transmutability", issue = "99571")]
88+
#[rustc_const_unstable(feature = "transmutability", issue = "99571")]
89+
impl const core::ops::Add for Assume {
90+
type Output = Assume;
91+
92+
fn add(self, other_assumptions: Assume) -> Assume {
93+
self.and(other_assumptions)
94+
}
95+
}
96+
97+
// FIXME(jswrenn): This const op is not actually usable. Why?
98+
// https://github.com/rust-lang/rust/pull/100726#issuecomment-1219928926
99+
#[unstable(feature = "transmutability", issue = "99571")]
100+
#[rustc_const_unstable(feature = "transmutability", issue = "99571")]
101+
impl const core::ops::Sub for Assume {
102+
type Output = Assume;
103+
104+
fn sub(self, other_assumptions: Assume) -> Assume {
105+
self.but_not(other_assumptions)
106+
}
43107
}

0 commit comments

Comments
 (0)