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

Shrink ParamEnv to 16 bytes #73978

Merged
merged 2 commits into from
Jul 7, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion src/librustc_infer/infer/outlives/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub fn explicit_outlives_bounds<'tcx>(
param_env: ty::ParamEnv<'tcx>,
) -> impl Iterator<Item = OutlivesBound<'tcx>> + 'tcx {
debug!("explicit_outlives_bounds()");
param_env.caller_bounds.into_iter().filter_map(move |predicate| match predicate.kind() {
param_env.caller_bounds().into_iter().filter_map(move |predicate| match predicate.kind() {
ty::PredicateKind::Projection(..)
| ty::PredicateKind::Trait(..)
| ty::PredicateKind::Subtype(..)
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_infer/infer/outlives/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
// dubious for projections, but it will work for simple cases
// like `T` and `T::Item`. It may not work as well for things
// like `<T as Foo<'a>>::Item`.
let c_b = self.param_env.caller_bounds;
let c_b = self.param_env.caller_bounds();
let param_bounds = self.collect_outlives_from_predicate_list(&compare_ty, c_b.into_iter());

// Next, collect regions we scraped from the well-formedness
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_infer/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ pub type TraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitPredicate<'tcx>>;

// `PredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger.
#[cfg(target_arch = "x86_64")]
static_assert_size!(PredicateObligation<'_>, 48);
static_assert_size!(PredicateObligation<'_>, 40);

pub type Obligations<'tcx, O> = Vec<Obligation<'tcx, O>>;
pub type PredicateObligations<'tcx> = Vec<PredicateObligation<'tcx>>;
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_middle/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1588,7 +1588,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
// Ignore layouts that are done with non-empty environments or
// non-monomorphic layouts, as the user only wants to see the stuff
// resulting from the final codegen session.
if layout.ty.has_param_types_or_consts() || !self.param_env.caller_bounds.is_empty() {
if layout.ty.has_param_types_or_consts() || !self.param_env.caller_bounds().is_empty() {
return;
}

Expand Down
122 changes: 112 additions & 10 deletions src/librustc_middle/ty/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// ignore-tidy-filelength

pub use self::fold::{TypeFoldable, TypeVisitor};
pub use self::AssocItemContainer::*;
pub use self::BorrowKind::*;
Expand Down Expand Up @@ -45,6 +47,7 @@ use std::cell::RefCell;
use std::cmp::Ordering;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use std::ops::Range;
use std::ptr;

Expand Down Expand Up @@ -1571,24 +1574,93 @@ pub type PlaceholderConst = Placeholder<BoundVar>;
/// When type checking, we use the `ParamEnv` to track
/// details about the set of where-clauses that are in scope at this
/// particular point.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable, TypeFoldable)]
#[derive(Copy, Clone)]
pub struct ParamEnv<'tcx> {
// We pack the caller_bounds List pointer and a Reveal enum into this usize.
// Specifically, the low bit represents Reveal, with 0 meaning `UserFacing`
// and 1 meaning `All`. The rest is the pointer.
//
// This relies on the List<ty::Predicate<'tcx>> type having at least 2-byte
// alignment. Lists start with a usize and are repr(C) so this should be
// fine; there is a debug_assert in the constructor as well.
//
// Note that the choice of 0 for UserFacing is intentional -- since it is the
// first variant in Reveal this means that joining the pointer is a simple `or`.
packed_data: usize,
Comment on lines +1579 to +1589
Copy link
Member

Choose a reason for hiding this comment

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

Late to the party, but maybe move all of the safety-critical parts of this into a ty::param_env module? That or we could try to build a bit-packed abstraction (since ty::GenericArg does something similar), or look for one on crates.io and audit it.


/// `Obligation`s that the caller must satisfy. This is basically
/// the set of bounds on the in-scope type parameters, translated
/// into `Obligation`s, and elaborated and normalized.
pub caller_bounds: &'tcx List<ty::Predicate<'tcx>>,
///
/// Note: This is packed into the `packed_data` usize above, use the
/// `caller_bounds()` method to access it.
caller_bounds: PhantomData<&'tcx List<ty::Predicate<'tcx>>>,
nnethercote marked this conversation as resolved.
Show resolved Hide resolved

/// Typically, this is `Reveal::UserFacing`, but during codegen we
/// want `Reveal::All` -- note that this is always paired with an
/// empty environment. To get that, use `ParamEnv::reveal()`.
pub reveal: traits::Reveal,
/// want `Reveal::All`.
///
/// Note: This is packed into the caller_bounds usize above, use the reveal()
/// method to access it.
reveal: PhantomData<traits::Reveal>,
nnethercote marked this conversation as resolved.
Show resolved Hide resolved

/// If this `ParamEnv` comes from a call to `tcx.param_env(def_id)`,
/// register that `def_id` (useful for transitioning to the chalk trait
/// solver).
pub def_id: Option<DefId>,
}

impl<'tcx> fmt::Debug for ParamEnv<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ParamEnv")
.field("caller_bounds", &self.caller_bounds())
.field("reveal", &self.reveal())
.field("def_id", &self.def_id)
.finish()
}
}

impl<'tcx> Hash for ParamEnv<'tcx> {
fn hash<H: Hasher>(&self, state: &mut H) {
// List hashes as the raw pointer, so we can skip splitting into the
// pointer and the enum.
self.packed_data.hash(state);
self.def_id.hash(state);
}
}

impl<'tcx> PartialEq for ParamEnv<'tcx> {
fn eq(&self, other: &Self) -> bool {
self.caller_bounds() == other.caller_bounds()
&& self.reveal() == other.reveal()
&& self.def_id == other.def_id
}
}
impl<'tcx> Eq for ParamEnv<'tcx> {}

impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ParamEnv<'tcx> {
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
self.caller_bounds().hash_stable(hcx, hasher);
self.reveal().hash_stable(hcx, hasher);
self.def_id.hash_stable(hcx, hasher);
}
}

impl<'tcx> TypeFoldable<'tcx> for ParamEnv<'tcx> {
fn super_fold_with<F: ty::fold::TypeFolder<'tcx>>(&self, folder: &mut F) -> Self {
ParamEnv::new(
self.caller_bounds().fold_with(folder),
self.reveal().fold_with(folder),
self.def_id.fold_with(folder),
)
}

fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
self.caller_bounds().visit_with(visitor)
|| self.reveal().visit_with(visitor)
|| self.def_id.visit_with(visitor)
}
}

