Skip to content

Commit

Permalink
Rollup merge of rust-lang#74314 - eddyb:closure-substs-direct, r=niko…
Browse files Browse the repository at this point in the history
…matsakis

rustc_typeck: construct {Closure,Generator}Substs more directly.

We've previously not had a way to create `{Closure,Generator}Substs` other than instantiating all generics as inference variables and unifying the inference types (extracted using the regular `{Closure,Generator}Substs` accessors), with the actual types.

With this PR, those hacks, and assumptions about the order of closure/generator-specific components, are replaced with a simple API where the base `Substs` are combined with the additional information into a `{Closure,Generator}Substs`.
This might also be faster than relying inference, although probably not by much.

r? @nikomatsakis cc rust-lang#53488 @blitzerr
  • Loading branch information
tmandry committed Aug 16, 2020
2 parents 8eba138 + 5d44d54 commit c518347
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 82 deletions.
1 change: 1 addition & 0 deletions src/librustc_middle/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ pub use self::sty::{Binder, BoundTy, BoundTyKind, BoundVar, DebruijnIndex, INNER
pub use self::sty::{BoundRegion, EarlyBoundRegion, FreeRegion, Region};
pub use self::sty::{CanonicalPolyFnSig, FnSig, GenSig, PolyFnSig, PolyGenSig};
pub use self::sty::{ClosureSubsts, GeneratorSubsts, TypeAndMut, UpvarSubsts};
pub use self::sty::{ClosureSubstsParts, GeneratorSubstsParts};
pub use self::sty::{ConstVid, FloatVid, IntVid, RegionVid, TyVid};
pub use self::sty::{ExistentialPredicate, InferTy, ParamConst, ParamTy, ProjectionTy};
pub use self::sty::{ExistentialProjection, PolyExistentialProjection};
Expand Down
94 changes: 67 additions & 27 deletions src/librustc_middle/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,24 +325,39 @@ pub struct ClosureSubsts<'tcx> {
pub substs: SubstsRef<'tcx>,
}

/// Struct returned by `split()`. Note that these are subslices of the
/// parent slice and not canonical substs themselves.
struct SplitClosureSubsts<'tcx> {
parent: &'tcx [GenericArg<'tcx>],
closure_kind_ty: GenericArg<'tcx>,
closure_sig_as_fn_ptr_ty: GenericArg<'tcx>,
tupled_upvars_ty: GenericArg<'tcx>,
/// Struct returned by `split()`.
pub struct ClosureSubstsParts<'tcx, T> {
pub parent_substs: &'tcx [GenericArg<'tcx>],
pub closure_kind_ty: T,
pub closure_sig_as_fn_ptr_ty: T,
pub tupled_upvars_ty: T,
}

impl<'tcx> ClosureSubsts<'tcx> {
/// Divides the closure substs into their respective
/// components. Single source of truth with respect to the
/// ordering.
fn split(self) -> SplitClosureSubsts<'tcx> {
/// Construct `ClosureSubsts` from `ClosureSubstsParts`, containing `Substs`
/// for the closure parent, alongside additional closure-specific components.
pub fn new(
tcx: TyCtxt<'tcx>,
parts: ClosureSubstsParts<'tcx, Ty<'tcx>>,
) -> ClosureSubsts<'tcx> {
ClosureSubsts {
substs: tcx.mk_substs(
parts.parent_substs.iter().copied().chain(
[parts.closure_kind_ty, parts.closure_sig_as_fn_ptr_ty, parts.tupled_upvars_ty]
.iter()
.map(|&ty| ty.into()),
),
),
}
}

