Skip to content

Commit

Permalink
Modify implicator to take a "syntactic" approach, ensuring that fns and
Browse files Browse the repository at this point in the history
object types impose minimal constraints on their arguments.  Also modify
regionck to consider `<P0 as TraitRef<P1..Pn>>::Foo: 'r` to hold if `Pi:
'r` (for all `i`). This is a fallback, because in some cases we can
impose looser requirements. Currently the inference is sound but not
precise. Fixes rust-lang#24622.

Adapted cherry-pick of 45f93290969f19d8217a2e4dff7b12dc81957eb6.
  • Loading branch information
nikomatsakis committed Jul 14, 2015
1 parent 5708b1a commit 970a3a3
Show file tree
Hide file tree
Showing 20 changed files with 659 additions and 127 deletions.
2 changes: 1 addition & 1 deletion src/librustc/middle/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ impl OverloadedCallType {
// supplies types from the tree. After type checking is complete, you
// can just use the tcx as the typer.

pub struct ExprUseVisitor<'d,'t,'a: 't, 'tcx:'a> {
pub struct ExprUseVisitor<'d, 't, 'a: 't, 'tcx:'a+'d> {
typer: &'t infer::InferCtxt<'a, 'tcx>,
mc: mc::MemCategorizationContext<'t, 'a, 'tcx>,
delegate: &'d mut (Delegate<'tcx>+'d),
Expand Down
69 changes: 46 additions & 23 deletions src/librustc/middle/implicator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ struct Implicator<'a, 'tcx: 'a> {
visited: FnvHashSet<Ty<'tcx>>,
}

/// This routine computes the well-formedness constraints that must hold for the type `ty` to
/// appear in a context with lifetime `outer_region`
/// This routine computes the full set of well-formedness constraints
/// that must hold for the type `ty` to appear in a context with
/// lifetime `outer_region`.
pub fn implications<'a,'tcx>(
infcx: &'a InferCtxt<'a,'tcx>,
body_id: ast::NodeId,
Expand Down Expand Up @@ -90,12 +91,15 @@ impl<'a, 'tcx> Implicator<'a, 'tcx> {
ty::TyInt(..) |
ty::TyUint(..) |
ty::TyFloat(..) |
ty::TyBareFn(..) |
ty::TyError |
ty::TyStr => {
// No borrowed content reachable here.
}

ty::TyBareFn(..) => {
self.accumulate_referenced_regions_and_types(ty);
}

ty::TyClosure(def_id, substs) => {
let &(r_a, opt_ty) = self.stack.last().unwrap();
self.out.push(Implication::RegionSubClosure(opt_ty, r_a, def_id, substs));
Expand Down Expand Up @@ -272,28 +276,16 @@ impl<'a, 'tcx> Implicator<'a, 'tcx> {
.map(|pred| Implication::Predicate(def_id, pred));
self.out.extend(obligations);

let variances = self.tcx().item_variances(def_id);

for (&region, &variance) in substs.regions().iter().zip(&variances.regions) {
match variance {
ty::Contravariant | ty::Invariant => {
// If any data with this lifetime is reachable
// within, it must be at least contravariant.
self.push_region_constraint_from_top(region)
}
ty::Covariant | ty::Bivariant => { }
}
for &region in substs.regions() {
// If any data with this lifetime is reachable
// within, it must be at least contravariant.
self.push_region_constraint_from_top(region)
}

for (&ty, &variance) in substs.types.iter().zip(&variances.types) {
match variance {
ty::Covariant | ty::Invariant => {
// If any data of this type is reachable within,
// it must be at least covariant.
self.accumulate_from_ty(ty);
}
ty::Contravariant | ty::Bivariant => { }
}
for &ty in &substs.types {
// If any data of this type is reachable within,
// it must be at least covariant.
self.accumulate_from_ty(ty);
}
}

Expand Down Expand Up @@ -394,6 +386,37 @@ impl<'a, 'tcx> Implicator<'a, 'tcx> {
// constraint above
self.out.push(Implication::RegionSubRegion(Some(ty), r_d, r_c));
}

self.accumulate_referenced_regions_and_types(ty);
}

fn accumulate_referenced_regions_and_types(&mut self,
iface_ty: Ty<'tcx>)
{
// for now, `iface_ty` represents some type that is a fn or
// trait object argument, and because those appear underneath
// forall binders, we do not enforce the full WF requirements,
// just the lifetime "outlives" requirements.
for ty in iface_ty.walk() {
match ty.sty {
ty::TyParam(p) => {
self.push_param_constraint_from_top(p);
}

_ => { }
}

for r in ty.regions() {
match r {
ty::ReLateBound(..) => {
// skip late-bound regions
}
_ => {
self.push_region_constraint_from_top(r);
}
}
}
}
}

fn fully_normalize<T>(&self, value: &T) -> Result<T,ErrorReported>
Expand Down
10 changes: 4 additions & 6 deletions src/librustc/middle/infer/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,7 @@ pub trait ErrorReporting<'tcx> {
fn report_generic_bound_failure(&self,
origin: SubregionOrigin<'tcx>,
kind: GenericKind<'tcx>,
sub: Region,
sups: Vec<Region>);
sub: Region);

fn report_sub_sup_conflict(&self,
var_origin: RegionVariableOrigin,
Expand Down Expand Up @@ -294,8 +293,8 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
self.report_concrete_failure(origin, sub, sup);
}

GenericBoundFailure(kind, param_ty, sub, sups) => {
self.report_generic_bound_failure(kind, param_ty, sub, sups);
GenericBoundFailure(kind, param_ty, sub) => {
self.report_generic_bound_failure(kind, param_ty, sub);
}

SubSupConflict(var_origin,
Expand Down Expand Up @@ -529,8 +528,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
fn report_generic_bound_failure(&self,
origin: SubregionOrigin<'tcx>,
bound_kind: GenericKind<'tcx>,
sub: Region,
_sups: Vec<Region>)
sub: Region)
{
// FIXME: it would be better to report the first error message
// with the span of the parameter itself, rather than the span
Expand Down
8 changes: 4 additions & 4 deletions src/librustc/middle/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub use self::TypeOrigin::*;
pub use self::ValuePairs::*;
pub use middle::ty::IntVarValue;
pub use self::freshen::TypeFreshener;
pub use self::region_inference::GenericKind;
pub use self::region_inference::{GenericKind, VerifyBound};

use middle::free_region::FreeRegionMap;
use middle::mem_categorization as mc;
Expand Down Expand Up @@ -1283,13 +1283,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
origin: SubregionOrigin<'tcx>,
kind: GenericKind<'tcx>,
a: ty::Region,
bs: Vec<ty::Region>) {
bound: VerifyBound) {
debug!("verify_generic_bound({:?}, {:?} <: {:?})",
kind,
a,
bs);
bound);

self.region_vars.verify_generic_bound(origin, kind, a, bs);
self.region_vars.verify_generic_bound(origin, kind, a, bound);
}

pub fn can_equate<'b,T>(&'b self, a: &T, b: &T) -> UnitResult<'tcx>
Expand Down
152 changes: 128 additions & 24 deletions src/librustc/middle/infer/region_inference/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,41 @@ pub enum Verify<'tcx> {
// outlive `RS`. Therefore verify that `R <= RS[i]` for some
// `i`. Inference variables may be involved (but this verification
// step doesn't influence inference).
VerifyGenericBound(GenericKind<'tcx>, SubregionOrigin<'tcx>, Region, Vec<Region>),
VerifyGenericBound(GenericKind<'tcx>, SubregionOrigin<'tcx>, Region, VerifyBound),
}

#[derive(Clone, PartialEq, Eq)]
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum GenericKind<'tcx> {
Param(ty::ParamTy),
Projection(ty::ProjectionTy<'tcx>),
}

// When we introduce a verification step, we wish to test that a
// particular region (let's call it `'min`) meets some bound.
// The bound is described the by the following grammar:
#[derive(Debug)]
pub enum VerifyBound {
// B = exists {R} --> some 'r in {R} must outlive 'min
//
// Put another way, the subject value is known to outlive all
// regions in {R}, so if any of those outlives 'min, then the
// bound is met.
AnyRegion(Vec<Region>),

// B = forall {R} --> all 'r in {R} must outlive 'min
//
// Put another way, the subject value is known to outlive some
// region in {R}, so if all of those outlives 'min, then the bound
// is met.
AllRegions(Vec<Region>),

// B = exists {B} --> 'min must meet some bound b in {B}
AnyBound(Vec<VerifyBound>),

// B = forall {B} --> 'min must meet all bounds b in {B}
AllBounds(Vec<VerifyBound>),
}

#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct TwoRegions {
a: Region,
Expand Down Expand Up @@ -102,12 +128,11 @@ pub enum RegionResolutionError<'tcx> {
/// `o` requires that `a <= b`, but this does not hold
ConcreteFailure(SubregionOrigin<'tcx>, Region, Region),

/// `GenericBoundFailure(p, s, a, bs)
/// `GenericBoundFailure(p, s, a)
///
/// The parameter/associated-type `p` must be known to outlive the lifetime
/// `a`, but it is only known to outlive `bs` (and none of the
/// regions in `bs` outlive `a`).
GenericBoundFailure(SubregionOrigin<'tcx>, GenericKind<'tcx>, Region, Vec<Region>),
/// `a` (but none of the known bounds are sufficient).
GenericBoundFailure(SubregionOrigin<'tcx>, GenericKind<'tcx>, Region),

/// `SubSupConflict(v, sub_origin, sub_r, sup_origin, sup_r)`:
///
Expand Down Expand Up @@ -408,6 +433,14 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
debug!("RegionVarBindings: add_verify({:?})",
verify);

// skip no-op cases known to be satisfied
match verify {
VerifyGenericBound(_, _, _, VerifyBound::AllBounds(ref bs)) if bs.len() == 0 => {
return;
}
_ => { }
}

let mut verifys = self.verifys.borrow_mut();
let index = verifys.len();
verifys.push(verify);
Expand Down Expand Up @@ -497,8 +530,8 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
origin: SubregionOrigin<'tcx>,
kind: GenericKind<'tcx>,
sub: Region,
sups: Vec<Region>) {
self.add_verify(VerifyGenericBound(kind, origin, sub, sups));
bound: VerifyBound) {
self.add_verify(VerifyGenericBound(kind, origin, sub, bound));
}

pub fn lub_regions(&self,
Expand Down Expand Up @@ -663,12 +696,11 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
&mut result_set, r,
a, b);
}
VerifyGenericBound(_, _, a, ref bs) => {
for &b in bs {
consider_adding_bidirectional_edges(
&mut result_set, r,
a, b);
}
VerifyGenericBound(_, _, a, ref bound) => {
bound.for_each_region(&mut |b| {
consider_adding_bidirectional_edges(&mut result_set, r,
a, b)
});
}
}
}
Expand Down Expand Up @@ -1263,20 +1295,13 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
errors.push(ConcreteFailure((*origin).clone(), sub, sup));
}

