summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_typeck/src/variance/constraints.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_typeck/src/variance/constraints.rs')
-rw-r--r--compiler/rustc_typeck/src/variance/constraints.rs449
1 files changed, 449 insertions, 0 deletions
diff --git a/compiler/rustc_typeck/src/variance/constraints.rs b/compiler/rustc_typeck/src/variance/constraints.rs
new file mode 100644
index 000000000..d79450e1a
--- /dev/null
+++ b/compiler/rustc_typeck/src/variance/constraints.rs
@@ -0,0 +1,449 @@
+//! 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 {
+ ty::Term::Ty(ty) => {
+ self.add_constraints_from_ty(current, ty, self.invariant);
+ }
+ ty::Term::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::ReEmpty(_)
+ | 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);
+ }
+ }
+ }
+}