11
11
//! Helper routines for higher-ranked things. See the `doc` module at
12
12
//! the end of the file for details.
13
13
14
- use super :: { CombinedSnapshot , InferCtxt , HigherRankedType , SkolemizationMap } ;
14
+ use super :: { CombinedSnapshot ,
15
+ InferCtxt ,
16
+ LateBoundRegion ,
17
+ HigherRankedType ,
18
+ SubregionOrigin ,
19
+ SkolemizationMap } ;
15
20
use super :: combine:: CombineFields ;
21
+ use super :: region_inference:: { TaintDirections } ;
16
22
23
+ use infer:: error_reporting;
17
24
use ty:: { self , TyCtxt , Binder , TypeFoldable } ;
18
25
use ty:: error:: TypeError ;
19
26
use ty:: relate:: { Relate , RelateResult , TypeRelation } ;
@@ -39,11 +46,13 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
39
46
// Start a snapshot so we can examine "all bindings that were
40
47
// created as part of this type comparison".
41
48
return self . infcx . commit_if_ok ( |snapshot| {
49
+ let span = self . trace . origin . span ( ) ;
50
+
42
51
// First, we instantiate each bound region in the subtype with a fresh
43
52
// region variable.
44
53
let ( a_prime, _) =
45
54
self . infcx . replace_late_bound_regions_with_fresh_var (
46
- self . trace . origin . span ( ) ,
55
+ span,
47
56
HigherRankedType ,
48
57
a) ;
49
58
@@ -60,7 +69,11 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
60
69
61
70
// Presuming type comparison succeeds, we need to check
62
71
// that the skolemized regions do not "leak".
63
- self . infcx . leak_check ( !self . a_is_expected , & skol_map, snapshot) ?;
72
+ self . infcx . leak_check ( !self . a_is_expected , span, & skol_map, snapshot) ?;
73
+
74
+ // We are finished with the skolemized regions now so pop
75
+ // them off.
76
+ self . infcx . pop_skolemized ( skol_map, snapshot) ;
64
77
65
78
debug ! ( "higher_ranked_sub: OK result={:?}" , result) ;
66
79
@@ -124,7 +137,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
124
137
return r0;
125
138
}
126
139
127
- let tainted = infcx. tainted_regions ( snapshot, r0) ;
140
+ let tainted = infcx. tainted_regions ( snapshot, r0, TaintDirections :: both ( ) ) ;
128
141
129
142
// Variables created during LUB computation which are
130
143
// *related* to regions that pre-date the LUB computation
@@ -219,7 +232,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
219
232
return r0;
220
233
}
221
234
222
- let tainted = infcx. tainted_regions ( snapshot, r0) ;
235
+ let tainted = infcx. tainted_regions ( snapshot, r0, TaintDirections :: both ( ) ) ;
223
236
224
237
let mut a_r = None ;
225
238
let mut b_r = None ;
@@ -341,8 +354,12 @@ fn fold_regions_in<'a, 'gcx, 'tcx, T, F>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
341
354
}
342
355
343
356
impl < ' a , ' gcx , ' tcx > InferCtxt < ' a , ' gcx , ' tcx > {
344
- fn tainted_regions ( & self , snapshot : & CombinedSnapshot , r : ty:: Region ) -> Vec < ty:: Region > {
345
- self . region_vars . tainted ( & snapshot. region_vars_snapshot , r)
357
+ fn tainted_regions ( & self ,
358
+ snapshot : & CombinedSnapshot ,
359
+ r : ty:: Region ,
360
+ directions : TaintDirections )
361
+ -> FnvHashSet < ty:: Region > {
362
+ self . region_vars . tainted ( & snapshot. region_vars_snapshot , r, directions)
346
363
}
347
364
348
365
fn region_vars_confined_to_snapshot ( & self ,
@@ -422,22 +439,27 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
422
439
region_vars
423
440
}
424
441
442
+ /// Replace all regions bound by `binder` with skolemized regions and
443
+ /// return a map indicating which bound-region was replaced with what
444
+ /// skolemized region. This is the first step of checking subtyping
445
+ /// when higher-ranked things are involved.
446
+ ///
447
+ /// **Important:** you must call this function from within a snapshot.
448
+ /// Moreover, before committing the snapshot, you must eventually call
449
+ /// either `plug_leaks` or `pop_skolemized` to remove the skolemized
450
+ /// regions. If you rollback the snapshot (or are using a probe), then
451
+ /// the pop occurs as part of the rollback, so an explicit call is not
452
+ /// needed (but is also permitted).
453
+ ///
454
+ /// See `README.md` for more details.
425
455
pub fn skolemize_late_bound_regions < T > ( & self ,
426
456
binder : & ty:: Binder < T > ,
427
457
snapshot : & CombinedSnapshot )
428
458
-> ( T , SkolemizationMap )
429
459
where T : TypeFoldable < ' tcx >
430
460
{
431
- /*!
432
- * Replace all regions bound by `binder` with skolemized regions and
433
- * return a map indicating which bound-region was replaced with what
434
- * skolemized region. This is the first step of checking subtyping
435
- * when higher-ranked things are involved. See `README.md` for more
436
- * details.
437
- */
438
-
439
461
let ( result, map) = self . tcx . replace_late_bound_regions ( binder, |br| {
440
- self . region_vars . new_skolemized ( br, & snapshot. region_vars_snapshot )
462
+ self . region_vars . push_skolemized ( br, & snapshot. region_vars_snapshot )
441
463
} ) ;
442
464
443
465
debug ! ( "skolemize_bound_regions(binder={:?}, result={:?}, map={:?})" ,
@@ -448,27 +470,29 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
448
470
( result, map)
449
471
}
450
472
473
+ /// Searches the region constriants created since `snapshot` was started
474
+ /// and checks to determine whether any of the skolemized regions created
475
+ /// in `skol_map` would "escape" -- meaning that they are related to
476
+ /// other regions in some way. If so, the higher-ranked subtyping doesn't
477
+ /// hold. See `README.md` for more details.
451
478
pub fn leak_check ( & self ,
452
479
overly_polymorphic : bool ,
480
+ span : Span ,
453
481
skol_map : & SkolemizationMap ,
454
482
snapshot : & CombinedSnapshot )
455
483
-> RelateResult < ' tcx , ( ) >
456
484
{
457
- /*!
458
- * Searches the region constriants created since `snapshot` was started
459
- * and checks to determine whether any of the skolemized regions created
460
- * in `skol_map` would "escape" -- meaning that they are related to
461
- * other regions in some way. If so, the higher-ranked subtyping doesn't
462
- * hold. See `README.md` for more details.
463
- */
464
-
465
485
debug ! ( "leak_check: skol_map={:?}" ,
466
486
skol_map) ;
467
487
468
488
let new_vars = self . region_vars_confined_to_snapshot ( snapshot) ;
469
489
for ( & skol_br, & skol) in skol_map {
470
- let tainted = self . tainted_regions ( snapshot, skol) ;
471
- for & tainted_region in & tainted {
490
+ // The inputs to a skolemized variable can only
491
+ // be itself or other new variables.
492
+ let incoming_taints = self . tainted_regions ( snapshot,
493
+ skol,
494
+ TaintDirections :: incoming ( ) ) ;
495
+ for & tainted_region in & incoming_taints {
472
496
// Each skolemized should only be relatable to itself
473
497
// or new variables:
474
498
match tainted_region {
@@ -496,6 +520,72 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
496
520
}
497
521
}
498
522
}
523
+
524
+ for ( _, & skol) in skol_map {
525
+ // The outputs from a skolemized variable must all be
526
+ // equatable with `'static`.
527
+ let outgoing_taints = self . tainted_regions ( snapshot,
528
+ skol,
529
+ TaintDirections :: outgoing ( ) ) ;
530
+ for & tainted_region in & outgoing_taints {
531
+ match tainted_region {
532
+ ty:: ReVar ( vid) if new_vars. contains ( & vid) => {
533
+ // There is a path from a skolemized variable
534
+ // to some region variable that doesn't escape
535
+ // this snapshot:
536
+ //
537
+ // [skol] -> [tainted_region]
538
+ //
539
+ // We can ignore this. The reasoning relies on
540
+ // the fact that the preivous loop
541
+ // completed. There are two possible cases
542
+ // here.
543
+ //
544
+ // - `tainted_region` eventually reaches a
545
+ // skolemized variable, which *must* be `skol`
546
+ // (because otherwise we would have already
547
+ // returned `Err`). In that case,
548
+ // `tainted_region` could be inferred to `skol`.
549
+ //
550
+ // - `tainted_region` never reaches a
551
+ // skolemized variable. In that case, we can
552
+ // safely choose `'static` as an upper bound
553
+ // incoming edges. This is a conservative
554
+ // choice -- the LUB might be one of the
555
+ // incoming skolemized variables, which we
556
+ // might know by ambient bounds. We can
557
+ // consider a more clever choice of upper
558
+ // bound later (modulo some theoretical
559
+ // breakage).
560
+ //
561
+ // We used to force such `tainted_region` to be
562
+ // `'static`, but that leads to problems when
563
+ // combined with `plug_leaks`. If you have a case
564
+ // where `[skol] -> [tainted_region] -> [skol]`,
565
+ // then `plug_leaks` concludes it should replace
566
+ // `'static` with a late-bound region, which is
567
+ // clearly wrong. (Well, what actually happens is
568
+ // you get assertion failures because it WOULD
569
+ // have to replace 'static with a late-bound
570
+ // region.)
571
+ }
572
+ ty:: ReSkolemized ( ..) => {
573
+ // the only skolemized region we find in the
574
+ // successors of X can be X; if there was another
575
+ // region Y, then X would have been in the preds
576
+ // of Y, and we would have aborted above
577
+ assert_eq ! ( skol, tainted_region) ;
578
+ }
579
+ _ => {
580
+ self . region_vars . make_subregion (
581
+ SubregionOrigin :: SkolemizeSuccessor ( span) ,
582
+ ty:: ReStatic ,
583
+ tainted_region) ;
584
+ }
585
+ }
586
+ }
587
+ }
588
+
499
589
Ok ( ( ) )
500
590
}
501
591
@@ -533,8 +623,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
533
623
value : & T ) -> T
534
624
where T : TypeFoldable < ' tcx >
535
625
{
536
- debug_assert ! ( self . leak_check( false , & skol_map, snapshot) . is_ok( ) ) ;
537
-
538
626
debug ! ( "plug_leaks(skol_map={:?}, value={:?})" ,
539
627
skol_map,
540
628
value) ;
@@ -545,9 +633,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
545
633
// these taint sets are mutually disjoint.
546
634
let inv_skol_map: FnvHashMap < ty:: Region , ty:: BoundRegion > =
547
635
skol_map
548
- . into_iter ( )
549
- . flat_map ( |( skol_br, skol) | {
550
- self . tainted_regions ( snapshot, skol)
636
+ . iter ( )
637
+ . flat_map ( |( & skol_br, & skol) | {
638
+ self . tainted_regions ( snapshot, skol, TaintDirections :: incoming ( ) )
551
639
. into_iter ( )
552
640
. map ( move |tainted_region| ( tainted_region, skol_br) )
553
641
} )
@@ -577,6 +665,19 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
577
665
// binders, so this assert is satisfied.
578
666
assert ! ( current_depth > 1 ) ;
579
667
668
+ // since leak-check passed, this skolemized region
669
+ // should only have incoming edges from variables
670
+ // (which ought not to escape the snapshot, but we
671
+ // don't check that) or itself
672
+ assert ! (
673
+ match r {
674
+ ty:: ReVar ( _) => true ,
675
+ ty:: ReSkolemized ( _, ref br1) => br == br1,
676
+ _ => false ,
677
+ } ,
678
+ "leak-check would have us replace {:?} with {:?}" ,
679
+ r, br) ;
680
+
580
681
ty:: ReLateBound ( ty:: DebruijnIndex :: new ( current_depth - 1 ) , br. clone ( ) )
581
682
}
582
683
}
@@ -585,6 +686,27 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
585
686
debug ! ( "plug_leaks: result={:?}" ,
586
687
result) ;
587
688
689
+ self . pop_skolemized ( skol_map, snapshot) ;
690
+
691
+ debug ! ( "plug_leaks: result={:?}" , result) ;
692
+
588
693
result
589
694
}
695
+
696
+ /// Pops the skolemized regions found in `skol_map` from the region
697
+ /// inference context. Whenever you create skolemized regions via
698
+ /// `skolemize_late_bound_regions`, they must be popped before you
699
+ /// commit the enclosing snapshot (if you do not commit, e.g. within a
700
+ /// probe or as a result of an error, then this is not necessary, as
701
+ /// popping happens as part of the rollback).
702
+ ///
703
+ /// Note: popping also occurs implicitly as part of `leak_check`.
704
+ pub fn pop_skolemized ( & self ,
705
+ skol_map : SkolemizationMap ,
706
+ snapshot : & CombinedSnapshot )
707
+ {
708
+ debug ! ( "pop_skolemized({:?})" , skol_map) ;
709
+ let skol_regions: FnvHashSet < _ > = skol_map. values ( ) . cloned ( ) . collect ( ) ;
710
+ self . region_vars . pop_skolemized ( & skol_regions, & snapshot. region_vars_snapshot ) ;
711
+ }
590
712
}
0 commit comments