Skip to content

Commit

Permalink
Auto merge of #77873 - sexxi-goose:use_tuple_inference_for_closures, …
Browse files Browse the repository at this point in the history
…r=nikomatsakis

Replace tuple of infer vars for upvar_tys with single infer var

This commit allows us to decide the number of captures required after
completing capture ananysis, which is required as part of implementing
RFC-2229.

closes rust-lang/project-rfc-2229#4
r? `@nikomatsakis`
  • Loading branch information
bors committed Oct 15, 2020
2 parents 19e1aac + a64ad51 commit 93deabc
Show file tree
Hide file tree
Showing 18 changed files with 152 additions and 85 deletions.
10 changes: 4 additions & 6 deletions compiler/rustc_middle/src/ty/outlives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,16 +96,14 @@ fn compute_components(
}

ty::Closure(_, ref substs) => {
for upvar_ty in substs.as_closure().upvar_tys() {
compute_components(tcx, upvar_ty, out, visited);
}
let tupled_ty = substs.as_closure().tupled_upvars_ty();
compute_components(tcx, tupled_ty, out, visited);
}

ty::Generator(_, ref substs, _) => {
// Same as the closure case
for upvar_ty in substs.as_generator().upvar_tys() {
compute_components(tcx, upvar_ty, out, visited);
}
let tupled_ty = substs.as_generator().tupled_upvars_ty();
compute_components(tcx, tupled_ty, out, visited);

// We ignore regions in the generator interior as we don't
// want these to affect region inference
Expand Down
46 changes: 17 additions & 29 deletions compiler/rustc_middle/src/ty/print/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -663,18 +663,13 @@ pub trait PrettyPrinter<'tcx>:
}
} else {
p!(print_def_path(did, substs));
if substs.as_generator().is_valid() {
// Search for the first inference variable
p!(" upvar_tys=(");
let mut uninferred_ty =
substs.as_generator().upvar_tys().filter(|ty| ty.is_ty_infer());
if uninferred_ty.next().is_some() {
p!(write("unavailable"));
} else {
self = self.comma_sep(substs.as_generator().upvar_tys())?;
}
p!(")");
p!(" upvar_tys=(");
if !substs.as_generator().is_valid() {
p!("unavailable");
} else {
self = self.comma_sep(substs.as_generator().upvar_tys())?;
}
p!(")");
}

if substs.as_generator().is_valid() {
Expand Down Expand Up @@ -704,24 +699,17 @@ pub trait PrettyPrinter<'tcx>:
}
} else {
p!(print_def_path(did, substs));
if substs.as_closure().is_valid() {
// Search for the first inference variable
let mut uninferred_ty =
substs.as_closure().upvar_tys().filter(|ty| ty.is_ty_infer());
if uninferred_ty.next().is_some() {
// If the upvar substs contain an inference variable we haven't
// finished capture analysis.
p!(" closure_substs=(unavailable)");
} else {
p!(" closure_kind_ty=", print(substs.as_closure().kind_ty()));
p!(
" closure_sig_as_fn_ptr_ty=",
print(substs.as_closure().sig_as_fn_ptr_ty())
);
p!(" upvar_tys=(");
self = self.comma_sep(substs.as_closure().upvar_tys())?;
p!(")");
}
if !substs.as_closure().is_valid() {
p!(" closure_substs=(unavailable)");
} else {
p!(" closure_kind_ty=", print(substs.as_closure().kind_ty()));
p!(
" closure_sig_as_fn_ptr_ty=",
print(substs.as_closure().sig_as_fn_ptr_ty())
);
p!(" upvar_tys=(");
self = self.comma_sep(substs.as_closure().upvar_tys())?;
p!(")");
}
}
p!("]");
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_middle/src/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,14 @@ impl<'tcx> UpvarSubsts<'tcx> {
};
tupled_upvars_ty.expect_ty().tuple_fields()
}

#[inline]
pub fn tupled_upvars_ty(self) -> Ty<'tcx> {
match self {
UpvarSubsts::Closure(substs) => substs.as_closure().tupled_upvars_ty(),
UpvarSubsts::Generator(substs) => substs.as_generator().tupled_upvars_ty(),
}
}
}

