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 Aug 5, 2015
1 parent 76ff835 commit 11fa4d2
Show file tree
Hide file tree
Showing 20 changed files with 654 additions and 132 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
80 changes: 42 additions & 38 deletions src/librustc/middle/implicator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,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 @@ -89,7 +90,6 @@ impl<'a, 'tcx> Implicator<'a, 'tcx> {
ty::TyInt(..) |
ty::TyUint(..) |
ty::TyFloat(..) |
ty::TyBareFn(..) |
ty::TyError |
ty::TyStr => {
// No borrowed content reachable here.
Expand Down Expand Up @@ -309,43 +309,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);
self.accumulate_from_substs(substs, Some(&variances));
}

fn accumulate_from_substs(&mut self,
substs: &Substs<'tcx>,
variances: Option<&ty::ItemVariances>)
{
let mut tmp_variances = None;
let variances = variances.unwrap_or_else(|| {
tmp_variances = Some(ty::ItemVariances {
types: substs.types.map(|_| ty::Variance::Invariant),
regions: substs.regions().map(|_| ty::Variance::Invariant),
});
tmp_variances.as_ref().unwrap()
});

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 @@ -446,6 +419,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 @@ -239,8 +239,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 @@ -292,8 +291,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 @@ -527,8 +526,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 @@ -1398,13 +1398,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 @@ -1264,20 +1296,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 @@ -1676,3 +1701,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 11fa4d2

Please sign in to comment.