/// Divides the closure substs into their respective components.
/// The ordering assumed here must match that used by `ClosureSubsts::new` above.
fn split(self) -> ClosureSubstsParts<'tcx, GenericArg<'tcx>> {
match self.substs[..] {
[ref parent @ .., closure_kind_ty, closure_sig_as_fn_ptr_ty, tupled_upvars_ty] => {
SplitClosureSubsts {
parent,
[ref parent_substs @ .., closure_kind_ty, closure_sig_as_fn_ptr_ty, tupled_upvars_ty] => {
ClosureSubstsParts {
parent_substs,
closure_kind_ty,
closure_sig_as_fn_ptr_ty,
tupled_upvars_ty,
Expand All @@ -363,7 +378,7 @@ impl<'tcx> ClosureSubsts<'tcx> {

/// Returns the substitutions of the closure's parent.
pub fn parent_substs(self) -> &'tcx [GenericArg<'tcx>] {
self.split().parent
self.split().parent_substs
}

#[inline]
Expand Down Expand Up @@ -418,21 +433,46 @@ pub struct GeneratorSubsts<'tcx> {
pub substs: SubstsRef<'tcx>,
}

struct SplitGeneratorSubsts<'tcx> {
parent: &'tcx [GenericArg<'tcx>],
resume_ty: GenericArg<'tcx>,
yield_ty: GenericArg<'tcx>,
return_ty: GenericArg<'tcx>,
witness: GenericArg<'tcx>,
tupled_upvars_ty: GenericArg<'tcx>,
pub struct GeneratorSubstsParts<'tcx, T> {
pub parent_substs: &'tcx [GenericArg<'tcx>],
pub resume_ty: T,
pub yield_ty: T,
pub return_ty: T,
pub witness: T,
pub tupled_upvars_ty: T,
}

impl<'tcx> GeneratorSubsts<'tcx> {
fn split(self) -> SplitGeneratorSubsts<'tcx> {
/// Construct `GeneratorSubsts` from `GeneratorSubstsParts`, containing `Substs`
/// for the generator parent, alongside additional generator-specific components.
pub fn new(
tcx: TyCtxt<'tcx>,
parts: GeneratorSubstsParts<'tcx, Ty<'tcx>>,
) -> GeneratorSubsts<'tcx> {
GeneratorSubsts {
substs: tcx.mk_substs(
parts.parent_substs.iter().copied().chain(
[
parts.resume_ty,
parts.yield_ty,
parts.return_ty,
parts.witness,
parts.tupled_upvars_ty,
]
.iter()
.map(|&ty| ty.into()),
),
),
}
}

/// Divides the generator substs into their respective components.
/// The ordering assumed here must match that used by `GeneratorSubsts::new` above.
fn split(self) -> GeneratorSubstsParts<'tcx, GenericArg<'tcx>> {
match self.substs[..] {
[ref parent @ .., resume_ty, yield_ty, return_ty, witness, tupled_upvars_ty] => {
SplitGeneratorSubsts {
parent,
[ref parent_substs @ .., resume_ty, yield_ty, return_ty, witness, tupled_upvars_ty] => {
GeneratorSubstsParts {
parent_substs,
resume_ty,
yield_ty,
return_ty,
Expand All @@ -455,7 +495,7 @@ impl<'tcx> GeneratorSubsts<'tcx> {

/// Returns the substitutions of the generator's parent.
pub fn parent_substs(self) -> &'tcx [GenericArg<'tcx>] {
self.split().parent
self.split().parent_substs
}

/// This describes the types that can be contained in a generator.
Expand Down
105 changes: 50 additions & 55 deletions src/librustc_typeck/check/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use rustc_infer::infer::LateBoundRegionConversionTime;
use rustc_infer::infer::{InferOk, InferResult};
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::{self, GenericParamDefKind, Ty};
use rustc_middle::ty::{self, Ty};
use rustc_span::source_map::Span;
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits::error_reporting::ArgKind;
Expand Down Expand Up @@ -76,60 +76,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let generator_types =
check_fn(self, self.param_env, liberated_sig, decl, expr.hir_id, body, gen).1;

let base_substs = InternalSubsts::identity_for_item(
let parent_substs = InternalSubsts::identity_for_item(
self.tcx,
self.tcx.closure_base_def_id(expr_def_id.to_def_id()),
);
// HACK(eddyb) this hardcodes indices into substs but it should rely on
// `ClosureSubsts` and `GeneratorSubsts` providing constructors, instead.
// That would also remove the need for most of the inference variables,
// as they immediately unified with the actual type below, including
// the `InferCtxt::closure_sig` and `ClosureSubsts::sig_ty` methods.
let tupled_upvars_idx = base_substs.len() + if generator_types.is_some() { 4 } else { 2 };
let substs =
base_substs.extend_to(self.tcx, expr_def_id.to_def_id(), |param, _| match param.kind {
GenericParamDefKind::Lifetime => span_bug!(expr.span, "closure has lifetime param"),
GenericParamDefKind::Type { .. } => if param.index as usize == tupled_upvars_idx {
self.tcx.mk_tup(self.tcx.upvars_mentioned(expr_def_id).iter().flat_map(
|upvars| {
upvars.iter().map(|(&var_hir_id, _)| {
// Create type variables (for now) to represent the transformed
// types of upvars. These will be unified during the upvar
// inference phase (`upvar.rs`).
self.infcx.next_ty_var(TypeVariableOrigin {
// FIXME(eddyb) distinguish upvar inference variables from the rest.
kind: TypeVariableOriginKind::ClosureSynthetic,
span: self.tcx.hir().span(var_hir_id),
})
})
},
))
} else {
// Create type variables (for now) to represent the various
// pieces of information kept in `{Closure,Generic}Substs`.
// They will either be unified below, or later during the upvar
// inference phase (`upvar.rs`)

let tupled_upvars_ty =
self.tcx.mk_tup(self.tcx.upvars_mentioned(expr_def_id).iter().flat_map(|upvars| {
upvars.iter().map(|(&var_hir_id, _)| {
// Create type variables (for now) to represent the transformed
// types of upvars. These will be unified during the upvar
// inference phase (`upvar.rs`).
self.infcx.next_ty_var(TypeVariableOrigin {
// FIXME(eddyb) distinguish upvar inference variables from the rest.
kind: TypeVariableOriginKind::ClosureSynthetic,
span: expr.span,
span: self.tcx.hir().span(var_hir_id),
})
}
.into(),
GenericParamDefKind::Const => span_bug!(expr.span, "closure has const param"),
});
})
}));

if let Some(GeneratorTypes { resume_ty, yield_ty, interior, movability }) = generator_types
{
let generator_substs = substs.as_generator();
self.demand_eqtype(expr.span, resume_ty, generator_substs.resume_ty());
self.demand_eqtype(expr.span, yield_ty, generator_substs.yield_ty());
self.demand_eqtype(expr.span, liberated_sig.output(), generator_substs.return_ty());
self.demand_eqtype(expr.span, interior, generator_substs.witness());

// HACK(eddyb) this forces the types equated above into `substs` but
// it should rely on `GeneratorSubsts` providing a constructor, instead.
let substs = self.resolve_vars_if_possible(&substs);
let generator_substs = ty::GeneratorSubsts::new(
self.tcx,
ty::GeneratorSubstsParts {
parent_substs,
resume_ty,
yield_ty,
return_ty: liberated_sig.output(),
witness: interior,
tupled_upvars_ty,
},
);

return self.tcx.mk_generator(expr_def_id.to_def_id(), substs, movability);
return self.tcx.mk_generator(
expr_def_id.to_def_id(),
generator_substs.substs,
movability,
);
}

// Tuple up the arguments and insert the resulting function type into
Expand All @@ -149,18 +133,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expr_def_id, sig, opt_kind
);

let sig_fn_ptr_ty = self.tcx.mk_fn_ptr(sig);
self.demand_eqtype(expr.span, sig_fn_ptr_ty, substs.as_closure().sig_as_fn_ptr_ty());
let closure_kind_ty = match opt_kind {
Some(kind) => kind.to_ty(self.tcx),

if let Some(kind) = opt_kind {
self.demand_eqtype(expr.span, kind.to_ty(self.tcx), substs.as_closure().kind_ty());
}
// Create a type variable (for now) to represent the closure kind.
// It will be unified during the upvar inference phase (`upvar.rs`)
None => self.infcx.next_ty_var(TypeVariableOrigin {
// FIXME(eddyb) distinguish closure kind inference variables from the rest.
kind: TypeVariableOriginKind::ClosureSynthetic,
span: expr.span,
}),
};

// HACK(eddyb) this forces the types equated above into `substs` but
// it should rely on `ClosureSubsts` providing a constructor, instead.
let substs = self.resolve_vars_if_possible(&substs);
let closure_substs = ty::ClosureSubsts::new(
self.tcx,
ty::ClosureSubstsParts {
parent_substs,
closure_kind_ty,
closure_sig_as_fn_ptr_ty: self.tcx.mk_fn_ptr(sig),
tupled_upvars_ty,
},
);

let closure_type = self.tcx.mk_closure(expr_def_id.to_def_id(), substs);
let closure_type = self.tcx.mk_closure(expr_def_id.to_def_id(), closure_substs.substs);

debug!("check_closure: expr.hir_id={:?} closure_type={:?}", expr.hir_id, closure_type);

Expand Down

0 comments on commit c518347

Please sign in to comment.