#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash, TyEncodable, TyDecodable)]
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_trait_selection/src/opaque_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,8 @@ where
ty::Closure(_, ref substs) => {
// Skip lifetime parameters of the enclosing item(s)

substs.as_closure().tupled_upvars_ty().visit_with(self);

for upvar_ty in substs.as_closure().upvar_tys() {
upvar_ty.visit_with(self);
}
Expand All @@ -728,6 +730,8 @@ where
// Skip lifetime parameters of the enclosing item(s)
// Also skip the witness type, because that has no free regions.

substs.as_generator().tupled_upvars_ty().visit_with(self);

for upvar_ty in substs.as_generator().upvar_tys() {
upvar_ty.visit_with(self);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1308,6 +1308,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
let mut generator = None;
let mut outer_generator = None;
let mut next_code = Some(&obligation.cause.code);

let mut seen_upvar_tys_infer_tuple = false;

while let Some(code) = next_code {
debug!("maybe_note_obligation_cause_for_async_await: code={:?}", code);
match code {
Expand All @@ -1328,6 +1331,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
outer_generator = Some(did);
}
ty::GeneratorWitness(..) => {}
ty::Tuple(_) if !seen_upvar_tys_infer_tuple => {
// By introducing a tuple of upvar types into the chain of obligations
// of a generator, the first non-generator item is now the tuple itself,
// we shall ignore this.

seen_upvar_tys_infer_tuple = true;
}
_ if generator.is_none() => {
trait_ref = Some(derived_obligation.parent_trait_ref.skip_binder());
target_ty = Some(ty);
Expand Down Expand Up @@ -1913,7 +1923,29 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
return;
}

err.note(&format!("required because it appears within the type `{}`", ty));
// If the obligation for a tuple is set directly by a Generator or Closure,
// then the tuple must be the one containing capture types.
let is_upvar_tys_infer_tuple = if !matches!(ty.kind(), ty::Tuple(..)) {
false
} else {
if let ObligationCauseCode::BuiltinDerivedObligation(ref data) =
*data.parent_code
{
let parent_trait_ref =
self.resolve_vars_if_possible(&data.parent_trait_ref);
let ty = parent_trait_ref.skip_binder().self_ty();
matches!(ty.kind(), ty::Generator(..))
|| matches!(ty.kind(), ty::Closure(..))
} else {
false
}
};

// Don't print the tuple of capture types
if !is_upvar_tys_infer_tuple {
err.note(&format!("required because it appears within the type `{}`", ty));
}

obligated_types.push(ty);

let parent_predicate = parent_trait_ref.without_const().to_predicate(tcx);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
// check if *any* of those are trivial.
ty::Tuple(ref tys) => tys.iter().all(|t| trivial_dropck_outlives(tcx, t.expect_ty())),
ty::Closure(_, ref substs) => {
substs.as_closure().upvar_tys().all(|t| trivial_dropck_outlives(tcx, t))
trivial_dropck_outlives(tcx, substs.as_closure().tupled_upvars_ty())
}

ty::Adt(def, _) => {
Expand Down
16 changes: 13 additions & 3 deletions compiler/rustc_trait_selection/src/traits/select/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1600,7 +1600,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {

ty::Closure(_, substs) => {
// (*) binder moved here
Where(ty::Binder::bind(substs.as_closure().upvar_tys().collect()))
let ty = self.infcx.shallow_resolve(substs.as_closure().tupled_upvars_ty());
if let ty::Infer(ty::TyVar(_)) = ty.kind() {
// Not yet resolved.
Ambiguous
} else {
Where(ty::Binder::bind(substs.as_closure().upvar_tys().collect()))
}
}

ty::Adt(..) | ty::Projection(..) | ty::Param(..) | ty::Opaque(..) => {
Expand Down Expand Up @@ -1669,11 +1675,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
tys.iter().map(|k| k.expect_ty()).collect()
}

ty::Closure(_, ref substs) => substs.as_closure().upvar_tys().collect(),
ty::Closure(_, ref substs) => {
let ty = self.infcx.shallow_resolve(substs.as_closure().tupled_upvars_ty());
vec![ty]
}

ty::Generator(_, ref substs, _) => {
let ty = self.infcx.shallow_resolve(substs.as_generator().tupled_upvars_ty());
let witness = substs.as_generator().witness();
substs.as_generator().upvar_tys().chain(iter::once(witness)).collect()
vec![ty].into_iter().chain(iter::once(witness)).collect()
}

ty::GeneratorWitness(types) => {
Expand Down
6 changes: 2 additions & 4 deletions compiler/rustc_trait_selection/src/traits/wf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -592,10 +592,8 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
// anyway, except via auto trait matching (which
// only inspects the upvar types).
walker.skip_current_subtree(); // subtree handled below
for upvar_ty in substs.as_closure().upvar_tys() {
// FIXME(eddyb) add the type to `walker` instead of recursing.
self.compute(upvar_ty.into());
}
// FIXME(eddyb) add the type to `walker` instead of recursing.
self.compute(substs.as_closure().tupled_upvars_ty().into());
}

ty::FnPtr(_) => {
Expand Down
33 changes: 28 additions & 5 deletions compiler/rustc_traits/src/dropck_outlives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,12 +210,25 @@ fn dtorck_constraint_for_ty<'tcx>(
Ok::<_, NoSolution>(())
})?,

ty::Closure(_, substs) => rustc_data_structures::stack::ensure_sufficient_stack(|| {
for ty in substs.as_closure().upvar_tys() {
dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty, constraints)?;
ty::Closure(_, substs) => {
if !substs.as_closure().is_valid() {
// By the time this code runs, all type variables ought to
// be fully resolved.

tcx.sess.delay_span_bug(
span,
&format!("upvar_tys for closure not found. Expected capture information for closure {}", ty,),
);
return Err(NoSolution);
}
Ok::<_, NoSolution>(())
})?,

rustc_data_structures::stack::ensure_sufficient_stack(|| {
for ty in substs.as_closure().upvar_tys() {
dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty, constraints)?;
}
Ok::<_, NoSolution>(())
})?
}

ty::Generator(_, substs, _movability) => {
// rust-lang/rust#49918: types can be constructed, stored
Expand All @@ -241,6 +254,16 @@ fn dtorck_constraint_for_ty<'tcx>(
// derived from lifetimes attached to the upvars and resume
// argument, and we *do* incorporate those here.

if !substs.as_generator().is_valid() {
// By the time this code runs, all type variables ought to
// be fully resolved.
tcx.sess.delay_span_bug(
span,
&format!("upvar_tys for generator not found. Expected capture information for generator {}", ty,),
);
return Err(NoSolution);
}

constraints.outlives.extend(
substs
.as_generator()
Expand Down
17 changes: 4 additions & 13 deletions compiler/rustc_typeck/src/check/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,19 +81,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.tcx.closure_base_def_id(expr_def_id.to_def_id()),
);

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: self.tcx.hir().span(var_hir_id),
})
})
}));
let tupled_upvars_ty = self.infcx.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::ClosureSynthetic,
span: self.tcx.hir().span(expr.hir_id),
});

