Skip to content

Commit

Permalink
Auto merge of rust-lang#100726 - jswrenn:transmute, r=oli-obk
Browse files Browse the repository at this point in the history
safe transmute: use `Assume` struct to provide analysis options

This task was left as a TODO in rust-lang#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
- rust-lang#99571
  • Loading branch information
bors committed Sep 4, 2022
2 parents c2d140b + fbcc038 commit 8521a8c
Show file tree
Hide file tree
Showing 79 changed files with 1,319 additions and 734 deletions.
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4234,6 +4234,7 @@ version = "0.1.0"
dependencies = [
"itertools",
"rustc_data_structures",
"rustc_hir",
"rustc_infer",
"rustc_macros",
"rustc_middle",
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,8 @@ language_item_table! {
DispatchFromDyn, sym::dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1);

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

Add(Op), sym::add, add_trait, Target::Trait, GenericRequirement::Exact(1);
Sub(Op), sym::sub, sub_trait, Target::Trait, GenericRequirement::Exact(1);
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ symbols! {
alias,
align,
align_offset,
alignment,
alignstack,
all,
alloc,
Expand Down Expand Up @@ -866,6 +867,7 @@ symbols! {
lib,
libc,
lifetime,
lifetimes,
likely,
line,
line_macro,
Expand Down Expand Up @@ -1294,6 +1296,7 @@ symbols! {
rustfmt,
rvalue_static_promotion,
s,
safety,
sanitize,
sanitizer_runtime,
saturating_add,
Expand Down Expand Up @@ -1478,6 +1481,7 @@ symbols! {
trait_alias,
trait_upcasting,
transmute,
transmute_opts,
transmute_trait,
transparent,
transparent_enums,
Expand Down Expand Up @@ -1573,6 +1577,7 @@ symbols! {
va_list,
va_start,
val,
validity,
values,
var,
variant_count,
Expand Down
20 changes: 4 additions & 16 deletions compiler/rustc_trait_selection/src/traits/select/confirmation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,29 +279,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
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 const_at = |i| predicate.skip_binder().trait_ref.substs.const_at(i);

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),
src: p.trait_ref.substs.type_at(1),
});

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 assume =
rustc_transmute::Assume::from_const(self.infcx.tcx, obligation.param_env, const_at(3));

let cause = obligation.cause.clone();

Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_transmute/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ edition = "2021"

[dependencies]
tracing = "0.1"
rustc_data_structures = { path = "../rustc_data_structures", optional = true}
rustc_data_structures = { path = "../rustc_data_structures"}
rustc_hir = { path = "../rustc_hir", optional = true}
rustc_infer = { path = "../rustc_infer", optional = true}
rustc_macros = { path = "../rustc_macros", optional = true}
rustc_middle = { path = "../rustc_middle", optional = true}
Expand All @@ -17,7 +18,7 @@ rustc_target = { path = "../rustc_target", optional = true}
[features]
rustc = [
"rustc_middle",
"rustc_data_structures",
"rustc_hir",
"rustc_infer",
"rustc_macros",
"rustc_span",
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_transmute/src/layout/dfa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@ where
}

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

Expand Down
6 changes: 0 additions & 6 deletions compiler/rustc_transmute/src/layout/nfa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,6 @@ where

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

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

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

// the iteration order doesn't matter
#[cfg_attr(feature = "rustc", allow(rustc::potential_query_instability))]
for (&(mut source), transition) in other.transitions.iter() {
// if source is starting state of `other`, replace with starting state of `self`
if source == other.start {
Expand All @@ -152,8 +148,6 @@ where
let entry = transitions.entry(source).or_default();
for (edge, destinations) in transition {
let entry = entry.entry(edge.clone()).or_default();
// the iteration order doesn't matter
#[cfg_attr(feature = "rustc", allow(rustc::potential_query_instability))]
for &(mut destination) in destinations {
// if dest is accepting state of `other`, replace with accepting state of `self`
if destination == other.accepting {
Expand Down
62 changes: 56 additions & 6 deletions compiler/rustc_transmute/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,7 @@
#[macro_use]
extern crate tracing;

#[cfg(feature = "rustc")]
pub(crate) use rustc_data_structures::fx::{FxHashMap as Map, FxHashSet as Set};

#[cfg(not(feature = "rustc"))]
pub(crate) use std::collections::{HashMap as Map, HashSet as Set};
pub(crate) use rustc_data_structures::fx::{FxIndexMap as Map, FxIndexSet as Set};

pub(crate) mod layout;
pub(crate) mod maybe_transmutable;
Expand All @@ -19,8 +15,8 @@ pub(crate) mod maybe_transmutable;
pub struct Assume {
pub alignment: bool,
pub lifetimes: bool,
pub safety: bool,
pub validity: bool,
pub visibility: bool,
}

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

#[cfg(feature = "rustc")]
mod rustc {
use super::*;

use rustc_hir::lang_items::LangItem;
use rustc_infer::infer::InferCtxt;
use rustc_macros::{TypeFoldable, TypeVisitable};
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::Binder;
use rustc_middle::ty::Const;
use rustc_middle::ty::ParamEnv;
use rustc_middle::ty::Ty;
use rustc_middle::ty::TyCtxt;

/// The source and destination types of a transmutation.
#[derive(TypeFoldable, TypeVisitable, Debug, Clone, Copy)]
Expand Down Expand Up @@ -106,6 +108,54 @@ mod rustc {
.answer()
}
}

impl Assume {
/// Constructs an `Assume` from a given const-`Assume`.
pub fn from_const<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
c: Const<'tcx>,
) -> Self {
use rustc_middle::ty::ScalarInt;
use rustc_middle::ty::TypeVisitable;
use rustc_span::symbol::sym;

let c = c.eval(tcx, param_env);

if let Some(err) = c.error_reported() {
return Self { alignment: true, lifetimes: true, safety: true, validity: true };
}

let adt_def = c.ty().ty_adt_def().expect("The given `Const` must be an ADT.");

assert_eq!(
tcx.require_lang_item(LangItem::TransmuteOpts, None),
adt_def.did(),
"The given `Const` was not marked with the `{}` lang item.",
LangItem::TransmuteOpts.name(),
);

let variant = adt_def.non_enum_variant();
let fields = c.to_valtree().unwrap_branch();

let get_field = |name| {
let (field_idx, _) = variant
.fields
.iter()
.enumerate()
.find(|(_, field_def)| name == field_def.name)
.expect(&format!("There were no fields named `{name}`."));
fields[field_idx].unwrap_leaf() == ScalarInt::TRUE
};

Self {
alignment: get_field(sym::alignment),
lifetimes: get_field(sym::lifetimes),
safety: get_field(sym::safety),
validity: get_field(sym::validity),
}
}
}
}

#[cfg(feature = "rustc")]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_transmute/src/maybe_transmutable/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ where
#[inline(always)]
#[instrument(level = "debug", skip(self), fields(src = ?self.src, dst = ?self.dst))]
pub(crate) fn answer(self) -> Answer<<C as QueryContext>::Ref> {
let assume_visibility = self.assume.visibility;
let assume_visibility = self.assume.safety;
let query_or_answer = self.map_layouts(|src, dst, scope, context| {
// Remove all `Def` nodes from `src`, without checking their visibility.
let src = src.prune(&|def| true);
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_transmute/src/maybe_transmutable/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ mod bool {
layout::Tree::<Def, !>::bool(),
layout::Tree::<Def, !>::bool(),
(),
crate::Assume { alignment: false, lifetimes: false, validity: true, visibility: false },
crate::Assume { alignment: false, lifetimes: false, validity: true, safety: false },
UltraMinimal,
)
.answer();
Expand All @@ -26,7 +26,7 @@ mod bool {
layout::Dfa::<!>::bool(),
layout::Dfa::<!>::bool(),
(),
crate::Assume { alignment: false, lifetimes: false, validity: true, visibility: false },
crate::Assume { alignment: false, lifetimes: false, validity: true, safety: false },
UltraMinimal,
)
.answer();
Expand Down
2 changes: 2 additions & 0 deletions library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
#![allow(explicit_outlives_requirements)]
#![allow(incomplete_features)]
//
// Library features:
#![feature(const_align_offset)]
Expand Down Expand Up @@ -160,6 +161,7 @@
//
// Language features:
#![feature(abi_unadjusted)]
#![feature(adt_const_params)]
#![feature(allow_internal_unsafe)]
#![feature(allow_internal_unstable)]
#![feature(associated_type_bounds)]
Expand Down
88 changes: 76 additions & 12 deletions library/core/src/mem/transmutability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,20 @@
/// any value of type `Self` are safely transmutable into a value of type `Dst`, in a given `Context`,
/// notwithstanding whatever safety checks you have asked the compiler to [`Assume`] are satisfied.
#[unstable(feature = "transmutability", issue = "99571")]
#[lang = "transmute_trait"]
#[cfg_attr(not(bootstrap), lang = "transmute_trait")]
#[rustc_on_unimplemented(
message = "`{Src}` cannot be safely transmuted into `{Self}` in the defining scope of `{Context}`.",
label = "`{Src}` cannot be safely transmuted into `{Self}` in the defining scope of `{Context}`."
)]
pub unsafe trait BikeshedIntrinsicFrom<
Src,
Context,
const ASSUME_ALIGNMENT: bool,
const ASSUME_LIFETIMES: bool,
const ASSUME_VALIDITY: bool,
const ASSUME_VISIBILITY: bool,
> where
pub unsafe trait BikeshedIntrinsicFrom<Src, Context, const ASSUME: Assume = { Assume::NOTHING }>
where
Src: ?Sized,
{
}

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

/// When `true`, the compiler assumes that *you* have ensured that it is safe for you to violate the
/// type and field privacy of the destination type (and sometimes of the source type, too).
pub safety: bool,

/// When `true`, the compiler assumes that *you* are ensuring that the source type is actually a valid
/// instance of the destination type.
pub validity: bool,
}

/// When `true`, the compiler assumes that *you* have ensured that it is safe for you to violate the
/// type and field privacy of the destination type (and sometimes of the source type, too).
pub visibility: bool,
impl Assume {
/// Do not assume that *you* have ensured any safety properties are met.
#[unstable(feature = "transmutability", issue = "99571")]
pub const NOTHING: Self =
Self { alignment: false, lifetimes: false, safety: false, validity: false };

/// Assume only that alignment conditions are met.
#[unstable(feature = "transmutability", issue = "99571")]
pub const ALIGNMENT: Self = Self { alignment: true, ..Self::NOTHING };

/// Assume only that lifetime conditions are met.
#[unstable(feature = "transmutability", issue = "99571")]
pub const LIFETIMES: Self = Self { lifetimes: true, ..Self::NOTHING };

/// Assume only that safety conditions are met.
#[unstable(feature = "transmutability", issue = "99571")]
pub const SAFETY: Self = Self { safety: true, ..Self::NOTHING };

/// Assume only that dynamically-satisfiable validity conditions are met.
#[unstable(feature = "transmutability", issue = "99571")]
pub const VALIDITY: Self = Self { validity: true, ..Self::NOTHING };

/// Assume both `self` and `other_assumptions`.
#[unstable(feature = "transmutability", issue = "99571")]
pub const fn and(self, other_assumptions: Self) -> Self {
Self {
alignment: self.alignment || other_assumptions.alignment,
lifetimes: self.lifetimes || other_assumptions.lifetimes,
safety: self.safety || other_assumptions.safety,
validity: self.validity || other_assumptions.validity,
}
}

/// Assume `self`, excepting `other_assumptions`.
#[unstable(feature = "transmutability", issue = "99571")]
pub const fn but_not(self, other_assumptions: Self) -> Self {
Self {
alignment: self.alignment && !other_assumptions.alignment,
lifetimes: self.lifetimes && !other_assumptions.lifetimes,
safety: self.safety && !other_assumptions.safety,
validity: self.validity && !other_assumptions.validity,
}
}
}

// FIXME(jswrenn): This const op is not actually usable. Why?
// https://github.com/rust-lang/rust/pull/100726#issuecomment-1219928926
#[unstable(feature = "transmutability", issue = "99571")]
#[rustc_const_unstable(feature = "transmutability", issue = "99571")]
impl const core::ops::Add for Assume {
type Output = Assume;

fn add(self, other_assumptions: Assume) -> Assume {
self.and(other_assumptions)
}
}

// FIXME(jswrenn): This const op is not actually usable. Why?
// https://github.com/rust-lang/rust/pull/100726#issuecomment-1219928926
#[unstable(feature = "transmutability", issue = "99571")]
#[rustc_const_unstable(feature = "transmutability", issue = "99571")]
impl const core::ops::Sub for Assume {
type Output = Assume;

fn sub(self, other_assumptions: Assume) -> Assume {
self.but_not(other_assumptions)
}
}
Loading

0 comments on commit 8521a8c

Please sign in to comment.