From 3349e7bb45c18485f1ba090f0f80bda67abd214e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 8 Nov 2017 05:45:20 -0500 Subject: [PATCH] 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`. --- src/librustc/infer/freshen.rs | 16 +---- src/librustc/infer/type_variable.rs | 5 +- src/librustc/ty/mod.rs | 10 +++ src/librustc/ty/sty.rs | 95 +++++++++++++++++++++++++--- src/librustc_typeck/check/closure.rs | 2 +- src/librustc_typeck/check/upvar.rs | 8 +++ src/librustc_typeck/collect.rs | 13 +++- 7 files changed, 121 insertions(+), 28 deletions(-) diff --git a/src/librustc/infer/freshen.rs b/src/librustc/infer/freshen.rs index 41e7dffe54dc..6d1ede8f1a5d 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 cc91a637b893..6aa094d2cd6d 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 a584f2ce1919..93cb4344cb3b 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 65406c3d16cc..bc55ffdc31fd 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> + '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 { + 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 { + 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 d475fb0cf1a1..90c4654dac9b 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 63cdac10321d..c4cdd958680d 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 b5fbbeb1692e..9ee3300e7537 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(""), + 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(""), def_id,