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:
parent
1d96819dc0
commit
3349e7bb45
7 changed files with 121 additions and 28 deletions
|
|
@ -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)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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!(
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue