Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

safe transmute: use Assume struct to provide analysis options #100726

Merged
merged 3 commits into from
Sep 4, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4581,6 +4581,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 @@ -192,7 +192,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 @@ -334,6 +334,7 @@ symbols! {
alias,
align,
align_offset,
alignment,
alignstack,
all,
alloc,
Expand Down Expand Up @@ -859,6 +860,7 @@ symbols! {
lib,
libc,
lifetime,
lifetimes,
likely,
line,
line_macro,
Expand Down Expand Up @@ -1284,6 +1286,7 @@ symbols! {
rustfmt,
rvalue_static_promotion,
s,
safety,
sanitize,
sanitizer_runtime,
saturating_add,
Expand Down Expand Up @@ -1466,6 +1469,7 @@ symbols! {
trait_alias,
trait_upcasting,
transmute,
transmute_opts,
transmute_trait,
transparent,
transparent_enums,
Expand Down Expand Up @@ -1560,6 +1564,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
65 changes: 59 additions & 6 deletions compiler/rustc_transmute/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,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 @@ -26,8 +22,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 @@ -69,11 +65,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 @@ -113,6 +115,57 @@ 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::DestructuredConst;
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 DestructuredConst { variant, fields } = tcx.destructure_const(c);
oli-obk marked this conversation as resolved.
Show resolved Hide resolved
let variant_idx = variant.expect("The given `Const` must be an ADT.");
let variant = adt_def.variant(variant_idx);

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].try_eval_bool(tcx, param_env).expect(&format!(
"The field named `{name}` lang item could not be evaluated to a bool."
))
};

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)]
Copy link
Member

@RalfJung RalfJung Apr 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am quite concerned about this allow. That makes it way too easy to use features such as generic_const_exprs that are just not ready yet for use in the standard library.

This PR should at least have consulted t-types to ensure that adt_const_params is sufficiently stable that it can be used internally in the standard library. Cc @lcnr

But even then I think we actually want forbid(incomplete_features) here to avoid people just adding feature gates when it seems convenient. Or even better, set some flag in bootstrap which ensures that the entire sysroot is built without 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
Loading