impl<'tcx> ParamEnv<'tcx> {
/// Construct a trait environment suitable for contexts where
/// there are no where-clauses in scope. Hidden types (like `impl
Expand All @@ -1599,6 +1671,17 @@ impl<'tcx> ParamEnv<'tcx> {
Self::new(List::empty(), Reveal::UserFacing, None)
}

#[inline]
pub fn caller_bounds(self) -> &'tcx List<ty::Predicate<'tcx>> {
// mask out bottom bit
unsafe { &*((self.packed_data & (!1)) as *const _) }
}

#[inline]
pub fn reveal(self) -> traits::Reveal {
if self.packed_data & 1 == 0 { traits::Reveal::UserFacing } else { traits::Reveal::All }
}

/// Construct a trait environment with no where-clauses in scope
/// where the values of all `impl Trait` and other hidden types
/// are revealed. This is suitable for monomorphized, post-typeck
Expand All @@ -1618,7 +1701,25 @@ impl<'tcx> ParamEnv<'tcx> {
reveal: Reveal,
def_id: Option<DefId>,
) -> Self {
ty::ParamEnv { caller_bounds, reveal, def_id }
let packed_data = caller_bounds as *const _ as usize;
// Check that we can pack the reveal data into the pointer.
debug_assert!(packed_data & 1 == 0);
ty::ParamEnv {
packed_data: packed_data
| match reveal {
Reveal::UserFacing => 0,
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: swap these arms so the 0 is first?

Reveal::All => 1,
},
caller_bounds: PhantomData,
reveal: PhantomData,
def_id,
}
}

pub fn with_user_facing(mut self) -> Self {
// clear bottom bit
self.packed_data &= !1;
self
}

/// Returns a new parameter environment with the same clauses, but
Expand All @@ -1627,13 +1728,14 @@ impl<'tcx> ParamEnv<'tcx> {
/// the desired behavior during codegen and certain other special
/// contexts; normally though we want to use `Reveal::UserFacing`,
/// which is the default.
pub fn with_reveal_all(self) -> Self {
ty::ParamEnv { reveal: Reveal::All, ..self }
pub fn with_reveal_all(mut self) -> Self {
self.packed_data |= 1;
self
}

/// Returns this same environment but with no caller bounds.
pub fn without_caller_bounds(self) -> Self {
ty::ParamEnv { caller_bounds: List::empty(), ..self }
Self::new(List::empty(), self.reveal(), self.def_id)
}

/// Creates a suitable environment in which to perform trait
Expand All @@ -1649,7 +1751,7 @@ impl<'tcx> ParamEnv<'tcx> {
/// satisfiable. We generally want to behave as if they were true,
/// although the surrounding function is never reachable.
pub fn and<T: TypeFoldable<'tcx>>(self, value: T) -> ParamEnvAnd<'tcx, T> {
match self.reveal {
match self.reveal() {
Reveal::UserFacing => ParamEnvAnd { param_env: self, value },

Reveal::All => {
Expand Down
7 changes: 2 additions & 5 deletions src/librustc_middle/ty/structural_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -521,11 +521,8 @@ impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for ty::Binder<T> {
impl<'a, 'tcx> Lift<'tcx> for ty::ParamEnv<'a> {
type Lifted = ty::ParamEnv<'tcx>;
fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
tcx.lift(&self.caller_bounds).map(|caller_bounds| ty::ParamEnv {
reveal: self.reveal,
caller_bounds,
def_id: self.def_id,
})
tcx.lift(&self.caller_bounds())
.map(|caller_bounds| ty::ParamEnv::new(caller_bounds, self.reveal(), self.def_id))
}
}

Expand Down
10 changes: 5 additions & 5 deletions src/librustc_mir/const_eval/eval_queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,9 @@ pub fn const_eval_validated_provider<'tcx>(
key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>,
) -> ::rustc_middle::mir::interpret::ConstEvalResult<'tcx> {
// see comment in const_eval_raw_provider for what we're doing here
if key.param_env.reveal == Reveal::All {
if key.param_env.reveal() == Reveal::All {
let mut key = key;
key.param_env.reveal = Reveal::UserFacing;
key.param_env = key.param_env.with_user_facing();
match tcx.const_eval_validated(key) {
// try again with reveal all as requested
Err(ErrorHandled::TooGeneric) => {}
Expand Down Expand Up @@ -267,9 +267,9 @@ pub fn const_eval_raw_provider<'tcx>(
// information being available.

// In case we fail in the `UserFacing` variant, we just do the real computation.
if key.param_env.reveal == Reveal::All {
if key.param_env.reveal() == Reveal::All {
let mut key = key;
key.param_env.reveal = Reveal::UserFacing;
key.param_env = key.param_env.with_user_facing();
match tcx.const_eval_raw(key) {
// try again with reveal all as requested
Err(ErrorHandled::TooGeneric) => {}
Expand Down Expand Up @@ -326,7 +326,7 @@ pub fn const_eval_raw_provider<'tcx>(
// this is `Reveal::UserFacing`, then it's expected that we could get a
// `TooGeneric` error. When we fall back to `Reveal::All`, then it will either
// succeed or we'll report this error then.
if key.param_env.reveal == Reveal::All {
if key.param_env.reveal() == Reveal::All {
tcx.sess.delay_span_bug(
err.span,
&format!("static eval failure did not emit an error: {:#?}", v),
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/util/elaborate_drops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ where
let subpath = self.elaborator.field_subpath(variant_path, field);
let tcx = self.tcx();

assert_eq!(self.elaborator.param_env().reveal, Reveal::All);
assert_eq!(self.elaborator.param_env().reveal(), Reveal::All);
let field_ty =
tcx.normalize_erasing_regions(self.elaborator.param_env(), f.ty(tcx, substs));
(tcx.mk_place_field(base_place, field, field_ty), subpath)
Expand Down
8 changes: 4 additions & 4 deletions src/librustc_trait_selection/traits/auto_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,8 +281,8 @@ impl AutoTraitFinder<'tcx> {
},
}));

let computed_preds = param_env.caller_bounds.iter();
let mut user_computed_preds: FxHashSet<_> = user_env.caller_bounds.iter().collect();
let computed_preds = param_env.caller_bounds().iter();
let mut user_computed_preds: FxHashSet<_> = user_env.caller_bounds().iter().collect();

let mut new_env = param_env;
let dummy_cause = ObligationCause::dummy();
Expand Down Expand Up @@ -368,12 +368,12 @@ impl AutoTraitFinder<'tcx> {
)
.map(|o| o.predicate);
new_env =
ty::ParamEnv::new(tcx.mk_predicates(normalized_preds), param_env.reveal, None);
ty::ParamEnv::new(tcx.mk_predicates(normalized_preds), param_env.reveal(), None);
}

let final_user_env = ty::ParamEnv::new(
tcx.mk_predicates(user_computed_preds.into_iter()),
user_env.reveal,
user_env.reveal(),
None,
);
debug!(
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_trait_selection/traits/chalk_fulfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ fn in_environment(

let environment = match obligation.param_env.def_id {
Some(def_id) => environment(infcx.tcx, def_id),
None if obligation.param_env.caller_bounds.is_empty() => ty::List::empty(),
None if obligation.param_env.caller_bounds().is_empty() => ty::List::empty(),
// FIXME(chalk): this is hit in ui/where-clauses/where-clause-constraints-are-local-for-trait-impl
// and ui/generics/generic-static-methods
//_ => bug!("non-empty `ParamEnv` with no def-id"),
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_trait_selection/traits/fulfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ pub struct PendingPredicateObligation<'tcx> {

// `PendingPredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger.
#[cfg(target_arch = "x86_64")]
static_assert_size!(PendingPredicateObligation<'_>, 72);
static_assert_size!(PendingPredicateObligation<'_>, 64);

impl<'a, 'tcx> FulfillmentContext<'tcx> {
/// Creates a new fulfillment context.
Expand Down
8 changes: 4 additions & 4 deletions src/librustc_trait_selection/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,15 +297,15 @@ pub fn normalize_param_env_or_error<'tcx>(
);

let mut predicates: Vec<_> =
util::elaborate_predicates(tcx, unnormalized_env.caller_bounds.into_iter())
util::elaborate_predicates(tcx, unnormalized_env.caller_bounds().into_iter())
.map(|obligation| obligation.predicate)
.collect();

debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates);

let elaborated_env = ty::ParamEnv::new(
tcx.intern_predicates(&predicates),
unnormalized_env.reveal,
unnormalized_env.reveal(),
unnormalized_env.def_id,
);

Expand Down Expand Up @@ -361,7 +361,7 @@ pub fn normalize_param_env_or_error<'tcx>(
let outlives_env: Vec<_> =
non_outlives_predicates.iter().chain(&outlives_predicates).cloned().collect();
let outlives_env =
ty::ParamEnv::new(tcx.intern_predicates(&outlives_env), unnormalized_env.reveal, None);
ty::ParamEnv::new(tcx.intern_predicates(&outlives_env), unnormalized_env.reveal(), None);
let outlives_predicates = match do_normalize_predicates(
tcx,
region_context,
Expand All @@ -383,7 +383,7 @@ pub fn normalize_param_env_or_error<'tcx>(
debug!("normalize_param_env_or_error: final predicates={:?}", predicates);
ty::ParamEnv::new(
tcx.intern_predicates(&predicates),
unnormalized_env.reveal,
unnormalized_env.reveal(),
unnormalized_env.def_id,
)
}
Expand Down
12 changes: 7 additions & 5 deletions src/librustc_trait_selection/traits/object_safety.rs
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,7 @@ fn receiver_is_dispatchable<'tcx>(
// create a modified param env, with `Self: Unsize<U>` and `U: Trait` added to caller bounds
// `U: ?Sized` is already implied here
let param_env = {
let mut param_env = tcx.param_env(method.def_id);
let param_env = tcx.param_env(method.def_id);

// Self: Unsize<U>
let unsize_predicate = ty::TraitRef {
Expand All @@ -656,15 +656,17 @@ fn receiver_is_dispatchable<'tcx>(
};

let caller_bounds: Vec<Predicate<'tcx>> = param_env
.caller_bounds
.caller_bounds()
.iter()
.chain(iter::once(unsize_predicate))
.chain(iter::once(trait_predicate))
.collect();

param_env.caller_bounds = tcx.intern_predicates(&caller_bounds);

param_env
ty::ParamEnv::new(
tcx.intern_predicates(&caller_bounds),
param_env.reveal(),
param_env.def_id,
)
};

// Receiver: DispatchFromDyn<Receiver[Self => U]>
Expand Down
Loading