From 6a229cbfac2c6ae8e1db4f8ae6320cac731a2c2a Mon Sep 17 00:00:00 2001 From: toidiu Date: Sun, 15 Oct 2017 01:13:56 -0400 Subject: [PATCH] Implement inferring outlives requirements for references, structs, enum, union, and projection types. added a feature gate and tests for these scenarios. --- .gitignore | 1 + src/librustc/dep_graph/dep_node.rs | 1 + src/librustc/ich/impls_ty.rs | 14 + src/librustc/ty/maps/config.rs | 6 + src/librustc/ty/maps/mod.rs | 7 +- src/librustc/ty/maps/plumbing.rs | 1 + src/librustc/ty/mod.rs | 16 + src/librustc_typeck/collect.rs | 9 +- src/librustc_typeck/diagnostics.rs | 2 +- src/librustc_typeck/lib.rs | 1 + src/librustc_typeck/outlives/explicit.rs | 82 ++++ .../outlives/implicit_empty.rs | 52 +++ .../outlives/implicit_infer.rs | 442 ++++++++++++++++++ src/librustc_typeck/outlives/mod.rs | 96 +++- src/librustc_typeck/outlives/test.rs | 22 +- src/libsyntax/feature_gate.rs | 9 + .../compile-fail/outlives-associated-types.rs | 4 +- ...eature-gate-infer_outlives_requirements.rs | 18 + ...re-gate-infer_outlives_requirements.stderr | 17 + .../ui/rfc-2093-infer-outlives/enum-pass.rs | 38 ++ src/test/ui/rfc-2093-infer-outlives/enum.rs | 37 ++ .../ui/rfc-2093-infer-outlives/enum.stderr | 31 ++ .../explicit-impl-lifetime-pass.rs | 30 ++ .../explicit-impl-pass.rs | 30 ++ .../rfc-2093-infer-outlives/explicit-impl.rs | 30 ++ .../explicit-impl.stderr | 17 + .../explicit-where-pass.rs | 27 ++ .../rfc-2093-infer-outlives/explicit-where.rs | 23 + .../explicit-where.stderr | 17 + .../multiple-regions-pass.rs | 22 + .../multiple-regions.rs | 19 + .../multiple-regions.stderr | 20 + .../nested-structs-pass.rs | 25 + .../rfc-2093-infer-outlives/nested-structs.rs | 26 ++ .../nested-structs.stderr | 17 + .../projections-pass.rs | 23 + .../ui/rfc-2093-infer-outlives/projections.rs | 20 + .../projections.stderr | 16 + .../rfc-2093-infer-outlives/reference-pass.rs | 23 + .../ui/rfc-2093-infer-outlives/reference.rs | 18 + .../rfc-2093-infer-outlives/reference.stderr | 17 + .../ui/rfc-2093-infer-outlives/union-pass.rs | 39 ++ src/test/ui/rfc-2093-infer-outlives/union.rs | 40 ++ .../ui/rfc-2093-infer-outlives/union.stderr | 31 ++ 44 files changed, 1415 insertions(+), 21 deletions(-) create mode 100644 src/librustc_typeck/outlives/explicit.rs create mode 100644 src/librustc_typeck/outlives/implicit_empty.rs create mode 100644 src/librustc_typeck/outlives/implicit_infer.rs create mode 100644 src/test/ui/feature-gate-infer_outlives_requirements.rs create mode 100644 src/test/ui/feature-gate-infer_outlives_requirements.stderr create mode 100644 src/test/ui/rfc-2093-infer-outlives/enum-pass.rs create mode 100644 src/test/ui/rfc-2093-infer-outlives/enum.rs create mode 100644 src/test/ui/rfc-2093-infer-outlives/enum.stderr create mode 100644 src/test/ui/rfc-2093-infer-outlives/explicit-impl-lifetime-pass.rs create mode 100644 src/test/ui/rfc-2093-infer-outlives/explicit-impl-pass.rs create mode 100644 src/test/ui/rfc-2093-infer-outlives/explicit-impl.rs create mode 100644 src/test/ui/rfc-2093-infer-outlives/explicit-impl.stderr create mode 100644 src/test/ui/rfc-2093-infer-outlives/explicit-where-pass.rs create mode 100644 src/test/ui/rfc-2093-infer-outlives/explicit-where.rs create mode 100644 src/test/ui/rfc-2093-infer-outlives/explicit-where.stderr create mode 100644 src/test/ui/rfc-2093-infer-outlives/multiple-regions-pass.rs create mode 100644 src/test/ui/rfc-2093-infer-outlives/multiple-regions.rs create mode 100644 src/test/ui/rfc-2093-infer-outlives/multiple-regions.stderr create mode 100644 src/test/ui/rfc-2093-infer-outlives/nested-structs-pass.rs create mode 100644 src/test/ui/rfc-2093-infer-outlives/nested-structs.rs create mode 100644 src/test/ui/rfc-2093-infer-outlives/nested-structs.stderr create mode 100644 src/test/ui/rfc-2093-infer-outlives/projections-pass.rs create mode 100644 src/test/ui/rfc-2093-infer-outlives/projections.rs create mode 100644 src/test/ui/rfc-2093-infer-outlives/projections.stderr create mode 100644 src/test/ui/rfc-2093-infer-outlives/reference-pass.rs create mode 100644 src/test/ui/rfc-2093-infer-outlives/reference.rs create mode 100644 src/test/ui/rfc-2093-infer-outlives/reference.stderr create mode 100644 src/test/ui/rfc-2093-infer-outlives/union-pass.rs create mode 100644 src/test/ui/rfc-2093-infer-outlives/union.rs create mode 100644 src/test/ui/rfc-2093-infer-outlives/union.stderr diff --git a/.gitignore b/.gitignore index 57407a2399a..efbbf22ffe5 100644 --- a/.gitignore +++ b/.gitignore @@ -83,6 +83,7 @@ __pycache__/ /src/libstd_unicode/UnicodeData.txt /stage[0-9]+/ /target +target/ /test/ /tmp/ TAGS diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 7c5318a96f5..f3f31e5740f 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -500,6 +500,7 @@ define_dep_nodes!( <'tcx> [] GenericsOfItem(DefId), [] PredicatesOfItem(DefId), [] InferredOutlivesOf(DefId), + [] InferredOutlivesCrate(CrateNum), [] SuperPredicatesOfItem(DefId), [] TraitDefOfItem(DefId), [] AdtDefOfItem(DefId), diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index af4d3429bb1..41cfac2674b 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -1100,6 +1100,20 @@ impl<'a> HashStable> for ty::CrateVariancesMap { } } +impl<'a, 'gcx> HashStable> for ty::CratePredicatesMap<'gcx> { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'a>, + hasher: &mut StableHasher) { + let ty::CratePredicatesMap { + ref predicates, + // This is just an irrelevant helper value. + empty_predicate: _, + } = *self; + + predicates.hash_stable(hcx, hasher); + } +} + impl_stable_hash_for!(struct ty::AssociatedItem { def_id, name, diff --git a/src/librustc/ty/maps/config.rs b/src/librustc/ty/maps/config.rs index 16866636cd9..a4571c161c4 100644 --- a/src/librustc/ty/maps/config.rs +++ b/src/librustc/ty/maps/config.rs @@ -155,6 +155,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::crate_variances<'tcx> { } } +impl<'tcx> QueryDescription<'tcx> for queries::inferred_outlives_crate<'tcx> { + fn describe(_tcx: TyCtxt, _: CrateNum) -> String { + format!("computing the inferred outlives predicates for items in this crate") + } +} + impl<'tcx> QueryDescription<'tcx> for queries::mir_shims<'tcx> { fn describe(tcx: TyCtxt, def: ty::InstanceDef<'tcx>) -> String { format!("generating MIR shim for `{}`", diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs index 5a23a3b952a..6dd43554a98 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -102,6 +102,7 @@ define_maps! { <'tcx> /// associated generics and predicates. [] fn generics_of: GenericsOfItem(DefId) -> &'tcx ty::Generics, [] fn predicates_of: PredicatesOfItem(DefId) -> ty::GenericPredicates<'tcx>, + [] fn explicit_predicates_of: PredicatesOfItem(DefId) -> ty::GenericPredicates<'tcx>, /// Maps from the def-id of a trait to the list of /// super-predicates. This is a subset of the full list of @@ -139,7 +140,11 @@ define_maps! { <'tcx> [] fn variances_of: ItemVariances(DefId) -> Lrc>, /// Maps from def-id of a type to its (inferred) outlives. - [] fn inferred_outlives_of: InferredOutlivesOf(DefId) -> Vec>, + [] fn inferred_outlives_of: InferredOutlivesOf(DefId) -> Lrc>>, + + /// Maps from def-id of a type to its (inferred) outlives. + [] fn inferred_outlives_crate: InferredOutlivesCrate(CrateNum) + -> Lrc>, /// Maps from an impl/trait def-id to a list of the def-ids of its items [] fn associated_item_def_ids: AssociatedItemDefIds(DefId) -> Lrc>, diff --git a/src/librustc/ty/maps/plumbing.rs b/src/librustc/ty/maps/plumbing.rs index c04e580a33d..2a1d87421aa 100644 --- a/src/librustc/ty/maps/plumbing.rs +++ b/src/librustc/ty/maps/plumbing.rs @@ -1007,6 +1007,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, DepKind::GenericsOfItem => { force!(generics_of, def_id!()); } DepKind::PredicatesOfItem => { force!(predicates_of, def_id!()); } DepKind::InferredOutlivesOf => { force!(inferred_outlives_of, def_id!()); } + DepKind::InferredOutlivesCrate => { force!(inferred_outlives_crate, LOCAL_CRATE); } DepKind::SuperPredicatesOfItem => { force!(super_predicates_of, def_id!()); } DepKind::TraitDefOfItem => { force!(trait_def, def_id!()); } DepKind::AdtDefOfItem => { force!(adt_def, def_id!()); } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 33b59eda7ce..fccba1e6aa7 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -956,6 +956,22 @@ pub enum Predicate<'tcx> { ConstEvaluatable(DefId, &'tcx Substs<'tcx>), } +/// The crate outlives map is computed during typeck and contains the +/// outlives of every item in the local crate. You should not use it +/// directly, because to do so will make your pass dependent on the +/// HIR of every item in the local crate. Instead, use +/// `tcx.inferred_outlives_of()` to get the outlives for a *particular* +/// item. +pub struct CratePredicatesMap<'tcx> { + /// For each struct with outlive bounds, maps to a vector of the + /// predicate of its outlive bounds. If an item has no outlives + /// bounds, it will have no entry. + pub predicates: FxHashMap>>>, + + /// An empty vector, useful for cloning. + pub empty_predicate: Lrc>>, +} + impl<'tcx> AsRef> for Predicate<'tcx> { fn as_ref(&self) -> &Predicate<'tcx> { self diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index a4f820d1fdc..e2e6a2d7aac 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -64,6 +64,7 @@ pub fn provide(providers: &mut Providers) { type_of, generics_of, predicates_of, + explicit_predicates_of, super_predicates_of, type_param_predicates, trait_def, @@ -1296,13 +1297,17 @@ fn predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> ty::GenericPredicates<'tcx> { let explicit = explicit_predicates_of(tcx, def_id); + let predicates = if tcx.sess.features_untracked().infer_outlives_requirements { + [&explicit.predicates[..], &tcx.inferred_outlives_of(def_id)[..]].concat() + } else { explicit.predicates }; + ty::GenericPredicates { parent: explicit.parent, - predicates: [&explicit.predicates[..], &tcx.inferred_outlives_of(def_id)[..]].concat() + predicates: predicates, } } -fn explicit_predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, +pub fn explicit_predicates_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> ty::GenericPredicates<'tcx> { use rustc::hir::map::*; diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 5a53c008f6c..063d83780d8 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -4840,11 +4840,11 @@ register_diagnostics! { E0588, // packed type cannot transitively contain a `[repr(align)]` type E0592, // duplicate definitions with name `{}` // E0613, // Removed (merged with E0609) - E0640, // infer outlives E0627, // yield statement outside of generator literal E0632, // cannot provide explicit type parameters when `impl Trait` is used in // argument position. E0634, // type has conflicting packed representaton hints + E0640, // infer outlives requirements E0641, // cannot cast to/from a pointer with an unknown kind E0645, // trait aliases not finished E0907, // type inside generator must be known in this context diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 3a48e1806af..1f5aa45e79a 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -82,6 +82,7 @@ This API is completely unstable and subject to change. #![feature(slice_patterns)] #![feature(slice_sort_by_cached_key)] #![feature(dyn_trait)] +#![feature(underscore_lifetimes)] #[macro_use] extern crate log; #[macro_use] extern crate syntax; diff --git a/src/librustc_typeck/outlives/explicit.rs b/src/librustc_typeck/outlives/explicit.rs new file mode 100644 index 00000000000..9a8fd46b0ef --- /dev/null +++ b/src/librustc_typeck/outlives/explicit.rs @@ -0,0 +1,82 @@ +// Copyright 2013 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use hir::map as hir_map; +use rustc::hir; +use rustc::hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; +use rustc::hir::itemlikevisit::ItemLikeVisitor; +use rustc::ty::maps::Providers; +use rustc::ty::{self, CratePredicatesMap, TyCtxt}; +use rustc_data_structures::sync::Lrc; +use util::nodemap::FxHashMap; + +pub fn explicit_predicates<'tcx>( + tcx: TyCtxt<'_, 'tcx, 'tcx>, + crate_num: CrateNum, +) -> FxHashMap>>> { + assert_eq!(crate_num, LOCAL_CRATE); + let mut predicates: FxHashMap>>> = FxHashMap(); + + // 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 +} + +pub struct ExplicitVisitor<'cx, 'tcx: 'cx> { + tcx: TyCtxt<'cx, 'tcx, 'tcx>, + explicit_predicates: &'cx mut FxHashMap>>>, + 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 local_explicit_predicate = self.tcx.explicit_predicates_of(def_id); + + let filtered_predicates = local_explicit_predicate + .predicates + .into_iter() + .filter(|pred| match pred { + ty::Predicate::TypeOutlives(..) | ty::Predicate::RegionOutlives(..) => true, + + ty::Predicate::Trait(..) + | ty::Predicate::Projection(..) + | ty::Predicate::WellFormed(..) + | ty::Predicate::ObjectSafe(..) + | ty::Predicate::ClosureKind(..) + | ty::Predicate::Subtype(..) + | ty::Predicate::ConstEvaluatable(..) => false, + }) + .collect(); + + match item.node { + hir::ItemStruct(..) | hir::ItemEnum(..) => { + self.tcx.adt_def(def_id); + } + _ => {} + } + + self.explicit_predicates + .insert(def_id, Lrc::new(filtered_predicates)); + } + + fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) {} + + fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) {} +} diff --git a/src/librustc_typeck/outlives/implicit_empty.rs b/src/librustc_typeck/outlives/implicit_empty.rs new file mode 100644 index 00000000000..b2259c63683 --- /dev/null +++ b/src/librustc_typeck/outlives/implicit_empty.rs @@ -0,0 +1,52 @@ +// Copyright 2013 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use hir::map as hir_map; +use rustc::hir; +use rustc::hir::def_id::{self, CrateNum, DefId, LOCAL_CRATE}; +use rustc::hir::itemlikevisit::ItemLikeVisitor; +use rustc::ty::maps::Providers; +use rustc::ty::{self, CratePredicatesMap, TyCtxt}; +use rustc_data_structures::sync::Lrc; +use util::nodemap::FxHashMap; + +// Create the sets of inferred predicates for each type. These sets +// are initially empty but will grow during the inference step. +pub fn empty_predicate_map<'tcx>( + tcx: TyCtxt<'_, 'tcx, 'tcx>, +) -> FxHashMap>>> { + let mut predicates = FxHashMap(); + + // iterate over the entire crate + tcx.hir + .krate() + .visit_all_item_likes(&mut EmptyImplicitVisitor { + tcx, + predicates: &mut predicates, + }); + + predicates +} + +pub struct EmptyImplicitVisitor<'cx, 'tcx: 'cx> { + tcx: TyCtxt<'cx, 'tcx, 'tcx>, + predicates: &'cx mut FxHashMap>>>, +} + +impl<'a, 'p, 'v> ItemLikeVisitor<'v> for EmptyImplicitVisitor<'a, 'p> { + fn visit_item(&mut self, item: &hir::Item) { + self.predicates + .insert(self.tcx.hir.local_def_id(item.id), Lrc::new(Vec::new())); + } + + fn visit_trait_item(&mut self, trait_item: &hir::TraitItem) {} + + fn visit_impl_item(&mut self, impl_item: &hir::ImplItem) {} +} diff --git a/src/librustc_typeck/outlives/implicit_infer.rs b/src/librustc_typeck/outlives/implicit_infer.rs new file mode 100644 index 00000000000..ac53a6d4a3f --- /dev/null +++ b/src/librustc_typeck/outlives/implicit_infer.rs @@ -0,0 +1,442 @@ +// Copyright 2013 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused)] + +use rustc::hir; +use rustc::hir::def::{CtorKind, Def}; +use rustc::hir::def_id::{self, CrateNum, DefId, LOCAL_CRATE}; +use rustc::hir::itemlikevisit::ItemLikeVisitor; +use rustc::hir::map as hir_map; +use rustc::ty::Slice; +use rustc::ty::maps::Providers; +use rustc::ty::outlives::Component; +use rustc::ty::subst::{Kind, Subst, UnpackedKind}; +use rustc::ty::{self, AdtKind, CratePredicatesMap, Region, RegionKind, ReprOptions, + ToPolyTraitRef, ToPredicate, Ty, TyCtxt}; +use rustc::util::nodemap::{FxHashMap, FxHashSet}; +use rustc_data_structures::sync::Lrc; +use syntax::{abi, ast}; +use syntax_pos::{Span, DUMMY_SP}; + +/// Infer predicates for the items in the crate. +/// +/// global_inferred_outlives: this is initially the empty map that +/// was generated by walking the items in the crate. This will +/// now be filled with inferred predicates. +pub fn infer_predicates<'tcx>( + tcx: TyCtxt<'_, 'tcx, 'tcx>, + explicit_map: &FxHashMap>>>, +) -> FxHashMap> { + debug!("infer_predicates"); + + let mut predicates_added = true; + + let mut global_inferred_outlives = FxHashMap::default(); + + // If new predicates were added then we need to re-calculate + // all crates since there could be new implied predicates. + while predicates_added { + predicates_added = false; + + let mut visitor = InferVisitor { + tcx: tcx, + global_inferred_outlives: &mut global_inferred_outlives, + predicates_added: &mut predicates_added, + explicit_map: explicit_map, + }; + + // Visit all the crates and infer predicates + tcx.hir.krate().visit_all_item_likes(&mut visitor); + } + + global_inferred_outlives +} + +pub struct InferVisitor<'cx, 'tcx: 'cx> { + tcx: TyCtxt<'cx, 'tcx, 'tcx>, + global_inferred_outlives: &'cx mut FxHashMap>, + predicates_added: &'cx mut bool, + explicit_map: &'cx FxHashMap>>>, +} + +/// Tracks the `T: 'a` or `'a: 'a` predicates that we have inferred +/// must be added to the struct header. +type RequiredPredicates<'tcx> = FxHashSet, ty::Region<'tcx>>>; + +impl<'cx, 'tcx> ItemLikeVisitor<'tcx> for InferVisitor<'cx, 'tcx> { + fn visit_item(&mut self, item: &hir::Item) { + let item_did = self.tcx.hir.local_def_id(item.id); + + debug!("InferVisitor::visit_item(item={:?})", item_did); + + let node_id = self.tcx + .hir + .as_local_node_id(item_did) + .expect("expected local def-id"); + let item = match self.tcx.hir.get(node_id) { + hir::map::NodeItem(item) => item, + _ => bug!(), + }; + + let mut item_required_predicates = RequiredPredicates::default(); + match item.node { + hir::ItemUnion(..) | hir::ItemEnum(..) | hir::ItemStruct(..) => { + let adt_def = self.tcx.adt_def(item_did); + + // Iterate over all fields in item_did + for field_def in adt_def.all_fields() { + // Calculating the predicate requirements necessary + // for item_did. + // + // For field of type &'a T (reference) or TyAdt + // (struct/enum/union) there will be outlive + // requirements for adt_def. + let field_ty = self.tcx.type_of(field_def.did); + insert_required_predicates_to_be_wf( + self.tcx, + field_ty, + self.global_inferred_outlives, + &mut item_required_predicates, + self.explicit_map, + ); + } + } + + _ => {} + }; + + // If new predicates were added (`local_predicate_map` has more + // predicates than the `global_inferred_outlives`), the new predicates + // might result in implied predicates for their parent types. + // Therefore mark `predicates_added` as true and which will ensure + // we walk the crates again and re-calculate predicates for all + // items. + let item_predicates_len: usize = self.global_inferred_outlives + .get(&item_did) + .map(|p| p.len()) + .unwrap_or(0); + if item_required_predicates.len() > item_predicates_len { + *self.predicates_added = true; + self.global_inferred_outlives + .insert(item_did, item_required_predicates); + } + } + + fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) {} + + fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) {} +} + +fn insert_required_predicates_to_be_wf<'tcx>( + tcx: TyCtxt<'_, 'tcx, 'tcx>, + field_ty: Ty<'tcx>, + global_inferred_outlives: &FxHashMap>, + required_predicates: &mut RequiredPredicates<'tcx>, + explicit_map: &FxHashMap>>>, +) { + for ty in field_ty.walk() { + match ty.sty { + // The field is of type &'a T which means that we will have + // a predicate requirement of T: 'a (T outlives 'a). + // + // We also want to calculate potential predicates for the T + ty::TyRef(region, mt) => { + insert_outlives_predicate(tcx, mt.ty.into(), region, required_predicates); + } + + // For each TyAdt (struct/enum/union) type `Foo<'a, T>`, we + // can load the current set of inferred and explicit + // predicates from `global_inferred_outlives` and filter the + // ones that are TypeOutlives. + // + ty::TyAdt(def, substs) => { + // First check the inferred predicates + // + // Example 1: + // + // struct Foo<'a, T> { + // field1: Bar<'a, T> + // } + // + // struct Bar<'b, U> { + // field2: &'b U + // } + // + // Here, when processing the type of `field1`, we would + // request the set of implicit predicates computed for `Bar` + // thus far. This will initially come back empty, but in next + // round we will get `U: 'b`. We then apply the substitution + // `['b => 'a, U => T]` and thus get the requirement that `T: + // 'a` holds for `Foo`. + if let Some(unsubstituted_predicates) = global_inferred_outlives.get(&def.did) { + for unsubstituted_predicate in unsubstituted_predicates { + // `unsubstituted_predicate` is `U: 'b` in the + // example above. So apply the substitution to + // get `T: 'a` (or `predicate`): + let predicate = unsubstituted_predicate.subst(tcx, substs); + insert_outlives_predicate( + tcx, + predicate.0, + predicate.1, + required_predicates, + ); + } + } + + // Check if the type has any explicit predicates that need + // to be added to `required_predicates` + // let _: () = substs.region_at(0); + check_explicit_predicates(tcx, &def.did, substs, required_predicates, explicit_map); + } + + ty::TyDynamic(obj, region) => { + // FIXME This corresponds to `dyn Trait<..>`. In this + // case, we should use the explicit predicates as + // well. + if let Some(p) = obj.principal() { + check_explicit_predicates( + tcx, + &p.skip_binder().def_id, + &[region.into()], + required_predicates, + explicit_map, + ); + } + } + + ty::TyProjection(obj) => { + // FIXME This corresponds to `>::Bar`. In this case, we should use the + // explicit predicates as well. + check_explicit_predicates( + tcx, + &obj.item_def_id, + obj.substs, + required_predicates, + explicit_map, + ); + } + + _ => {} + } + } +} + +/// We also have to check the explicit predicates +/// declared on the type. +/// +/// struct Foo<'a, T> { +/// field1: Bar +/// } +/// +/// struct Bar where U: 'static, U: Foo { +/// ... +/// } +/// +/// Here, we should fetch the explicit predicates, which +/// will give us `U: 'static` and `U: Foo`. The latter we +/// can ignore, but we will want to process `U: 'static`, +/// applying the substitution as above. +fn check_explicit_predicates<'tcx>( + tcx: TyCtxt<'_, 'tcx, 'tcx>, + def_id: &DefId, + substs: &[Kind<'tcx>], + required_predicates: &mut RequiredPredicates<'tcx>, + explicit_map: &FxHashMap>>>, +) { + if let Some(general_predicates) = explicit_map.get(def_id) { + for general_predicate in general_predicates.iter() { + match general_predicate { + // `poly` is `PolyTypeOutlivesPredicate>` + // where OutlivesPredicate is the predicate + // we want to add. + ty::Predicate::TypeOutlives(poly) => { + let predicate = poly.0.subst(tcx, substs); + insert_outlives_predicate( + tcx, + predicate.0.into(), + predicate.1, + required_predicates, + ); + } + + // `poly` is `PolyRegionOutlivesPredicate>` + // where OutlivesPredicate is the predicate + // we want to add. + ty::Predicate::RegionOutlives(poly) => { + let predicate = poly.0.subst(tcx, substs); + insert_outlives_predicate( + tcx, + predicate.0.into(), + predicate.1, + required_predicates, + ); + } + + ty::Predicate::Trait(..) + | ty::Predicate::Projection(..) + | ty::Predicate::WellFormed(..) + | ty::Predicate::ObjectSafe(..) + | ty::Predicate::ClosureKind(..) + | ty::Predicate::Subtype(..) + | ty::Predicate::ConstEvaluatable(..) => (), + } + } + } +} + +/// Given a requirement `T: 'a` or `'b: 'a`, deduce the +/// outlives_component and add it to `required_predicates` +fn insert_outlives_predicate<'tcx>( + tcx: TyCtxt<'_, 'tcx, 'tcx>, + kind: Kind<'tcx>, + outlived_region: Region<'tcx>, + 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() { + UnpackedKind::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` you had `T = Vec`, then + // we would want to add `U: 'outlived_region` + for component in tcx.outlives_components(ty) { + 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, + required_predicates, + ); + } + + Component::Param(param_ty) => { + // param_ty: ty::ParamTy + // This would arise from something like: + // + // ``` + // struct Foo<'a, U> { + // x: &'a Vec + // } + // ``` + // + // Here `outlived_region = 'a` and `kind = + // Vec`. Decomposing `Vec` into + // components would yield `U`, and we add the + // where clause that `U: 'a`. + let ty: Ty<'tcx> = tcx.mk_param(param_ty.idx, param_ty.name); + required_predicates + .insert(ty::OutlivesPredicate(ty.into(), outlived_region)); + } + + Component::Projection(proj_ty) => { + // This would arise from something like: + // + // ``` + // struct Foo<'a, T: Iterator> { + // x: &'a ::Item + // } + // ``` + // + // Here we want to add an explicit `where ::Item: 'a`. + let ty: Ty<'tcx> = tcx.mk_projection(proj_ty.item_def_id, proj_ty.substs); + required_predicates + .insert(ty::OutlivesPredicate(ty.into(), outlived_region)); + } + + 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"), + } + } + } + + UnpackedKind::Lifetime(r) => { + if !is_free_region(r) { + return; + } + required_predicates.insert(ty::OutlivesPredicate(kind, outlived_region)); + } + } +} + +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 where `'a` is + // either declared on the type or `'static`: + // + // struct Foo<'a, T> { + // field: &'a T, // this would generate a ReEarlyBound referencing `'a` + // field2: &'static T, // this would generate a ReStatic + // } + // + // We care about these, so fall through. + RegionKind::ReStatic | RegionKind::ReEarlyBound(_) => true, + + // Late-bound regions can appear in `fn` types: + // + // struct Foo { + // 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. + RegionKind::ReLateBound(..) => false, + + // These regions don't appear in types from type declarations: + RegionKind::ReEmpty + | RegionKind::ReErased + | RegionKind::ReClosureBound(..) + | RegionKind::ReCanonical(..) + | RegionKind::ReScope(..) + | RegionKind::ReVar(..) + | RegionKind::ReSkolemized(..) + | RegionKind::ReFree(..) => { + bug!("unexpected region in outlives inference: {:?}", region); + } + } +} diff --git a/src/librustc_typeck/outlives/mod.rs b/src/librustc_typeck/outlives/mod.rs index 1127028cbc8..bad0c68a6fe 100644 --- a/src/librustc_typeck/outlives/mod.rs +++ b/src/librustc_typeck/outlives/mod.rs @@ -7,23 +7,105 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. - -use rustc::hir::def_id::DefId; -use rustc::ty::{self, TyCtxt}; +#![allow(unused)] +#[allow(dead_code)] +use hir::map as hir_map; +use rustc::dep_graph::DepKind; +use rustc::hir; +use rustc::hir::Ty_::*; +use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc::ty::maps::Providers; +use rustc::ty::subst::UnpackedKind; +use rustc::ty::{self, CratePredicatesMap, TyCtxt}; +use rustc_data_structures::sync::Lrc; +use util::nodemap::FxHashMap; +mod explicit; +mod implicit_empty; +mod implicit_infer; /// Code to write unit test for outlives. pub mod test; pub fn provide(providers: &mut Providers) { *providers = Providers { inferred_outlives_of, + inferred_outlives_crate, ..*providers }; } -//todo -fn inferred_outlives_of<'a, 'tcx>(_tcx: TyCtxt<'a, 'tcx, 'tcx>, _def_id: DefId) - -> Vec> { - Vec::new() +fn inferred_outlives_of<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + item_def_id: DefId, +) -> Lrc>> { + let id = tcx.hir + .as_local_node_id(item_def_id) + .expect("expected local def-id"); + + match tcx.hir.get(id) { + hir_map::NodeItem(item) => match item.node { + hir::ItemStruct(..) | hir::ItemEnum(..) | hir::ItemUnion(..) => { + let crate_map = tcx.inferred_outlives_crate(LOCAL_CRATE); + let dep_node = item_def_id.to_dep_node(tcx, DepKind::InferredOutlivesOf); + tcx.dep_graph.read(dep_node); + + crate_map + .predicates + .get(&item_def_id) + .unwrap_or(&crate_map.empty_predicate) + .clone() + } + + _ => Lrc::new(Vec::new()), + }, + + _ => Lrc::new(Vec::new()), + } +} + +fn inferred_outlives_crate<'tcx>( + tcx: TyCtxt<'_, 'tcx, 'tcx>, + crate_num: CrateNum, +) -> Lrc> { + // 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 exp = explicit::explicit_predicates(tcx, crate_num); + let mut global_inferred_outlives = implicit_infer::infer_predicates(tcx, &exp); + + // 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 mut predicates = global_inferred_outlives + .iter() + .map(|(&def_id, set)| { + let vec: Vec> = set.iter() + .map( + |ty::OutlivesPredicate(kind1, region2)| match kind1.unpack() { + UnpackedKind::Type(ty1) => ty::Predicate::TypeOutlives(ty::Binder( + ty::OutlivesPredicate(ty1, region2), + )), + UnpackedKind::Lifetime(region1) => ty::Predicate::RegionOutlives( + ty::Binder(ty::OutlivesPredicate(region1, region2)), + ), + }, + ) + .collect(); + (def_id, Lrc::new(vec)) + }) + .collect(); + + let empty_predicate = Lrc::new(Vec::new()); + + Lrc::new(ty::CratePredicatesMap { + predicates, + empty_predicate, + }) } diff --git a/src/librustc_typeck/outlives/test.rs b/src/librustc_typeck/outlives/test.rs index 196e6605494..c3c2ae667dd 100644 --- a/src/librustc_typeck/outlives/test.rs +++ b/src/librustc_typeck/outlives/test.rs @@ -13,11 +13,13 @@ use rustc::hir::itemlikevisit::ItemLikeVisitor; use rustc::ty::TyCtxt; pub fn test_inferred_outlives<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { - tcx.hir.krate().visit_all_item_likes(&mut OutlivesTest { tcx }); + tcx.hir + .krate() + .visit_all_item_likes(&mut OutlivesTest { tcx }); } struct OutlivesTest<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx> + tcx: TyCtxt<'a, 'tcx, 'tcx>, } impl<'a, 'tcx> ItemLikeVisitor<'tcx> for OutlivesTest<'a, 'tcx> { @@ -28,14 +30,16 @@ impl<'a, 'tcx> ItemLikeVisitor<'tcx> for OutlivesTest<'a, 'tcx> { // attribute and report an error with various results if found. if self.tcx.has_attr(item_def_id, "rustc_outlives") { let inferred_outlives_of = self.tcx.inferred_outlives_of(item_def_id); - span_err!(self.tcx.sess, - item.span, - E0640, - "{:?}", - inferred_outlives_of); + span_err!( + self.tcx.sess, + item.span, + E0640, + "{:?}", + inferred_outlives_of + ); } } - fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem) { } - fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem) { } + fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem) {} + fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem) {} } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index df39757d1eb..73ebfc20876 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -426,6 +426,9 @@ declare_features! ( // Use `?` as the Kleene "at most one" operator (active, macro_at_most_once_rep, "1.25.0", Some(48075), None), + // Infer outlives requirements; RFC 2093 + (active, infer_outlives_requirements, "1.26.0", Some(44493), None), + // Multiple patterns with `|` in `if let` and `while let` (active, if_while_or_patterns, "1.26.0", Some(48215), None), @@ -1023,6 +1026,12 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG "never will be stable", cfg_fn!(rustc_attrs))), + // RFC #2093 + ("infer_outlives_requirements", Normal, Gated(Stability::Unstable, + "infer_outlives_requirements", + "infer outlives requirements is an experimental feature", + cfg_fn!(infer_outlives_requirements))), + ("wasm_custom_section", Whitelisted, Gated(Stability::Unstable, "wasm_custom_section", "attribute is currently unstable", diff --git a/src/test/compile-fail/outlives-associated-types.rs b/src/test/compile-fail/outlives-associated-types.rs index 778394c9fc8..5c392223f88 100644 --- a/src/test/compile-fail/outlives-associated-types.rs +++ b/src/test/compile-fail/outlives-associated-types.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-tidy-linelength + // Test that the outlives computation runs for now... #![feature(rustc_attrs)] @@ -16,7 +18,7 @@ // https://github.com/rust-lang/rfcs/blob/master/text/2093-infer-outlives.md#example-1-a-reference #[rustc_outlives] -struct Direct<'a, T> { //~ ERROR 19:1: 21:2: [] [E0640] +struct Direct<'a, T> { //~ ERROR 21:1: 23:2: [Binder(OutlivesPredicate(T, ReEarlyBound(0, 'a)))] [E0640] field: &'a T } diff --git a/src/test/ui/feature-gate-infer_outlives_requirements.rs b/src/test/ui/feature-gate-infer_outlives_requirements.rs new file mode 100644 index 00000000000..01ccc50a130 --- /dev/null +++ b/src/test/ui/feature-gate-infer_outlives_requirements.rs @@ -0,0 +1,18 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Needs an explicit where clause stating outlives condition. (RFC 2093) + +// Type T needs to outlive lifetime 'a. +struct Foo<'a, T> { + bar: &'a [T] //~ ERROR the parameter type `T` may not live long enough [E0309] +} + +fn main() { } diff --git a/src/test/ui/feature-gate-infer_outlives_requirements.stderr b/src/test/ui/feature-gate-infer_outlives_requirements.stderr new file mode 100644 index 00000000000..560e494b582 --- /dev/null +++ b/src/test/ui/feature-gate-infer_outlives_requirements.stderr @@ -0,0 +1,17 @@ +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/feature-gate-infer_outlives_requirements.rs:15:5 + | +LL | struct Foo<'a, T> { + | - help: consider adding an explicit lifetime bound `T: 'a`... +LL | bar: &'a [T] //~ ERROR the parameter type `T` may not live long enough [E0309] + | ^^^^^^^^^^^^ + | +note: ...so that the reference type `&'a [T]` does not outlive the data it points at + --> $DIR/feature-gate-infer_outlives_requirements.rs:15:5 + | +LL | bar: &'a [T] //~ ERROR the parameter type `T` may not live long enough [E0309] + | ^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/rfc-2093-infer-outlives/enum-pass.rs b/src/test/ui/rfc-2093-infer-outlives/enum-pass.rs new file mode 100644 index 00000000000..8c7275bb1a7 --- /dev/null +++ b/src/test/ui/rfc-2093-infer-outlives/enum-pass.rs @@ -0,0 +1,38 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// must-compile-successfully + +#![feature(infer_outlives_requirements)] + +// Type T needs to outlive lifetime 'a. +enum Foo<'a, T> { + + One(Bar<'a, T>) +} + +// Type U needs to outlive lifetime 'b +struct Bar<'b, U> { + field2: &'b U +} + + + +// Type K needs to outlive lifetime 'c. +enum Ying<'c, K> { + One(&'c Yang) +} + +struct Yang { + field2: V +} + +fn main() {} + diff --git a/src/test/ui/rfc-2093-infer-outlives/enum.rs b/src/test/ui/rfc-2093-infer-outlives/enum.rs new file mode 100644 index 00000000000..7d0427adb9f --- /dev/null +++ b/src/test/ui/rfc-2093-infer-outlives/enum.rs @@ -0,0 +1,37 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength + +// Needs an explicit where clause stating outlives condition. (RFC 2093) + +// Type T needs to outlive lifetime 'a. +enum Foo<'a, T> { + + One(Bar<'a, T>) +} + +// Type U needs to outlive lifetime 'b +struct Bar<'b, U> { + field2: &'b U //~ ERROR 23:5: 23:18: the parameter type `U` may not live long enough [E0309] +} + + + +// Type K needs to outlive lifetime 'c. +enum Ying<'c, K> { + One(&'c Yang) //~ ERROR 30:9: 30:21: the parameter type `K` may not live long enough [E0309] +} + +struct Yang { + field2: V +} + +fn main() {} diff --git a/src/test/ui/rfc-2093-infer-outlives/enum.stderr b/src/test/ui/rfc-2093-infer-outlives/enum.stderr new file mode 100644 index 00000000000..e6eaf9b4754 --- /dev/null +++ b/src/test/ui/rfc-2093-infer-outlives/enum.stderr @@ -0,0 +1,31 @@ +error[E0309]: the parameter type `U` may not live long enough + --> $DIR/enum.rs:23:5 + | +LL | struct Bar<'b, U> { + | - help: consider adding an explicit lifetime bound `U: 'b`... +LL | field2: &'b U //~ ERROR 23:5: 23:18: the parameter type `U` may not live long enough [E0309] + | ^^^^^^^^^^^^^ + | +note: ...so that the reference type `&'b U` does not outlive the data it points at + --> $DIR/enum.rs:23:5 + | +LL | field2: &'b U //~ ERROR 23:5: 23:18: the parameter type `U` may not live long enough [E0309] + | ^^^^^^^^^^^^^ + +error[E0309]: the parameter type `K` may not live long enough + --> $DIR/enum.rs:30:9 + | +LL | enum Ying<'c, K> { + | - help: consider adding an explicit lifetime bound `K: 'c`... +LL | One(&'c Yang) //~ ERROR 30:9: 30:21: the parameter type `K` may not live long enough [E0309] + | ^^^^^^^^^^^^ + | +note: ...so that the reference type `&'c Yang` does not outlive the data it points at + --> $DIR/enum.rs:30:9 + | +LL | One(&'c Yang) //~ ERROR 30:9: 30:21: the parameter type `K` may not live long enough [E0309] + | ^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/rfc-2093-infer-outlives/explicit-impl-lifetime-pass.rs b/src/test/ui/rfc-2093-infer-outlives/explicit-impl-lifetime-pass.rs new file mode 100644 index 00000000000..da578386adb --- /dev/null +++ b/src/test/ui/rfc-2093-infer-outlives/explicit-impl-lifetime-pass.rs @@ -0,0 +1,30 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-test +// must-compile-successfully + +#![feature(infer_outlives_requirements)] +// Outlives requirementes are inferred (RFC 2093) + +trait MakeRef<'a>: 'a { + type Type; +} +impl<'a, T> MakeRef<'a> for Vec +where T: 'a, +{ + type Type = &'a T; +} +// explicit-impl: T: 'a +struct Foo<'a, T> { + foo: as MakeRef<'a>>::Type, +} + +fn main() {} diff --git a/src/test/ui/rfc-2093-infer-outlives/explicit-impl-pass.rs b/src/test/ui/rfc-2093-infer-outlives/explicit-impl-pass.rs new file mode 100644 index 00000000000..fd74fe30bb6 --- /dev/null +++ b/src/test/ui/rfc-2093-infer-outlives/explicit-impl-pass.rs @@ -0,0 +1,30 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-test +// must-compile-successfully + +#![feature(infer_outlives_requirements)] +// Outlives requirementes are inferred (RFC 2093) + +trait MakeRef<'a> { + type Type; +} +impl<'a, T> MakeRef<'a> for Vec +where T: 'a, +{ + type Type = &'a T; +} +// explicit-impl: T: 'a +struct Foo<'a, T> { + foo: as MakeRef<'a>>::Type, +} + +fn main() {} diff --git a/src/test/ui/rfc-2093-infer-outlives/explicit-impl.rs b/src/test/ui/rfc-2093-infer-outlives/explicit-impl.rs new file mode 100644 index 00000000000..3a10087551c --- /dev/null +++ b/src/test/ui/rfc-2093-infer-outlives/explicit-impl.rs @@ -0,0 +1,30 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength + +// Needs an explicit where clause stating outlives condition. (RFC 2093) + +trait MakeRef<'a> { + type Type; +} + +impl<'a, T> MakeRef<'a> for Vec + where T: 'a +{ + type Type = &'a T; +} + +// Type T needs to outlive lifetime 'a, as stated in impl. +struct Foo<'a, T> { + foo: as MakeRef<'a>>::Type //~ Error the parameter type `T` may not live long enough [E0309] +} + +fn main() { } diff --git a/src/test/ui/rfc-2093-infer-outlives/explicit-impl.stderr b/src/test/ui/rfc-2093-infer-outlives/explicit-impl.stderr new file mode 100644 index 00000000000..498d66ef9a5 --- /dev/null +++ b/src/test/ui/rfc-2093-infer-outlives/explicit-impl.stderr @@ -0,0 +1,17 @@ +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/explicit-impl.rs:27:5 + | +LL | struct Foo<'a, T> { + | - help: consider adding an explicit lifetime bound `T: 'a`... +LL | foo: as MakeRef<'a>>::Type //~ Error the parameter type `T` may not live long enough [E0309] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: ...so that the type `T` will meet its required lifetime bounds + --> $DIR/explicit-impl.rs:27:5 + | +LL | foo: as MakeRef<'a>>::Type //~ Error the parameter type `T` may not live long enough [E0309] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/rfc-2093-infer-outlives/explicit-where-pass.rs b/src/test/ui/rfc-2093-infer-outlives/explicit-where-pass.rs new file mode 100644 index 00000000000..e51b5a16b45 --- /dev/null +++ b/src/test/ui/rfc-2093-infer-outlives/explicit-where-pass.rs @@ -0,0 +1,27 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// must-compile-successfully + +#![feature(infer_outlives_requirements)] +// Outlives requirementes are inferred (RFC 2093) + +// explicit-where: infer U: 'b +struct ExFoo<'b, U> { + bar: ExBar<'b, U> +} +struct ExBar<'a, T> where T: 'a { + x: &'a (), + y: T, +} + + +fn main() {} + diff --git a/src/test/ui/rfc-2093-infer-outlives/explicit-where.rs b/src/test/ui/rfc-2093-infer-outlives/explicit-where.rs new file mode 100644 index 00000000000..81734bf514e --- /dev/null +++ b/src/test/ui/rfc-2093-infer-outlives/explicit-where.rs @@ -0,0 +1,23 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Needs an explicit where clause stating outlives condition. (RFC 2093) + +// Type U needs to outlive lifetime 'b. +struct Foo<'b, U> { + bar: Bar<'b, U> //~ Error the parameter type `U` may not live long enough [E0309] +} + +struct Bar<'a, T> where T: 'a { + x: &'a (), + y: T, +} + +fn main() { } diff --git a/src/test/ui/rfc-2093-infer-outlives/explicit-where.stderr b/src/test/ui/rfc-2093-infer-outlives/explicit-where.stderr new file mode 100644 index 00000000000..436754c7dc1 --- /dev/null +++ b/src/test/ui/rfc-2093-infer-outlives/explicit-where.stderr @@ -0,0 +1,17 @@ +error[E0309]: the parameter type `U` may not live long enough + --> $DIR/explicit-where.rs:15:5 + | +LL | struct Foo<'b, U> { + | - help: consider adding an explicit lifetime bound `U: 'b`... +LL | bar: Bar<'b, U> //~ Error the parameter type `U` may not live long enough [E0309] + | ^^^^^^^^^^^^^^^ + | +note: ...so that the type `U` will meet its required lifetime bounds + --> $DIR/explicit-where.rs:15:5 + | +LL | bar: Bar<'b, U> //~ Error the parameter type `U` may not live long enough [E0309] + | ^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/rfc-2093-infer-outlives/multiple-regions-pass.rs b/src/test/ui/rfc-2093-infer-outlives/multiple-regions-pass.rs new file mode 100644 index 00000000000..be686a80048 --- /dev/null +++ b/src/test/ui/rfc-2093-infer-outlives/multiple-regions-pass.rs @@ -0,0 +1,22 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// must-compile-successfully + +#![feature(infer_outlives_requirements)] +// Outlives requirementes are inferred (RFC 2093) + +// multiple-regions: infer 'b: 'a +struct MultiFoo<'a, 'b, T> { + x: &'a &'b T +} + +fn main() {} + diff --git a/src/test/ui/rfc-2093-infer-outlives/multiple-regions.rs b/src/test/ui/rfc-2093-infer-outlives/multiple-regions.rs new file mode 100644 index 00000000000..7ea1ce2d3dc --- /dev/null +++ b/src/test/ui/rfc-2093-infer-outlives/multiple-regions.rs @@ -0,0 +1,19 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Needs an explicit where clause stating outlives condition. (RFC 2093) + +// Lifetime 'b needs to outlive lifetime 'a +struct Foo<'a,'b,T> { + x: &'a &'b T //~ ERROR reference has a longer lifetime than the data it references [E0491] +} + +fn main() {} + diff --git a/src/test/ui/rfc-2093-infer-outlives/multiple-regions.stderr b/src/test/ui/rfc-2093-infer-outlives/multiple-regions.stderr new file mode 100644 index 00000000000..3722abd5ad6 --- /dev/null +++ b/src/test/ui/rfc-2093-infer-outlives/multiple-regions.stderr @@ -0,0 +1,20 @@ +error[E0491]: in type `&'a &'b T`, reference has a longer lifetime than the data it references + --> $DIR/multiple-regions.rs:15:5 + | +LL | x: &'a &'b T //~ ERROR reference has a longer lifetime than the data it references [E0491] + | ^^^^^^^^^^^^ + | +note: the pointer is valid for the lifetime 'a as defined on the struct at 14:1 + --> $DIR/multiple-regions.rs:14:1 + | +LL | struct Foo<'a,'b,T> { + | ^^^^^^^^^^^^^^^^^^^ +note: but the referenced data is only valid for the lifetime 'b as defined on the struct at 14:1 + --> $DIR/multiple-regions.rs:14:1 + | +LL | struct Foo<'a,'b,T> { + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0491`. diff --git a/src/test/ui/rfc-2093-infer-outlives/nested-structs-pass.rs b/src/test/ui/rfc-2093-infer-outlives/nested-structs-pass.rs new file mode 100644 index 00000000000..02581457fcc --- /dev/null +++ b/src/test/ui/rfc-2093-infer-outlives/nested-structs-pass.rs @@ -0,0 +1,25 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// must-compile-successfully + +#![feature(infer_outlives_requirements)] +// Outlives requirementes are inferred (RFC 2093) + +// nested-structs: infer U: 'b and therefore T: 'a +struct NestFoo<'a, T> { + field1: NestBar<'a, T> +} +struct NestBar<'b, U> { + field2: &'b U +} + +fn main() {} + diff --git a/src/test/ui/rfc-2093-infer-outlives/nested-structs.rs b/src/test/ui/rfc-2093-infer-outlives/nested-structs.rs new file mode 100644 index 00000000000..7c444dbd3b0 --- /dev/null +++ b/src/test/ui/rfc-2093-infer-outlives/nested-structs.rs @@ -0,0 +1,26 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Needs an explicit where clause stating outlives condition. (RFC 2093) + + +// Type T needs to outlive lifetime 'a. This is not reported due to +// a compilation error in Bar. +struct Foo<'a, T> { + field1: Bar<'a, T> +} + +// Type U needs to outlive lifetime 'b +struct Bar<'b, U> { + field2: &'b U //~ ERROR the parameter type `U` may not live long enough [E0309] +} + +fn main() {} + diff --git a/src/test/ui/rfc-2093-infer-outlives/nested-structs.stderr b/src/test/ui/rfc-2093-infer-outlives/nested-structs.stderr new file mode 100644 index 00000000000..94d6cbdb5fe --- /dev/null +++ b/src/test/ui/rfc-2093-infer-outlives/nested-structs.stderr @@ -0,0 +1,17 @@ +error[E0309]: the parameter type `U` may not live long enough + --> $DIR/nested-structs.rs:22:5 + | +LL | struct Bar<'b, U> { + | - help: consider adding an explicit lifetime bound `U: 'b`... +LL | field2: &'b U //~ ERROR the parameter type `U` may not live long enough [E0309] + | ^^^^^^^^^^^^^ + | +note: ...so that the reference type `&'b U` does not outlive the data it points at + --> $DIR/nested-structs.rs:22:5 + | +LL | field2: &'b U //~ ERROR the parameter type `U` may not live long enough [E0309] + | ^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/rfc-2093-infer-outlives/projections-pass.rs b/src/test/ui/rfc-2093-infer-outlives/projections-pass.rs new file mode 100644 index 00000000000..1234e27b866 --- /dev/null +++ b/src/test/ui/rfc-2093-infer-outlives/projections-pass.rs @@ -0,0 +1,23 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// must-compile-successfully + +#![feature(infer_outlives_requirements)] +// Outlives requirementes are inferred (RFC 2093) + +// projections: infer ::Item: 'a +struct ProjFoo<'a, T: Iterator> { + bar: &'a T::Item +} + + +fn main() {} + diff --git a/src/test/ui/rfc-2093-infer-outlives/projections.rs b/src/test/ui/rfc-2093-infer-outlives/projections.rs new file mode 100644 index 00000000000..f6a557c174c --- /dev/null +++ b/src/test/ui/rfc-2093-infer-outlives/projections.rs @@ -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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength + +// Needs an explicit where clause stating outlives condition. RFC 2093 + +// Associated type ::Item needs to outlives lifetime 'a. +struct Foo<'a, T: Iterator> { + bar: &'a T::Item //~ Error the associated type `::Item` may not live long enough [E0309] +} + +fn main() { } diff --git a/src/test/ui/rfc-2093-infer-outlives/projections.stderr b/src/test/ui/rfc-2093-infer-outlives/projections.stderr new file mode 100644 index 00000000000..9969cf48ecd --- /dev/null +++ b/src/test/ui/rfc-2093-infer-outlives/projections.stderr @@ -0,0 +1,16 @@ +error[E0309]: the associated type `::Item` may not live long enough + --> $DIR/projections.rs:17:5 + | +LL | bar: &'a T::Item //~ Error the associated type `::Item` may not live long enough [E0309] + | ^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `::Item: 'a`... +note: ...so that the reference type `&'a ::Item` does not outlive the data it points at + --> $DIR/projections.rs:17:5 + | +LL | bar: &'a T::Item //~ Error the associated type `::Item` may not live long enough [E0309] + | ^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/rfc-2093-infer-outlives/reference-pass.rs b/src/test/ui/rfc-2093-infer-outlives/reference-pass.rs new file mode 100644 index 00000000000..f357685e139 --- /dev/null +++ b/src/test/ui/rfc-2093-infer-outlives/reference-pass.rs @@ -0,0 +1,23 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// must-compile-successfully + +#![feature(infer_outlives_requirements)] +// Outlives requirementes are inferred (RFC 2093) + +// reference: infer T: 'a +struct RefFoo<'a, T> { + bar: &'a [T] +} + + +fn main() {} + diff --git a/src/test/ui/rfc-2093-infer-outlives/reference.rs b/src/test/ui/rfc-2093-infer-outlives/reference.rs new file mode 100644 index 00000000000..01ccc50a130 --- /dev/null +++ b/src/test/ui/rfc-2093-infer-outlives/reference.rs @@ -0,0 +1,18 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Needs an explicit where clause stating outlives condition. (RFC 2093) + +// Type T needs to outlive lifetime 'a. +struct Foo<'a, T> { + bar: &'a [T] //~ ERROR the parameter type `T` may not live long enough [E0309] +} + +fn main() { } diff --git a/src/test/ui/rfc-2093-infer-outlives/reference.stderr b/src/test/ui/rfc-2093-infer-outlives/reference.stderr new file mode 100644 index 00000000000..7236bd535c9 --- /dev/null +++ b/src/test/ui/rfc-2093-infer-outlives/reference.stderr @@ -0,0 +1,17 @@ +error[E0309]: the parameter type `T` may not live long enough + --> $DIR/reference.rs:15:5 + | +LL | struct Foo<'a, T> { + | - help: consider adding an explicit lifetime bound `T: 'a`... +LL | bar: &'a [T] //~ ERROR the parameter type `T` may not live long enough [E0309] + | ^^^^^^^^^^^^ + | +note: ...so that the reference type `&'a [T]` does not outlive the data it points at + --> $DIR/reference.rs:15:5 + | +LL | bar: &'a [T] //~ ERROR the parameter type `T` may not live long enough [E0309] + | ^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0309`. diff --git a/src/test/ui/rfc-2093-infer-outlives/union-pass.rs b/src/test/ui/rfc-2093-infer-outlives/union-pass.rs new file mode 100644 index 00000000000..b4a61346b01 --- /dev/null +++ b/src/test/ui/rfc-2093-infer-outlives/union-pass.rs @@ -0,0 +1,39 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// must-compile-successfully + +#![feature(infer_outlives_requirements)] +#![feature(untagged_unions)] +#![allow(unions_with_drop_fields)] + +// Type T needs to outlive lifetime 'a. This is not reported due to +// a compilation error in Bar. +union Foo<'a, T> { + field1: Bar<'a, T> +} + +// Type U needs to outlive lifetime 'b +union Bar<'b, U> { + field2: &'b U +} + + +// Type K needs to outlive lifetime 'c. +union Ying<'c, K> { + field1: &'c Yang +} + +union Yang { + field2: V +} + +fn main() {} + diff --git a/src/test/ui/rfc-2093-infer-outlives/union.rs b/src/test/ui/rfc-2093-infer-outlives/union.rs new file mode 100644 index 00000000000..36b1dccb13e --- /dev/null +++ b/src/test/ui/rfc-2093-infer-outlives/union.rs @@ -0,0 +1,40 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength + +// Needs an explicit where clause stating outlives condition. (RFC 2093) + +#![feature(untagged_unions)] + +// Type T needs to outlive lifetime 'a. This is not reported due to +// a compilation error in Bar. +union Foo<'a, T> { + field1: Bar<'a, T> +} + +// Type U needs to outlive lifetime 'b +union Bar<'b, U> { + field2: &'b U //~ ERROR 25:5: 25:18: the parameter type `U` may not live long enough [E0309] +} + + +// Type K needs to outlive lifetime 'c. +union Ying<'c, K> { + field1: &'c Yang //~ ERROR 31:5: 31:24: the parameter type `K` may not live long enough [E0309] +} + +union Yang { + field2: V +} + + +fn main() {} + diff --git a/src/test/ui/rfc-2093-infer-outlives/union.stderr b/src/test/ui/rfc-2093-infer-outlives/union.stderr new file mode 100644 index 00000000000..cd13c423293 --- /dev/null +++ b/src/test/ui/rfc-2093-infer-outlives/union.stderr @@ -0,0 +1,31 @@ +error[E0309]: the parameter type `U` may not live long enough + --> $DIR/union.rs:25:5 + | +LL | union Bar<'b, U> { + | - help: consider adding an explicit lifetime bound `U: 'b`... +LL | field2: &'b U //~ ERROR 25:5: 25:18: the parameter type `U` may not live long enough [E0309] + | ^^^^^^^^^^^^^ + | +note: ...so that the reference type `&'b U` does not outlive the data it points at + --> $DIR/union.rs:25:5 + | +LL | field2: &'b U //~ ERROR 25:5: 25:18: the parameter type `U` may not live long enough [E0309] + | ^^^^^^^^^^^^^ + +error[E0309]: the parameter type `K` may not live long enough + --> $DIR/union.rs:31:5 + | +LL | union Ying<'c, K> { + | - help: consider adding an explicit lifetime bound `K: 'c`... +LL | field1: &'c Yang //~ ERROR 31:5: 31:24: the parameter type `K` may not live long enough [E0309] + | ^^^^^^^^^^^^^^^^^^^ + | +note: ...so that the reference type `&'c Yang` does not outlive the data it points at + --> $DIR/union.rs:31:5 + | +LL | field1: &'c Yang //~ ERROR 31:5: 31:24: the parameter type `K` may not live long enough [E0309] + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0309`.