Skip to content

Commit c1bd8d4

Browse files
committed
Abort on recursion in type_must_outlive (rust-lang#25954)
Display E0399 and stop recursing in type_must_outlive if a previous function application was encountered.
1 parent dedd430 commit c1bd8d4

File tree

2 files changed

+102
-82
lines changed

2 files changed

+102
-82
lines changed

src/librustc_typeck/check/regionck.rs

Lines changed: 100 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ use middle::ty::{self, ClosureTyper, ReScope, Ty, MethodCall};
9595
use middle::infer::{self, GenericKind};
9696
use middle::pat_util;
9797

98+
use std::collections::HashSet;
9899
use std::mem;
99100
use syntax::{ast, ast_util};
100101
use syntax::codemap::Span;
@@ -1397,115 +1398,133 @@ pub fn type_must_outlive<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
13971398
ty: Ty<'tcx>,
13981399
region: ty::Region)
13991400
{
1400-
debug!("type_must_outlive(ty={:?}, region={:?})",
1401+
type_must_outlive_inner(rcx, origin, ty, region, &mut HashSet::new());
1402+
1403+
fn type_must_outlive_inner<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
1404+
origin: infer::SubregionOrigin<'tcx>,
1405+
ty: Ty<'tcx>,
1406+
region: ty::Region,
1407+
visited: &mut HashSet<(Ty<'tcx>, ty::Region, Span)>) {
1408+
debug!("type_must_outlive(ty={:?}, region={:?})",
14011409
ty,
14021410
region);
14031411

1404-
let implications = implicator::implications(rcx.fcx.infcx(), rcx.fcx, rcx.body_id,
1405-
ty, region, origin.span());
1406-
for implication in implications {
1407-
debug!("implication: {:?}", implication);
1408-
match implication {
1409-
implicator::Implication::RegionSubRegion(None, r_a, r_b) => {
1410-
rcx.fcx.mk_subr(origin.clone(), r_a, r_b);
1411-
}
1412-
implicator::Implication::RegionSubRegion(Some(ty), r_a, r_b) => {
1413-
let o1 = infer::ReferenceOutlivesReferent(ty, origin.span());
1414-
rcx.fcx.mk_subr(o1, r_a, r_b);
1415-
}
1416-
implicator::Implication::RegionSubGeneric(None, r_a, ref generic_b) => {
1417-
generic_must_outlive(rcx, origin.clone(), r_a, generic_b);
1418-
}
1419-
implicator::Implication::RegionSubGeneric(Some(ty), r_a, ref generic_b) => {
1420-
let o1 = infer::ReferenceOutlivesReferent(ty, origin.span());
1421-
generic_must_outlive(rcx, o1, r_a, generic_b);
1422-
}
1423-
implicator::Implication::RegionSubClosure(_, r_a, def_id, substs) => {
1424-
closure_must_outlive(rcx, origin.clone(), r_a, def_id, substs);
1425-
}
1426-
implicator::Implication::Predicate(def_id, predicate) => {
1427-
let cause = traits::ObligationCause::new(origin.span(),
1428-
rcx.body_id,
1429-
traits::ItemObligation(def_id));
1430-
let obligation = traits::Obligation::new(cause, predicate);
1431-
rcx.fcx.register_predicate(obligation);
1412+
let implications = implicator::implications(rcx.fcx.infcx(), rcx.fcx, rcx.body_id,
1413+
ty, region, origin.span());
1414+
1415+
let candidate = (ty, region, origin.span());
1416+
if visited.contains(&candidate) {
1417+
span_err!(rcx.tcx().sess, origin.span(), E0399,
1418+
"recursive overflow detected while checking type lifetime rules for {}", ty);
1419+
return;
1420+
}
1421+
visited.insert(candidate);
1422+
1423+
for implication in implications {
1424+
debug!("implication: {:?}", implication);
1425+
match implication {
1426+
implicator::Implication::RegionSubRegion(None, r_a, r_b) => {
1427+
rcx.fcx.mk_subr(origin.clone(), r_a, r_b);
1428+
}
1429+
implicator::Implication::RegionSubRegion(Some(ty), r_a, r_b) => {
1430+
let o1 = infer::ReferenceOutlivesReferent(ty, origin.span());
1431+
rcx.fcx.mk_subr(o1, r_a, r_b);
1432+
}
1433+
implicator::Implication::RegionSubGeneric(None, r_a, ref generic_b) => {
1434+
generic_must_outlive_inner(rcx, origin.clone(), r_a, generic_b);
1435+
}
1436+
implicator::Implication::RegionSubGeneric(Some(ty), r_a, ref generic_b) => {
1437+
let o1 = infer::ReferenceOutlivesReferent(ty, origin.span());
1438+
generic_must_outlive_inner(rcx, o1, r_a, generic_b);
1439+
}
1440+
implicator::Implication::RegionSubClosure(_, r_a, def_id, substs) => {
1441+
closure_must_outlive_inner(rcx, origin.clone(), r_a, def_id, substs, visited);
1442+
}
1443+
implicator::Implication::Predicate(def_id, predicate) => {
1444+
let cause = traits::ObligationCause::new(origin.span(),
1445+
rcx.body_id,
1446+
traits::ItemObligation(def_id));
1447+
let obligation = traits::Obligation::new(cause, predicate);
1448+
rcx.fcx.register_predicate(obligation);
1449+
}
14321450
}
14331451
}
14341452
}
1435-
}
14361453

1437-
fn closure_must_outlive<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
1454+
fn closure_must_outlive_inner<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
14381455
origin: infer::SubregionOrigin<'tcx>,
14391456
region: ty::Region,
14401457
def_id: ast::DefId,
1441-
substs: &'tcx Substs<'tcx>) {
1442-
debug!("closure_must_outlive(region={:?}, def_id={:?}, substs={:?})",
1458+
substs: &'tcx Substs<'tcx>,
1459+
visited: &mut HashSet<(Ty<'tcx>, ty::Region, Span)>) {
1460+
debug!("closure_must_outlive(region={:?}, def_id={:?}, substs={:?})",
14431461
region, def_id, substs);
14441462

1445-
let upvars = rcx.fcx.closure_upvars(def_id, substs).unwrap();
1446-
for upvar in upvars {
1447-
let var_id = upvar.def.def_id().local_id();
1448-
type_must_outlive(
1449-
rcx, infer::FreeVariable(origin.span(), var_id),
1450-
upvar.ty, region);
1463+
let upvars = rcx.fcx.closure_upvars(def_id, substs).unwrap();
1464+
for upvar in upvars {
1465+
let var_id = upvar.def.def_id().local_id();
1466+
type_must_outlive_inner(
1467+
rcx, infer::FreeVariable(origin.span(), var_id),
1468+
upvar.ty, region, &mut visited.clone());
1469+
}
14511470
}
1452-
}
14531471

1454-
fn generic_must_outlive<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
1472+
fn generic_must_outlive_inner<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>,
14551473
origin: infer::SubregionOrigin<'tcx>,
14561474
region: ty::Region,
14571475
generic: &GenericKind<'tcx>) {
1458-
let param_env = &rcx.fcx.inh.param_env;
1476+
let param_env = &rcx.fcx.inh.param_env;
14591477

1460-
debug!("param_must_outlive(region={:?}, generic={:?})",
1478+
debug!("param_must_outlive(region={:?}, generic={:?})",
14611479
region,
14621480
generic);
14631481

1464-
// To start, collect bounds from user:
1465-
let mut param_bounds =
1466-
ty::required_region_bounds(rcx.tcx(),
1467-
generic.to_ty(rcx.tcx()),
1468-
param_env.caller_bounds.clone());
1469-
1470-
// In the case of a projection T::Foo, we may be able to extract bounds from the trait def:
1471-
match *generic {
1472-
GenericKind::Param(..) => { }
1473-
GenericKind::Projection(ref projection_ty) => {
1474-
param_bounds.push_all(
1475-
&projection_bounds(rcx, origin.span(), projection_ty));
1482+
// To start, collect bounds from user:
1483+
let mut param_bounds =
1484+
ty::required_region_bounds(rcx.tcx(),
1485+
generic.to_ty(rcx.tcx()),
1486+
param_env.caller_bounds.clone());
1487+
1488+
// In the case of a projection T::Foo, we may be able to extract bounds from the trait def:
1489+
match *generic {
1490+
GenericKind::Param(..) => { }
1491+
GenericKind::Projection(ref projection_ty) => {
1492+
param_bounds.push_all(
1493+
&projection_bounds(rcx, origin.span(), projection_ty));
1494+
}
14761495
}
1477-
}
14781496

1479-
// Add in the default bound of fn body that applies to all in
1480-
// scope type parameters:
1481-
param_bounds.push(param_env.implicit_region_bound);
1497+
// Add in the default bound of fn body that applies to all in
1498+
// scope type parameters:
1499+
param_bounds.push(param_env.implicit_region_bound);
14821500

1483-
// Finally, collect regions we scraped from the well-formedness
1484-
// constraints in the fn signature. To do that, we walk the list
1485-
// of known relations from the fn ctxt.
1486-
//
1487-
// This is crucial because otherwise code like this fails:
1488-
//
1489-
// fn foo<'a, A>(x: &'a A) { x.bar() }
1490-
//
1491-
// The problem is that the type of `x` is `&'a A`. To be
1492-
// well-formed, then, A must be lower-generic by `'a`, but we
1493-
// don't know that this holds from first principles.
1494-
for &(ref r, ref p) in &rcx.region_bound_pairs {
1495-
debug!("generic={:?} p={:?}",
1501+
// Finally, collect regions we scraped from the well-formedness
1502+
// constraints in the fn signature. To do that, we walk the list
1503+
// of known relations from the fn ctxt.
1504+
//
1505+
// This is crucial because otherwise code like this fails:
1506+
//
1507+
// fn foo<'a, A>(x: &'a A) { x.bar() }
1508+
//
1509+
// The problem is that the type of `x` is `&'a A`. To be
1510+
// well-formed, then, A must be lower-generic by `'a`, but we
1511+
// don't know that this holds from first principles.
1512+
for &(ref r, ref p) in &rcx.region_bound_pairs {
1513+
debug!("generic={:?} p={:?}",
14961514
generic,
14971515
p);
1498-
if generic == p {
1499-
param_bounds.push(*r);
1516+
if generic == p {
1517+
param_bounds.push(*r);
1518+
}
15001519
}
1501-
}
15021520

1503-
// Inform region inference that this generic must be properly
1504-
// bounded.
1505-
rcx.fcx.infcx().verify_generic_bound(origin,
1506-
generic.clone(),
1507-
region,
1508-
param_bounds);
1521+
// Inform region inference that this generic must be properly
1522+
// bounded.
1523+
rcx.fcx.infcx().verify_generic_bound(origin,
1524+
generic.clone(),
1525+
region,
1526+
param_bounds);
1527+
}
15091528
}
15101529

15111530
fn projection_bounds<'a,'tcx>(rcx: &Rcx<'a, 'tcx>,

src/librustc_typeck/diagnostics.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1585,6 +1585,7 @@ register_diagnostics! {
15851585
// `#[lang = \"{}\"]` is allowed for the `{}` primitive
15861586
E0391, // unsupported cyclic reference between types/traits detected
15871587
E0392, // parameter `{}` is never used
1588-
E0393 // the type parameter `{}` must be explicitly specified in an object
1588+
E0393, // the type parameter `{}` must be explicitly specified in an object
15891589
// type because its default value `{}` references the type `Self`"
1590+
E0399 // recursive overflow detected during regionck
15901591
}

0 commit comments

Comments
 (0)