VerifyGenericBound(ref kind, ref origin, sub, ref sups) => {
VerifyGenericBound(ref kind, ref origin, sub, ref bound) => {
let sub = normalize(values, sub);
if sups.iter()
.map(|&sup| normalize(values, sup))
.any(|sup| free_regions.is_subregion_of(self.tcx, sub, sup))
{
if bound.is_met(self.tcx, free_regions, values, sub) {
continue;
}

let sups = sups.iter().map(|&sup| normalize(values, sup))
.collect();
errors.push(
GenericBoundFailure(
(*origin).clone(), kind.clone(), sub, sups));
errors.push(GenericBoundFailure((*origin).clone(), kind.clone(), sub));
}
}
}
Expand Down Expand Up @@ -1722,3 +1747,82 @@ impl<'tcx> GenericKind<'tcx> {
}
}
}

impl VerifyBound {
fn for_each_region(&self, f: &mut FnMut(ty::Region)) {
match self {
&VerifyBound::AnyRegion(ref rs) |
&VerifyBound::AllRegions(ref rs) =>
for &r in rs { f(r); },

&VerifyBound::AnyBound(ref bs) |
&VerifyBound::AllBounds(ref bs) =>
for b in bs { b.for_each_region(f); },
}
}

pub fn must_hold(&self) -> bool {
match self {
&VerifyBound::AnyRegion(ref bs) => bs.contains(&ty::ReStatic),
&VerifyBound::AllRegions(ref bs) => bs.is_empty(),
&VerifyBound::AnyBound(ref bs) => bs.iter().any(|b| b.must_hold()),
&VerifyBound::AllBounds(ref bs) => bs.iter().all(|b| b.must_hold()),
}
}

pub fn cannot_hold(&self) -> bool {
match self {
&VerifyBound::AnyRegion(ref bs) => bs.is_empty(),
&VerifyBound::AllRegions(ref bs) => bs.contains(&ty::ReEmpty),
&VerifyBound::AnyBound(ref bs) => bs.iter().all(|b| b.cannot_hold()),
&VerifyBound::AllBounds(ref bs) => bs.iter().any(|b| b.cannot_hold()),
}
}

pub fn or(self, vb: VerifyBound) -> VerifyBound {
if self.must_hold() || vb.cannot_hold() {
self
} else if self.cannot_hold() || vb.must_hold() {
vb
} else {
VerifyBound::AnyBound(vec![self, vb])
}
}

pub fn and(self, vb: VerifyBound) -> VerifyBound {
if self.must_hold() && vb.must_hold() {
self
} else if self.cannot_hold() && vb.cannot_hold() {
self
} else {
VerifyBound::AllBounds(vec![self, vb])
}
}

fn is_met<'tcx>(&self,
tcx: &ty::ctxt<'tcx>,
free_regions: &FreeRegionMap,
var_values: &Vec<VarValue>,
min: ty::Region)
-> bool {
match self {
&VerifyBound::AnyRegion(ref rs) =>
rs.iter()
.map(|&r| normalize(var_values, r))
.any(|r| free_regions.is_subregion_of(tcx, min, r)),

&VerifyBound::AllRegions(ref rs) =>
rs.iter()
.map(|&r| normalize(var_values, r))
.all(|r| free_regions.is_subregion_of(tcx, min, r)),

&VerifyBound::AnyBound(ref bs) =>
bs.iter()
.any(|b| b.is_met(tcx, free_regions, var_values, min)),

&VerifyBound::AllBounds(ref bs) =>
bs.iter()
.all(|b| b.is_met(tcx, free_regions, var_values, min)),
}
}
}
Loading

0 comments on commit 970a3a3

Please sign in to comment.