Skip to content

Commit 93deabc

Browse files
committed
Auto merge of #77873 - sexxi-goose:use_tuple_inference_for_closures, 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`
2 parents 19e1aac + a64ad51 commit 93deabc

File tree

18 files changed

+152
-85
lines changed

18 files changed

+152
-85
lines changed

compiler/rustc_middle/src/ty/outlives.rs

+4-6
Original file line numberDiff line numberDiff line change
@@ -96,16 +96,14 @@ fn compute_components(
9696
}
9797

9898
ty::Closure(_, ref substs) => {
99-
for upvar_ty in substs.as_closure().upvar_tys() {
100-
compute_components(tcx, upvar_ty, out, visited);
101-
}
99+
let tupled_ty = substs.as_closure().tupled_upvars_ty();
100+
compute_components(tcx, tupled_ty, out, visited);
102101
}
103102

104103
ty::Generator(_, ref substs, _) => {
105104
// Same as the closure case
106-
for upvar_ty in substs.as_generator().upvar_tys() {
107-
compute_components(tcx, upvar_ty, out, visited);
108-
}
105+
let tupled_ty = substs.as_generator().tupled_upvars_ty();
106+
compute_components(tcx, tupled_ty, out, visited);
109107

110108
// We ignore regions in the generator interior as we don't
111109
// want these to affect region inference

compiler/rustc_middle/src/ty/print/pretty.rs

+17-29
Original file line numberDiff line numberDiff line change
@@ -663,18 +663,13 @@ pub trait PrettyPrinter<'tcx>:
663663
}
664664
} else {
665665
p!(print_def_path(did, substs));
666-
if substs.as_generator().is_valid() {
667-
// Search for the first inference variable
668-
p!(" upvar_tys=(");
669-
let mut uninferred_ty =
670-
substs.as_generator().upvar_tys().filter(|ty| ty.is_ty_infer());
671-
if uninferred_ty.next().is_some() {
672-
p!(write("unavailable"));
673-
} else {
674-
self = self.comma_sep(substs.as_generator().upvar_tys())?;
675-
}
676-
p!(")");
666+
p!(" upvar_tys=(");
667+
if !substs.as_generator().is_valid() {
668+
p!("unavailable");
669+
} else {
670+
self = self.comma_sep(substs.as_generator().upvar_tys())?;
677671
}
672+
p!(")");
678673
}
679674

680675
if substs.as_generator().is_valid() {
@@ -704,24 +699,17 @@ pub trait PrettyPrinter<'tcx>:
704699
}
705700
} else {
706701
p!(print_def_path(did, substs));
707-
if substs.as_closure().is_valid() {
708-
// Search for the first inference variable
709-
let mut uninferred_ty =
710-
substs.as_closure().upvar_tys().filter(|ty| ty.is_ty_infer());
711-
if uninferred_ty.next().is_some() {
712-
// If the upvar substs contain an inference variable we haven't
713-
// finished capture analysis.
714-
p!(" closure_substs=(unavailable)");
715-
} else {
716-
p!(" closure_kind_ty=", print(substs.as_closure().kind_ty()));
717-
p!(
718-
" closure_sig_as_fn_ptr_ty=",
719-
print(substs.as_closure().sig_as_fn_ptr_ty())
720-
);
721-
p!(" upvar_tys=(");
722-
self = self.comma_sep(substs.as_closure().upvar_tys())?;
723-
p!(")");
724-
}
702+
if !substs.as_closure().is_valid() {
703+
p!(" closure_substs=(unavailable)");
704+
} else {
705+
p!(" closure_kind_ty=", print(substs.as_closure().kind_ty()));
706+
p!(
707+
" closure_sig_as_fn_ptr_ty=",
708+
print(substs.as_closure().sig_as_fn_ptr_ty())
709+
);
710+
p!(" upvar_tys=(");
711+
self = self.comma_sep(substs.as_closure().upvar_tys())?;
712+
p!(")");
725713
}
726714
}
727715
p!("]");

compiler/rustc_middle/src/ty/sty.rs

+8
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,14 @@ impl<'tcx> UpvarSubsts<'tcx> {
656656
};
657657
tupled_upvars_ty.expect_ty().tuple_fields()
658658
}
659+
660+
#[inline]
661+
pub fn tupled_upvars_ty(self) -> Ty<'tcx> {
662+
match self {
663+
UpvarSubsts::Closure(substs) => substs.as_closure().tupled_upvars_ty(),
664+
UpvarSubsts::Generator(substs) => substs.as_generator().tupled_upvars_ty(),
665+
}
666+
}
659667
}
660668

661669
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash, TyEncodable, TyDecodable)]

