blob: bea24044db8d14c8eaa7dab26ffbc5207f7a8ccb [file] [log] [blame] [edit]
use crate::rust_ir::*;
use crate::RustIrDatabase;
use chalk_ir::interner::Interner;
use chalk_ir::*;
use std::sync::Arc;
use tracing::{debug, instrument};
/// Methods for splitting up the projections for associated types from
/// the surrounding context.
pub trait Split<I: Interner>: RustIrDatabase<I> {
/// Given a projection of an associated type, split the type
/// parameters into those that come from the *trait* and those
/// that come from the *associated type itself*. So e.g. if you
/// have `(Iterator::Item)<F>`, this would return `([F], [])`,
/// since `Iterator::Item` is not generic and hence doesn't have
/// any type parameters itself.
fn split_projection<'p>(
&self,
projection: &'p ProjectionTy<I>,
) -> (
Arc<AssociatedTyDatum<I>>,
&'p [GenericArg<I>],
&'p [GenericArg<I>],
) {
let interner = self.interner();
let ProjectionTy {
associated_ty_id,
ref substitution,
} = *projection;
let parameters = substitution.as_slice(interner);
let associated_ty_data = &self.associated_ty_data(associated_ty_id);
let (trait_params, other_params) =
self.split_associated_ty_parameters(parameters, &**associated_ty_data);
(associated_ty_data.clone(), trait_params, other_params)
}
/// Given a projection `<P0 as Trait<P1..Pn>>::Item<Pn..Pm>`,
/// returns the trait parameters `[P0..Pn]` (see
/// `split_projection`).
fn trait_parameters_from_projection<'p>(
&self,
projection: &'p ProjectionTy<I>,
) -> &'p [GenericArg<I>] {
let (_, trait_params, _) = self.split_projection(projection);
trait_params
}
/// Given a projection `<P0 as Trait<P1..Pn>>::Item<Pn..Pm>`,
/// returns the trait parameters `[P0..Pn]` (see
/// `split_projection`).
fn trait_ref_from_projection(&self, projection: &ProjectionTy<I>) -> TraitRef<I> {
let interner = self.interner();
let (associated_ty_data, trait_params, _) = self.split_projection(projection);
TraitRef {
trait_id: associated_ty_data.trait_id,
substitution: Substitution::from_iter(interner, trait_params),
}
}
/// Given the full set of parameters (or binders) for an
/// associated type *value* (which appears in an impl), splits
/// them into the substitutions for the *impl* and those for the
/// *associated type*.
///
/// # Example
///
/// ```ignore (example)
/// impl<T> Iterable for Vec<T> {
/// type Iter<'a>;
/// }
/// ```
///
/// in this example, the full set of parameters would be `['x,
/// Y]`, where `'x` is the value for `'a` and `Y` is the value for
/// `T`.
///
/// # Returns
///
/// Returns the pair of:
///
/// * the parameters for the impl (`[Y]`, in our example)
/// * the parameters for the associated type value (`['a]`, in our example)
fn split_associated_ty_value_parameters<'p, P>(
&self,
parameters: &'p [P],
associated_ty_value: &AssociatedTyValue<I>,
) -> (&'p [P], &'p [P]) {
let interner = self.interner();
let impl_datum = self.impl_datum(associated_ty_value.impl_id);
let impl_params_len = impl_datum.binders.len(interner);
assert!(parameters.len() >= impl_params_len);
// the impl parameters are a suffix
//
// [ P0..Pn, Pn...Pm ]
// ^^^^^^^ impl parameters
let split_point = parameters.len() - impl_params_len;
let (other_params, impl_params) = parameters.split_at(split_point);
(impl_params, other_params)
}
/// Given the full set of parameters for an associated type *value*
/// (which appears in an impl), returns the trait reference
/// and projection that are being satisfied by that value.
///
/// # Example
///
/// ```ignore (example)
/// impl<T> Iterable for Vec<T> {
/// type Iter<'a>;
/// }
/// ```
///
/// Here we expect the full set of parameters for `Iter`, which
/// would be `['x, Y]`, where `'x` is the value for `'a` and `Y`
/// is the value for `T`.
///
/// Returns the pair of:
///
/// * the parameters that apply to the impl (`Y`, in our example)
/// * the projection `<Vec<Y> as Iterable>::Iter<'x>`
#[instrument(level = "debug", skip(self, associated_ty_value))]
fn impl_parameters_and_projection_from_associated_ty_value<'p>(
&self,
parameters: &'p [GenericArg<I>],
associated_ty_value: &AssociatedTyValue<I>,
) -> (&'p [GenericArg<I>], ProjectionTy<I>) {
let interner = self.interner();
let impl_datum = self.impl_datum(associated_ty_value.impl_id);
// Get the trait ref from the impl -- so in our example above
// this would be `Box<!T>: Foo`.
let (impl_parameters, atv_parameters) =
self.split_associated_ty_value_parameters(parameters, associated_ty_value);
let trait_ref = {
let opaque_ty_ref = impl_datum.binders.map_ref(|b| &b.trait_ref).cloned();
debug!(?opaque_ty_ref);
opaque_ty_ref.substitute(interner, impl_parameters)
};
// Create the parameters for the projection -- in our example
// above, this would be `['!a, Box<!T>]`, corresponding to
// `<Box<!T> as Foo>::Item<'!a>`
let projection_substitution = Substitution::from_iter(
interner,
atv_parameters
.iter()
.chain(trait_ref.substitution.iter(interner))
.cloned(),
);
let projection = ProjectionTy {
associated_ty_id: associated_ty_value.associated_ty_id,
substitution: projection_substitution,
};
debug!(?impl_parameters, ?trait_ref, ?projection);
(impl_parameters, projection)
}
/// Given the full set of parameters (or binders) for an
/// associated type datum (the one appearing in a trait), splits
/// them into the parameters for the *trait* and those for the
/// *associated type*.
///
/// # Example
///
/// ```ignore (example)
/// trait Foo<T> {
/// type Assoc<'a>;
/// }
/// ```
///
/// in this example, the full set of parameters would be `['x,
/// Y]`, where `'x` is the value for `'a` and `Y` is the value for
/// `T`.
///
/// # Returns
///
/// Returns the tuple of:
///
/// * the parameters for the impl (`[Y]`, in our example)
/// * the parameters for the associated type value (`['a]`, in our example)
fn split_associated_ty_parameters<'p, P>(
&self,
parameters: &'p [P],
associated_ty_datum: &AssociatedTyDatum<I>,
) -> (&'p [P], &'p [P]) {
let trait_datum = &self.trait_datum(associated_ty_datum.trait_id);
let trait_num_params = trait_datum.binders.len(self.interner());
let split_point = parameters.len() - trait_num_params;
let (other_params, trait_params) = parameters.split_at(split_point);
(trait_params, other_params)
}
}
impl<DB: RustIrDatabase<I> + ?Sized, I: Interner> Split<I> for DB {}