if let Some(GeneratorTypes { resume_ty, yield_ty, interior, movability }) = generator_types
{
Expand Down
23 changes: 18 additions & 5 deletions compiler/rustc_typeck/src/check/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ use crate::astconv::AstConv;
use crate::check::FnCtxt;
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::{Coercion, InferOk, InferResult};
use rustc_middle::ty::adjustment::{
Expand Down Expand Up @@ -221,11 +222,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
// unsafe qualifier.
self.coerce_from_fn_pointer(a, a_f, b)
}
ty::Closure(_, substs_a) => {
ty::Closure(closure_def_id_a, substs_a) => {
// Non-capturing closures are coercible to
// function pointers or unsafe function pointers.
// It cannot convert closures that require unsafe.
self.coerce_closure_to_fn(a, substs_a, b)
self.coerce_closure_to_fn(a, closure_def_id_a, substs_a, b)
}
_ => {
// Otherwise, just use unification rules.
Expand Down Expand Up @@ -762,6 +763,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
fn coerce_closure_to_fn(
&self,
a: Ty<'tcx>,
closure_def_id_a: DefId,
substs_a: SubstsRef<'tcx>,
b: Ty<'tcx>,
) -> CoerceResult<'tcx> {
Expand All @@ -772,7 +774,18 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
let b = self.shallow_resolve(b);

match b.kind() {
ty::FnPtr(fn_ty) if substs_a.as_closure().upvar_tys().next().is_none() => {
// At this point we haven't done capture analysis, which means
// that the ClosureSubsts just contains an inference variable instead
// of tuple of captured types.
//
// All we care here is if any variable is being captured and not the exact paths,
// so we check `upvars_mentioned` for root variables being captured.
ty::FnPtr(fn_ty)
if self
.tcx
.upvars_mentioned(closure_def_id_a.expect_local())
.map_or(true, |u| u.is_empty()) =>
{
// We coerce the closure, which has fn type
// `extern "rust-call" fn((arg0,arg1,...)) -> _`
// to
Expand Down Expand Up @@ -906,8 +919,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Function items or non-capturing closures of differing IDs or InternalSubsts.
let (a_sig, b_sig) = {
let is_capturing_closure = |ty| {
if let &ty::Closure(_, substs) = ty {
substs.as_closure().upvar_tys().next().is_some()
if let &ty::Closure(closure_def_id, _substs) = ty {
self.tcx.upvars_mentioned(closure_def_id.expect_local()).is_some()
} else {
false
}
Expand Down
8 changes: 5 additions & 3 deletions compiler/rustc_typeck/src/check/upvar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,9 +202,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
"analyze_closure: id={:?} substs={:?} final_upvar_tys={:?}",
closure_hir_id, substs, final_upvar_tys
);
for (upvar_ty, final_upvar_ty) in substs.upvar_tys().zip(final_upvar_tys) {
self.demand_suptype(span, upvar_ty, final_upvar_ty);
}

// Build a tuple (U0..Un) of the final upvar types U0..Un
// and unify the upvar tupe type in the closure with it:
let final_tupled_upvars_type = self.tcx.mk_tup(final_upvar_tys.iter());
self.demand_suptype(span, substs.tupled_upvars_ty(), final_tupled_upvars_type);

// If we are also inferred the closure kind here,
// process any deferred resolutions.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error[E0271]: type mismatch resolving `<[generator@$DIR/generator-yielding-or-returning-itself.rs:15:34: 19:6 _] as Generator>::Return == [generator@$DIR/generator-yielding-or-returning-itself.rs:15:34: 19:6 _]`
error[E0271]: type mismatch resolving `<[generator@$DIR/generator-yielding-or-returning-itself.rs:15:34: 19:6] as Generator>::Return == [generator@$DIR/generator-yielding-or-returning-itself.rs:15:34: 19:6]`
--> $DIR/generator-yielding-or-returning-itself.rs:15:5
|
LL | pub fn want_cyclic_generator_return<T>(_: T)
Expand All @@ -14,7 +14,7 @@ LL | want_cyclic_generator_return(|| {
see issue #46062 <https://github.com/rust-lang/rust/issues/46062>
for more information

error[E0271]: type mismatch resolving `<[generator@$DIR/generator-yielding-or-returning-itself.rs:28:33: 32:6 _] as Generator>::Yield == [generator@$DIR/generator-yielding-or-returning-itself.rs:28:33: 32:6 _]`
error[E0271]: type mismatch resolving `<[generator@$DIR/generator-yielding-or-returning-itself.rs:28:33: 32:6] as Generator>::Yield == [generator@$DIR/generator-yielding-or-returning-itself.rs:28:33: 32:6]`
--> $DIR/generator-yielding-or-returning-itself.rs:28:5
|
LL | pub fn want_cyclic_generator_yield<T>(_: T)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ LL | assert_send(|| {
|
= help: the trait `Sync` is not implemented for `Cell<i32>`
= note: required because of the requirements on the impl of `Send` for `&'_#3r Cell<i32>`
= note: required because it appears within the type `[main::{closure#1} upvar_tys=(&'_#3r Cell<i32>) _#16t]`
= note: required because it appears within the type `[main::{closure#1} upvar_tys=(&'_#3r Cell<i32>) _#17t]`

error: generator cannot be shared between threads safely
--> $DIR/generator-print-verbose-2.rs:12:5
Expand Down
Loading

0 comments on commit 93deabc

Please sign in to comment.