@@ -44,7 +44,7 @@ use crate::borrow_check::{
44
44
45
45
use self :: values:: { LivenessValues , RegionValueElements , RegionValues } ;
46
46
use super :: universal_regions:: UniversalRegions ;
47
- use super :: ToRegionVid ;
47
+ use super :: { PoloniusOutput , ToRegionVid } ;
48
48
49
49
mod dump_mir;
50
50
mod graphviz;
@@ -484,6 +484,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
484
484
upvars : & [ Upvar ] ,
485
485
mir_def_id : DefId ,
486
486
errors_buffer : & mut Vec < Diagnostic > ,
487
+ polonius_output : Option < Rc < PoloniusOutput > > ,
487
488
) -> Option < ClosureRegionRequirements < ' tcx > > {
488
489
self . propagate_constraints ( body) ;
489
490
@@ -509,16 +510,33 @@ impl<'tcx> RegionInferenceContext<'tcx> {
509
510
// multiple problems.
510
511
let mut region_naming = RegionErrorNamingCtx :: new ( ) ;
511
512
512
- self . check_universal_regions (
513
- infcx,
514
- body,
515
- local_names,
516
- upvars,
517
- mir_def_id,
518
- outlives_requirements. as_mut ( ) ,
519
- errors_buffer,
520
- & mut region_naming,
521
- ) ;
513
+ // In Polonius mode, the errors about missing universal region relations are in the output
514
+ // and need to be emitted or propagated. Otherwise, we need to check whether the
515
+ // constraints were too strong, and if so, emit or propagate those errors.
516
+ if infcx. tcx . sess . opts . debugging_opts . polonius {
517
+ self . check_polonius_subset_errors (
518
+ infcx,
519
+ body,
520
+ local_names,
521
+ upvars,
522
+ mir_def_id,
523
+ outlives_requirements. as_mut ( ) ,
524
+ errors_buffer,
525
+ & mut region_naming,
526
+ polonius_output. expect ( "Polonius output is unavailable despite `-Z polonius`" ) ,
527
+ ) ;
528
+ } else {
529
+ self . check_universal_regions (
530
+ infcx,
531
+ body,
532
+ local_names,
533
+ upvars,
534
+ mir_def_id,
535
+ outlives_requirements. as_mut ( ) ,
536
+ errors_buffer,
537
+ & mut region_naming,
538
+ ) ;
539
+ }
522
540
523
541
self . check_member_constraints ( infcx, mir_def_id, errors_buffer) ;
524
542
@@ -1375,6 +1393,111 @@ impl<'tcx> RegionInferenceContext<'tcx> {
1375
1393
outlives_suggestion. add_suggestion ( body, self , infcx, errors_buffer, region_naming) ;
1376
1394
}
1377
1395
1396
+ /// Checks if Polonius has found any unexpected free region relations.
1397
+ ///
1398
+ /// In Polonius terms, a "subset error" (or "illegal subset relation error") is the equivalent
1399
+ /// of NLL's "checking if any region constraints were too strong": a placeholder origin `'a`
1400
+ /// was unexpectedly found to be a subset of another placeholder origin `'b`, and means in NLL
1401
+ /// terms that the "longer free region" `'a` outlived the "shorter free region" `'b`.
1402
+ ///
1403
+ /// More details can be found in this blog post by Niko:
1404
+ /// http://smallcultfollowing.com/babysteps/blog/2019/01/17/polonius-and-region-errors/
1405
+ ///
1406
+ /// In the canonical example
1407
+ ///
1408
+ /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x }
1409
+ ///
1410
+ /// returning `x` requires `&'a u32 <: &'b u32` and hence we establish (transitively) a
1411
+ /// constraint that `'a: 'b`. It is an error that we have no evidence that this
1412
+ /// constraint holds.
1413
+ ///
1414
+ /// If `propagated_outlives_requirements` is `Some`, then we will
1415
+ /// push unsatisfied obligations into there. Otherwise, we'll
1416
+ /// report them as errors.
1417
+ fn check_polonius_subset_errors (
1418
+ & self ,
1419
+ infcx : & InferCtxt < ' _ , ' tcx > ,
1420
+ body : & Body < ' tcx > ,
1421
+ local_names : & IndexVec < Local , Option < Symbol > > ,
1422
+ upvars : & [ Upvar ] ,
1423
+ mir_def_id : DefId ,
1424
+ mut propagated_outlives_requirements : Option < & mut Vec < ClosureOutlivesRequirement < ' tcx > > > ,
1425
+ errors_buffer : & mut Vec < Diagnostic > ,
1426
+ region_naming : & mut RegionErrorNamingCtx ,
1427
+ polonius_output : Rc < PoloniusOutput > ,
1428
+ ) {
1429
+ debug ! ( "check_polonius_subset_errors: {} subset_errors" , polonius_output. subset_errors. len( ) ) ;
1430
+
1431
+ let mut outlives_suggestion = OutlivesSuggestionBuilder :: new ( mir_def_id, local_names) ;
1432
+
1433
+ // Similarly to `check_universal_regions`: a free region relation, which was not explicitly
1434
+ // declared ("known") was found by Polonius, so emit an error, or propagate the
1435
+ // requirements for our caller into the `propagated_outlives_requirements` vector.
1436
+ //
1437
+ // Polonius doesn't model regions ("origins") as CFG-subsets or durations, but the
1438
+ // `longer_fr` and `shorter_fr` terminology will still be used here, for consistency with
1439
+ // the rest of the NLL infrastructure. The "subset origin" is the "longer free region",
1440
+ // and the "superset origin" is the outlived "shorter free region".
1441
+ //
1442
+ // Note: Polonius will produce a subset error at every point where the unexpected
1443
+ // `longer_fr`'s "placeholder loan" is contained in the `shorter_fr`. This can be helpful
1444
+ // for diagnostics in the future, e.g. to point more precisely at the key locations
1445
+ // requiring this constraint to hold. However, the error and diagnostics code downstream
1446
+ // expects that these errors are not duplicated (and that they are in a certain order).
1447
+ // Otherwise, diagnostics messages such as the ones giving names like `'1` to elided or
1448
+ // anonymous lifetimes for example, could give these names differently, while others like
1449
+ // the outlives suggestions or the debug output from `#[rustc_regions]` would be
1450
+ // duplicated. The polonius subset errors are deduplicated here, while keeping the
1451
+ // CFG-location ordering.
1452
+ let mut subset_errors: Vec < _ > = polonius_output
1453
+ . subset_errors
1454
+ . iter ( )
1455
+ . flat_map ( |( _location, subset_errors) | subset_errors. iter ( ) )
1456
+ . collect ( ) ;
1457
+ subset_errors. sort ( ) ;
1458
+ subset_errors. dedup ( ) ;
1459
+
1460
+ for ( longer_fr, shorter_fr) in subset_errors. into_iter ( ) {
1461
+ debug ! ( "check_polonius_subset_errors: subset_error longer_fr={:?},\
1462
+ shorter_fr={:?}", longer_fr, shorter_fr) ;
1463
+
1464
+ self . report_or_propagate_universal_region_error (
1465
+ * longer_fr,
1466
+ * shorter_fr,
1467
+ infcx,
1468
+ body,
1469
+ local_names,
1470
+ upvars,
1471
+ mir_def_id,
1472
+ & mut propagated_outlives_requirements,
1473
+ & mut outlives_suggestion,
1474
+ errors_buffer,
1475
+ region_naming,
1476
+ ) ;
1477
+ }
1478
+
1479
+ // Handle the placeholder errors as usual, until the chalk-rustc-polonius triumvirate has
1480
+ // a more complete picture on how to separate this responsibility.
1481
+ for ( fr, fr_definition) in self . definitions . iter_enumerated ( ) {
1482
+ match fr_definition. origin {
1483
+ NLLRegionVariableOrigin :: FreeRegion => {
1484
+ // handled by polonius above
1485
+ }
1486
+
1487
+ NLLRegionVariableOrigin :: Placeholder ( placeholder) => {
1488
+ self . check_bound_universal_region ( infcx, body, mir_def_id, fr, placeholder) ;
1489
+ }
1490
+
1491
+ NLLRegionVariableOrigin :: Existential { .. } => {
1492
+ // nothing to check here
1493
+ }
1494
+ }
1495
+ }
1496
+
1497
+ // Emit outlives suggestions
1498
+ outlives_suggestion. add_suggestion ( body, self , infcx, errors_buffer, region_naming) ;
1499
+ }
1500
+
1378
1501
/// Checks the final value for the free region `fr` to see if it
1379
1502
/// grew too large. In particular, examine what `end(X)` points
1380
1503
/// wound up in `fr`'s final value; for each `end(X)` where `X !=
@@ -1474,8 +1597,37 @@ impl<'tcx> RegionInferenceContext<'tcx> {
1474
1597
return None ;
1475
1598
}
1476
1599
1600
+ self . report_or_propagate_universal_region_error (
1601
+ longer_fr,
1602
+ shorter_fr,
1603
+ infcx,
1604
+ body,
1605
+ local_names,
1606
+ upvars,
1607
+ mir_def_id,
1608
+ propagated_outlives_requirements,
1609
+ outlives_suggestion,
1610
+ errors_buffer,
1611
+ region_naming,
1612
+ )
1613
+ }
1614
+
1615
+ fn report_or_propagate_universal_region_error (
1616
+ & self ,
1617
+ longer_fr : RegionVid ,
1618
+ shorter_fr : RegionVid ,
1619
+ infcx : & InferCtxt < ' _ , ' tcx > ,
1620
+ body : & Body < ' tcx > ,
1621
+ local_names : & IndexVec < Local , Option < Symbol > > ,
1622
+ upvars : & [ Upvar ] ,
1623
+ mir_def_id : DefId ,
1624
+ propagated_outlives_requirements : & mut Option < & mut Vec < ClosureOutlivesRequirement < ' tcx > > > ,
1625
+ outlives_suggestion : & mut OutlivesSuggestionBuilder < ' _ > ,
1626
+ errors_buffer : & mut Vec < Diagnostic > ,
1627
+ region_naming : & mut RegionErrorNamingCtx ,
1628
+ ) -> Option < ErrorReported > {
1477
1629
debug ! (
1478
- "check_universal_region_relation : fr={:?} does not outlive shorter_fr={:?}" ,
1630
+ "report_or_propagate_universal_region_error : fr={:?} does not outlive shorter_fr={:?}" ,
1479
1631
longer_fr, shorter_fr,
1480
1632
) ;
1481
1633
@@ -1484,9 +1636,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
1484
1636
// We'll call it `fr-` -- it's ever so slightly smaller than
1485
1637
// `longer_fr`.
1486
1638
1487
- if let Some ( fr_minus) = self . universal_region_relations . non_local_lower_bound ( longer_fr )
1488
- {
1489
- debug ! ( "check_universal_region : fr_minus={:?}" , fr_minus) ;
1639
+ if let Some ( fr_minus) =
1640
+ self . universal_region_relations . non_local_lower_bound ( longer_fr ) {
1641
+ debug ! ( "report_or_propagate_universal_region_error : fr_minus={:?}" , fr_minus) ;
1490
1642
1491
1643
let blame_span_category =
1492
1644
self . find_outlives_blame_span ( body, longer_fr,
@@ -1497,7 +1649,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
1497
1649
// so slightly larger than `shorter_fr`.
1498
1650
let shorter_fr_plus =
1499
1651
self . universal_region_relations . non_local_upper_bounds ( & shorter_fr) ;
1500
- debug ! ( "check_universal_region: shorter_fr_plus={:?}" , shorter_fr_plus) ;
1652
+ debug ! (
1653
+ "report_or_propagate_universal_region_error: shorter_fr_plus={:?}" , shorter_fr_plus
1654
+ ) ;
1501
1655
for & & fr in & shorter_fr_plus {
1502
1656
// Push the constraint `fr-: shorter_fr+`
1503
1657
propagated_outlives_requirements. push ( ClosureOutlivesRequirement {
0 commit comments