Skip to content

Commit d6d09e0

Browse files
committed
Auto merge of #45879 - nikomatsakis:nll-kill-cyclic-closures, r=arielb1
move closure kind, signature into `ClosureSubsts` Instead of using side-tables, store the closure-kind and signature in the substitutions themselves. This has two key effects: - It means that the closure's type changes as inference finds out more things, which is very nice. - As a result, it avoids the need for the `freshen_closure_like` code (though we still use it for generators). - It avoids cyclic closures calls. - These were never meant to be supported, precisely because they make a lot of the fancy inference that we do much more complicated. However, due to an oversight, it was previously possible -- if challenging -- to create a setup where a closure *directly* called itself (see e.g. #21410). We have to see what the effect of this change is, though. Needs a crater run. Marking as [WIP] until that has been assessed. r? @arielb1
2 parents 63739ab + 00732a3 commit d6d09e0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+1003
-782
lines changed

src/librustc/dep_graph/dep_node.rs

-2
Original file line numberDiff line numberDiff line change
@@ -498,9 +498,7 @@ define_dep_nodes!( <'tcx>
498498
[] IsAutoImpl(DefId),
499499
[] ImplTraitRef(DefId),
500500
[] ImplPolarity(DefId),
501-
[] ClosureKind(DefId),
502501
[] FnSignature(DefId),
503-
[] GenSignature(DefId),
504502
[] CoerceUnsizedInfo(DefId),
505503

506504
[] ItemVarianceConstraints(DefId),

src/librustc/diagnostics.rs

+31
Original file line numberDiff line numberDiff line change
@@ -1969,8 +1969,39 @@ fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32 {
19691969
```
19701970
"##,
19711971

1972+
E0644: r##"
1973+
A closure or generator was constructed that references its own type.
1974+
1975+
Erroneous example:
1976+
1977+
```compile-fail,E0644
1978+
fn fix<F>(f: &F)
1979+
where F: Fn(&F)
1980+
{
1981+
f(&f);
19721982
}
19731983
1984+
fn main() {
1985+
fix(&|y| {
1986+
// Here, when `x` is called, the parameter `y` is equal to `x`.
1987+
});
1988+
}
1989+
```
1990+
1991+
Rust does not permit a closure to directly reference its own type,
1992+
either through an argument (as in the example above) or by capturing
1993+
itself through its environment. This restriction helps keep closure
1994+
inference tractable.
1995+
1996+
The easiest fix is to rewrite your closure into a top-level function,
1997+
or into a method. In some cases, you may also be able to have your
1998+
closure call itself by capturing a `&Fn()` object or `fn()` pointer
1999+
that refers to itself. That is permitting, since the closure would be
2000+
invoking itself via a virtual call, and hence does not directly
2001+
reference its own *type*.
2002+
2003+
"##, }
2004+
19742005

19752006
register_diagnostics! {
19762007
// E0006 // merged with E0005

src/librustc/ich/impls_ty.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,9 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for ty::Predicate<'gcx> {
236236
ty::Predicate::ObjectSafe(def_id) => {
237237
def_id.hash_stable(hcx, hasher);
238238
}
239-
ty::Predicate::ClosureKind(def_id, closure_kind) => {
239+
ty::Predicate::ClosureKind(def_id, closure_substs, closure_kind) => {
240240
def_id.hash_stable(hcx, hasher);
241+
closure_substs.hash_stable(hcx, hasher);
241242
closure_kind.hash_stable(hcx, hasher);
242243
}
243244
ty::Predicate::ConstEvaluatable(def_id, substs) => {

src/librustc/infer/combine.rs

+16-2
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
270270
for_vid_sub_root: self.infcx.type_variables.borrow_mut().sub_root_var(for_vid),
271271
ambient_variance,
272272
needs_wf: false,
273+
root_ty: ty,
273274
};
274275

275276
let ty = generalize.relate(&ty, &ty)?;
@@ -280,10 +281,23 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
280281

281282
struct Generalizer<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> {
282283
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
284+
285+
/// Span, used when creating new type variables and things.
283286
span: Span,
287+
288+
/// The vid of the type variable that is in the process of being
289+
/// instantiated; if we find this within the type we are folding,
290+
/// that means we would have created a cyclic type.
284291
for_vid_sub_root: ty::TyVid,
292+
293+
/// Track the variance as we descend into the type.
285294
ambient_variance: ty::Variance,
286-
needs_wf: bool, // see the field `needs_wf` in `Generalization`
295+
296+
/// See the field `needs_wf` in `Generalization`.
297+
needs_wf: bool,
298+
299+
/// The root type that we are generalizing. Used when reporting cycles.
300+
root_ty: Ty<'tcx>,
287301
}
288302

289303
/// Result from a generalization operation. This includes
@@ -386,7 +400,7 @@ impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, '
386400
if sub_vid == self.for_vid_sub_root {
387401
// If sub-roots are equal, then `for_vid` and
388402
// `vid` are related via subtyping.
389-
return Err(TypeError::CyclicTy);
403+
return Err(TypeError::CyclicTy(self.root_ty));
390404
} else {
391405
match variables.probe_root(vid) {
392406
Some(u) => {

src/librustc/infer/error_reporting/mod.rs

+45-18
Original file line numberDiff line numberDiff line change
@@ -689,9 +689,16 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
689689
diag: &mut DiagnosticBuilder<'tcx>,
690690
cause: &ObligationCause<'tcx>,
691691
secondary_span: Option<(Span, String)>,
692-
values: Option<ValuePairs<'tcx>>,
692+
mut values: Option<ValuePairs<'tcx>>,
693693
terr: &TypeError<'tcx>)
694694
{
695+
// For some types of errors, expected-found does not make
696+
// sense, so just ignore the values we were given.
697+
match terr {
698+
TypeError::CyclicTy(_) => { values = None; }
699+
_ => { }
700+
}
701+
695702
let (expected_found, exp_found, is_simple_error) = match values {
696703
None => (None, None, false),
697704
Some(values) => {
@@ -780,17 +787,20 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
780787
terr);
781788

782789
let span = trace.cause.span;
783-
let failure_str = trace.cause.as_failure_str();
784-
let mut diag = match trace.cause.code {
785-
ObligationCauseCode::IfExpressionWithNoElse => {
790+
let failure_code = trace.cause.as_failure_code(terr);
791+
let mut diag = match failure_code {
792+
FailureCode::Error0317(failure_str) => {
786793
struct_span_err!(self.tcx.sess, span, E0317, "{}", failure_str)
787794
}
788-
ObligationCauseCode::MainFunctionType => {
795+
FailureCode::Error0580(failure_str) => {
789796
struct_span_err!(self.tcx.sess, span, E0580, "{}", failure_str)
790797
}
791-
_ => {
798+
FailureCode::Error0308(failure_str) => {
792799
struct_span_err!(self.tcx.sess, span, E0308, "{}", failure_str)
793800
}
801+
FailureCode::Error0644(failure_str) => {
802+
struct_span_err!(self.tcx.sess, span, E0644, "{}", failure_str)
803+
}
794804
};
795805
self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr);
796806
diag
@@ -1040,23 +1050,40 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
10401050
}
10411051
}
10421052

1053+
enum FailureCode {
1054+
Error0317(&'static str),
1055+
Error0580(&'static str),
1056+
Error0308(&'static str),
1057+
Error0644(&'static str),
1058+
}
1059+
10431060
impl<'tcx> ObligationCause<'tcx> {
1044-
fn as_failure_str(&self) -> &'static str {
1061+
fn as_failure_code(&self, terr: &TypeError<'tcx>) -> FailureCode {
1062+
use self::FailureCode::*;
10451063
use traits::ObligationCauseCode::*;
10461064
match self.code {
1047-
CompareImplMethodObligation { .. } => "method not compatible with trait",
1048-
MatchExpressionArm { source, .. } => match source {
1065+
CompareImplMethodObligation { .. } => Error0308("method not compatible with trait"),
1066+
MatchExpressionArm { source, .. } => Error0308(match source {
10491067
hir::MatchSource::IfLetDesugar{..} => "`if let` arms have incompatible types",
10501068
_ => "match arms have incompatible types",
1051-
},
1052-
IfExpression => "if and else have incompatible types",
1053-
IfExpressionWithNoElse => "if may be missing an else clause",
1054-
EquatePredicate => "equality predicate not satisfied",
1055-
MainFunctionType => "main function has wrong type",
1056-
StartFunctionType => "start function has wrong type",
1057-
IntrinsicType => "intrinsic has wrong type",
1058-
MethodReceiver => "mismatched method receiver",
1059-
_ => "mismatched types",
1069+
}),
1070+
IfExpression => Error0308("if and else have incompatible types"),
1071+
IfExpressionWithNoElse => Error0317("if may be missing an else clause"),
1072+
EquatePredicate => Error0308("equality predicate not satisfied"),
1073+
MainFunctionType => Error0580("main function has wrong type"),
1074+
StartFunctionType => Error0308("start function has wrong type"),
1075+
IntrinsicType => Error0308("intrinsic has wrong type"),
1076+
MethodReceiver => Error0308("mismatched method receiver"),
1077+
1078+
// In the case where we have no more specific thing to
1079+
// say, also take a look at the error code, maybe we can
1080+
// tailor to that.
1081+
_ => match terr {
1082+
TypeError::CyclicTy(ty) if ty.is_closure() || ty.is_generator() =>
1083+
Error0644("closure/generator type that references itself"),
1084+
_ =>
1085+
Error0308("mismatched types"),
1086+
}
10601087
}
10611088
}
10621089

src/librustc/infer/error_reporting/need_type_info.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
125125
// ```
126126
labels.clear();
127127
labels.push((pattern.span, format!("consider giving this closure parameter a type")));
128-
}
129-
130-
if let Some(pattern) = local_visitor.found_local_pattern {
128+
} else if let Some(pattern) = local_visitor.found_local_pattern {
131129
if let Some(simple_name) = pattern.simple_name() {
132130
labels.push((pattern.span, format!("consider giving `{}` a type", simple_name)));
133131
} else {

src/librustc/infer/freshen.rs

+2-131
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,7 @@
4343
4444
use ty::{self, Ty, TyCtxt, TypeFoldable};
4545
use ty::fold::TypeFolder;
46-
use ty::subst::Substs;
4746
use util::nodemap::FxHashMap;
48-
use hir::def_id::DefId;
4947

5048
use std::collections::hash_map::Entry;
5149

@@ -56,7 +54,6 @@ pub struct TypeFreshener<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
5654
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
5755
freshen_count: u32,
5856
freshen_map: FxHashMap<ty::InferTy, Ty<'tcx>>,
59-
closure_set: Vec<DefId>,
6057
}
6158

6259
impl<'a, 'gcx, 'tcx> TypeFreshener<'a, 'gcx, 'tcx> {
@@ -66,7 +63,6 @@ impl<'a, 'gcx, 'tcx> TypeFreshener<'a, 'gcx, 'tcx> {
6663
infcx,
6764
freshen_count: 0,
6865
freshen_map: FxHashMap(),
69-
closure_set: vec![],
7066
}
7167
}
7268

@@ -92,88 +88,6 @@ impl<'a, 'gcx, 'tcx> TypeFreshener<'a, 'gcx, 'tcx> {
9288
}
9389
}
9490
}
95-
96-
fn next_fresh<F>(&mut self,
97-
freshener: F)
98-
-> Ty<'tcx>
99-
where F: FnOnce(u32) -> ty::InferTy,
100-
{
101-
let index = self.freshen_count;
102-
self.freshen_count += 1;
103-
self.infcx.tcx.mk_infer(freshener(index))
104-
}
105-
106-
fn freshen_closure_like<M, C>(&mut self,
107-
def_id: DefId,
108-
substs: ty::ClosureSubsts<'tcx>,
109-
t: Ty<'tcx>,
110-
markers: M,
111-
combine: C)
112-
-> Ty<'tcx>
113-
where M: FnOnce(&mut Self) -> (Ty<'tcx>, Ty<'tcx>),
114-
C: FnOnce(&'tcx Substs<'tcx>) -> Ty<'tcx>
115-
{
116-
let tcx = self.infcx.tcx;
117-
118-
let closure_in_progress = self.infcx.in_progress_tables.map_or(false, |tables| {
119-
tcx.hir.as_local_node_id(def_id).map_or(false, |closure_id| {
120-
tables.borrow().local_id_root ==
121-
Some(DefId::local(tcx.hir.node_to_hir_id(closure_id).owner))
122-
})
123-
});
124-
125-
if !closure_in_progress {
126-
// If this closure belongs to another infcx, its kind etc. were
127-
// fully inferred and its signature/kind are exactly what's listed
128-
// in its infcx. So we don't need to add the markers for them.
129-
return t.super_fold_with(self);
130-
}
131-
132-
// We are encoding a closure in progress. Because we want our freshening
133-
// key to contain all inference information needed to make sense of our
134-
// value, we need to encode the closure signature and kind. The way
135-
// we do that is to add them as 2 variables to the closure substs,
136-
// basically because it's there (and nobody cares about adding extra stuff
137-
// to substs).
138-
//
139-
// This means the "freshened" closure substs ends up looking like
140-
// fresh_substs = [PARENT_SUBSTS* ; UPVARS* ; SIG_MARKER ; KIND_MARKER]
141-
let (marker_1, marker_2) = if self.closure_set.contains(&def_id) {
142-
// We found the closure def-id within its own signature. Just
143-
// leave a new freshened type - any matching operations would
144-
// have found and compared the exterior closure already to
145-
// get here.
146-
//
147-
// In that case, we already know what the signature would
148-
// be - the parent closure on the stack already contains a
149-
// "copy" of the signature, so there is no reason to encode
150-
// it again for injectivity. Just use a fresh type variable
151-
// to make everything comparable.
152-
//
153-
// For example (closure kinds omitted for clarity)
154-
// t=[closure FOO sig=[closure BAR sig=[closure FOO ..]]]
155-
// Would get encoded to
156-
// t=[closure FOO sig=[closure BAR sig=[closure FOO sig=$0]]]
157-
//
158-
// and we can decode by having
159-
// $0=[closure BAR {sig doesn't exist in decode}]
160-
// and get
161-
// t=[closure FOO]
162-
// sig[FOO] = [closure BAR]
163-
// sig[BAR] = [closure FOO]
164-
(self.next_fresh(ty::FreshTy), self.next_fresh(ty::FreshTy))
165-
} else {
166-
self.closure_set.push(def_id);
167-
let markers = markers(self);
168-
self.closure_set.pop();
169-
markers
170-
};
171-
172-
combine(tcx.mk_substs(
173-
substs.substs.iter().map(|k| k.fold_with(self)).chain(
174-
[marker_1, marker_2].iter().cloned().map(From::from)
175-
)))
176-
}
17791
}
17892

