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:
commit
062a416dd4
|
@ -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
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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() {}
|
||||
|
|
@ -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
|
||||
|
Loading…
Reference in New Issue