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`.
This commit is contained in:
Niko Matsakis 2017-11-08 05:45:20 -05:00
parent 1d96819dc0
commit 3349e7bb45
7 changed files with 121 additions and 28 deletions

View file

@ -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)
)

View file

@ -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),

View file

@ -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> {

View file

@ -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.

View file

@ -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);

View file

@ -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!(

View file

@ -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,