17993
impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> {
@@ -249,51 +163,7 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> {
249163
t
250164
}
251165

252-
ty::TyClosure(def_id, substs) => {
253-
self.freshen_closure_like(
254-
def_id, substs, t,
255-
|this| {
256-
// HACK: use a "random" integer type to mark the kind. Because
257-
// different closure kinds shouldn't get unified during
258-
// selection, the "subtyping" relationship (where any kind is
259-
// better than no kind) shouldn't matter here, just that the
260-
// types are different.
261-
let closure_kind = this.infcx.closure_kind(def_id);
262-
let closure_kind_marker = match closure_kind {
263-
None => tcx.types.i8,
264-
Some(ty::ClosureKind::Fn) => tcx.types.i16,
265-
Some(ty::ClosureKind::FnMut) => tcx.types.i32,
266-
Some(ty::ClosureKind::FnOnce) => tcx.types.i64,
267-
};
268-
269-
let closure_sig = this.infcx.fn_sig(def_id);
270-
(tcx.mk_fn_ptr(closure_sig.fold_with(this)),
271-
closure_kind_marker)
272-
},
273-
|substs| tcx.mk_closure(def_id, substs)
274-
)
275-
}
276-
277-
ty::TyGenerator(def_id, substs, interior) => {
278-
self.freshen_closure_like(
279-
def_id, substs, t,
280-
|this| {
281-
let gen_sig = this.infcx.generator_sig(def_id).unwrap();
282-
// FIXME: want to revise this strategy when generator
283-
// signatures can actually contain LBRs.
284-
let sig = this.tcx().no_late_bound_regions(&gen_sig)
285-
.unwrap_or_else(|| {
286-
bug!("late-bound regions in signature of {:?}",
287-
def_id)
288-
});
289-
(sig.yield_ty, sig.return_ty).fold_with(this)
290-
},
291-
|substs| {
292-
tcx.mk_generator(def_id, ty::ClosureSubsts { substs }, interior)
293-
}
294-
)
295-
}
296-
166+
ty::TyGenerator(..) |
297167
ty::TyBool |
298168
ty::TyChar |
299169
ty::TyInt(..) |
@@ -314,6 +184,7 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> {
314184
ty::TyProjection(..) |
315185
ty::TyForeign(..) |
316186
ty::TyParam(..) |
187+
ty::TyClosure(..) |
317188
ty::TyAnon(..) => {
318189
t.super_fold_with(self)
319190
}

0 commit comments

Comments
 (0)