compiler/rustc_trait_selection/src/opaque_types.rs

+4
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,8 @@ where
717717
ty::Closure(_, ref substs) => {
718718
// Skip lifetime parameters of the enclosing item(s)
719719

720+
substs.as_closure().tupled_upvars_ty().visit_with(self);
721+
720722
for upvar_ty in substs.as_closure().upvar_tys() {
721723
upvar_ty.visit_with(self);
722724
}
@@ -728,6 +730,8 @@ where
728730
// Skip lifetime parameters of the enclosing item(s)
729731
// Also skip the witness type, because that has no free regions.
730732

733+
substs.as_generator().tupled_upvars_ty().visit_with(self);
734+
731735
for upvar_ty in substs.as_generator().upvar_tys() {
732736
upvar_ty.visit_with(self);
733737
}

compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs

+33-1
Original file line numberDiff line numberDiff line change
@@ -1308,6 +1308,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
13081308
let mut generator = None;
13091309
let mut outer_generator = None;
13101310
let mut next_code = Some(&obligation.cause.code);
1311+
1312+
let mut seen_upvar_tys_infer_tuple = false;
1313+
13111314
while let Some(code) = next_code {
13121315
debug!("maybe_note_obligation_cause_for_async_await: code={:?}", code);
13131316
match code {
@@ -1328,6 +1331,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
13281331
outer_generator = Some(did);
13291332
}
13301333
ty::GeneratorWitness(..) => {}
1334+
ty::Tuple(_) if !seen_upvar_tys_infer_tuple => {
1335+
// By introducing a tuple of upvar types into the chain of obligations
1336+
// of a generator, the first non-generator item is now the tuple itself,
1337+
// we shall ignore this.
1338+
1339+
seen_upvar_tys_infer_tuple = true;
1340+
}
13311341
_ if generator.is_none() => {
13321342
trait_ref = Some(derived_obligation.parent_trait_ref.skip_binder());
13331343
target_ty = Some(ty);
@@ -1913,7 +1923,29 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
19131923
return;
19141924
}
19151925

1916-
err.note(&format!("required because it appears within the type `{}`", ty));
1926+
// If the obligation for a tuple is set directly by a Generator or Closure,
1927+
// then the tuple must be the one containing capture types.
1928+
let is_upvar_tys_infer_tuple = if !matches!(ty.kind(), ty::Tuple(..)) {
1929+
false
1930+
} else {
1931+
if let ObligationCauseCode::BuiltinDerivedObligation(ref data) =
1932+
*data.parent_code
1933+
{
1934+
let parent_trait_ref =
1935+
self.resolve_vars_if_possible(&data.parent_trait_ref);
1936+
let ty = parent_trait_ref.skip_binder().self_ty();
1937+
matches!(ty.kind(), ty::Generator(..))
1938+
|| matches!(ty.kind(), ty::Closure(..))
1939+
} else {
1940+
false
1941+
}
1942+
};
1943+
1944+
// Don't print the tuple of capture types
1945+
if !is_upvar_tys_infer_tuple {
1946+
err.note(&format!("required because it appears within the type `{}`", ty));
1947+
}
1948+
19171949
obligated_types.push(ty);
19181950

19191951
let parent_predicate = parent_trait_ref.without_const().to_predicate(tcx);

compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
110110
// check if *any* of those are trivial.
111111
ty::Tuple(ref tys) => tys.iter().all(|t| trivial_dropck_outlives(tcx, t.expect_ty())),
112112
ty::Closure(_, ref substs) => {
113-
substs.as_closure().upvar_tys().all(|t| trivial_dropck_outlives(tcx, t))
113+
trivial_dropck_outlives(tcx, substs.as_closure().tupled_upvars_ty())
114114
}
115115

116116
ty::Adt(def, _) => {

compiler/rustc_trait_selection/src/traits/select/mod.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -1600,7 +1600,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
16001600

16011601
ty::Closure(_, substs) => {
16021602
// (*) binder moved here
1603-
Where(ty::Binder::bind(substs.as_closure().upvar_tys().collect()))
1603+
let ty = self.infcx.shallow_resolve(substs.as_closure().tupled_upvars_ty());
1604+
if let ty::Infer(ty::TyVar(_)) = ty.kind() {
1605+
// Not yet resolved.
1606+
Ambiguous
1607+
} else {
1608+
Where(ty::Binder::bind(substs.as_closure().upvar_tys().collect()))
1609+
}
16041610
}
16051611

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

1672-
ty::Closure(_, ref substs) => substs.as_closure().upvar_tys().collect(),
1678+
ty::Closure(_, ref substs) => {
1679+
let ty = self.infcx.shallow_resolve(substs.as_closure().tupled_upvars_ty());
1680+
vec![ty]
1681+
}
16731682

16741683
ty::Generator(_, ref substs, _) => {
1684+
let ty = self.infcx.shallow_resolve(substs.as_generator().tupled_upvars_ty());
16751685
let witness = substs.as_generator().witness();
1676-
substs.as_generator().upvar_tys().chain(iter::once(witness)).collect()
1686+
vec![ty].into_iter().chain(iter::once(witness)).collect()
16771687
}
16781688

16791689
ty::GeneratorWitness(types) => {

compiler/rustc_trait_selection/src/traits/wf.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -592,10 +592,8 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
592592
// anyway, except via auto trait matching (which
593593
// only inspects the upvar types).
594594
walker.skip_current_subtree(); // subtree handled below
595-
for upvar_ty in substs.as_closure().upvar_tys() {
596-
// FIXME(eddyb) add the type to `walker` instead of recursing.
597-
self.compute(upvar_ty.into());
598-
}
595+
// FIXME(eddyb) add the type to `walker` instead of recursing.
596+
self.compute(substs.as_closure().tupled_upvars_ty().into());
599597
}
600598

601599
ty::FnPtr(_) => {

compiler/rustc_traits/src/dropck_outlives.rs

+28-5
Original file line numberDiff line numberDiff line change
@@ -210,12 +210,25 @@ fn dtorck_constraint_for_ty<'tcx>(
210210
Ok::<_, NoSolution>(())
211211
})?,
212212

213-
ty::Closure(_, substs) => rustc_data_structures::stack::ensure_sufficient_stack(|| {
214-
for ty in substs.as_closure().upvar_tys() {
215-
dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty, constraints)?;
213+
ty::Closure(_, substs) => {
214+
if !substs.as_closure().is_valid() {
215+
// By the time this code runs, all type variables ought to
216+
// be fully resolved.
217+
218+
tcx.sess.delay_span_bug(
219+
span,
220+
&format!("upvar_tys for closure not found. Expected capture information for closure {}", ty,),
221+
);
222+
return Err(NoSolution);
216223
}
217-
Ok::<_, NoSolution>(())
218-
})?,
224+
225+
rustc_data_structures::stack::ensure_sufficient_stack(|| {
226+
for ty in substs.as_closure().upvar_tys() {
227+
dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty, constraints)?;
228+
}
229+
Ok::<_, NoSolution>(())
230+
})?
231+
}
219232

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

257+
if !substs.as_generator().is_valid() {
258+
// By the time this code runs, all type variables ought to
259+
// be fully resolved.
260+
tcx.sess.delay_span_bug(
261+
span,
262+
&format!("upvar_tys for generator not found. Expected capture information for generator {}", ty,),
263+
);
264+
return Err(NoSolution);
265+
}
266+
244267
constraints.outlives.extend(
245268
substs
246269
.as_generator()

compiler/rustc_typeck/src/check/closure.rs

+4-13
Original file line numberDiff line numberDiff line change
@@ -81,19 +81,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
8181
self.tcx.closure_base_def_id(expr_def_id.to_def_id()),
8282
);
8383

84-
let tupled_upvars_ty =
85-
self.tcx.mk_tup(self.tcx.upvars_mentioned(expr_def_id).iter().flat_map(|upvars| {
86-
upvars.iter().map(|(&var_hir_id, _)| {
87-
// Create type variables (for now) to represent the transformed
88-
// types of upvars. These will be unified during the upvar
89-
// inference phase (`upvar.rs`).
90-
self.infcx.next_ty_var(TypeVariableOrigin {
91-
// FIXME(eddyb) distinguish upvar inference variables from the rest.
92-
kind: TypeVariableOriginKind::ClosureSynthetic,
93-
span: self.tcx.hir().span(var_hir_id),
94-
})
95-
})
96-
}));
84+
let tupled_upvars_ty = self.infcx.next_ty_var(TypeVariableOrigin {
85+
kind: TypeVariableOriginKind::ClosureSynthetic,
86+
span: self.tcx.hir().span(expr.hir_id),
87+
});
9788

9889
if let Some(GeneratorTypes { resume_ty, yield_ty, interior, movability }) = generator_types
9990
{

compiler/rustc_typeck/src/check/coercion.rs

+18-5
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ use crate::astconv::AstConv;
3939
use crate::check::FnCtxt;
4040
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
4141
use rustc_hir as hir;
42+
use rustc_hir::def_id::DefId;
4243
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
4344
use rustc_infer::infer::{Coercion, InferOk, InferResult};
4445
use rustc_middle::ty::adjustment::{
@@ -221,11 +222,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
221222
// unsafe qualifier.
222223
self.coerce_from_fn_pointer(a, a_f, b)
223224
}
224-
ty::Closure(_, substs_a) => {
225+
ty::Closure(closure_def_id_a, substs_a) => {
225226
// Non-capturing closures are coercible to
226227
// function pointers or unsafe function pointers.
227228
// It cannot convert closures that require unsafe.
228-
self.coerce_closure_to_fn(a, substs_a, b)
229+
self.coerce_closure_to_fn(a, closure_def_id_a, substs_a, b)
229230
}
230231
_ => {
231232
// Otherwise, just use unification rules.
@@ -762,6 +763,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
762763
fn coerce_closure_to_fn(
763764
&self,
764765
a: Ty<'tcx>,
766+
closure_def_id_a: DefId,
765767
substs_a: SubstsRef<'tcx>,
766768
b: Ty<'tcx>,
767769
) -> CoerceResult<'tcx> {
@@ -772,7 +774,18 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
772774
let b = self.shallow_resolve(b);
773775

774776
match b.kind() {
775-
ty::FnPtr(fn_ty) if substs_a.as_closure().upvar_tys().next().is_none() => {
777+
// At this point we haven't done capture analysis, which means
778+
// that the ClosureSubsts just contains an inference variable instead
779+
// of tuple of captured types.
780+
//
781+
// All we care here is if any variable is being captured and not the exact paths,
782+
// so we check `upvars_mentioned` for root variables being captured.
783+
ty::FnPtr(fn_ty)
784+
if self
785+
.tcx
786+
.upvars_mentioned(closure_def_id_a.expect_local())
787+
.map_or(true, |u| u.is_empty()) =>
788+
{
776789
// We coerce the closure, which has fn type
777790
// `extern "rust-call" fn((arg0,arg1,...)) -> _`
778791
// to
@@ -906,8 +919,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
906919
// Function items or non-capturing closures of differing IDs or InternalSubsts.
907920
let (a_sig, b_sig) = {
908921
let is_capturing_closure = |ty| {
909-
if let &ty::Closure(_, substs) = ty {
910-
substs.as_closure().upvar_tys().next().is_some()
922+
if let &ty::Closure(closure_def_id, _substs) = ty {
923+
self.tcx.upvars_mentioned(closure_def_id.expect_local()).is_some()
911924
} else {
912925
false
913926
}

compiler/rustc_typeck/src/check/upvar.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -202,9 +202,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
202202
"analyze_closure: id={:?} substs={:?} final_upvar_tys={:?}",
203203
closure_hir_id, substs, final_upvar_tys
204204
);
205-
for (upvar_ty, final_upvar_ty) in substs.upvar_tys().zip(final_upvar_tys) {
206-
self.demand_suptype(span, upvar_ty, final_upvar_ty);
207-
}
205+
206+
// Build a tuple (U0..Un) of the final upvar types U0..Un
207+
// and unify the upvar tupe type in the closure with it:
208+
let final_tupled_upvars_type = self.tcx.mk_tup(final_upvar_tys.iter());
209+
self.demand_suptype(span, substs.tupled_upvars_ty(), final_tupled_upvars_type);
208210

209211
// If we are also inferred the closure kind here,
210212
// process any deferred resolutions.

src/test/ui/generator/generator-yielding-or-returning-itself.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
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 _]`
1+
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]`
22
--> $DIR/generator-yielding-or-returning-itself.rs:15:5
33
|
44
LL | pub fn want_cyclic_generator_return<T>(_: T)
@@ -14,7 +14,7 @@ LL | want_cyclic_generator_return(|| {
1414
see issue #46062 <https://github.com/rust-lang/rust/issues/46062>
1515
for more information
1616

17-
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 _]`
17+
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]`
1818
--> $DIR/generator-yielding-or-returning-itself.rs:28:5
1919
|
2020
LL | pub fn want_cyclic_generator_yield<T>(_: T)

src/test/ui/generator/print/generator-print-verbose-2.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ LL | assert_send(|| {
99
|
1010
= help: the trait `Sync` is not implemented for `Cell<i32>`
1111
= note: required because of the requirements on the impl of `Send` for `&'_#3r Cell<i32>`
12-
= note: required because it appears within the type `[main::{closure#1} upvar_tys=(&'_#3r Cell<i32>) _#16t]`
12+
= note: required because it appears within the type `[main::{closure#1} upvar_tys=(&'_#3r Cell<i32>) _#17t]`
1313

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

0 commit comments

Comments
 (0)