Auto merge of #52010 - toidiu:ak-crossCrateOutlives, r=nikomatsakis

Fix: infer outlives requirements across crates

Fixes https://github.com/rust-lang/rust/issues/51858
This commit is contained in:
bors 2018-07-06 16:01:25 +00:00
commit 062a416dd4
5 changed files with 134 additions and 108 deletions

View File

@ -8,77 +8,66 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use rustc::hir;
use rustc::hir::def_id::{CrateNum, DefId};
use rustc::hir::itemlikevisit::ItemLikeVisitor;
use rustc::ty::{self, TyCtxt};
use rustc::hir::def_id::DefId;
use rustc::ty::{self, OutlivesPredicate, TyCtxt};
use util::nodemap::FxHashMap;
use super::utils::*;
pub fn explicit_predicates<'tcx>(
tcx: TyCtxt<'_, 'tcx, 'tcx>,
crate_num: CrateNum,
) -> FxHashMap<DefId, RequiredPredicates<'tcx>> {
let mut predicates = FxHashMap::default();
// iterate over the entire crate
tcx.hir.krate().visit_all_item_likes(&mut ExplicitVisitor {
tcx: tcx,
explicit_predicates: &mut predicates,
crate_num: crate_num,
});
predicates
#[derive(Debug)]
pub struct ExplicitPredicatesMap<'tcx> {
map: FxHashMap<DefId, RequiredPredicates<'tcx>>,
}
pub struct ExplicitVisitor<'cx, 'tcx: 'cx> {
tcx: TyCtxt<'cx, 'tcx, 'tcx>,
explicit_predicates: &'cx mut FxHashMap<DefId, RequiredPredicates<'tcx>>,
crate_num: CrateNum,
}
impl<'cx, 'tcx> ItemLikeVisitor<'tcx> for ExplicitVisitor<'cx, 'tcx> {
fn visit_item(&mut self, item: &'tcx hir::Item) {
let def_id = DefId {
krate: self.crate_num,
index: item.hir_id.owner,
};
let mut required_predicates = RequiredPredicates::default();
let local_explicit_predicate = self.tcx.explicit_predicates_of(def_id).predicates;
for pred in local_explicit_predicate.into_iter() {
match pred {
ty::Predicate::TypeOutlives(predicate) => {
let ty::OutlivesPredicate(ref ty, ref reg) = predicate.skip_binder();
insert_outlives_predicate(self.tcx, (*ty).into(), reg, &mut required_predicates)
}
ty::Predicate::RegionOutlives(predicate) => {
let ty::OutlivesPredicate(ref reg1, ref reg2) = predicate.skip_binder();
insert_outlives_predicate(
self.tcx,
(*reg1).into(),
reg2,
&mut required_predicates,
)
}
ty::Predicate::Trait(..)
| ty::Predicate::Projection(..)
| ty::Predicate::WellFormed(..)
| ty::Predicate::ObjectSafe(..)
| ty::Predicate::ClosureKind(..)
| ty::Predicate::Subtype(..)
| ty::Predicate::ConstEvaluatable(..) => (),
}
impl<'tcx> ExplicitPredicatesMap<'tcx> {
pub fn new() -> ExplicitPredicatesMap<'tcx> {
ExplicitPredicatesMap {
map: FxHashMap::default(),
}
self.explicit_predicates.insert(def_id, required_predicates);
}
fn visit_trait_item(&mut self, _trait_item: &'tcx hir::TraitItem) {}
pub fn explicit_predicates_of(
&mut self,
tcx: TyCtxt<'_, 'tcx, 'tcx>,
def_id: DefId,
) -> &RequiredPredicates<'tcx> {
self.map.entry(def_id).or_insert_with(|| {
let predicates = if def_id.is_local() {
tcx.explicit_predicates_of(def_id).predicates
} else {
tcx.predicates_of(def_id).predicates
};
let mut required_predicates = RequiredPredicates::default();
fn visit_impl_item(&mut self, _impl_item: &'tcx hir::ImplItem) {}
// process predicates and convert to `RequiredPredicates` entry, see below
for pred in predicates.into_iter() {
match pred {
ty::Predicate::TypeOutlives(predicate) => {
let OutlivesPredicate(ref ty, ref reg) = predicate.skip_binder();
insert_outlives_predicate(tcx, (*ty).into(), reg, &mut required_predicates)
}
ty::Predicate::RegionOutlives(predicate) => {
let OutlivesPredicate(ref reg1, ref reg2) = predicate.skip_binder();
insert_outlives_predicate(
tcx,
(*reg1).into(),
reg2,
&mut required_predicates,
)
}
ty::Predicate::Trait(..)
| ty::Predicate::Projection(..)
| ty::Predicate::WellFormed(..)
| ty::Predicate::ObjectSafe(..)
| ty::Predicate::ClosureKind(..)
| ty::Predicate::Subtype(..)
| ty::Predicate::ConstEvaluatable(..) => (),
}
}
required_predicates
})
}
}

View File

@ -15,6 +15,7 @@ use rustc::ty::subst::{Kind, Subst, UnpackedKind};
use rustc::ty::{self, Ty, TyCtxt};
use rustc::util::nodemap::FxHashMap;
use super::explicit::ExplicitPredicatesMap;
use super::utils::*;
/// Infer predicates for the items in the crate.
@ -24,7 +25,7 @@ use super::utils::*;
/// now be filled with inferred predicates.
pub fn infer_predicates<'tcx>(
tcx: TyCtxt<'_, 'tcx, 'tcx>,
explicit_map: &FxHashMap<DefId, RequiredPredicates<'tcx>>,
explicit_map: &mut ExplicitPredicatesMap<'tcx>,
) -> FxHashMap<DefId, RequiredPredicates<'tcx>> {
debug!("infer_predicates");
@ -55,7 +56,7 @@ pub struct InferVisitor<'cx, 'tcx: 'cx> {
tcx: TyCtxt<'cx, 'tcx, 'tcx>,
global_inferred_outlives: &'cx mut FxHashMap<DefId, RequiredPredicates<'tcx>>,
predicates_added: &'cx mut bool,
explicit_map: &'cx FxHashMap<DefId, RequiredPredicates<'tcx>>,
explicit_map: &'cx mut ExplicitPredicatesMap<'tcx>,
}
impl<'cx, 'tcx> ItemLikeVisitor<'tcx> for InferVisitor<'cx, 'tcx> {
@ -93,7 +94,7 @@ impl<'cx, 'tcx> ItemLikeVisitor<'tcx> for InferVisitor<'cx, 'tcx> {
field_ty,
self.global_inferred_outlives,
&mut item_required_predicates,
self.explicit_map,
&mut self.explicit_map,
);
}
}
@ -129,7 +130,7 @@ fn insert_required_predicates_to_be_wf<'tcx>(
field_ty: Ty<'tcx>,
global_inferred_outlives: &FxHashMap<DefId, RequiredPredicates<'tcx>>,
required_predicates: &mut RequiredPredicates<'tcx>,
explicit_map: &FxHashMap<DefId, RequiredPredicates<'tcx>>,
explicit_map: &mut ExplicitPredicatesMap<'tcx>,
) {
for ty in field_ty.walk() {
match ty.sty {
@ -257,53 +258,54 @@ pub fn check_explicit_predicates<'tcx>(
def_id: &DefId,
substs: &[Kind<'tcx>],
required_predicates: &mut RequiredPredicates<'tcx>,
explicit_map: &FxHashMap<DefId, RequiredPredicates<'tcx>>,
explicit_map: &mut ExplicitPredicatesMap<'tcx>,
ignore_self_ty: bool,
) {
debug!("def_id = {:?}", &def_id);
debug!("substs = {:?}", &substs);
debug!("explicit_map = {:?}", explicit_map);
debug!("required_predicates = {:?}", required_predicates);
if let Some(explicit_predicates) = explicit_map.get(def_id) {
for outlives_predicate in explicit_predicates.iter() {
debug!("outlives_predicate = {:?}", &outlives_predicate);
let explicit_predicates = explicit_map.explicit_predicates_of(tcx, *def_id);
// Careful: If we are inferring the effects of a `dyn Trait<..>`
// type, then when we look up the predicates for `Trait`,
// we may find some that reference `Self`. e.g., perhaps the
// definition of `Trait` was:
//
// ```
// trait Trait<'a, T> where Self: 'a { .. }
// ```
//
// we want to ignore such predicates here, because
// there is no type parameter for them to affect. Consider
// a struct containing `dyn Trait`:
//
// ```
// struct MyStruct<'x, X> { field: Box<dyn Trait<'x, X>> }
// ```
//
// The `where Self: 'a` predicate refers to the *existential, hidden type*
// that is represented by the `dyn Trait`, not to the `X` type parameter
// (or any other generic parameter) declared on `MyStruct`.
//
// Note that we do this check for self **before** applying `substs`. In the
// case that `substs` come from a `dyn Trait` type, our caller will have
// included `Self = dyn Trait<'x, X>` as the value for `Self`. If we were
// to apply the substs, and not filter this predicate, we might then falsely
// conclude that e.g. `X: 'x` was a reasonable inferred requirement.
if let UnpackedKind::Type(ty) = outlives_predicate.0.unpack() {
if ty.is_self() && ignore_self_ty {
debug!("skipping self ty = {:?}", &ty);
continue;
}
for outlives_predicate in explicit_predicates.iter() {
debug!("outlives_predicate = {:?}", &outlives_predicate);
// Careful: If we are inferring the effects of a `dyn Trait<..>`
// type, then when we look up the predicates for `Trait`,
// we may find some that reference `Self`. e.g., perhaps the
// definition of `Trait` was:
//
// ```
// trait Trait<'a, T> where Self: 'a { .. }
// ```
//
// we want to ignore such predicates here, because
// there is no type parameter for them to affect. Consider
// a struct containing `dyn Trait`:
//
// ```
// struct MyStruct<'x, X> { field: Box<dyn Trait<'x, X>> }
// ```
//
// The `where Self: 'a` predicate refers to the *existential, hidden type*
// that is represented by the `dyn Trait`, not to the `X` type parameter
// (or any other generic parameter) declared on `MyStruct`.
//
// Note that we do this check for self **before** applying `substs`. In the
// case that `substs` come from a `dyn Trait` type, our caller will have
// included `Self = dyn Trait<'x, X>` as the value for `Self`. If we were
// to apply the substs, and not filter this predicate, we might then falsely
// conclude that e.g. `X: 'x` was a reasonable inferred requirement.
if let UnpackedKind::Type(ty) = outlives_predicate.0.unpack() {
if ty.is_self() && ignore_self_ty {
debug!("skipping self ty = {:?}", &ty);
continue;
}
let predicate = outlives_predicate.subst(tcx, substs);
debug!("predicate = {:?}", &predicate);
insert_outlives_predicate(tcx, predicate.0.into(), predicate.1, required_predicates);
}
let predicate = outlives_predicate.subst(tcx, substs);
debug!("predicate = {:?}", &predicate);
insert_outlives_predicate(tcx, predicate.0.into(), predicate.1, required_predicates);
}
// }
}

View File

@ -84,6 +84,8 @@ fn inferred_outlives_crate<'tcx>(
tcx: TyCtxt<'_, 'tcx, 'tcx>,
crate_num: CrateNum,
) -> Lrc<CratePredicatesMap<'tcx>> {
assert_eq!(crate_num, LOCAL_CRATE);
// Compute a map from each struct/enum/union S to the **explicit**
// outlives predicates (`T: 'a`, `'a: 'b`) that the user wrote.
// Typically there won't be many of these, except in older code where
@ -92,8 +94,9 @@ fn inferred_outlives_crate<'tcx>(
// for the type.
// Compute the inferred predicates
let exp = explicit::explicit_predicates(tcx, crate_num);
let global_inferred_outlives = implicit_infer::infer_predicates(tcx, &exp);
let mut exp_map = explicit::ExplicitPredicatesMap::new();
let global_inferred_outlives = implicit_infer::infer_predicates(tcx, &mut exp_map);
// Convert the inferred predicates into the "collected" form the
// global data structure expects.

View File

@ -0,0 +1,20 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(rustc_attrs)]
#![feature(infer_outlives_requirements)]
#[rustc_outlives]
struct Foo<'a, T> { //~ ERROR 15:1: 17:2: rustc_outlives
bar: std::slice::IterMut<'a, T>
}
fn main() {}

View File

@ -0,0 +1,12 @@
error: rustc_outlives
--> $DIR/cross-crate.rs:15:1
|
LL | / struct Foo<'a, T> { //~ ERROR 15:1: 17:2: rustc_outlives
LL | | bar: std::slice::IterMut<'a, T>
LL | | }
| |_^
|
= note: T : 'a
error: aborting due to previous error