about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2017-11-08 05:45:20 -0500
committerNiko Matsakis <niko@alum.mit.edu>2017-11-18 07:47:36 -0500
commit3349e7bb45c18485f1ba090f0f80bda67abd214e (patch)
treed4ca86d5eff7043f850118de41fc25cad31b3897
parent1d96819dc0c300a22c08e57051a93652d728931a (diff)
downloadrust-3349e7bb45c18485f1ba090f0f80bda67abd214e.tar.gz
rust-3349e7bb45c18485f1ba090f0f80bda67abd214e.zip
thread the closure-kind through in the closure substs
Similar to how freshen handled things, but "always happening"; we can
thus remove the corresponding code from `freshen`.
-rw-r--r--src/librustc/infer/freshen.rs16
-rw-r--r--src/librustc/infer/type_variable.rs5
-rw-r--r--src/librustc/ty/mod.rs10
-rw-r--r--src/librustc/ty/sty.rs95
-rw-r--r--src/librustc_typeck/check/closure.rs2
-rw-r--r--src/librustc_typeck/check/upvar.rs8
-rw-r--r--src/librustc_typeck/collect.rs13
7 files changed, 121 insertions, 28 deletions
diff --git a/src/librustc/infer/freshen.rs b/src/librustc/infer/freshen.rs
index 41e7dffe54d..6d1ede8f1a5 100644
--- a/src/librustc/infer/freshen.rs
+++ b/src/librustc/infer/freshen.rs
@@ -253,22 +253,8 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> {
                 self.freshen_closure_like(
                     def_id, substs, t,
                     |this| {
-                        // HACK: use a "random" integer type to mark the kind. Because
-                        // different closure kinds shouldn't get unified during
-                        // selection, the "subtyping" relationship (where any kind is
-                        // better than no kind) shouldn't  matter here, just that the
-                        // types are different.
-                        let closure_kind = this.infcx.closure_kind(def_id);
-                        let closure_kind_marker = match closure_kind {
-                            None => tcx.types.i8,
-                            Some(ty::ClosureKind::Fn) => tcx.types.i16,
-                            Some(ty::ClosureKind::FnMut) => tcx.types.i32,
-                            Some(ty::ClosureKind::FnOnce) => tcx.types.i64,
-                        };
-
                         let closure_sig = this.infcx.fn_sig(def_id);
-                        (tcx.mk_fn_ptr(closure_sig.fold_with(this)),
-                         closure_kind_marker)
+                        (tcx.mk_fn_ptr(closure_sig.fold_with(this)), tcx.types.char)
                     },
                     |substs| tcx.mk_closure(def_id, substs)
                 )
diff --git a/src/librustc/infer/type_variable.rs b/src/librustc/infer/type_variable.rs
index cc91a637b89..6aa094d2cd6 100644
--- a/src/librustc/infer/type_variable.rs
+++ b/src/librustc/infer/type_variable.rs
@@ -56,7 +56,10 @@ pub enum TypeVariableOrigin {
     NormalizeProjectionType(Span),
     TypeInference(Span),
     TypeParameterDefinition(Span, ast::Name),
-    TransformedUpvar(Span),
+
+    /// one of the upvars or closure kind parameters in a `ClosureSubsts`
+    /// (before it has been determined)
+    ClosureSynthetic(Span),
     SubstitutionPlaceholder(Span),
     AutoDeref(Span),
     AdjustmentType(Span),
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index a584f2ce191..93cb4344cb3 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -1962,6 +1962,16 @@ impl<'a, 'tcx> ClosureKind {
             _ => false,
         }
     }
+
+    /// Returns the representative scalar type for this closure kind.
+    /// See `TyS::to_opt_closure_kind` for more details.
+    pub fn to_ty(self, tcx: TyCtxt<'_, '_, 'tcx>) -> Ty<'tcx> {
+        match self {
+            ty::ClosureKind::Fn => tcx.types.i8,
+            ty::ClosureKind::FnMut => tcx.types.i16,
+            ty::ClosureKind::FnOnce => tcx.types.i32,
+        }
+    }
 }
 
 impl<'tcx> TyS<'tcx> {
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index 65406c3d16c..bc55ffdc31f 100644
--- a/src/librustc/ty/sty.rs
+++ b/src/librustc/ty/sty.rs
@@ -174,16 +174,22 @@ pub enum TypeVariants<'tcx> {
 
 /// A closure can be modeled as a struct that looks like:
 ///
-///     struct Closure<'l0...'li, T0...Tj, U0...Uk> {
+///     struct Closure<'l0...'li, T0...Tj, CK, U0...Uk> {
 ///         upvar0: U0,
 ///         ...
 ///         upvark: Uk
 ///     }
 ///
-/// where 'l0...'li and T0...Tj are the lifetime and type parameters
-/// in scope on the function that defined the closure, and U0...Uk are
-/// type parameters representing the types of its upvars (borrowed, if
-/// appropriate).
+/// where:
+///
+/// - 'l0...'li and T0...Tj are the lifetime and type parameters
+///   in scope on the function that defined the closure,
+/// - CK represents the *closure kind* (Fn vs FnMut vs FnOnce). This
+///   is rather hackily encoded via a scalar type. See
+///   `TyS::to_opt_closure_kind` for details.
+/// - U0...Uk are type parameters representing the types of its upvars
+///   (borrowed, if appropriate; that is, if Ui represents a by-ref upvar,
+///    and the up-var has the type `Foo`, then `Ui = &Foo`).
 ///
 /// So, for example, given this function:
 ///
@@ -256,14 +262,54 @@ pub struct ClosureSubsts<'tcx> {
     pub substs: &'tcx Substs<'tcx>,
 }
 
-impl<'a, 'gcx, 'acx, 'tcx> ClosureSubsts<'tcx> {
+/// Struct returned by `split()`. Note that these are subslices of the
+/// parent slice and not canonical substs themselves.
+struct SplitClosureSubsts<'tcx> {
+    closure_kind_ty: Ty<'tcx>,
+    upvar_kinds: &'tcx [Kind<'tcx>],
+}
+
+impl<'tcx> ClosureSubsts<'tcx> {
+    /// Divides the closure substs into their respective
+    /// components. Single source of truth with respect to the
+    /// ordering.
+    fn split(self, def_id: DefId, tcx: TyCtxt<'_, '_, '_>) -> SplitClosureSubsts<'tcx> {
+        let generics = tcx.generics_of(def_id);
+        let parent_len = generics.parent_count();
+        SplitClosureSubsts {
+            closure_kind_ty: self.substs[parent_len].as_type().expect("closure-kind should be type"),
+            upvar_kinds: &self.substs[parent_len + 1..],
+        }
+    }
+
     #[inline]
-    pub fn upvar_tys(self, def_id: DefId, tcx: TyCtxt<'a, 'gcx, 'acx>) ->
+    pub fn upvar_tys(self, def_id: DefId, tcx: TyCtxt<'_, '_, '_>) ->
         impl Iterator<Item=Ty<'tcx>> + 'tcx
     {
-        let generics = tcx.generics_of(def_id);
-        self.substs[self.substs.len()-generics.own_count()..].iter().map(
-            |t| t.as_type().expect("unexpected region in upvars"))
+        let SplitClosureSubsts { upvar_kinds, .. } = self.split(def_id, tcx);
+        upvar_kinds.iter().map(|t| t.as_type().expect("upvar should be type"))
+    }
+
+    /// Returns the closure kind for this closure; may return `None`
+    /// if inference has not yet completed.
+    pub fn opt_closure_kind(self, def_id: DefId, tcx: TyCtxt<'_, '_, '_>)
+                            -> Option<ty::ClosureKind> {
+        let closure_kind_ty = self.closure_kind_ty(def_id, tcx);
+        closure_kind_ty.to_opt_closure_kind()
+    }
+
+    /// Returns the closure kind for this closure; may return `None`
+    /// if inference has not yet completed.
+    pub fn closure_kind_ty(self, def_id: DefId, tcx: TyCtxt<'_, '_, '_>) -> Ty<'tcx> {
+        self.split(def_id, tcx).closure_kind_ty
+    }
+}
+
+impl<'tcx> ClosureSubsts<'tcx> {
+    /// Returns the closure kind for this closure; only usable outside
+    /// of an inference context.
+    pub fn closure_kind(self, def_id: DefId, tcx: TyCtxt<'_, 'tcx, 'tcx>) -> ty::ClosureKind {
+        self.opt_closure_kind(def_id, tcx).unwrap()
     }
 }
 
@@ -1442,6 +1488,35 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
             }
         }
     }
+
+    /// When we create a closure, we record its kind (i.e., what trait
+    /// it implements) into its `ClosureSubsts` using a type
+    /// parameter. This is kind of a phantom type, except that the
+    /// most convenient thing for us to are the integral types. This
+    /// function converts such a special type into the closure
+    /// kind. To go the other way, use
+    /// `tcx.closure_kind_ty(closure_kind)`.
+    ///
+    /// Note that during type checking, we use an inference variable
+    /// to represent the closure kind, because it has not yet been
+    /// inferred. Once [upvar inference] is complete, that type varibale
+    /// will be unified.
+    ///
+    /// [upvar inference]: src/librustc_typeck/check/upvar.rs
+    pub fn to_opt_closure_kind(&self) -> Option<ty::ClosureKind> {
+        match self.sty {
+            TyInt(int_ty) => match int_ty {
+                ast::IntTy::I8 => Some(ty::ClosureKind::Fn),
+                ast::IntTy::I16 => Some(ty::ClosureKind::FnMut),
+                ast::IntTy::I32 => Some(ty::ClosureKind::FnOnce),
+                _ => bug!("cannot convert type `{:?}` to a closure kind", self),
+            },
+
+            TyInfer(_) => None,
+
+            _ => bug!("cannot convert type `{:?}` to a closure kind", self),
+        }
+    }
 }
 
 /// Typed constant value.
diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs
index d475fb0cf1a..90c4654dac9 100644
--- a/src/librustc_typeck/check/closure.rs
+++ b/src/librustc_typeck/check/closure.rs
@@ -100,7 +100,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             |_, _| span_bug!(expr.span, "closure has region param"),
             |_, _| {
                 self.infcx
-                    .next_ty_var(TypeVariableOrigin::TransformedUpvar(expr.span))
+                    .next_ty_var(TypeVariableOrigin::ClosureSynthetic(expr.span))
             },
         );
         let closure_type = self.tcx.mk_closure(expr_def_id, substs);
diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs
index 63cdac10321..c4cdd958680 100644
--- a/src/librustc_typeck/check/upvar.rs
+++ b/src/librustc_typeck/check/upvar.rs
@@ -46,6 +46,7 @@ use middle::expr_use_visitor as euv;
 use middle::mem_categorization as mc;
 use middle::mem_categorization::Categorization;
 use rustc::ty::{self, Ty, TyCtxt};
+use rustc::ty::TypeFoldable;
 use rustc::infer::UpvarRegion;
 use syntax::ast;
 use syntax_pos::Span;
@@ -216,6 +217,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             }
         };
 
+        // Equate the type variable representing the closure kind.
+        let closure_kind_ty = closure_substs.closure_kind_ty(def_id, self.tcx);
+        if closure_kind_ty.needs_infer() {
+            let final_closure_kind = self.tables.borrow().closure_kinds()[closure_hir_id].0;
+            self.demand_eqtype(span, final_closure_kind.to_ty(self.tcx), closure_kind_ty);
+        }
+
         // Equate the type variables with the actual types.
         let final_upvar_tys = self.final_upvar_tys(closure_node_id);
         debug!(
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index b5fbbeb1692..9ee3300e753 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -1015,8 +1015,19 @@ fn generics_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     // cares about anything but the length is instantiation,
     // and we don't do that for closures.
     if let NodeExpr(&hir::Expr { node: hir::ExprClosure(..), .. }) = node {
+        // add a dummy parameter for the closure kind
+        types.push(ty::TypeParameterDef {
+            index: type_start as u32,
+            name: Symbol::intern("<closure_kind>"),
+            def_id,
+            has_default: false,
+            object_lifetime_default: rl::Set1::Empty,
+            pure_wrt_drop: false,
+            synthetic: None,
+        });
+
         tcx.with_freevars(node_id, |fv| {
-            types.extend(fv.iter().enumerate().map(|(i, _)| ty::TypeParameterDef {
+            types.extend(fv.iter().zip(1..).map(|(_, i)| ty::TypeParameterDef {
                 index: type_start + i as u32,
                 name: Symbol::intern("<upvar>"),
                 def_id,