rustc_typeck to rustc_hir_analysis
This commit is contained in:
parent
de0b511daa
commit
1fc86a63f4
140 changed files with 101 additions and 102 deletions
445
compiler/rustc_hir_analysis/src/variance/constraints.rs
Normal file
445
compiler/rustc_hir_analysis/src/variance/constraints.rs
Normal file
|
@ -0,0 +1,445 @@
|
|||
//! Constraint construction and representation
|
||||
//!
|
||||
//! The second pass over the AST determines the set of constraints.
|
||||
//! We walk the set of items and, for each member, generate new constraints.
|
||||
|
||||
use hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
|
||||
use super::terms::VarianceTerm::*;
|
||||
use super::terms::*;
|
||||
|
||||
pub struct ConstraintContext<'a, 'tcx> {
|
||||
pub terms_cx: TermsContext<'a, 'tcx>,
|
||||
|
||||
// These are pointers to common `ConstantTerm` instances
|
||||
covariant: VarianceTermPtr<'a>,
|
||||
contravariant: VarianceTermPtr<'a>,
|
||||
invariant: VarianceTermPtr<'a>,
|
||||
bivariant: VarianceTermPtr<'a>,
|
||||
|
||||
pub constraints: Vec<Constraint<'a>>,
|
||||
}
|
||||
|
||||
/// Declares that the variable `decl_id` appears in a location with
|
||||
/// variance `variance`.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Constraint<'a> {
|
||||
pub inferred: InferredIndex,
|
||||
pub variance: &'a VarianceTerm<'a>,
|
||||
}
|
||||
|
||||
/// To build constraints, we visit one item (type, trait) at a time
|
||||
/// and look at its contents. So e.g., if we have
|
||||
/// ```ignore (illustrative)
|
||||
/// struct Foo<T> {
|
||||
/// b: Bar<T>
|
||||
/// }
|
||||
/// ```
|
||||
/// then while we are visiting `Bar<T>`, the `CurrentItem` would have
|
||||
/// the `DefId` and the start of `Foo`'s inferreds.
|
||||
pub struct CurrentItem {
|
||||
inferred_start: InferredIndex,
|
||||
}
|
||||
|
||||
pub fn add_constraints_from_crate<'a, 'tcx>(
|
||||
terms_cx: TermsContext<'a, 'tcx>,
|
||||
) -> ConstraintContext<'a, 'tcx> {
|
||||
let tcx = terms_cx.tcx;
|
||||
let covariant = terms_cx.arena.alloc(ConstantTerm(ty::Covariant));
|
||||
let contravariant = terms_cx.arena.alloc(ConstantTerm(ty::Contravariant));
|
||||
let invariant = terms_cx.arena.alloc(ConstantTerm(ty::Invariant));
|
||||
let bivariant = terms_cx.arena.alloc(ConstantTerm(ty::Bivariant));
|
||||
let mut constraint_cx = ConstraintContext {
|
||||
terms_cx,
|
||||
covariant,
|
||||
contravariant,
|
||||
invariant,
|
||||
bivariant,
|
||||
constraints: Vec::new(),
|
||||
};
|
||||
|
||||
let crate_items = tcx.hir_crate_items(());
|
||||
|
||||
for def_id in crate_items.definitions() {
|
||||
let def_kind = tcx.def_kind(def_id);
|
||||
match def_kind {
|
||||
DefKind::Struct | DefKind::Union | DefKind::Enum => {
|
||||
constraint_cx.build_constraints_for_item(def_id);
|
||||
|
||||
let adt = tcx.adt_def(def_id);
|
||||
for variant in adt.variants() {
|
||||
if let Some(ctor) = variant.ctor_def_id {
|
||||
constraint_cx.build_constraints_for_item(ctor.expect_local());
|
||||
}
|
||||
}
|
||||
}
|
||||
DefKind::Fn | DefKind::AssocFn => constraint_cx.build_constraints_for_item(def_id),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
constraint_cx
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.terms_cx.tcx
|
||||
}
|
||||
|
||||
fn build_constraints_for_item(&mut self, def_id: LocalDefId) {
|
||||
let tcx = self.tcx();
|
||||
debug!("build_constraints_for_item({})", tcx.def_path_str(def_id.to_def_id()));
|
||||
|
||||
// Skip items with no generics - there's nothing to infer in them.
|
||||
if tcx.generics_of(def_id).count() == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let inferred_start = self.terms_cx.inferred_starts[&def_id];
|
||||
let current_item = &CurrentItem { inferred_start };
|
||||
match tcx.type_of(def_id).kind() {
|
||||
ty::Adt(def, _) => {
|
||||
// Not entirely obvious: constraints on structs/enums do not
|
||||
// affect the variance of their type parameters. See discussion
|
||||
// in comment at top of module.
|
||||
//
|
||||
// self.add_constraints_from_generics(generics);
|
||||
|
||||
for field in def.all_fields() {
|
||||
self.add_constraints_from_ty(
|
||||
current_item,
|
||||
tcx.type_of(field.did),
|
||||
self.covariant,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ty::FnDef(..) => {
|
||||
self.add_constraints_from_sig(current_item, tcx.fn_sig(def_id), self.covariant);
|
||||
}
|
||||
|
||||
ty::Error(_) => {}
|
||||
_ => {
|
||||
span_bug!(
|
||||
tcx.def_span(def_id),
|
||||
"`build_constraints_for_item` unsupported for this item"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_constraint(&mut self, current: &CurrentItem, index: u32, variance: VarianceTermPtr<'a>) {
|
||||
debug!("add_constraint(index={}, variance={:?})", index, variance);
|
||||
self.constraints.push(Constraint {
|
||||
inferred: InferredIndex(current.inferred_start.0 + index as usize),
|
||||
variance,
|
||||
});
|
||||
}
|
||||
|
||||
fn contravariant(&mut self, variance: VarianceTermPtr<'a>) -> VarianceTermPtr<'a> {
|
||||
self.xform(variance, self.contravariant)
|
||||
}
|
||||
|
||||
fn invariant(&mut self, variance: VarianceTermPtr<'a>) -> VarianceTermPtr<'a> {
|
||||
self.xform(variance, self.invariant)
|
||||
}
|
||||
|
||||
fn constant_term(&self, v: ty::Variance) -> VarianceTermPtr<'a> {
|
||||
match v {
|
||||
ty::Covariant => self.covariant,
|
||||
ty::Invariant => self.invariant,
|
||||
ty::Contravariant => self.contravariant,
|
||||
ty::Bivariant => self.bivariant,
|
||||
}
|
||||
}
|
||||
|
||||
fn xform(&mut self, v1: VarianceTermPtr<'a>, v2: VarianceTermPtr<'a>) -> VarianceTermPtr<'a> {
|
||||
match (*v1, *v2) {
|
||||
(_, ConstantTerm(ty::Covariant)) => {
|
||||
// Applying a "covariant" transform is always a no-op
|
||||
v1
|
||||
}
|
||||
|
||||
(ConstantTerm(c1), ConstantTerm(c2)) => self.constant_term(c1.xform(c2)),
|
||||
|
||||
_ => &*self.terms_cx.arena.alloc(TransformTerm(v1, v2)),
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, current))]
|
||||
fn add_constraints_from_invariant_substs(
|
||||
&mut self,
|
||||
current: &CurrentItem,
|
||||
substs: SubstsRef<'tcx>,
|
||||
variance: VarianceTermPtr<'a>,
|
||||
) {
|
||||
// Trait are always invariant so we can take advantage of that.
|
||||
let variance_i = self.invariant(variance);
|
||||
|
||||
for k in substs {
|
||||
match k.unpack() {
|
||||
GenericArgKind::Lifetime(lt) => {
|
||||
self.add_constraints_from_region(current, lt, variance_i)
|
||||
}
|
||||
GenericArgKind::Type(ty) => self.add_constraints_from_ty(current, ty, variance_i),
|
||||
GenericArgKind::Const(val) => {
|
||||
self.add_constraints_from_const(current, val, variance_i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds constraints appropriate for an instance of `ty` appearing
|
||||
/// in a context with the generics defined in `generics` and
|
||||
/// ambient variance `variance`
|
||||
fn add_constraints_from_ty(
|
||||
&mut self,
|
||||
current: &CurrentItem,
|
||||
ty: Ty<'tcx>,
|
||||
variance: VarianceTermPtr<'a>,
|
||||
) {
|
||||
debug!("add_constraints_from_ty(ty={:?}, variance={:?})", ty, variance);
|
||||
|
||||
match *ty.kind() {
|
||||
ty::Bool
|
||||
| ty::Char
|
||||
| ty::Int(_)
|
||||
| ty::Uint(_)
|
||||
| ty::Float(_)
|
||||
| ty::Str
|
||||
| ty::Never
|
||||
| ty::Foreign(..) => {
|
||||
// leaf type -- noop
|
||||
}
|
||||
|
||||
ty::FnDef(..) | ty::Generator(..) | ty::Closure(..) => {
|
||||
bug!("Unexpected closure type in variance computation");
|
||||
}
|
||||
|
||||
ty::Ref(region, ty, mutbl) => {
|
||||
let contra = self.contravariant(variance);
|
||||
self.add_constraints_from_region(current, region, contra);
|
||||
self.add_constraints_from_mt(current, &ty::TypeAndMut { ty, mutbl }, variance);
|
||||
}
|
||||
|
||||
ty::Array(typ, len) => {
|
||||
self.add_constraints_from_const(current, len, variance);
|
||||
self.add_constraints_from_ty(current, typ, variance);
|
||||
}
|
||||
|
||||
ty::Slice(typ) => {
|
||||
self.add_constraints_from_ty(current, typ, variance);
|
||||
}
|
||||
|
||||
ty::RawPtr(ref mt) => {
|
||||
self.add_constraints_from_mt(current, mt, variance);
|
||||
}
|
||||
|
||||
ty::Tuple(subtys) => {
|
||||
for subty in subtys {
|
||||
self.add_constraints_from_ty(current, subty, variance);
|
||||
}
|
||||
}
|
||||
|
||||
ty::Adt(def, substs) => {
|
||||
self.add_constraints_from_substs(current, def.did(), substs, variance);
|
||||
}
|
||||
|
||||
ty::Projection(ref data) => {
|
||||
self.add_constraints_from_invariant_substs(current, data.substs, variance);
|
||||
}
|
||||
|
||||
ty::Opaque(_, substs) => {
|
||||
self.add_constraints_from_invariant_substs(current, substs, variance);
|
||||
}
|
||||
|
||||
ty::Dynamic(data, r, _) => {
|
||||
// The type `Foo<T+'a>` is contravariant w/r/t `'a`:
|
||||
let contra = self.contravariant(variance);
|
||||
self.add_constraints_from_region(current, r, contra);
|
||||
|
||||
if let Some(poly_trait_ref) = data.principal() {
|
||||
self.add_constraints_from_invariant_substs(
|
||||
current,
|
||||
poly_trait_ref.skip_binder().substs,
|
||||
variance,
|
||||
);
|
||||
}
|
||||
|
||||
for projection in data.projection_bounds() {
|
||||
match projection.skip_binder().term.unpack() {
|
||||
ty::TermKind::Ty(ty) => {
|
||||
self.add_constraints_from_ty(current, ty, self.invariant);
|
||||
}
|
||||
ty::TermKind::Const(c) => {
|
||||
self.add_constraints_from_const(current, c, self.invariant)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ty::Param(ref data) => {
|
||||
self.add_constraint(current, data.index, variance);
|
||||
}
|
||||
|
||||
ty::FnPtr(sig) => {
|
||||
self.add_constraints_from_sig(current, sig, variance);
|
||||
}
|
||||
|
||||
ty::Error(_) => {
|
||||
// we encounter this when walking the trait references for object
|
||||
// types, where we use Error as the Self type
|
||||
}
|
||||
|
||||
ty::Placeholder(..) | ty::GeneratorWitness(..) | ty::Bound(..) | ty::Infer(..) => {
|
||||
bug!(
|
||||
"unexpected type encountered in \
|
||||
variance inference: {}",
|
||||
ty
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds constraints appropriate for a nominal type (enum, struct,
|
||||
/// object, etc) appearing in a context with ambient variance `variance`
|
||||
fn add_constraints_from_substs(
|
||||
&mut self,
|
||||
current: &CurrentItem,
|
||||
def_id: DefId,
|
||||
substs: SubstsRef<'tcx>,
|
||||
variance: VarianceTermPtr<'a>,
|
||||
) {
|
||||
debug!(
|
||||
"add_constraints_from_substs(def_id={:?}, substs={:?}, variance={:?})",
|
||||
def_id, substs, variance
|
||||
);
|
||||
|
||||
// We don't record `inferred_starts` entries for empty generics.
|
||||
if substs.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let (local, remote) = if let Some(def_id) = def_id.as_local() {
|
||||
(Some(self.terms_cx.inferred_starts[&def_id]), None)
|
||||
} else {
|
||||
(None, Some(self.tcx().variances_of(def_id)))
|
||||
};
|
||||
|
||||
for (i, k) in substs.iter().enumerate() {
|
||||
let variance_decl = if let Some(InferredIndex(start)) = local {
|
||||
// Parameter on an item defined within current crate:
|
||||
// variance not yet inferred, so return a symbolic
|
||||
// variance.
|
||||
self.terms_cx.inferred_terms[start + i]
|
||||
} else {
|
||||
// Parameter on an item defined within another crate:
|
||||
// variance already inferred, just look it up.
|
||||
self.constant_term(remote.as_ref().unwrap()[i])
|
||||
};
|
||||
let variance_i = self.xform(variance, variance_decl);
|
||||
debug!(
|
||||
"add_constraints_from_substs: variance_decl={:?} variance_i={:?}",
|
||||
variance_decl, variance_i
|
||||
);
|
||||
match k.unpack() {
|
||||
GenericArgKind::Lifetime(lt) => {
|
||||
self.add_constraints_from_region(current, lt, variance_i)
|
||||
}
|
||||
GenericArgKind::Type(ty) => self.add_constraints_from_ty(current, ty, variance_i),
|
||||
GenericArgKind::Const(val) => {
|
||||
self.add_constraints_from_const(current, val, variance)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds constraints appropriate for a const expression `val`
|
||||
/// in a context with ambient variance `variance`
|
||||
fn add_constraints_from_const(
|
||||
&mut self,
|
||||
current: &CurrentItem,
|
||||
c: ty::Const<'tcx>,
|
||||
variance: VarianceTermPtr<'a>,
|
||||
) {
|
||||
debug!("add_constraints_from_const(c={:?}, variance={:?})", c, variance);
|
||||
|
||||
match &c.kind() {
|
||||
ty::ConstKind::Unevaluated(uv) => {
|
||||
self.add_constraints_from_invariant_substs(current, uv.substs, variance);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds constraints appropriate for a function with signature
|
||||
/// `sig` appearing in a context with ambient variance `variance`
|
||||
fn add_constraints_from_sig(
|
||||
&mut self,
|
||||
current: &CurrentItem,
|
||||
sig: ty::PolyFnSig<'tcx>,
|
||||
variance: VarianceTermPtr<'a>,
|
||||
) {
|
||||
let contra = self.contravariant(variance);
|
||||
for &input in sig.skip_binder().inputs() {
|
||||
self.add_constraints_from_ty(current, input, contra);
|
||||
}
|
||||
self.add_constraints_from_ty(current, sig.skip_binder().output(), variance);
|
||||
}
|
||||
|
||||
/// Adds constraints appropriate for a region appearing in a
|
||||
/// context with ambient variance `variance`
|
||||
fn add_constraints_from_region(
|
||||
&mut self,
|
||||
current: &CurrentItem,
|
||||
region: ty::Region<'tcx>,
|
||||
variance: VarianceTermPtr<'a>,
|
||||
) {
|
||||
match *region {
|
||||
ty::ReEarlyBound(ref data) => {
|
||||
self.add_constraint(current, data.index, variance);
|
||||
}
|
||||
|
||||
ty::ReStatic => {}
|
||||
|
||||
ty::ReLateBound(..) => {
|
||||
// Late-bound regions do not get substituted the same
|
||||
// way early-bound regions do, so we skip them here.
|
||||
}
|
||||
|
||||
ty::ReFree(..) | ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReErased => {
|
||||
// We don't expect to see anything but 'static or bound
|
||||
// regions when visiting member types or method types.
|
||||
bug!(
|
||||
"unexpected region encountered in variance \
|
||||
inference: {:?}",
|
||||
region
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds constraints appropriate for a mutability-type pair
|
||||
/// appearing in a context with ambient variance `variance`
|
||||
fn add_constraints_from_mt(
|
||||
&mut self,
|
||||
current: &CurrentItem,
|
||||
mt: &ty::TypeAndMut<'tcx>,
|
||||
variance: VarianceTermPtr<'a>,
|
||||
) {
|
||||
match mt.mutbl {
|
||||
hir::Mutability::Mut => {
|
||||
let invar = self.invariant(variance);
|
||||
self.add_constraints_from_ty(current, mt.ty, invar);
|
||||
}
|
||||
|
||||
hir::Mutability::Not => {
|
||||
self.add_constraints_from_ty(current, mt.ty, variance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue