150 lines
4.8 KiB
Rust
150 lines
4.8 KiB
Rust
// Representing terms
|
|
//
|
|
// Terms are structured as a straightforward tree. Rather than rely on
|
|
// GC, we allocate terms out of a bounded arena (the lifetime of this
|
|
// arena is the lifetime 'a that is threaded around).
|
|
//
|
|
// We assign a unique index to each type/region parameter whose variance
|
|
// is to be inferred. We refer to such variables as "inferreds". An
|
|
// `InferredIndex` is a newtype'd int representing the index of such
|
|
// a variable.
|
|
|
|
use rustc_arena::DroplessArena;
|
|
use rustc_hir::def::DefKind;
|
|
use rustc_hir::def_id::{LocalDefId, LocalDefIdMap};
|
|
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
|
|
use std::fmt;
|
|
|
|
use self::VarianceTerm::*;
|
|
|
|
pub type VarianceTermPtr<'a> = &'a VarianceTerm<'a>;
|
|
|
|
#[derive(Copy, Clone, Debug)]
|
|
pub struct InferredIndex(pub usize);
|
|
|
|
#[derive(Copy, Clone)]
|
|
pub enum VarianceTerm<'a> {
|
|
ConstantTerm(ty::Variance),
|
|
TransformTerm(VarianceTermPtr<'a>, VarianceTermPtr<'a>),
|
|
InferredTerm(InferredIndex),
|
|
}
|
|
|
|
impl<'a> fmt::Debug for VarianceTerm<'a> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match *self {
|
|
ConstantTerm(c1) => write!(f, "{c1:?}"),
|
|
TransformTerm(v1, v2) => write!(f, "({v1:?} \u{00D7} {v2:?})"),
|
|
InferredTerm(id) => write!(f, "[{}]", {
|
|
let InferredIndex(i) = id;
|
|
i
|
|
}),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// The first pass over the crate simply builds up the set of inferreds.
|
|
|
|
pub struct TermsContext<'a, 'tcx> {
|
|
pub tcx: TyCtxt<'tcx>,
|
|
pub arena: &'a DroplessArena,
|
|
|
|
/// For marker types, `UnsafeCell`, and other lang items where
|
|
/// variance is hardcoded, records the item-id and the hardcoded
|
|
/// variance.
|
|
pub lang_items: Vec<(LocalDefId, Vec<ty::Variance>)>,
|
|
|
|
/// Maps from the node id of an item to the first inferred index
|
|
/// used for its type & region parameters.
|
|
pub inferred_starts: LocalDefIdMap<InferredIndex>,
|
|
|
|
/// Maps from an InferredIndex to the term for that variable.
|
|
pub inferred_terms: Vec<VarianceTermPtr<'a>>,
|
|
}
|
|
|
|
pub fn determine_parameters_to_be_inferred<'a, 'tcx>(
|
|
tcx: TyCtxt<'tcx>,
|
|
arena: &'a DroplessArena,
|
|
) -> TermsContext<'a, 'tcx> {
|
|
let mut terms_cx = TermsContext {
|
|
tcx,
|
|
arena,
|
|
inferred_starts: Default::default(),
|
|
inferred_terms: vec![],
|
|
|
|
lang_items: lang_items(tcx),
|
|
};
|
|
|
|
// See the following for a discussion on dep-graph management.
|
|
//
|
|
// - https://rustc-dev-guide.rust-lang.org/query.html
|
|
// - https://rustc-dev-guide.rust-lang.org/variance.html
|
|
let crate_items = tcx.hir_crate_items(());
|
|
|
|
for def_id in crate_items.definitions() {
|
|
debug!("add_inferreds for item {:?}", def_id);
|
|
|
|
let def_kind = tcx.def_kind(def_id);
|
|
|
|
match def_kind {
|
|
DefKind::Struct | DefKind::Union | DefKind::Enum => {
|
|
terms_cx.add_inferreds_for_item(def_id);
|
|
|
|
let adt = tcx.adt_def(def_id);
|
|
for variant in adt.variants() {
|
|
if let Some(ctor_def_id) = variant.ctor_def_id() {
|
|
terms_cx.add_inferreds_for_item(ctor_def_id.expect_local());
|
|
}
|
|
}
|
|
}
|
|
DefKind::Fn | DefKind::AssocFn => terms_cx.add_inferreds_for_item(def_id),
|
|
DefKind::TyAlias { lazy }
|
|
if lazy || tcx.type_of(def_id).instantiate_identity().has_opaque_types() =>
|
|
{
|
|
terms_cx.add_inferreds_for_item(def_id)
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
terms_cx
|
|
}
|
|
|
|
fn lang_items(tcx: TyCtxt<'_>) -> Vec<(LocalDefId, Vec<ty::Variance>)> {
|
|
let lang_items = tcx.lang_items();
|
|
let all = [
|
|
(lang_items.phantom_data(), vec![ty::Covariant]),
|
|
(lang_items.unsafe_cell_type(), vec![ty::Invariant]),
|
|
];
|
|
|
|
all.into_iter() // iterating over (Option<DefId>, Variance)
|
|
.filter_map(|(d, v)| {
|
|
let def_id = d?.as_local()?; // LocalDefId
|
|
Some((def_id, v))
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
impl<'a, 'tcx> TermsContext<'a, 'tcx> {
|
|
fn add_inferreds_for_item(&mut self, def_id: LocalDefId) {
|
|
let tcx = self.tcx;
|
|
let count = tcx.generics_of(def_id).count();
|
|
|
|
if count == 0 {
|
|
return;
|
|
}
|
|
|
|
// Record the start of this item's inferreds.
|
|
let start = self.inferred_terms.len();
|
|
let newly_added = self.inferred_starts.insert(def_id, InferredIndex(start)).is_none();
|
|
assert!(newly_added);
|
|
|
|
// N.B., in the code below for writing the results back into the
|
|
// `CrateVariancesMap`, we rely on the fact that all inferreds
|
|
// for a particular item are assigned continuous indices.
|
|
|
|
let arena = self.arena;
|
|
self.inferred_terms.extend(
|
|
(start..(start + count)).map(|i| &*arena.alloc(InferredTerm(InferredIndex(i)))),
|
|
);
|
|
}
|
|
}
|