diff options
Diffstat (limited to '')
-rw-r--r-- | compiler/rustc_hir_analysis/src/outlives/explicit.rs (renamed from compiler/rustc_typeck/src/outlives/explicit.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs (renamed from compiler/rustc_typeck/src/outlives/implicit_infer.rs) | 4 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/outlives/mod.rs | 129 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/outlives/test.rs | 21 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/outlives/utils.rs | 186 |
5 files changed, 338 insertions, 2 deletions
diff --git a/compiler/rustc_typeck/src/outlives/explicit.rs b/compiler/rustc_hir_analysis/src/outlives/explicit.rs index 7534482cc..7534482cc 100644 --- a/compiler/rustc_typeck/src/outlives/explicit.rs +++ b/compiler/rustc_hir_analysis/src/outlives/explicit.rs diff --git a/compiler/rustc_typeck/src/outlives/implicit_infer.rs b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs index 3b779280e..90c6edb65 100644 --- a/compiler/rustc_typeck/src/outlives/implicit_infer.rs +++ b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs @@ -1,8 +1,8 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; -use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst}; use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt}; +use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_span::Span; use super::explicit::ExplicitPredicatesMap; @@ -29,7 +29,7 @@ pub(super) fn infer_predicates<'tcx>( // Visit all the crates and infer predicates for id in tcx.hir().items() { - let item_did = id.def_id; + let item_did = id.owner_id; debug!("InferVisitor::visit_item(item={:?})", item_did); diff --git a/compiler/rustc_hir_analysis/src/outlives/mod.rs b/compiler/rustc_hir_analysis/src/outlives/mod.rs new file mode 100644 index 000000000..e50c26765 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/outlives/mod.rs @@ -0,0 +1,129 @@ +use hir::Node; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::{self, CratePredicatesMap, ToPredicate, TyCtxt}; +use rustc_span::symbol::sym; +use rustc_span::Span; + +mod explicit; +mod implicit_infer; +/// Code to write unit test for outlives. +pub mod test; +mod utils; + +pub fn provide(providers: &mut Providers) { + *providers = Providers { inferred_outlives_of, inferred_outlives_crate, ..*providers }; +} + +fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[(ty::Predicate<'_>, Span)] { + let id = tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()); + + if matches!(tcx.def_kind(item_def_id), hir::def::DefKind::AnonConst) && tcx.lazy_normalization() + { + if tcx.hir().opt_const_param_default_param_hir_id(id).is_some() { + // In `generics_of` we set the generics' parent to be our parent's parent which means that + // we lose out on the predicates of our actual parent if we dont return those predicates here. + // (See comment in `generics_of` for more information on why the parent shenanigans is necessary) + // + // struct Foo<'a, 'b, const N: usize = { ... }>(&'a &'b ()); + // ^^^ ^^^^^^^ the def id we are calling + // ^^^ inferred_outlives_of on + // parent item we dont have set as the + // parent of generics returned by `generics_of` + // + // In the above code we want the anon const to have predicates in its param env for `'b: 'a` + let item_def_id = tcx.hir().get_parent_item(id); + // In the above code example we would be calling `inferred_outlives_of(Foo)` here + return tcx.inferred_outlives_of(item_def_id); + } + } + + match tcx.hir().get(id) { + Node::Item(item) => match item.kind { + hir::ItemKind::Struct(..) | hir::ItemKind::Enum(..) | hir::ItemKind::Union(..) => { + let crate_map = tcx.inferred_outlives_crate(()); + + let predicates = crate_map.predicates.get(&item_def_id).copied().unwrap_or(&[]); + + if tcx.has_attr(item_def_id, sym::rustc_outlives) { + let mut pred: Vec<String> = predicates + .iter() + .map(|(out_pred, _)| match out_pred.kind().skip_binder() { + ty::PredicateKind::RegionOutlives(p) => p.to_string(), + ty::PredicateKind::TypeOutlives(p) => p.to_string(), + err => bug!("unexpected predicate {:?}", err), + }) + .collect(); + pred.sort(); + + let span = tcx.def_span(item_def_id); + let mut err = tcx.sess.struct_span_err(span, "rustc_outlives"); + for p in &pred { + err.note(p); + } + err.emit(); + } + + debug!("inferred_outlives_of({:?}) = {:?}", item_def_id, predicates); + + predicates + } + + _ => &[], + }, + + _ => &[], + } +} + +fn inferred_outlives_crate(tcx: TyCtxt<'_>, (): ()) -> CratePredicatesMap<'_> { + // 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 + // they were mandatory. Nonetheless, we have to ensure that every such + // predicate is satisfied, so they form a kind of base set of requirements + // for the type. + + // Compute the inferred predicates + let global_inferred_outlives = implicit_infer::infer_predicates(tcx); + + // Convert the inferred predicates into the "collected" form the + // global data structure expects. + // + // FIXME -- consider correcting impedance mismatch in some way, + // probably by updating the global data structure. + let predicates = global_inferred_outlives + .iter() + .map(|(&def_id, set)| { + let predicates = &*tcx.arena.alloc_from_iter(set.0.iter().filter_map( + |(ty::OutlivesPredicate(kind1, region2), &span)| { + match kind1.unpack() { + GenericArgKind::Type(ty1) => Some(( + ty::Binder::dummy(ty::PredicateKind::TypeOutlives( + ty::OutlivesPredicate(ty1, *region2), + )) + .to_predicate(tcx), + span, + )), + GenericArgKind::Lifetime(region1) => Some(( + ty::Binder::dummy(ty::PredicateKind::RegionOutlives( + ty::OutlivesPredicate(region1, *region2), + )) + .to_predicate(tcx), + span, + )), + GenericArgKind::Const(_) => { + // Generic consts don't impose any constraints. + None + } + } + }, + )); + (def_id, predicates) + }) + .collect(); + + ty::CratePredicatesMap { predicates } +} diff --git a/compiler/rustc_hir_analysis/src/outlives/test.rs b/compiler/rustc_hir_analysis/src/outlives/test.rs new file mode 100644 index 000000000..fa2ac5659 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/outlives/test.rs @@ -0,0 +1,21 @@ +use rustc_errors::struct_span_err; +use rustc_middle::ty::TyCtxt; +use rustc_span::symbol::sym; + +pub fn test_inferred_outlives(tcx: TyCtxt<'_>) { + for id in tcx.hir().items() { + // For unit testing: check for a special "rustc_outlives" + // attribute and report an error with various results if found. + if tcx.has_attr(id.owner_id.to_def_id(), sym::rustc_outlives) { + let inferred_outlives_of = tcx.inferred_outlives_of(id.owner_id); + struct_span_err!( + tcx.sess, + tcx.def_span(id.owner_id), + E0640, + "{:?}", + inferred_outlives_of + ) + .emit(); + } + } +} diff --git a/compiler/rustc_hir_analysis/src/outlives/utils.rs b/compiler/rustc_hir_analysis/src/outlives/utils.rs new file mode 100644 index 000000000..0409c7081 --- /dev/null +++ b/compiler/rustc_hir_analysis/src/outlives/utils.rs @@ -0,0 +1,186 @@ +use rustc_infer::infer::outlives::components::{push_outlives_components, Component}; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; +use rustc_middle::ty::{self, Region, Ty, TyCtxt}; +use rustc_span::Span; +use smallvec::smallvec; +use std::collections::BTreeMap; + +/// Tracks the `T: 'a` or `'a: 'a` predicates that we have inferred +/// must be added to the struct header. +pub(crate) type RequiredPredicates<'tcx> = + BTreeMap<ty::OutlivesPredicate<GenericArg<'tcx>, ty::Region<'tcx>>, Span>; + +/// Given a requirement `T: 'a` or `'b: 'a`, deduce the +/// outlives_component and add it to `required_predicates` +pub(crate) fn insert_outlives_predicate<'tcx>( + tcx: TyCtxt<'tcx>, + kind: GenericArg<'tcx>, + outlived_region: Region<'tcx>, + span: Span, + required_predicates: &mut RequiredPredicates<'tcx>, +) { + // If the `'a` region is bound within the field type itself, we + // don't want to propagate this constraint to the header. + if !is_free_region(outlived_region) { + return; + } + + match kind.unpack() { + GenericArgKind::Type(ty) => { + // `T: 'outlived_region` for some type `T` + // But T could be a lot of things: + // e.g., if `T = &'b u32`, then `'b: 'outlived_region` is + // what we want to add. + // + // Or if within `struct Foo<U>` you had `T = Vec<U>`, then + // we would want to add `U: 'outlived_region` + let mut components = smallvec![]; + push_outlives_components(tcx, ty, &mut components); + for component in components { + match component { + Component::Region(r) => { + // This would arise from something like: + // + // ``` + // struct Foo<'a, 'b> { + // x: &'a &'b u32 + // } + // ``` + // + // Here `outlived_region = 'a` and `kind = &'b + // u32`. Decomposing `&'b u32` into + // components would yield `'b`, and we add the + // where clause that `'b: 'a`. + insert_outlives_predicate( + tcx, + r.into(), + outlived_region, + span, + required_predicates, + ); + } + + Component::Param(param_ty) => { + // param_ty: ty::ParamTy + // This would arise from something like: + // + // ``` + // struct Foo<'a, U> { + // x: &'a Vec<U> + // } + // ``` + // + // Here `outlived_region = 'a` and `kind = + // Vec<U>`. Decomposing `Vec<U>` into + // components would yield `U`, and we add the + // where clause that `U: 'a`. + let ty: Ty<'tcx> = param_ty.to_ty(tcx); + required_predicates + .entry(ty::OutlivesPredicate(ty.into(), outlived_region)) + .or_insert(span); + } + + Component::Projection(proj_ty) => { + // This would arise from something like: + // + // ``` + // struct Foo<'a, T: Iterator> { + // x: &'a <T as Iterator>::Item + // } + // ``` + // + // Here we want to add an explicit `where <T as Iterator>::Item: 'a`. + let ty: Ty<'tcx> = tcx.mk_projection(proj_ty.item_def_id, proj_ty.substs); + required_predicates + .entry(ty::OutlivesPredicate(ty.into(), outlived_region)) + .or_insert(span); + } + + Component::Opaque(def_id, substs) => { + // This would arise from something like: + // + // ```rust + // type Opaque<T> = impl Sized; + // fn defining<T>() -> Opaque<T> {} + // struct Ss<'a, T>(&'a Opaque<T>); + // ``` + // + // Here we want to have an implied bound `Opaque<T>: 'a` + + let ty = tcx.mk_opaque(def_id, substs); + required_predicates + .entry(ty::OutlivesPredicate(ty.into(), outlived_region)) + .or_insert(span); + } + + Component::EscapingProjection(_) => { + // As above, but the projection involves + // late-bound regions. Therefore, the WF + // requirement is not checked in type definition + // but at fn call site, so ignore it. + // + // ``` + // struct Foo<'a, T: Iterator> { + // x: for<'b> fn(<&'b T as Iterator>::Item) + // // ^^^^^^^^^^^^^^^^^^^^^^^^^ + // } + // ``` + // + // Since `'b` is not in scope on `Foo`, can't + // do anything here, ignore it. + } + + Component::UnresolvedInferenceVariable(_) => bug!("not using infcx"), + } + } + } + + GenericArgKind::Lifetime(r) => { + if !is_free_region(r) { + return; + } + required_predicates.entry(ty::OutlivesPredicate(kind, outlived_region)).or_insert(span); + } + + GenericArgKind::Const(_) => { + // Generic consts don't impose any constraints. + } + } +} + +fn is_free_region(region: Region<'_>) -> bool { + // First, screen for regions that might appear in a type header. + match *region { + // These correspond to `T: 'a` relationships: + // + // struct Foo<'a, T> { + // field: &'a T, // this would generate a ReEarlyBound referencing `'a` + // } + // + // We care about these, so fall through. + ty::ReEarlyBound(_) => true, + + // These correspond to `T: 'static` relationships which can be + // rather surprising. + // + // struct Foo<'a, T> { + // field: &'static T, // this would generate a ReStatic + // } + ty::ReStatic => false, + + // Late-bound regions can appear in `fn` types: + // + // struct Foo<T> { + // field: for<'b> fn(&'b T) // e.g., 'b here + // } + // + // The type above might generate a `T: 'b` bound, but we can + // ignore it. We can't put it on the struct header anyway. + ty::ReLateBound(..) => false, + + // These regions don't appear in types from type declarations: + ty::ReErased | ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReFree(..) => { + bug!("unexpected region in outlives inference: {:?}", region); + } + } +} |