//! 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>, } /// 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 { /// b: Bar /// } /// ``` /// then while we are visiting `Bar`, 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_def_id) = variant.ctor_def_id() { constraint_cx.build_constraints_for_item(ctor_def_id.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::Alias(_, ref data) => { self.add_constraints_from_invariant_substs(current, data.substs, variance); } ty::Dynamic(data, r, _) => { // The type `Foo` 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); } } } }