From aab48c963a43b28f88e428ecfe9e31d12530c47c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 28 Jun 2019 16:47:53 -0400 Subject: [PATCH 01/68] opaque_types/mod.rs: rustfmt --- src/librustc/infer/opaque_types/mod.rs | 141 +++++++++---------------- 1 file changed, 51 insertions(+), 90 deletions(-) diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs index 6b6dbd43167fa..30084668f9aad 100644 --- a/src/librustc/infer/opaque_types/mod.rs +++ b/src/librustc/infer/opaque_types/mod.rs @@ -1,15 +1,15 @@ use rustc_data_structures::fx::FxHashMap; use syntax_pos::Span; -use crate::hir::def_id::DefId; use crate::hir; +use crate::hir::def_id::DefId; use crate::hir::Node; -use crate::infer::{self, InferCtxt, InferOk, TypeVariableOrigin, TypeVariableOriginKind}; use crate::infer::outlives::free_region_map::FreeRegionRelations; +use crate::infer::{self, InferCtxt, InferOk, TypeVariableOrigin, TypeVariableOriginKind}; use crate::traits::{self, PredicateObligation}; -use crate::ty::{self, Ty, TyCtxt, GenericParamDefKind}; use crate::ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder, TypeVisitor}; -use crate::ty::subst::{Kind, InternalSubsts, SubstsRef, UnpackedKind}; +use crate::ty::subst::{InternalSubsts, Kind, SubstsRef, UnpackedKind}; +use crate::ty::{self, GenericParamDefKind, Ty, TyCtxt}; use crate::util::nodemap::DefIdMap; pub type OpaqueTypeMap<'tcx> = DefIdMap>; @@ -105,9 +105,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { param_env: ty::ParamEnv<'tcx>, value: &T, ) -> InferOk<'tcx, (T, OpaqueTypeMap<'tcx>)> { - debug!("instantiate_opaque_types(value={:?}, parent_def_id={:?}, body_id={:?}, \ - param_env={:?})", - value, parent_def_id, body_id, param_env, + debug!( + "instantiate_opaque_types(value={:?}, parent_def_id={:?}, body_id={:?}, \ + param_env={:?})", + value, parent_def_id, body_id, param_env, ); let mut instantiator = Instantiator { infcx: self, @@ -118,10 +119,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { obligations: vec![], }; let value = instantiator.instantiate_opaque_types_in_map(value); - InferOk { - value: (value, instantiator.opaque_types), - obligations: instantiator.obligations, - } + InferOk { value: (value, instantiator.opaque_types), obligations: instantiator.obligations } } /// Given the map `opaque_types` containing the existential `impl @@ -297,18 +295,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // If there are required region bounds, we can use them. if opaque_defn.has_required_region_bounds { let predicates_of = tcx.predicates_of(def_id); - debug!( - "constrain_opaque_type: predicates: {:#?}", - predicates_of, - ); + debug!("constrain_opaque_type: predicates: {:#?}", predicates_of,); let bounds = predicates_of.instantiate(tcx, opaque_defn.substs); debug!("constrain_opaque_type: bounds={:#?}", bounds); let opaque_type = tcx.mk_opaque(def_id, opaque_defn.substs); - let required_region_bounds = tcx.required_region_bounds( - opaque_type, - bounds.predicates, - ); + let required_region_bounds = tcx.required_region_bounds(opaque_type, bounds.predicates); debug_assert!(!required_region_bounds.is_empty()); for region in required_region_bounds { @@ -332,7 +324,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { for param in &abstract_type_generics.params { match param.kind { GenericParamDefKind::Lifetime => {} - _ => continue + _ => continue, } // Get the value supplied for this region from the substs. let subst_arg = opaque_defn.substs.region_at(param.index as usize); @@ -358,9 +350,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { hir::ExistTyOrigin::AsyncFn => "async fn", }; let msg = format!("ambiguous lifetime bound in `{}`", context_name); - let mut err = self.tcx - .sess - .struct_span_err(span, &msg); + let mut err = self.tcx.sess.struct_span_err(span, &msg); let lr_name = lr.to_string(); let subst_arg_name = subst_arg.to_string(); @@ -370,8 +360,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { _ => { label_owned = format!( "neither `{}` nor `{}` outlives the other", - lr_name, - subst_arg_name, + lr_name, subst_arg_name, ); &label_owned } @@ -379,10 +368,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err.span_label(span, label); if let hir::ExistTyOrigin::AsyncFn = opaque_defn.origin { - err.note("multiple unrelated lifetimes are not allowed in \ - `async fn`."); - err.note("if you're using argument-position elided lifetimes, consider \ - switching to a single named lifetime."); + err.note( + "multiple unrelated lifetimes are not allowed in \ + `async fn`.", + ); + err.note( + "if you're using argument-position elided lifetimes, consider \ + switching to a single named lifetime.", + ); } err.emit(); @@ -396,11 +389,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let least_region = least_region.unwrap_or(tcx.lifetimes.re_static); debug!("constrain_opaque_types: least_region={:?}", least_region); - concrete_ty.visit_with(&mut OpaqueTypeOutlivesVisitor { - infcx: self, - least_region, - span, - }); + concrete_ty.visit_with(&mut OpaqueTypeOutlivesVisitor { infcx: self, least_region, span }); } /// Given the fully resolved, instantiated type for an opaque @@ -456,18 +445,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // Convert the type from the function into a type valid outside // the function, by replacing invalid regions with 'static, // after producing an error for each of them. - let definition_ty = - instantiated_ty.fold_with(&mut ReverseMapper::new( - self.tcx, - self.is_tainted_by_errors(), - def_id, - map, - instantiated_ty, - )); - debug!( - "infer_opaque_definition_from_instantiation: definition_ty={:?}", - definition_ty - ); + let definition_ty = instantiated_ty.fold_with(&mut ReverseMapper::new( + self.tcx, + self.is_tainted_by_errors(), + def_id, + map, + instantiated_ty, + )); + debug!("infer_opaque_definition_from_instantiation: definition_ty={:?}", definition_ty); definition_ty } @@ -642,14 +627,14 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> { &mut err, &format!("hidden type `{}` captures ", hidden_ty), r, - "" + "", ); err.emit(); } } self.tcx.lifetimes.re_empty - }, + } } } @@ -681,8 +666,8 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> { // during codegen. let generics = self.tcx.generics_of(def_id); - let substs = self.tcx.mk_substs(substs.substs.iter().enumerate().map( - |(index, &kind)| { + let substs = + self.tcx.mk_substs(substs.substs.iter().enumerate().map(|(index, &kind)| { if index < generics.parent_count { // Accommodate missing regions in the parent kinds... self.fold_kind_mapping_missing_regions_to_empty(kind) @@ -690,16 +675,15 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> { // ...but not elsewhere. self.fold_kind_normally(kind) } - }, - )); + })); self.tcx.mk_closure(def_id, ty::ClosureSubsts { substs }) } ty::Generator(def_id, substs, movability) => { let generics = self.tcx.generics_of(def_id); - let substs = self.tcx.mk_substs(substs.substs.iter().enumerate().map( - |(index, &kind)| { + let substs = + self.tcx.mk_substs(substs.substs.iter().enumerate().map(|(index, &kind)| { if index < generics.parent_count { // Accommodate missing regions in the parent kinds... self.fold_kind_mapping_missing_regions_to_empty(kind) @@ -707,8 +691,7 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> { // ...but not elsewhere. self.fold_kind_normally(kind) } - }, - )); + })); self.tcx.mk_generator(def_id, ty::GeneratorSubsts { substs }, movability) } @@ -773,12 +756,10 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { let parent_def_id = self.parent_def_id; let def_scope_default = || { let opaque_parent_hir_id = tcx.hir().get_parent_item(opaque_hir_id); - parent_def_id == tcx.hir() - .local_def_id_from_hir_id(opaque_parent_hir_id) + parent_def_id + == tcx.hir().local_def_id_from_hir_id(opaque_parent_hir_id) }; - let (in_definition_scope, origin) = - match tcx.hir().find(opaque_hir_id) - { + let (in_definition_scope, origin) = match tcx.hir().find(opaque_hir_id) { Some(Node::Item(item)) => match item.node { // Anonymous `impl Trait` hir::ItemKind::Existential(hir::ExistTy { @@ -847,10 +828,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { let infcx = self.infcx; let tcx = infcx.tcx; - debug!( - "instantiate_opaque_types: Opaque(def_id={:?}, substs={:?})", - def_id, substs - ); + debug!("instantiate_opaque_types: Opaque(def_id={:?}, substs={:?})", def_id, substs); // Use the same type variable if the exact same opaque type appears more // than once in the return type (e.g., if it's passed to a type alias). @@ -858,36 +836,22 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { return opaque_defn.concrete_ty; } let span = tcx.def_span(def_id); - let ty_var = infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - }); + let ty_var = infcx + .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }); let predicates_of = tcx.predicates_of(def_id); - debug!( - "instantiate_opaque_types: predicates={:#?}", - predicates_of, - ); + debug!("instantiate_opaque_types: predicates={:#?}", predicates_of,); let bounds = predicates_of.instantiate(tcx, substs); debug!("instantiate_opaque_types: bounds={:?}", bounds); let required_region_bounds = tcx.required_region_bounds(ty, bounds.predicates.clone()); - debug!( - "instantiate_opaque_types: required_region_bounds={:?}", - required_region_bounds - ); + debug!("instantiate_opaque_types: required_region_bounds={:?}", required_region_bounds); // Make sure that we are in fact defining the *entire* type // (e.g., `existential type Foo: Bar;` needs to be // defined by a function like `fn foo() -> Foo`). - debug!( - "instantiate_opaque_types: param_env={:#?}", - self.param_env, - ); - debug!( - "instantiate_opaque_types: generics={:#?}", - tcx.generics_of(def_id), - ); + debug!("instantiate_opaque_types: param_env={:#?}", self.param_env,); + debug!("instantiate_opaque_types: generics={:#?}", tcx.generics_of(def_id),); self.opaque_types.insert( def_id, @@ -911,8 +875,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { // Require that the predicate holds for the concrete type. debug!("instantiate_opaque_types: predicate={:?}", predicate); - self.obligations - .push(traits::Obligation::new(cause, self.param_env, predicate)); + self.obligations.push(traits::Obligation::new(cause, self.param_env, predicate)); } ty_var @@ -950,9 +913,7 @@ pub fn may_define_existential_type( ); // Named existential types can be defined by any siblings or children of siblings. - let scope = tcx.hir() - .get_defining_scope(opaque_hir_id) - .expect("could not get defining scope"); + let scope = tcx.hir().get_defining_scope(opaque_hir_id).expect("could not get defining scope"); // We walk up the node tree until we hit the root or the scope of the opaque type. while hir_id != scope && hir_id != hir::CRATE_HIR_ID { hir_id = tcx.hir().get_parent_item(hir_id); From 2eb3fcc10d4cda8b5ae2e525b128f79d930520ca Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 28 May 2019 17:02:07 -0400 Subject: [PATCH 02/68] introduce `constrain_regions` helper --- src/librustc/infer/opaque_types/mod.rs | 45 ++++++++++++++------------ 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs index 30084668f9aad..47dfa3ea50821 100644 --- a/src/librustc/infer/opaque_types/mod.rs +++ b/src/librustc/infer/opaque_types/mod.rs @@ -1,6 +1,3 @@ -use rustc_data_structures::fx::FxHashMap; -use syntax_pos::Span; - use crate::hir; use crate::hir::def_id::DefId; use crate::hir::Node; @@ -11,6 +8,7 @@ use crate::ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder, TypeVisitor}; use crate::ty::subst::{InternalSubsts, Kind, SubstsRef, UnpackedKind}; use crate::ty::{self, GenericParamDefKind, Ty, TyCtxt}; use crate::util::nodemap::DefIdMap; +use rustc_data_structures::fx::FxHashMap; pub type OpaqueTypeMap<'tcx> = DefIdMap>; @@ -303,11 +301,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let required_region_bounds = tcx.required_region_bounds(opaque_type, bounds.predicates); debug_assert!(!required_region_bounds.is_empty()); - for region in required_region_bounds { - concrete_ty.visit_with(&mut OpaqueTypeOutlivesVisitor { - infcx: self, - least_region: region, - span, + for required_region in required_region_bounds { + concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { + tcx: self.tcx, + op: |r| self.sub_regions(infer::CallReturn(span), required_region, r), }); } return; @@ -389,7 +386,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let least_region = least_region.unwrap_or(tcx.lifetimes.re_static); debug!("constrain_opaque_types: least_region={:?}", least_region); - concrete_ty.visit_with(&mut OpaqueTypeOutlivesVisitor { infcx: self, least_region, span }); + concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { + tcx: self.tcx, + op: |r| self.sub_regions(infer::CallReturn(span), least_region, r), + }); } /// Given the fully resolved, instantiated type for an opaque @@ -471,13 +471,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // // We ignore any type parameters because impl trait values are assumed to // capture all the in-scope type parameters. -struct OpaqueTypeOutlivesVisitor<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, - least_region: ty::Region<'tcx>, - span: Span, +struct ConstrainOpaqueTypeRegionVisitor<'tcx, OP> +where + OP: FnMut(ty::Region<'tcx>), +{ + tcx: TyCtxt<'tcx>, + op: OP, } -impl<'tcx> TypeVisitor<'tcx> for OpaqueTypeOutlivesVisitor<'_, 'tcx> { +impl<'tcx, OP> TypeVisitor<'tcx> for ConstrainOpaqueTypeRegionVisitor<'tcx, OP> +where + OP: FnMut(ty::Region<'tcx>), +{ fn visit_binder>(&mut self, t: &ty::Binder) -> bool { t.skip_binder().visit_with(self); false // keep visiting @@ -488,7 +493,7 @@ impl<'tcx> TypeVisitor<'tcx> for OpaqueTypeOutlivesVisitor<'_, 'tcx> { // ignore bound regions, keep visiting ty::ReLateBound(_, _) => false, _ => { - self.infcx.sub_regions(infer::CallReturn(self.span), self.least_region, r); + (self.op)(r); false } } @@ -504,23 +509,23 @@ impl<'tcx> TypeVisitor<'tcx> for OpaqueTypeOutlivesVisitor<'_, 'tcx> { ty::Closure(def_id, ref substs) => { // Skip lifetime parameters of the enclosing item(s) - for upvar_ty in substs.upvar_tys(def_id, self.infcx.tcx) { + for upvar_ty in substs.upvar_tys(def_id, self.tcx) { upvar_ty.visit_with(self); } - substs.closure_sig_ty(def_id, self.infcx.tcx).visit_with(self); + substs.closure_sig_ty(def_id, self.tcx).visit_with(self); } ty::Generator(def_id, ref substs, _) => { // Skip lifetime parameters of the enclosing item(s) // Also skip the witness type, because that has no free regions. - for upvar_ty in substs.upvar_tys(def_id, self.infcx.tcx) { + for upvar_ty in substs.upvar_tys(def_id, self.tcx) { upvar_ty.visit_with(self); } - substs.return_ty(def_id, self.infcx.tcx).visit_with(self); - substs.yield_ty(def_id, self.infcx.tcx).visit_with(self); + substs.return_ty(def_id, self.tcx).visit_with(self); + substs.yield_ty(def_id, self.tcx).visit_with(self); } _ => { ty.super_visit_with(self); From 14e23a5835a385d9aa392abb0ddda5a80208f780 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 30 May 2019 10:36:35 -0400 Subject: [PATCH 03/68] introduce an "in" constraint instead of error --- .../infer/canonical/query_response.rs | 4 + src/librustc/infer/mod.rs | 14 ++ src/librustc/infer/opaque_types/mod.rs | 129 +++++++++++------- src/librustc/infer/region_constraints/mod.rs | 35 ++++- 4 files changed, 133 insertions(+), 49 deletions(-) diff --git a/src/librustc/infer/canonical/query_response.rs b/src/librustc/infer/canonical/query_response.rs index 3e92fed005cd1..f0ea61cf9b493 100644 --- a/src/librustc/infer/canonical/query_response.rs +++ b/src/librustc/infer/canonical/query_response.rs @@ -654,11 +654,15 @@ pub fn make_query_outlives<'tcx>( constraints, verifys, givens, + in_constraints, } = region_constraints; assert!(verifys.is_empty()); assert!(givens.is_empty()); + // FIXME(ndm) -- we have to think about what to do here, perhaps + assert!(in_constraints.is_empty()); + let outlives: Vec<_> = constraints .into_iter() .map(|(k, _)| match *k { diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index eca1ada851814..d4386b321e32a 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -30,6 +30,7 @@ use rustc_data_structures::unify as ut; use std::cell::{Cell, Ref, RefCell, RefMut}; use std::collections::BTreeMap; use std::fmt; +use std::rc::Rc; use syntax::ast; use syntax_pos::symbol::InternedString; use syntax_pos::Span; @@ -904,6 +905,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .make_subregion(origin, a, b); } + /// Require that the region `r` be equal to one of the regions in + /// the set `regions`. + pub fn in_constraint( + &self, + origin: SubregionOrigin<'tcx>, + region: ty::Region<'tcx>, + in_regions: &Rc>>, + ) { + debug!("sub_regions({:?} <: {:?})", region, in_regions); + self.borrow_region_constraints() + .in_constraint(origin, region, in_regions); + } + pub fn subtype_predicate( &self, cause: &ObligationCause<'tcx>, diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs index 47dfa3ea50821..3072c398df356 100644 --- a/src/librustc/infer/opaque_types/mod.rs +++ b/src/librustc/infer/opaque_types/mod.rs @@ -9,6 +9,8 @@ use crate::ty::subst::{InternalSubsts, Kind, SubstsRef, UnpackedKind}; use crate::ty::{self, GenericParamDefKind, Ty, TyCtxt}; use crate::util::nodemap::DefIdMap; use rustc_data_structures::fx::FxHashMap; +use std::rc::Rc; +use syntax::source_map::Span; pub type OpaqueTypeMap<'tcx> = DefIdMap>; @@ -212,13 +214,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// /// # The Solution /// - /// We make use of the constraint that we *do* have in the `<=` - /// relation. To do that, we find the "minimum" of all the - /// arguments that appear in the substs: that is, some region - /// which is less than all the others. In the case of `Foo1<'a>`, - /// that would be `'a` (it's the only choice, after all). Then we - /// apply that as a least bound to the variables (e.g., `'a <= - /// '0`). + /// We generally prefer to make us our `<=` constraints, since + /// they integrate best into the region solve. To do that, we find + /// the "minimum" of all the arguments that appear in the substs: + /// that is, some region which is less than all the others. In the + /// case of `Foo1<'a>`, that would be `'a` (it's the only choice, + /// after all). Then we apply that as a least bound to the + /// variables (e.g., `'a <= '0`). /// /// In some cases, there is no minimum. Consider this example: /// @@ -226,8 +228,32 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// fn baz<'a, 'b>() -> impl Trait<'a, 'b> { ... } /// ``` /// - /// Here we would report an error, because `'a` and `'b` have no - /// relation to one another. + /// Here we would report a more complex "in constraint", like `'r + /// in ['a, 'b, 'static]` (where `'r` is some regon appearing in + /// the hidden type). + /// + /// # Constrain regions, not the hidden concrete type + /// + /// Note that generating constraints on each region `Rc` is *not* + /// the same as generating an outlives constraint on `Tc` iself. + /// For example, if we had a function like this: + /// + /// ```rust + /// fn foo<'a, T>(x: &'a u32, y: T) -> impl Foo<'a> { + /// (x, y) + /// } + /// + /// // Equivalent to: + /// existential type FooReturn<'a, T>: Foo<'a>; + /// fn foo<'a, T>(..) -> FooReturn<'a, T> { .. } + /// ``` + /// + /// then the hidden type `Tc` would be `(&'0 u32, T)` (where `'0` + /// is an inference variable). If we generated a constraint that + /// `Tc: 'a`, then this would incorrectly require that `T: 'a` -- + /// but this is not necessary, because the existential type we + /// create will be allowed to reference `T`. So instead we just + /// generate a constraint that `'0: 'a`. /// /// # The `free_region_relations` parameter /// @@ -270,6 +296,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } + /// See `constrain_opaque_types` for docs pub fn constrain_opaque_type>( &self, def_id: DefId, @@ -323,6 +350,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { GenericParamDefKind::Lifetime => {} _ => continue, } + // Get the value supplied for this region from the substs. let subst_arg = opaque_defn.substs.region_at(param.index as usize); @@ -339,45 +367,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { least_region = Some(subst_arg); } else { // There are two regions (`lr` and - // `subst_arg`) which are not relatable. We can't - // find a best choice. - let context_name = match opaque_defn.origin { - hir::ExistTyOrigin::ExistentialType => "existential type", - hir::ExistTyOrigin::ReturnImplTrait => "impl Trait", - hir::ExistTyOrigin::AsyncFn => "async fn", - }; - let msg = format!("ambiguous lifetime bound in `{}`", context_name); - let mut err = self.tcx.sess.struct_span_err(span, &msg); - - let lr_name = lr.to_string(); - let subst_arg_name = subst_arg.to_string(); - let label_owned; - let label = match (&*lr_name, &*subst_arg_name) { - ("'_", "'_") => "the elided lifetimes here do not outlive one another", - _ => { - label_owned = format!( - "neither `{}` nor `{}` outlives the other", - lr_name, subst_arg_name, - ); - &label_owned - } - }; - err.span_label(span, label); - - if let hir::ExistTyOrigin::AsyncFn = opaque_defn.origin { - err.note( - "multiple unrelated lifetimes are not allowed in \ - `async fn`.", - ); - err.note( - "if you're using argument-position elided lifetimes, consider \ - switching to a single named lifetime.", - ); - } - err.emit(); - - least_region = Some(self.tcx.mk_region(ty::ReEmpty)); - break; + // `subst_arg`) which are not relatable. We + // can't find a best choice. Therefore, + // instead of creating a single bound like + // `'r: 'a` (which is our preferred choice), + // we will create a "in bound" like `'r in + // ['a, 'b, 'c]`, where `'a..'c` are the + // regions that appear in the impl trait. + return self.generate_in_constraint( + span, + concrete_ty, + abstract_type_generics, + opaque_defn, + ); } } } @@ -392,6 +394,37 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }); } + /// As a fallback, we sometimes generate an "in constraint". For + /// case like `impl Foo<'a, 'b>`, where `'a` and `'b` cannot be + /// related, we would generate a constraint `'r in ['a, 'b, + /// 'static]` for each region `'r` that appears in the hidden type + /// (i.e., it must be equal to `'a`, `'b`, or `'static`). + fn generate_in_constraint( + &self, + span: Span, + concrete_ty: Ty<'tcx>, + abstract_type_generics: &ty::Generics, + opaque_defn: &OpaqueTypeDecl<'tcx>, + ) { + let in_regions: Rc>> = Rc::new( + abstract_type_generics + .params + .iter() + .filter(|param| match param.kind { + GenericParamDefKind::Lifetime => true, + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => false, + }) + .map(|param| opaque_defn.substs.region_at(param.index as usize)) + .chain(std::iter::once(self.tcx.lifetimes.re_static)) + .collect(), + ); + + concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { + tcx: self.tcx, + op: |r| self.in_constraint(infer::CallReturn(span), r, &in_regions), + }); + } + /// Given the fully resolved, instantiated type for an opaque /// type, i.e., the value of an inference variable like C1 or C2 /// (*), computes the "definition type" for an abstract type diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index f2235fe8d6d12..14572c050b009 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -17,6 +17,7 @@ use crate::ty::{Region, RegionVid}; use std::collections::BTreeMap; use std::{cmp, fmt, mem}; use std::ops::Range; +use std::rc::Rc; mod leak_check; @@ -78,6 +79,11 @@ pub struct RegionConstraintData<'tcx> { /// be a region variable (or neither, as it happens). pub constraints: BTreeMap, SubregionOrigin<'tcx>>, + /// Constraints of the form `R0 in [R1, ..., Rn]`, meaning that + /// `R0` must be equal to one of the regions `R1..Rn`. These occur + /// with `impl Trait` quite frequently. + pub in_constraints: Vec>, + /// A "verify" is something that we need to verify after inference /// is done, but which does not directly affect inference in any /// way. @@ -137,6 +143,14 @@ impl Constraint<'_> { } } +/// Requires that `region` must be equal to one of the regions in `in_regions`. +#[derive(Debug, Clone)] +pub struct InConstraint<'tcx> { + pub origin: SubregionOrigin<'tcx>, + pub region: Region<'tcx>, + pub in_regions: Rc>>, +} + /// `VerifyGenericBound(T, _, R, RS)`: the parameter type `T` (or /// associated type) must outlive the region `R`. `T` is known to /// outlive `RS`. Therefore, verify that `R <= RS[i]` for some @@ -643,6 +657,24 @@ impl<'tcx> RegionConstraintCollector<'tcx> { } } + pub fn in_constraint( + &mut self, + origin: SubregionOrigin<'tcx>, + region: ty::Region<'tcx>, + in_regions: &Rc>>, + ) { + debug!("in_constraint({:?} in {:#?})", region, in_regions); + + if in_regions.iter().any(|&r| r == region) { + return; + } + + self.data.in_constraints.push(InConstraint { + origin, region, in_regions: in_regions.clone() + }); + + } + pub fn make_subregion( &mut self, origin: SubregionOrigin<'tcx>, @@ -906,9 +938,10 @@ impl<'tcx> RegionConstraintData<'tcx> { pub fn is_empty(&self) -> bool { let RegionConstraintData { constraints, + in_constraints, verifys, givens, } = self; - constraints.is_empty() && verifys.is_empty() && givens.is_empty() + constraints.is_empty() && in_constraints.is_empty() && verifys.is_empty() && givens.is_empty() } } From 979f566603bad15fadb48b9ce4d9f87632cbf849 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 31 May 2019 14:29:43 -0400 Subject: [PATCH 04/68] lexical_region_resolve: rustfmt --- .../infer/lexical_region_resolve/mod.rs | 154 ++++++++---------- 1 file changed, 68 insertions(+), 86 deletions(-) diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index 2613f4c7c2ae3..cebaef21912a7 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -8,6 +8,11 @@ use crate::infer::region_constraints::VerifyBound; use crate::infer::RegionVariableOrigin; use crate::infer::SubregionOrigin; use crate::middle::free_region::RegionRelations; +use crate::ty::fold::TypeFoldable; +use crate::ty::{self, Ty, TyCtxt}; +use crate::ty::{ReEarlyBound, ReEmpty, ReErased, ReFree, ReStatic}; +use crate::ty::{ReLateBound, RePlaceholder, ReScope, ReVar}; +use crate::ty::{Region, RegionVid}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::graph::implementation::{ Direction, Graph, NodeIndex, INCOMING, OUTGOING, @@ -16,11 +21,6 @@ use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use smallvec::SmallVec; use std::fmt; use std::u32; -use crate::ty::fold::TypeFoldable; -use crate::ty::{self, Ty, TyCtxt}; -use crate::ty::{ReEarlyBound, ReEmpty, ReErased, ReFree, ReStatic}; -use crate::ty::{ReLateBound, ReScope, RePlaceholder, ReVar}; -use crate::ty::{Region, RegionVid}; mod graphviz; @@ -36,11 +36,7 @@ pub fn resolve<'tcx>( ) -> (LexicalRegionResolutions<'tcx>, Vec>) { debug!("RegionConstraintData: resolve_regions()"); let mut errors = vec![]; - let mut resolver = LexicalResolver { - region_rels, - var_infos, - data, - }; + let mut resolver = LexicalResolver { region_rels, var_infos, data }; let values = resolver.infer_variable_values(&mut errors); (values, errors) } @@ -136,7 +132,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { fn construct_var_data(&self, tcx: TyCtxt<'tcx>) -> LexicalRegionResolutions<'tcx> { LexicalRegionResolutions { error_region: tcx.lifetimes.re_static, - values: IndexVec::from_elem_n(VarValue::Value(tcx.lifetimes.re_empty), self.num_vars()) + values: IndexVec::from_elem_n(VarValue::Value(tcx.lifetimes.re_empty), self.num_vars()), } } @@ -196,7 +192,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { let b_data = var_values.value_mut(b_vid); let retain = match *b_data { VarValue::Value(ReStatic) | VarValue::ErrorValue => false, - _ => true + _ => true, }; (a_region, b_vid, b_data, retain) } @@ -204,7 +200,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { Constraint::RegSubReg(..) | Constraint::VarSubReg(..) => { // These constraints are checked after expansion // is done, in `collect_errors`. - return (false, false) + return (false, false); } }; @@ -226,16 +222,16 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { match *a_region { // Check if this relationship is implied by a given. - ty::ReEarlyBound(_) | ty::ReFree(_) => if self.data.givens.contains(&(a_region, b_vid)) - { - debug!("given"); - return false; - }, + ty::ReEarlyBound(_) | ty::ReFree(_) => { + if self.data.givens.contains(&(a_region, b_vid)) { + debug!("given"); + return false; + } + } _ => {} } - match *b_data { VarValue::Value(cur_region) => { // Identical scopes can show up quite often, if the fixed point @@ -267,10 +263,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } } - debug!( - "Expanding value of {:?} from {:?} to {:?}", - b_vid, cur_region, lub - ); + debug!("Expanding value of {:?} from {:?} to {:?}", b_vid, cur_region, lub); *b_data = VarValue::Value(lub); return true; @@ -321,17 +314,16 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // at least as big as fr.scope". So, we can // reasonably compare free regions and scopes: let fr_scope = match (a, b) { - (&ReEarlyBound(ref br), _) | (_, &ReEarlyBound(ref br)) => self.region_rels - .region_scope_tree - .early_free_scope(self.tcx(), br), - (&ReFree(ref fr), _) | (_, &ReFree(ref fr)) => self.region_rels - .region_scope_tree - .free_scope(self.tcx(), fr), + (&ReEarlyBound(ref br), _) | (_, &ReEarlyBound(ref br)) => { + self.region_rels.region_scope_tree.early_free_scope(self.tcx(), br) + } + (&ReFree(ref fr), _) | (_, &ReFree(ref fr)) => { + self.region_rels.region_scope_tree.free_scope(self.tcx(), fr) + } _ => bug!(), }; - let r_id = self.region_rels - .region_scope_tree - .nearest_common_ancestor(fr_scope, s_id); + let r_id = + self.region_rels.region_scope_tree.nearest_common_ancestor(fr_scope, s_id); if r_id == fr_scope { // if the free region's scope `fr.scope` is bigger than // the scope region `s_id`, then the LUB is the free @@ -352,9 +344,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // The region corresponding to an outer block is a // subtype of the region corresponding to an inner // block. - let lub = self.region_rels - .region_scope_tree - .nearest_common_ancestor(a_id, b_id); + let lub = self.region_rels.region_scope_tree.nearest_common_ancestor(a_id, b_id); tcx.mk_region(ReScope(lub)) } @@ -365,11 +355,13 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // For these types, we cannot define any additional // relationship: - (&RePlaceholder(..), _) | (_, &RePlaceholder(..)) => if a == b { - a - } else { - tcx.lifetimes.re_static - }, + (&RePlaceholder(..), _) | (_, &RePlaceholder(..)) => { + if a == b { + a + } else { + tcx.lifetimes.re_static + } + } } } @@ -382,10 +374,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { errors: &mut Vec>, ) { for (constraint, origin) in &self.data.constraints { - debug!( - "collect_errors: constraint={:?} origin={:?}", - constraint, origin - ); + debug!("collect_errors: constraint={:?} origin={:?}", constraint, origin); match *constraint { Constraint::RegSubVar(..) | Constraint::VarSubVar(..) => { // Expansion will ensure that these constraints hold. Ignore. @@ -490,27 +479,27 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { VarValue::Value(_) => { /* Inference successful */ } VarValue::ErrorValue => { /* Inference impossible: this value contains - inconsistent constraints. - - I think that in this case we should report an - error now -- unlike the case above, we can't - wait to see whether the user needs the result - of this variable. The reason is that the mere - existence of this variable implies that the - region graph is inconsistent, whether or not it - is used. - - For example, we may have created a region - variable that is the GLB of two other regions - which do not have a GLB. Even if that variable - is not used, it implies that those two regions - *should* have a GLB. - - At least I think this is true. It may be that - the mere existence of a conflict in a region variable - that is not used is not a problem, so if this rule - starts to create problems we'll have to revisit - this portion of the code and think hard about it. =) */ + inconsistent constraints. + + I think that in this case we should report an + error now -- unlike the case above, we can't + wait to see whether the user needs the result + of this variable. The reason is that the mere + existence of this variable implies that the + region graph is inconsistent, whether or not it + is used. + + For example, we may have created a region + variable that is the GLB of two other regions + which do not have a GLB. Even if that variable + is not used, it implies that those two regions + *should* have a GLB. + + At least I think this is true. It may be that + the mere existence of a conflict in a region variable + that is not used is not a problem, so if this rule + starts to create problems we'll have to revisit + this portion of the code and think hard about it. =) */ self.collect_error_for_expanding_node(graph, &mut dup_vec, node_vid, errors); } } @@ -604,9 +593,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { }; for upper_bound in &upper_bounds { - if !self.region_rels - .is_subregion_of(effective_lower_bound, upper_bound.region) - { + if !self.region_rels.is_subregion_of(effective_lower_bound, upper_bound.region) { let origin = self.var_infos[node_idx].origin.clone(); debug!( "region inference error at {:?} for {:?}: SubSupConflict sub: {:?} \ @@ -681,9 +668,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { process_edges(&self.data, &mut state, graph, node_idx, dir); } - let WalkState { - result, dup_found, .. - } = state; + let WalkState { result, dup_found, .. } = state; return (result, dup_found); fn process_edges<'tcx>( @@ -699,11 +684,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { for (_, edge) in graph.adjacent_edges(source_node_index, dir) { match edge.data { Constraint::VarSubVar(from_vid, to_vid) => { - let opp_vid = if from_vid == source_vid { - to_vid - } else { - from_vid - }; + let opp_vid = if from_vid == source_vid { to_vid } else { from_vid }; if state.set.insert(opp_vid) { state.stack.push(opp_vid); } @@ -726,7 +707,8 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } fn iterate_until_fixed_point(&self, tag: &str, mut body: F) - where F: FnMut(&Constraint<'tcx>) -> (bool, bool), + where + F: FnMut(&Constraint<'tcx>) -> (bool, bool), { let mut constraints: SmallVec<[_; 16]> = self.data.constraints.keys().collect(); let mut iteration = 0; @@ -760,17 +742,17 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { && self.bound_is_met(b, var_values, generic_ty, min) } - VerifyBound::OutlivedBy(r) => - self.region_rels.is_subregion_of( - min, - var_values.normalize(self.tcx(), r), - ), + VerifyBound::OutlivedBy(r) => { + self.region_rels.is_subregion_of(min, var_values.normalize(self.tcx(), r)) + } - VerifyBound::AnyBound(bs) => bs.iter() - .any(|b| self.bound_is_met(b, var_values, generic_ty, min)), + VerifyBound::AnyBound(bs) => { + bs.iter().any(|b| self.bound_is_met(b, var_values, generic_ty, min)) + } - VerifyBound::AllBounds(bs) => bs.iter() - .all(|b| self.bound_is_met(b, var_values, generic_ty, min)), + VerifyBound::AllBounds(bs) => { + bs.iter().all(|b| self.bound_is_met(b, var_values, generic_ty, min)) + } } } } From dfcd1c632897779c30d1fee9a6d3c8822bab4d4f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 31 May 2019 14:30:06 -0400 Subject: [PATCH 05/68] make `dup_vec` optional --- .../infer/lexical_region_resolve/mod.rs | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index cebaef21912a7..1f49cb950082d 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -500,7 +500,12 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { that is not used is not a problem, so if this rule starts to create problems we'll have to revisit this portion of the code and think hard about it. =) */ - self.collect_error_for_expanding_node(graph, &mut dup_vec, node_vid, errors); + self.collect_error_for_expanding_node( + graph, + &mut dup_vec, + node_vid, + errors, + ); } } } @@ -558,9 +563,9 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // Errors in expanding nodes result from a lower-bound that is // not contained by an upper-bound. let (mut lower_bounds, lower_dup) = - self.collect_concrete_regions(graph, node_idx, INCOMING, dup_vec); + self.collect_concrete_regions(graph, node_idx, INCOMING, Some(dup_vec)); let (mut upper_bounds, upper_dup) = - self.collect_concrete_regions(graph, node_idx, OUTGOING, dup_vec); + self.collect_concrete_regions(graph, node_idx, OUTGOING, Some(dup_vec)); if lower_dup || upper_dup { return; @@ -630,7 +635,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { graph: &RegionGraph<'tcx>, orig_node_idx: RegionVid, dir: Direction, - dup_vec: &mut [u32], + mut dup_vec: Option<&mut [u32]>, ) -> (Vec>, bool) { struct WalkState<'tcx> { set: FxHashSet, @@ -654,16 +659,18 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { let node_idx = state.stack.pop().unwrap(); // check whether we've visited this node on some previous walk - if dup_vec[node_idx.index() as usize] == u32::MAX { - dup_vec[node_idx.index() as usize] = orig_node_idx.index() as u32; - } else if dup_vec[node_idx.index() as usize] != orig_node_idx.index() as u32 { - state.dup_found = true; - } + if let Some(dup_vec) = &mut dup_vec { + if dup_vec[node_idx.index() as usize] == u32::MAX { + dup_vec[node_idx.index() as usize] = orig_node_idx.index() as u32; + } else if dup_vec[node_idx.index() as usize] != orig_node_idx.index() as u32 { + state.dup_found = true; + } - debug!( - "collect_concrete_regions(orig_node_idx={:?}, node_idx={:?})", - orig_node_idx, node_idx - ); + debug!( + "collect_concrete_regions(orig_node_idx={:?}, node_idx={:?})", + orig_node_idx, node_idx + ); + } process_edges(&self.data, &mut state, graph, node_idx, dir); } From c36205b48e83517e96a2887296114db3df831e21 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 1 Jun 2019 07:01:49 -0400 Subject: [PATCH 06/68] add some tests, currently ICE-ing --- .../infer/lexical_region_resolve/mod.rs | 96 +++++++++++++++++-- src/librustc/infer/opaque_types/mod.rs | 48 ++++++---- .../multiple-lifetimes/inverse-bounds.rs | 54 +++++++++++ .../multiple-lifetimes/inverse-bounds.stderr | 15 +++ .../ordinary-bounds-pick-original.rs | 26 +++++ .../ordinary-bounds-pick-other.rs | 43 +++++++++ .../ordinary-bounds-unrelated.rs | 38 ++++++++ .../ordinary-bounds-unrelated.stderr | 12 +++ .../ordinary-bounds-unsuited.rs | 41 ++++++++ .../ordinary-bounds-unsuited.stderr | 12 +++ 10 files changed, 360 insertions(+), 25 deletions(-) create mode 100644 src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs create mode 100644 src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.stderr create mode 100644 src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs create mode 100644 src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.rs create mode 100644 src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.rs create mode 100644 src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr create mode 100644 src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.rs create mode 100644 src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index 1f49cb950082d..61c711967ad59 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -2,6 +2,7 @@ use crate::infer::region_constraints::Constraint; use crate::infer::region_constraints::GenericKind; +use crate::infer::region_constraints::InConstraint; use crate::infer::region_constraints::RegionConstraintData; use crate::infer::region_constraints::VarInfos; use crate::infer::region_constraints::VerifyBound; @@ -117,6 +118,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { let graph = self.construct_graph(); self.expand_givens(&graph); + self.enforce_in_constraints(&graph, &mut var_data); self.expansion(&mut var_data); self.collect_errors(&mut var_data, errors); self.collect_var_errors(&var_data, &graph, errors); @@ -178,6 +180,87 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } } + /// Enforce constraints of the form: + /// + /// ``` + /// 'r0 in ['o1...'oN] + /// ``` + /// + /// such a constraint simply means that `'r0` must be equal to one + /// of the regions `'o1...'oN`. This is an annoying constraint to + /// integrate into our inference, which generally works by + /// iteratively growing regions until we find a match -- that's + /// not an option here. + /// + /// What we currently do: + /// + /// - Search forward in the graph from `'r0` to find each region `'b` + /// where `'r0 <= 'b` must hold. + /// - Try to rule out some of the `'o1..'oN` options: + /// - if `'o[i] <= 'b` is false, then `'o[i]` is not an option + /// + /// Hopefully this narrows it down to just one option. + fn enforce_in_constraints( + &self, + graph: &RegionGraph<'tcx>, + var_values: &mut LexicalRegionResolutions<'tcx>, + ) { + for in_constraint in &self.data.in_constraints { + let _ = self.enforce_in_constraint(graph, in_constraint, var_values); + } + } + + fn enforce_in_constraint( + &self, + graph: &RegionGraph<'tcx>, + in_constraint: &InConstraint<'tcx>, + var_values: &mut LexicalRegionResolutions<'tcx>, + ) -> Result<(), ()> { + debug!("enforce_in_constraint(in_constraint={:#?})", in_constraint); + + // the constraint is some inference variable (`vid`) which + // must be equal to one of the options + let vid = match in_constraint.region { + ty::ReVar(vid) => *vid, + _ => return Err(()), + }; + + // find all the "bounds" -- that is, each region `b` such that + // `r0 <= b` must hold. + let (bounds, _) = self.collect_concrete_regions(graph, vid, OUTGOING, None); + + // get an iterator over the *available options* -- that is, + // each constraint regions `o` where `o <= b` for all the + // bounds `b`. + debug!("enforce_in_constraint: bounds={:#?}", bounds); + let mut options = in_constraint.in_regions.iter().filter(|option| { + bounds.iter().all(|bound| self.sub_concrete_regions(option, bound.region)) + }); + + // if there >1 option, we only make a choice if there is a + // single *least* choice -- i.e., some available region that + // is `<=` all the others. + let mut least_choice = options.next().ok_or(())?; + debug!("enforce_in_constraint: least_choice={:?}", least_choice); + for option in options { + debug!("enforce_in_constraint: option={:?}", option); + if !self.sub_concrete_regions(least_choice, option) { + if self.sub_concrete_regions(option, least_choice) { + debug!("enforce_in_constraint: new least choice"); + least_choice = option; + } else { + debug!("enforce_in_constraint: no least choice"); + return Err(()); + } + } + } + + debug!("enforce_in_constraint: final least choice = {:?}", least_choice); + *var_values.value_mut(vid) = VarValue::Value(least_choice); + + Ok(()) + } + fn expansion(&self, var_values: &mut LexicalRegionResolutions<'tcx>) { self.iterate_until_fixed_point("Expansion", |constraint| { debug!("expansion: constraint={:?}", constraint); @@ -275,6 +358,12 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } } + /// True if `a <= b`, but not defined over inference variables. + fn sub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> bool { + self.lub_concrete_regions(a, b) == b + } + + /// Returns the smallest region `c` such that `a <= c` and `b <= c`. fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> { let tcx = self.tcx(); @@ -500,12 +589,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { that is not used is not a problem, so if this rule starts to create problems we'll have to revisit this portion of the code and think hard about it. =) */ - self.collect_error_for_expanding_node( - graph, - &mut dup_vec, - node_vid, - errors, - ); + self.collect_error_for_expanding_node(graph, &mut dup_vec, node_vid, errors); } } } diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs index 3072c398df356..d4c6c8b99b5ef 100644 --- a/src/librustc/infer/opaque_types/mod.rs +++ b/src/librustc/infer/opaque_types/mod.rs @@ -445,6 +445,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// # Parameters /// /// - `def_id`, the `impl Trait` type + /// - `opaque_defn`, the opaque definition created in `instantiate_opaque_types` /// - `instantiated_ty`, the inferred type C1 -- fully resolved, lifted version of /// `opaque_defn.concrete_ty` @@ -648,25 +649,34 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> { does not appear in bounds", ); - // Assuming regionck succeeded, then we must - // be capturing *some* region from the fn - // header, and hence it must be free, so it's - // ok to invoke this fn (which doesn't accept - // all regions, and would ICE if an - // inappropriate region is given). We check - // `is_tainted_by_errors` by errors above, so - // we don't get in here unless regionck - // succeeded. (Note also that if regionck - // failed, then the regions we are attempting - // to map here may well be giving errors - // *because* the constraints were not - // satisfiable.) - self.tcx.note_and_explain_free_region( - &mut err, - &format!("hidden type `{}` captures ", hidden_ty), - r, - "", - ); + // Explain the region we are capturing. + match r { + ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic | ty::ReEmpty => { + // Assuming regionck succeeded (*), we + // ought to always be capturing *some* region + // from the fn header, and hence it ought to + // be free. So under normal circumstances, we will + // go down this path which gives a decent human readable + // explanation. + // + // (*) if not, the `tainted_by_errors` + // flag would be set to true in any + // case, so we wouldn't be here at + // all. + self.tcx.note_and_explain_free_region( + &mut err, + &format!("hidden type `{}` captures ", hidden_ty), + r, + "", + ); + } + _ => { + // This case should not happen: it indicates that regionck + // failed to enforce an "in constraint". + err.note(&format!("hidden type `{}` captures `{:?}`", hidden_ty, r)); + err.note(&format!("this is likely a bug in the compiler, please file an issue on github")); + } + } err.emit(); } diff --git a/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs b/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs new file mode 100644 index 0000000000000..41d4b59e8f029 --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs @@ -0,0 +1,54 @@ +// edition:2018 + +#![feature(arbitrary_self_types, async_await, await_macro, pin)] + +trait Trait<'a, 'b> {} +impl Trait<'_, '_> for T {} + +// `Invert<'a> <: Invert<'b>` if `'b: 'a`, unlike most types. +// +// I am purposefully avoiding the terms co- and contra-variant because +// their application to regions depends on how you interpreted Rust +// regions. -nikomatsakis +struct Invert<'a>(fn(&'a u8)); + +fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Invert<'a>, b: Invert<'b>) -> impl Trait<'d, 'e> +//~^ ERROR hidden type for `impl Trait` captures lifetime that does not appear in bounds +// FIXME -- we ought to be able to pick `'d` here, but our handling of in constraints +// is not smart enough +where + 'c: 'a, + 'c: 'b, + 'd: 'c, +{ + // Representing the where clauses as a graph, where `A: B` is an + // edge `B -> A`: + // + // ``` + // 'a -> 'c -> 'd + // ^ + // | + // 'b + // ``` + // + // Meanwhile we return a value &'0 u8 where we have the constraints: + // + // ``` + // '0: 'a + // '0: 'b + // '0 in ['d, 'e] + // ``` + // + // Here, ignoring the "in" constraint, the minimal choice for `'0` + // is `'c`, but that is not in the "in set". Still, that reduces + // the range of options in the "in set" to just `'d` (`'e: 'c` + // does not hold). + let p = if condition() { a } else { b }; + p +} + +fn condition() -> bool { + true +} + +fn main() {} diff --git a/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.stderr b/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.stderr new file mode 100644 index 0000000000000..182d07733ffd8 --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.stderr @@ -0,0 +1,15 @@ +error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds + --> $DIR/inverse-bounds.rs:15:70 + | +LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Invert<'a>, b: Invert<'b>) -> impl Trait<'d, 'e> + | ^^^^^^^^^^^^^^^^^^ + | +note: hidden type `Invert<'c>` captures the lifetime 'c as defined on the function body at 15:25 + --> $DIR/inverse-bounds.rs:15:25 + | +LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Invert<'a>, b: Invert<'b>) -> impl Trait<'d, 'e> + | ^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0700`. diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs new file mode 100644 index 0000000000000..dde3e6d72c9ae --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs @@ -0,0 +1,26 @@ +// edition:2018 + +#![feature(arbitrary_self_types, async_await, await_macro, pin)] + +trait Trait<'a, 'b> { } +impl Trait<'_, '_> for T { } + +// Here we wind up selecting `'a` and `'b` in the hidden type because +// those are the types that appear inth e original values. + +fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { + // In this simple case, you have a hidden type `(&'0 u8, &'1 u8)` and constraints like + // + // ``` + // 'a: '0 + // 'b: '1 + // '0 in ['a, 'b] + // '1 in ['a, 'b] + // ``` + // + // We use the fact that `'a: 0'` must hold (combined with the in + // constraint) to determine that `'0 = 'a` must be the answer. + (a, b) +} + +fn main() { } diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.rs new file mode 100644 index 0000000000000..b53ef1dde42b2 --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.rs @@ -0,0 +1,43 @@ +// edition:2018 + +#![feature(arbitrary_self_types, async_await, await_macro, pin)] + +trait Trait<'a, 'b> {} +impl Trait<'_, '_> for T {} + +// `Ordinary<'a> <: Ordinary<'b>` if `'a: 'b`, as with most types. +// +// I am purposefully avoiding the terms co- and contra-variant because +// their application to regions depends on how you interpreted Rust +// regions. -nikomatsakis +struct Ordinary<'a>(&'a u8); + +// Here we wind up selecting `'e` in the hidden type because +// we need something outlived by both `'a` and `'b` and only `'e` applies. + +fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> +where + 'a: 'e, + 'b: 'e, + 'a: 'd, +{ + // We return a value: + // + // ``` + // 'a: '0 + // 'b: '1 + // '0 in ['d, 'e] + // ``` + // + // but we don't have it. + // + // We are forced to pick that '0 = 'e, because only 'e is outlived by *both* 'a and 'b. + let p = if condition() { a } else { b }; + p +} + +fn condition() -> bool { + true +} + +fn main() {} diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.rs new file mode 100644 index 0000000000000..c1322a391402e --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.rs @@ -0,0 +1,38 @@ +// edition:2018 + +#![feature(arbitrary_self_types, async_await, await_macro, pin)] + +trait Trait<'a, 'b> {} +impl Trait<'_, '_> for T {} + +// `Ordinary<'a> <: Ordinary<'b>` if `'a: 'b`, as with most types. +// +// I am purposefully avoiding the terms co- and contra-variant because +// their application to regions depends on how you interpreted Rust +// regions. -nikomatsakis +struct Ordinary<'a>(&'a u8); + +// Here we get an error because none of our choices (either `'d` nor `'e`) are outlived +// by both `'a` and `'b`. + +fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> +//~^ ERROR hidden type for `impl Trait` captures lifetime that does not appear in bounds +where + 'a: 'e, + 'b: 'd, +{ + // Hidden type `Ordinary<'0>` with constraints: + // + // ``` + // 'a: '0 + // 'b: '0 + // 'a in ['d, 'e] + // ``` + if condition() { a } else { b } +} + +fn condition() -> bool { + true +} + +fn main() {} diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr new file mode 100644 index 0000000000000..9b505c66dc115 --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr @@ -0,0 +1,12 @@ +error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds + --> $DIR/ordinary-bounds-unrelated.rs:18:74 + | +LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> + | ^^^^^^^^^^^^^^^^^^ + | + = note: hidden type `Ordinary<'_>` captures `ReScope(CallSite(24))` + = note: this is likely a bug in the compiler, please file an issue on github + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0700`. diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.rs new file mode 100644 index 0000000000000..a28bbd5df7828 --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.rs @@ -0,0 +1,41 @@ +// edition:2018 + +#![feature(arbitrary_self_types, async_await, await_macro, pin)] + +trait Trait<'a, 'b> {} +impl Trait<'_, '_> for T {} + +// `Ordinary<'a> <: Ordinary<'b>` if `'a: 'b`, as with most types. +// +// I am purposefully avoiding the terms co- and contra-variant because +// their application to regions depends on how you interpreted Rust +// regions. -nikomatsakis +struct Ordinary<'a>(&'a u8); + +// Here we need something outlived by `'a` *and* outlived by `'b`, but +// we can only name `'a` and `'b` (and neither suits). So we get an +// error. Somewhat unfortunate, though, since the caller would have to +// consider the loans for both `'a` and `'b` alive. + +fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> + //~^ ERROR hidden type for `impl Trait` captures lifetime that does not appear in bounds +{ + // We return a value: + // + // ``` + // 'a: '0 + // 'b: '1 + // '0 in ['d, 'e] + // ``` + // + // but we don't have it. + // + // We are forced to pick that '0 = 'e, because only 'e is outlived by *both* 'a and 'b. + if condition() { a } else { b } +} + +fn condition() -> bool { + true +} + +fn main() {} diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr new file mode 100644 index 0000000000000..d258948c7735d --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr @@ -0,0 +1,12 @@ +error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds + --> $DIR/ordinary-bounds-unsuited.rs:20:62 + | +LL | fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> + | ^^^^^^^^^^^^^^^^^^ + | + = note: hidden type `Ordinary<'_>` captures `ReScope(CallSite(24))` + = note: this is likely a bug in the compiler, please file an issue on github + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0700`. From 02609b85e36fd9c049e1975171f65b47e3ccdd1b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 3 Jun 2019 11:58:57 -0400 Subject: [PATCH 07/68] rename from "in constraint" to "pick constraint" --- .../infer/canonical/query_response.rs | 4 +- .../infer/lexical_region_resolve/mod.rs | 38 +++++++++---------- src/librustc/infer/mod.rs | 4 +- src/librustc/infer/opaque_types/mod.rs | 2 +- src/librustc/infer/region_constraints/mod.rs | 35 +++++++++-------- 5 files changed, 44 insertions(+), 39 deletions(-) diff --git a/src/librustc/infer/canonical/query_response.rs b/src/librustc/infer/canonical/query_response.rs index f0ea61cf9b493..d00da11d17d45 100644 --- a/src/librustc/infer/canonical/query_response.rs +++ b/src/librustc/infer/canonical/query_response.rs @@ -654,14 +654,14 @@ pub fn make_query_outlives<'tcx>( constraints, verifys, givens, - in_constraints, + pick_constraints, } = region_constraints; assert!(verifys.is_empty()); assert!(givens.is_empty()); // FIXME(ndm) -- we have to think about what to do here, perhaps - assert!(in_constraints.is_empty()); + assert!(pick_constraints.is_empty()); let outlives: Vec<_> = constraints .into_iter() diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index 61c711967ad59..a37eff82e6eb3 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -2,7 +2,7 @@ use crate::infer::region_constraints::Constraint; use crate::infer::region_constraints::GenericKind; -use crate::infer::region_constraints::InConstraint; +use crate::infer::region_constraints::PickConstraint; use crate::infer::region_constraints::RegionConstraintData; use crate::infer::region_constraints::VarInfos; use crate::infer::region_constraints::VerifyBound; @@ -118,7 +118,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { let graph = self.construct_graph(); self.expand_givens(&graph); - self.enforce_in_constraints(&graph, &mut var_data); + self.enforce_pick_constraints(&graph, &mut var_data); self.expansion(&mut var_data); self.collect_errors(&mut var_data, errors); self.collect_var_errors(&var_data, &graph, errors); @@ -200,63 +200,63 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { /// - if `'o[i] <= 'b` is false, then `'o[i]` is not an option /// /// Hopefully this narrows it down to just one option. - fn enforce_in_constraints( + fn enforce_pick_constraints( &self, graph: &RegionGraph<'tcx>, var_values: &mut LexicalRegionResolutions<'tcx>, ) { - for in_constraint in &self.data.in_constraints { - let _ = self.enforce_in_constraint(graph, in_constraint, var_values); + for pick_constraint in &self.data.pick_constraints { + let _ = self.enforce_pick_constraint(graph, pick_constraint, var_values); } } - fn enforce_in_constraint( + fn enforce_pick_constraint( &self, graph: &RegionGraph<'tcx>, - in_constraint: &InConstraint<'tcx>, + pick_constraint: &PickConstraint<'tcx>, var_values: &mut LexicalRegionResolutions<'tcx>, ) -> Result<(), ()> { - debug!("enforce_in_constraint(in_constraint={:#?})", in_constraint); + debug!("enforce_pick_constraint(pick_constraint={:#?})", pick_constraint); // the constraint is some inference variable (`vid`) which // must be equal to one of the options - let vid = match in_constraint.region { + let pick_vid = match pick_constraint.pick_region { ty::ReVar(vid) => *vid, _ => return Err(()), }; // find all the "bounds" -- that is, each region `b` such that // `r0 <= b` must hold. - let (bounds, _) = self.collect_concrete_regions(graph, vid, OUTGOING, None); + let (pick_bounds, _) = self.collect_concrete_regions(graph, pick_vid, OUTGOING, None); // get an iterator over the *available options* -- that is, // each constraint regions `o` where `o <= b` for all the // bounds `b`. - debug!("enforce_in_constraint: bounds={:#?}", bounds); - let mut options = in_constraint.in_regions.iter().filter(|option| { - bounds.iter().all(|bound| self.sub_concrete_regions(option, bound.region)) + debug!("enforce_pick_constraint: bounds={:#?}", pick_bounds); + let mut options = pick_constraint.option_regions.iter().filter(|option| { + pick_bounds.iter().all(|bound| self.sub_concrete_regions(option, bound.region)) }); // if there >1 option, we only make a choice if there is a // single *least* choice -- i.e., some available region that // is `<=` all the others. let mut least_choice = options.next().ok_or(())?; - debug!("enforce_in_constraint: least_choice={:?}", least_choice); + debug!("enforce_pick_constraint: least_choice={:?}", least_choice); for option in options { - debug!("enforce_in_constraint: option={:?}", option); + debug!("enforce_pick_constraint: option={:?}", option); if !self.sub_concrete_regions(least_choice, option) { if self.sub_concrete_regions(option, least_choice) { - debug!("enforce_in_constraint: new least choice"); + debug!("enforce_pick_constraint: new least choice"); least_choice = option; } else { - debug!("enforce_in_constraint: no least choice"); + debug!("enforce_pick_constraint: no least choice"); return Err(()); } } } - debug!("enforce_in_constraint: final least choice = {:?}", least_choice); - *var_values.value_mut(vid) = VarValue::Value(least_choice); + debug!("enforce_pick_constraint: final least choice = {:?}", least_choice); + *var_values.value_mut(pick_vid) = VarValue::Value(least_choice); Ok(()) } diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index d4386b321e32a..ba42c7a54418e 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -907,7 +907,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// Require that the region `r` be equal to one of the regions in /// the set `regions`. - pub fn in_constraint( + pub fn pick_constraint( &self, origin: SubregionOrigin<'tcx>, region: ty::Region<'tcx>, @@ -915,7 +915,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ) { debug!("sub_regions({:?} <: {:?})", region, in_regions); self.borrow_region_constraints() - .in_constraint(origin, region, in_regions); + .pick_constraint(origin, region, in_regions); } pub fn subtype_predicate( diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs index d4c6c8b99b5ef..733155ad501a1 100644 --- a/src/librustc/infer/opaque_types/mod.rs +++ b/src/librustc/infer/opaque_types/mod.rs @@ -421,7 +421,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { tcx: self.tcx, - op: |r| self.in_constraint(infer::CallReturn(span), r, &in_regions), + op: |r| self.pick_constraint(infer::CallReturn(span), r, &in_regions), }); } diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index 14572c050b009..fa64690ded474 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -79,10 +79,10 @@ pub struct RegionConstraintData<'tcx> { /// be a region variable (or neither, as it happens). pub constraints: BTreeMap, SubregionOrigin<'tcx>>, - /// Constraints of the form `R0 in [R1, ..., Rn]`, meaning that + /// Constraints of the form `pick R0 from [R1, ..., Rn]`, meaning that /// `R0` must be equal to one of the regions `R1..Rn`. These occur /// with `impl Trait` quite frequently. - pub in_constraints: Vec>, + pub pick_constraints: Vec>, /// A "verify" is something that we need to verify after inference /// is done, but which does not directly affect inference in any @@ -143,12 +143,17 @@ impl Constraint<'_> { } } -/// Requires that `region` must be equal to one of the regions in `in_regions`. +/// Requires that `region` must be equal to one of the regions in `option_regions`. +/// We often denote this using the syntax: +/// +/// ``` +/// pick R0 from [O1..On] +/// ``` #[derive(Debug, Clone)] -pub struct InConstraint<'tcx> { +pub struct PickConstraint<'tcx> { pub origin: SubregionOrigin<'tcx>, - pub region: Region<'tcx>, - pub in_regions: Rc>>, + pub pick_region: Region<'tcx>, + pub option_regions: Rc>>, } /// `VerifyGenericBound(T, _, R, RS)`: the parameter type `T` (or @@ -657,20 +662,20 @@ impl<'tcx> RegionConstraintCollector<'tcx> { } } - pub fn in_constraint( + pub fn pick_constraint( &mut self, origin: SubregionOrigin<'tcx>, - region: ty::Region<'tcx>, - in_regions: &Rc>>, + pick_region: ty::Region<'tcx>, + option_regions: &Rc>>, ) { - debug!("in_constraint({:?} in {:#?})", region, in_regions); + debug!("pick_constraint({:?} in {:#?})", pick_region, option_regions); - if in_regions.iter().any(|&r| r == region) { + if option_regions.iter().any(|&r| r == pick_region) { return; } - self.data.in_constraints.push(InConstraint { - origin, region, in_regions: in_regions.clone() + self.data.pick_constraints.push(PickConstraint { + origin, pick_region, option_regions: option_regions.clone() }); } @@ -938,10 +943,10 @@ impl<'tcx> RegionConstraintData<'tcx> { pub fn is_empty(&self) -> bool { let RegionConstraintData { constraints, - in_constraints, + pick_constraints, verifys, givens, } = self; - constraints.is_empty() && in_constraints.is_empty() && verifys.is_empty() && givens.is_empty() + constraints.is_empty() && pick_constraints.is_empty() && verifys.is_empty() && givens.is_empty() } } From f0eebcd02f1dd123c0ff4e8ddf04a520cc1cf2a4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 3 Jun 2019 15:54:21 -0400 Subject: [PATCH 08/68] integrate pick constraints into lexical solver more completely --- .../infer/lexical_region_resolve/mod.rs | 73 ++++++++++++++----- .../multiple-lifetimes/inverse-bounds.rs | 3 - 2 files changed, 56 insertions(+), 20 deletions(-) diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index a37eff82e6eb3..68371a0d6cbe8 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -118,8 +118,12 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { let graph = self.construct_graph(); self.expand_givens(&graph); - self.enforce_pick_constraints(&graph, &mut var_data); - self.expansion(&mut var_data); + loop { + self.expansion(&mut var_data); + if !self.enforce_pick_constraints(&graph, &mut var_data) { + break; + } + } self.collect_errors(&mut var_data, errors); self.collect_var_errors(&var_data, &graph, errors); var_data @@ -204,43 +208,75 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { &self, graph: &RegionGraph<'tcx>, var_values: &mut LexicalRegionResolutions<'tcx>, - ) { + ) -> bool { + let mut any_changed = false; for pick_constraint in &self.data.pick_constraints { - let _ = self.enforce_pick_constraint(graph, pick_constraint, var_values); + if self.enforce_pick_constraint(graph, pick_constraint, var_values) { + any_changed = true; + } } + any_changed } + /// Enforce a constraint like + /// + /// ``` + /// pick 'r from ['o...] + /// ``` + /// + /// We look to see if there is a unique option `'o` from the list of options + /// that: + /// + /// (a) is greater than the current value of `'r` (which is a lower bound) + /// + /// and + /// + /// (b) is compatible with the upper bounds of `'r` that we can + /// find by traversing the graph. fn enforce_pick_constraint( &self, graph: &RegionGraph<'tcx>, pick_constraint: &PickConstraint<'tcx>, var_values: &mut LexicalRegionResolutions<'tcx>, - ) -> Result<(), ()> { + ) -> bool { debug!("enforce_pick_constraint(pick_constraint={:#?})", pick_constraint); // the constraint is some inference variable (`vid`) which // must be equal to one of the options let pick_vid = match pick_constraint.pick_region { ty::ReVar(vid) => *vid, - _ => return Err(()), + _ => return false, + }; + + // The current value of `vid` is a lower bound LB -- i.e., we + // know that `LB <= vid` must be true. + let pick_lower_bound = match var_values.value(pick_vid) { + VarValue::ErrorValue => return false, + VarValue::Value(r) => r, }; - // find all the "bounds" -- that is, each region `b` such that + // find all the "upper bounds" -- that is, each region `b` such that // `r0 <= b` must hold. - let (pick_bounds, _) = self.collect_concrete_regions(graph, pick_vid, OUTGOING, None); + let (pick_upper_bounds, _) = self.collect_concrete_regions(graph, pick_vid, OUTGOING, None); // get an iterator over the *available options* -- that is, - // each constraint regions `o` where `o <= b` for all the - // bounds `b`. - debug!("enforce_pick_constraint: bounds={:#?}", pick_bounds); + // each constraint regions `o` where `lb <= o` and `o <= ub` for all the + // upper bounds `ub`. + debug!("enforce_pick_constraint: upper_bounds={:#?}", pick_upper_bounds); let mut options = pick_constraint.option_regions.iter().filter(|option| { - pick_bounds.iter().all(|bound| self.sub_concrete_regions(option, bound.region)) + self.sub_concrete_regions(pick_lower_bound, option) + && pick_upper_bounds + .iter() + .all(|upper_bound| self.sub_concrete_regions(option, upper_bound.region)) }); // if there >1 option, we only make a choice if there is a // single *least* choice -- i.e., some available region that // is `<=` all the others. - let mut least_choice = options.next().ok_or(())?; + let mut least_choice = match options.next() { + Some(r) => r, + None => return false, + }; debug!("enforce_pick_constraint: least_choice={:?}", least_choice); for option in options { debug!("enforce_pick_constraint: option={:?}", option); @@ -250,15 +286,18 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { least_choice = option; } else { debug!("enforce_pick_constraint: no least choice"); - return Err(()); + return false; } } } debug!("enforce_pick_constraint: final least choice = {:?}", least_choice); - *var_values.value_mut(pick_vid) = VarValue::Value(least_choice); - - Ok(()) + if least_choice != pick_lower_bound { + *var_values.value_mut(pick_vid) = VarValue::Value(least_choice); + true + } else { + false + } } fn expansion(&self, var_values: &mut LexicalRegionResolutions<'tcx>) { diff --git a/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs b/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs index 41d4b59e8f029..60275ac3ab0d2 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs @@ -13,9 +13,6 @@ impl Trait<'_, '_> for T {} struct Invert<'a>(fn(&'a u8)); fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Invert<'a>, b: Invert<'b>) -> impl Trait<'d, 'e> -//~^ ERROR hidden type for `impl Trait` captures lifetime that does not appear in bounds -// FIXME -- we ought to be able to pick `'d` here, but our handling of in constraints -// is not smart enough where 'c: 'a, 'c: 'b, From d6ec0ae77742713800613685cbb5a28fd75e8662 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 3 Jun 2019 16:31:44 -0400 Subject: [PATCH 09/68] enforce and report pick-constraint errors The error message here is not great. --- src/librustc/infer/error_reporting/mod.rs | 22 ++- .../infer/lexical_region_resolve/mod.rs | 31 ++++ src/librustc/infer/mod.rs | 5 +- src/librustc/infer/opaque_types/mod.rs | 144 ++++++++++++------ src/librustc/infer/region_constraints/mod.rs | 19 ++- .../multiple-lifetimes/inverse-bounds.stderr | 22 ++- .../ordinary-bounds-pick-original.stderr | 48 ++++++ .../ordinary-bounds-pick-other.stderr | 25 +++ .../ordinary-bounds-unrelated.stderr | 13 +- .../ordinary-bounds-unsuited.stderr | 13 +- .../needs_least_region_or_bound.stderr | 46 +++++- 11 files changed, 323 insertions(+), 65 deletions(-) create mode 100644 src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.stderr create mode 100644 src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.stderr diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 65225163a25a4..ead75dcd7980e 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -53,6 +53,7 @@ use crate::infer::{self, SuppressRegionErrors}; use crate::hir; use crate::hir::def_id::DefId; use crate::hir::Node; +use crate::infer::opaque_types; use crate::middle::region; use crate::traits::{ObligationCause, ObligationCauseCode}; use crate::ty::error::TypeError; @@ -375,6 +376,23 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ); } } + + RegionResolutionError::PickConstraintFailure { + opaque_type_def_id, + hidden_ty, + pick_region, + span: _, + option_regions: _, + } => { + let hidden_ty = self.resolve_vars_if_possible(&hidden_ty); + opaque_types::report_unexpected_hidden_region( + self.tcx, + Some(region_scope_tree), + opaque_type_def_id, + hidden_ty, + pick_region, + ); + } } } } @@ -411,7 +429,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let is_bound_failure = |e: &RegionResolutionError<'tcx>| match *e { RegionResolutionError::GenericBoundFailure(..) => true, RegionResolutionError::ConcreteFailure(..) - | RegionResolutionError::SubSupConflict(..) => false, + | RegionResolutionError::SubSupConflict(..) + | RegionResolutionError::PickConstraintFailure { .. } => false, }; let mut errors = if errors.iter().all(|e| is_bound_failure(e)) { @@ -429,6 +448,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(), RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(), RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _) => rvo.span(), + RegionResolutionError::PickConstraintFailure { span, .. } => span, }); errors } diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index 68371a0d6cbe8..ac8db3c43365a 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -1,5 +1,6 @@ //! Lexical region resolution. +use crate::hir::def_id::DefId; use crate::infer::region_constraints::Constraint; use crate::infer::region_constraints::GenericKind; use crate::infer::region_constraints::PickConstraint; @@ -22,6 +23,7 @@ use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use smallvec::SmallVec; use std::fmt; use std::u32; +use syntax_pos::Span; mod graphviz; @@ -81,6 +83,17 @@ pub enum RegionResolutionError<'tcx> { SubregionOrigin<'tcx>, Region<'tcx>, ), + + /// Indicates a failure of a `PickConstraint`. These arise during + /// impl trait processing explicitly -- basically, the impl trait's hidden type + /// included some region that it was not supposed to. + PickConstraintFailure { + span: Span, + opaque_type_def_id: DefId, + hidden_ty: Ty<'tcx>, + pick_region: Region<'tcx>, + option_regions: Vec>, + }, } struct RegionAndOrigin<'tcx> { @@ -550,6 +563,24 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } } + for pick_constraint in &self.data.pick_constraints { + let pick_region = var_data.normalize(self.tcx(), pick_constraint.pick_region); + let option_regions = pick_constraint + .option_regions + .iter() + .map(|&option_region| var_data.normalize(self.tcx(), option_region)); + if !option_regions.clone().any(|option_region| pick_region == option_region) { + let span = self.tcx().def_span(pick_constraint.opaque_type_def_id); + errors.push(RegionResolutionError::PickConstraintFailure { + span, + opaque_type_def_id: pick_constraint.opaque_type_def_id, + hidden_ty: pick_constraint.hidden_ty, + pick_region, + option_regions: option_regions.collect(), + }); + } + } + for verify in &self.data.verifys { debug!("collect_errors: verify={:?}", verify); let sub = var_data.normalize(self.tcx(), verify.region); diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index ba42c7a54418e..f6c6b5e93a791 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -909,13 +909,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// the set `regions`. pub fn pick_constraint( &self, - origin: SubregionOrigin<'tcx>, + opaque_type_def_id: DefId, + hidden_ty: Ty<'tcx>, region: ty::Region<'tcx>, in_regions: &Rc>>, ) { debug!("sub_regions({:?} <: {:?})", region, in_regions); self.borrow_region_constraints() - .pick_constraint(origin, region, in_regions); + .pick_constraint(opaque_type_def_id, hidden_ty, region, in_regions); } pub fn subtype_predicate( diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs index 733155ad501a1..ca859045c6bbc 100644 --- a/src/librustc/infer/opaque_types/mod.rs +++ b/src/librustc/infer/opaque_types/mod.rs @@ -3,6 +3,7 @@ use crate::hir::def_id::DefId; use crate::hir::Node; use crate::infer::outlives::free_region_map::FreeRegionRelations; use crate::infer::{self, InferCtxt, InferOk, TypeVariableOrigin, TypeVariableOriginKind}; +use crate::middle::region; use crate::traits::{self, PredicateObligation}; use crate::ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder, TypeVisitor}; use crate::ty::subst::{InternalSubsts, Kind, SubstsRef, UnpackedKind}; @@ -10,7 +11,6 @@ use crate::ty::{self, GenericParamDefKind, Ty, TyCtxt}; use crate::util::nodemap::DefIdMap; use rustc_data_structures::fx::FxHashMap; use std::rc::Rc; -use syntax::source_map::Span; pub type OpaqueTypeMap<'tcx> = DefIdMap>; @@ -374,11 +374,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // we will create a "in bound" like `'r in // ['a, 'b, 'c]`, where `'a..'c` are the // regions that appear in the impl trait. - return self.generate_in_constraint( - span, + return self.generate_pick_constraint( concrete_ty, abstract_type_generics, opaque_defn, + def_id, ); } } @@ -399,14 +399,17 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// related, we would generate a constraint `'r in ['a, 'b, /// 'static]` for each region `'r` that appears in the hidden type /// (i.e., it must be equal to `'a`, `'b`, or `'static`). - fn generate_in_constraint( + fn generate_pick_constraint( &self, - span: Span, concrete_ty: Ty<'tcx>, abstract_type_generics: &ty::Generics, opaque_defn: &OpaqueTypeDecl<'tcx>, + opaque_type_def_id: DefId, ) { - let in_regions: Rc>> = Rc::new( + // Create the set of option regions: each region in the hidden + // type can be equal to any of the region parameters of the + // opaque type definition. + let option_regions: Rc>> = Rc::new( abstract_type_generics .params .iter() @@ -421,7 +424,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { tcx: self.tcx, - op: |r| self.pick_constraint(infer::CallReturn(span), r, &in_regions), + op: |r| self.pick_constraint( + opaque_type_def_id, + concrete_ty, + r, + &option_regions, + ), }); } @@ -445,7 +453,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// # Parameters /// /// - `def_id`, the `impl Trait` type - /// - `opaque_defn`, the opaque definition created in `instantiate_opaque_types` /// - `instantiated_ty`, the inferred type C1 -- fully resolved, lifted version of /// `opaque_defn.concrete_ty` @@ -492,6 +499,83 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } +pub fn report_unexpected_hidden_region( + tcx: TyCtxt<'_, '_, 'tcx>, + region_scope_tree: Option<®ion::ScopeTree>, + opaque_type_def_id: DefId, + hidden_ty: Ty<'tcx>, + hidden_region: ty::Region<'tcx>, +) { + let span = tcx.def_span(opaque_type_def_id); + let mut err = struct_span_err!( + tcx.sess, + span, + E0700, + "hidden type for `impl Trait` captures lifetime that does not appear in bounds", + ); + + // Explain the region we are capturing. + if let ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic | ty::ReEmpty = hidden_region { + // Assuming regionck succeeded (*), we ought to always be + // capturing *some* region from the fn header, and hence it + // ought to be free. So under normal circumstances, we will go + // down this path which gives a decent human readable + // explanation. + // + // (*) if not, the `tainted_by_errors` flag would be set to + // true in any case, so we wouldn't be here at all. + tcx.note_and_explain_free_region( + &mut err, + &format!("hidden type `{}` captures ", hidden_ty), + hidden_region, + "", + ); + } else { + // Ugh. This is a painful case: the hidden region is not one + // that we can easily summarize or explain. This can happens + // in a case like + // `src/test/ui/multiple-lifetimes/ordinary-bounds-unsuited.rs`: + // + // ``` + // fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> { + // if condition() { a } else { b } + // } + // ``` + // + // Here the captured lifetime is the intersection of `'a` and + // `'b`, which we can't quite express. This prticulararticular + // is kind of an unfortunate error anyway. + + if let Some(region_scope_tree) = region_scope_tree { + // If the `region_scope_tree` is available, this is being + // invoked from the "region inferencer error". We can at + // least report a really cryptic error for now. + tcx.note_and_explain_region( + region_scope_tree, + &mut err, + &format!("hidden type `{}` captures ", hidden_ty), + hidden_region, + "", + ); + } else { + // If the `region_scope_tree` is *unavailable*, this is + // being invoked by the code that comes *after* region + // inferencing. This is a bug, as the region inferencer + // ought to have noticed the failed constraint and invoked + // error reporting, which in turn should have prevented us + // from getting trying to infer the hidden type + // completely. + span_bug!( + span, + "hidden type captures unexpected lifetime `{:?}` but no region inference failure", + hidden_region, + ); + } + } + + err.emit(); +} + // Visitor that requires that (almost) all regions in the type visited outlive // `least_region`. We cannot use `push_outlives_components` because regions in // closure signatures are not included in their outlives components. We need to @@ -640,45 +724,13 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> { None => { if !self.map_missing_regions_to_empty && !self.tainted_by_errors { if let Some(hidden_ty) = self.hidden_ty.take() { - let span = self.tcx.def_span(self.opaque_type_def_id); - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0700, - "hidden type for `impl Trait` captures lifetime that \ - does not appear in bounds", + report_unexpected_hidden_region( + self.tcx, + None, + self.opaque_type_def_id, + hidden_ty, + r, ); - - // Explain the region we are capturing. - match r { - ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic | ty::ReEmpty => { - // Assuming regionck succeeded (*), we - // ought to always be capturing *some* region - // from the fn header, and hence it ought to - // be free. So under normal circumstances, we will - // go down this path which gives a decent human readable - // explanation. - // - // (*) if not, the `tainted_by_errors` - // flag would be set to true in any - // case, so we wouldn't be here at - // all. - self.tcx.note_and_explain_free_region( - &mut err, - &format!("hidden type `{}` captures ", hidden_ty), - r, - "", - ); - } - _ => { - // This case should not happen: it indicates that regionck - // failed to enforce an "in constraint". - err.note(&format!("hidden type `{}` captures `{:?}`", hidden_ty, r)); - err.note(&format!("this is likely a bug in the compiler, please file an issue on github")); - } - } - - err.emit(); } } self.tcx.lifetimes.re_empty diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index fa64690ded474..1340fb807ee2a 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -9,6 +9,7 @@ use super::{MiscVariable, RegionVariableOrigin, SubregionOrigin}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::unify as ut; +use crate::hir::def_id::DefId; use crate::ty::ReStatic; use crate::ty::{self, Ty, TyCtxt}; use crate::ty::{ReLateBound, ReVar}; @@ -151,8 +152,16 @@ impl Constraint<'_> { /// ``` #[derive(Debug, Clone)] pub struct PickConstraint<'tcx> { - pub origin: SubregionOrigin<'tcx>, + /// the def-id of the opaque type causing this constraint: used for error reporting + pub opaque_type_def_id: DefId, + + /// the hidden type in which `pick_region` appears: used for error reporting + pub hidden_ty: Ty<'tcx>, + + /// the region R0 pub pick_region: Region<'tcx>, + + /// the options O1..On pub option_regions: Rc>>, } @@ -664,7 +673,8 @@ impl<'tcx> RegionConstraintCollector<'tcx> { pub fn pick_constraint( &mut self, - origin: SubregionOrigin<'tcx>, + opaque_type_def_id: DefId, + hidden_ty: Ty<'tcx>, pick_region: ty::Region<'tcx>, option_regions: &Rc>>, ) { @@ -675,7 +685,10 @@ impl<'tcx> RegionConstraintCollector<'tcx> { } self.data.pick_constraints.push(PickConstraint { - origin, pick_region, option_regions: option_regions.clone() + opaque_type_def_id, + hidden_ty, + pick_region, + option_regions: option_regions.clone() }); } diff --git a/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.stderr b/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.stderr index 182d07733ffd8..13fb6f5d73e02 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.stderr +++ b/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.stderr @@ -1,15 +1,25 @@ -error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds +error: impl Trait captures unexpected lifetime --> $DIR/inverse-bounds.rs:15:70 | LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Invert<'a>, b: Invert<'b>) -> impl Trait<'d, 'e> - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ only lifetimes that appear in the impl Trait bounds may be captured | -note: hidden type `Invert<'c>` captures the lifetime 'c as defined on the function body at 15:25 - --> $DIR/inverse-bounds.rs:15:25 +note: hidden type captures the lifetime 'd as defined on the function body at 15:29 + --> $DIR/inverse-bounds.rs:15:29 | LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Invert<'a>, b: Invert<'b>) -> impl Trait<'d, 'e> - | ^^ + | ^^ +note: hidden type would be allowed to capture the lifetime 'd as defined on the function body at 15:29 + --> $DIR/inverse-bounds.rs:15:29 + | +LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Invert<'a>, b: Invert<'b>) -> impl Trait<'d, 'e> + | ^^ +note: hidden type would be allowed to capture the lifetime 'e as defined on the function body at 15:33 + --> $DIR/inverse-bounds.rs:15:33 + | +LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Invert<'a>, b: Invert<'b>) -> impl Trait<'d, 'e> + | ^^ + = note: hidden type would be allowed to capture the static lifetime error: aborting due to previous error -For more information about this error, try `rustc --explain E0700`. diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.stderr new file mode 100644 index 0000000000000..43e92ac68272b --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.stderr @@ -0,0 +1,48 @@ +error: impl Trait captures unexpected lifetime + --> $DIR/ordinary-bounds-pick-original.rs:11:50 + | +LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { + | ^^^^^^^^^^^^^^^^^^ only lifetimes that appear in the impl Trait bounds may be captured + | +note: hidden type captures the lifetime 'a as defined on the function body at 11:17 + --> $DIR/ordinary-bounds-pick-original.rs:11:17 + | +LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { + | ^^ +note: hidden type would be allowed to capture the lifetime 'a as defined on the function body at 11:17 + --> $DIR/ordinary-bounds-pick-original.rs:11:17 + | +LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { + | ^^ +note: hidden type would be allowed to capture the lifetime 'b as defined on the function body at 11:21 + --> $DIR/ordinary-bounds-pick-original.rs:11:21 + | +LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { + | ^^ + = note: hidden type would be allowed to capture the static lifetime + +error: impl Trait captures unexpected lifetime + --> $DIR/ordinary-bounds-pick-original.rs:11:50 + | +LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { + | ^^^^^^^^^^^^^^^^^^ only lifetimes that appear in the impl Trait bounds may be captured + | +note: hidden type captures the lifetime 'b as defined on the function body at 11:21 + --> $DIR/ordinary-bounds-pick-original.rs:11:21 + | +LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { + | ^^ +note: hidden type would be allowed to capture the lifetime 'a as defined on the function body at 11:17 + --> $DIR/ordinary-bounds-pick-original.rs:11:17 + | +LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { + | ^^ +note: hidden type would be allowed to capture the lifetime 'b as defined on the function body at 11:21 + --> $DIR/ordinary-bounds-pick-original.rs:11:21 + | +LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { + | ^^ + = note: hidden type would be allowed to capture the static lifetime + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.stderr new file mode 100644 index 0000000000000..ff09f9471079f --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.stderr @@ -0,0 +1,25 @@ +error: impl Trait captures unexpected lifetime + --> $DIR/ordinary-bounds-pick-other.rs:18:74 + | +LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> + | ^^^^^^^^^^^^^^^^^^ only lifetimes that appear in the impl Trait bounds may be captured + | +note: hidden type captures the lifetime 'e as defined on the function body at 18:33 + --> $DIR/ordinary-bounds-pick-other.rs:18:33 + | +LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> + | ^^ +note: hidden type would be allowed to capture the lifetime 'd as defined on the function body at 18:29 + --> $DIR/ordinary-bounds-pick-other.rs:18:29 + | +LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> + | ^^ +note: hidden type would be allowed to capture the lifetime 'e as defined on the function body at 18:33 + --> $DIR/ordinary-bounds-pick-other.rs:18:33 + | +LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> + | ^^ + = note: hidden type would be allowed to capture the static lifetime + +error: aborting due to previous error + diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr index 9b505c66dc115..cd2d46ac18218 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr @@ -4,8 +4,17 @@ error[E0700]: hidden type for `impl Trait` captures lifetime that does not appea LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> | ^^^^^^^^^^^^^^^^^^ | - = note: hidden type `Ordinary<'_>` captures `ReScope(CallSite(24))` - = note: this is likely a bug in the compiler, please file an issue on github +note: hidden type `Ordinary<'_>` captures the scope of call-site for function at 23:1 + --> $DIR/ordinary-bounds-unrelated.rs:23:1 + | +LL | / { +LL | | // Hidden type `Ordinary<'0>` with constraints: +LL | | // +LL | | // ``` +... | +LL | | if condition() { a } else { b } +LL | | } + | |_^ error: aborting due to previous error diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr index d258948c7735d..59ce93fa78b6b 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr @@ -4,8 +4,17 @@ error[E0700]: hidden type for `impl Trait` captures lifetime that does not appea LL | fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> | ^^^^^^^^^^^^^^^^^^ | - = note: hidden type `Ordinary<'_>` captures `ReScope(CallSite(24))` - = note: this is likely a bug in the compiler, please file an issue on github +note: hidden type `Ordinary<'_>` captures the scope of call-site for function at 22:1 + --> $DIR/ordinary-bounds-unsuited.rs:22:1 + | +LL | / { +LL | | // We return a value: +LL | | // +LL | | // ``` +... | +LL | | if condition() { a } else { b } +LL | | } + | |_^ error: aborting due to previous error diff --git a/src/test/ui/impl-trait/needs_least_region_or_bound.stderr b/src/test/ui/impl-trait/needs_least_region_or_bound.stderr index f1b4d9c58f39f..9b9196a213da8 100644 --- a/src/test/ui/impl-trait/needs_least_region_or_bound.stderr +++ b/src/test/ui/impl-trait/needs_least_region_or_bound.stderr @@ -1,8 +1,48 @@ -error: ambiguous lifetime bound in `impl Trait` +error: impl Trait captures unexpected lifetime --> $DIR/needs_least_region_or_bound.rs:6:55 | LL | fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ neither `'a` nor `'b` outlives the other + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only lifetimes that appear in the impl Trait bounds may be captured + | +note: hidden type captures the lifetime 'a as defined on the function body at 6:20 + --> $DIR/needs_least_region_or_bound.rs:6:20 + | +LL | fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> { + | ^^ +note: hidden type would be allowed to capture the lifetime 'a as defined on the function body at 6:20 + --> $DIR/needs_least_region_or_bound.rs:6:20 + | +LL | fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> { + | ^^ +note: hidden type would be allowed to capture the lifetime 'b as defined on the function body at 6:24 + --> $DIR/needs_least_region_or_bound.rs:6:24 + | +LL | fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> { + | ^^ + = note: hidden type would be allowed to capture the static lifetime + +error: impl Trait captures unexpected lifetime + --> $DIR/needs_least_region_or_bound.rs:6:55 + | +LL | fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only lifetimes that appear in the impl Trait bounds may be captured + | +note: hidden type captures the lifetime 'b as defined on the function body at 6:24 + --> $DIR/needs_least_region_or_bound.rs:6:24 + | +LL | fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> { + | ^^ +note: hidden type would be allowed to capture the lifetime 'a as defined on the function body at 6:20 + --> $DIR/needs_least_region_or_bound.rs:6:20 + | +LL | fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> { + | ^^ +note: hidden type would be allowed to capture the lifetime 'b as defined on the function body at 6:24 + --> $DIR/needs_least_region_or_bound.rs:6:24 + | +LL | fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> { + | ^^ + = note: hidden type would be allowed to capture the static lifetime -error: aborting due to previous error +error: aborting due to 2 previous errors From fd5f7673a76306265dbcdb2fa640fab877b9374a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 3 Jun 2019 17:24:30 -0400 Subject: [PATCH 10/68] rename `QueryRegionConstraint` to `QueryOutlivesConstraint` --- src/librustc/infer/canonical/mod.rs | 4 +-- .../infer/canonical/query_response.rs | 26 +++++++++---------- src/librustc/infer/opaque_types/mod.rs | 2 +- src/librustc/traits/query/type_op/custom.rs | 6 ++--- src/librustc/traits/query/type_op/mod.rs | 8 +++--- .../borrow_check/nll/region_infer/mod.rs | 6 ++--- .../nll/type_check/constraint_conversion.rs | 6 ++--- .../nll/type_check/free_region_relations.rs | 4 +-- .../nll/type_check/liveness/trace.rs | 4 +-- .../borrow_check/nll/type_check/mod.rs | 4 +-- 10 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/librustc/infer/canonical/mod.rs b/src/librustc/infer/canonical/mod.rs index b2c7bd73b6812..274dbd1efd5d8 100644 --- a/src/librustc/infer/canonical/mod.rs +++ b/src/librustc/infer/canonical/mod.rs @@ -189,7 +189,7 @@ pub enum CanonicalTyVarKind { #[derive(Clone, Debug, HashStable)] pub struct QueryResponse<'tcx, R> { pub var_values: CanonicalVarValues<'tcx>, - pub region_constraints: Vec>, + pub region_constraints: Vec>, pub certainty: Certainty, pub value: R, } @@ -292,7 +292,7 @@ impl<'tcx, V> Canonical<'tcx, V> { } } -pub type QueryRegionConstraint<'tcx> = ty::Binder, Region<'tcx>>>; +pub type QueryOutlivesConstraint<'tcx> = ty::Binder, Region<'tcx>>>; impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// Creates a substitution S for the canonical value with fresh diff --git a/src/librustc/infer/canonical/query_response.rs b/src/librustc/infer/canonical/query_response.rs index d00da11d17d45..c72af20a86301 100644 --- a/src/librustc/infer/canonical/query_response.rs +++ b/src/librustc/infer/canonical/query_response.rs @@ -11,7 +11,7 @@ use crate::arena::ArenaAllocatable; use crate::infer::canonical::substitute::substitute_value; use crate::infer::canonical::{ Canonical, CanonicalVarValues, CanonicalizedQueryResponse, Certainty, - OriginalQueryValues, QueryRegionConstraint, QueryResponse, + OriginalQueryValues, QueryOutlivesConstraint, QueryResponse, }; use crate::infer::region_constraints::{Constraint, RegionConstraintData}; use crate::infer::InferCtxtBuilder; @@ -222,7 +222,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { mut obligations, } = self.query_response_substitution(cause, param_env, original_values, query_response)?; - obligations.extend(self.query_region_constraints_into_obligations( + obligations.extend(self.query_outlives_constraints_into_obligations( cause, param_env, &query_response.value.region_constraints, @@ -248,9 +248,9 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// that come out of these queries, which it wants to convert into /// MIR-based constraints and solve. Therefore, it is most /// convenient for the NLL Type Checker to **directly consume** - /// the `QueryRegionConstraint` values that arise from doing a + /// the `QueryOutlivesConstraint` values that arise from doing a /// query. This is contrast to other parts of the compiler, which - /// would prefer for those `QueryRegionConstraint` to be converted + /// would prefer for those `QueryOutlivesConstraint` to be converted /// into the older infcx-style constraints (e.g., calls to /// `sub_regions` or `register_region_obligation`). /// @@ -263,7 +263,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// result. If any errors arise, they are propagated back as an /// `Err` result. /// - In the case of a successful substitution, we will append - /// `QueryRegionConstraint` values onto the + /// `QueryOutlivesConstraint` values onto the /// `output_query_region_constraints` vector for the solver to /// use (if an error arises, some values may also be pushed, but /// they should be ignored). @@ -279,7 +279,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { param_env: ty::ParamEnv<'tcx>, original_values: &OriginalQueryValues<'tcx>, query_response: &Canonical<'tcx, QueryResponse<'tcx, R>>, - output_query_region_constraints: &mut Vec>, + output_query_outlives_constraints: &mut Vec>, ) -> InferResult<'tcx, R> where R: Debug + TypeFoldable<'tcx>, @@ -287,7 +287,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { let result_subst = self.query_response_substitution_guess(cause, original_values, query_response); - // Compute `QueryRegionConstraint` values that unify each of + // Compute `QueryOutlivesConstraint` values that unify each of // the original values `v_o` that was canonicalized into a // variable... let mut obligations = vec![]; @@ -305,9 +305,9 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { (UnpackedKind::Lifetime(v_o), UnpackedKind::Lifetime(v_r)) => { // To make `v_o = v_r`, we emit `v_o: v_r` and `v_r: v_o`. if v_o != v_r { - output_query_region_constraints + output_query_outlives_constraints .push(ty::Binder::dummy(ty::OutlivesPredicate(v_o.into(), v_r))); - output_query_region_constraints + output_query_outlives_constraints .push(ty::Binder::dummy(ty::OutlivesPredicate(v_r.into(), v_o))); } } @@ -333,7 +333,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { } // ...also include the other query region constraints from the query. - output_query_region_constraints.extend( + output_query_outlives_constraints.extend( query_response.value.region_constraints.iter().filter_map(|r_c| { let r_c = substitute_value(self.tcx, &result_subst, r_c); @@ -560,11 +560,11 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// Converts the region constraints resulting from a query into an /// iterator of obligations. - fn query_region_constraints_into_obligations<'a>( + fn query_outlives_constraints_into_obligations<'a>( &'a self, cause: &'a ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, - unsubstituted_region_constraints: &'a [QueryRegionConstraint<'tcx>], + unsubstituted_region_constraints: &'a [QueryOutlivesConstraint<'tcx>], result_subst: &'a CanonicalVarValues<'tcx>, ) -> impl Iterator> + 'a + Captures<'tcx> { unsubstituted_region_constraints @@ -649,7 +649,7 @@ pub fn make_query_outlives<'tcx>( tcx: TyCtxt<'tcx>, outlives_obligations: impl Iterator, ty::Region<'tcx>)>, region_constraints: &RegionConstraintData<'tcx>, -) -> Vec> { +) -> Vec> { let RegionConstraintData { constraints, verifys, diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs index ca859045c6bbc..656b9271115d2 100644 --- a/src/librustc/infer/opaque_types/mod.rs +++ b/src/librustc/infer/opaque_types/mod.rs @@ -500,7 +500,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } pub fn report_unexpected_hidden_region( - tcx: TyCtxt<'_, '_, 'tcx>, + tcx: TyCtxt<'tcx>, region_scope_tree: Option<®ion::ScopeTree>, opaque_type_def_id: DefId, hidden_ty: Ty<'tcx>, diff --git a/src/librustc/traits/query/type_op/custom.rs b/src/librustc/traits/query/type_op/custom.rs index 72550e23460e6..42d0608d358ac 100644 --- a/src/librustc/traits/query/type_op/custom.rs +++ b/src/librustc/traits/query/type_op/custom.rs @@ -3,7 +3,7 @@ use std::fmt; use crate::traits::query::Fallible; use crate::infer::canonical::query_response; -use crate::infer::canonical::QueryRegionConstraint; +use crate::infer::canonical::QueryOutlivesConstraint; use std::rc::Rc; use syntax::source_map::DUMMY_SP; use crate::traits::{ObligationCause, TraitEngine, TraitEngineExt}; @@ -39,7 +39,7 @@ where fn fully_perform( self, infcx: &InferCtxt<'_, 'tcx>, - ) -> Fallible<(Self::Output, Option>>>)> { + ) -> Fallible<(Self::Output, Option>>>)> { if cfg!(debug_assertions) { info!("fully_perform({:?})", self); } @@ -62,7 +62,7 @@ where fn scrape_region_constraints<'tcx, R>( infcx: &InferCtxt<'_, 'tcx>, op: impl FnOnce() -> Fallible>, -) -> Fallible<(R, Option>>>)> { +) -> Fallible<(R, Option>>>)> { let mut fulfill_cx = TraitEngine::new(infcx.tcx); let dummy_body_id = ObligationCause::dummy().body_id; diff --git a/src/librustc/traits/query/type_op/mod.rs b/src/librustc/traits/query/type_op/mod.rs index 4a07a3120f3e8..0c415876247da 100644 --- a/src/librustc/traits/query/type_op/mod.rs +++ b/src/librustc/traits/query/type_op/mod.rs @@ -1,6 +1,6 @@ use crate::infer::canonical::{ Canonical, Canonicalized, CanonicalizedQueryResponse, OriginalQueryValues, - QueryRegionConstraint, QueryResponse, + QueryOutlivesConstraint, QueryResponse, }; use crate::infer::{InferCtxt, InferOk}; use std::fmt; @@ -32,7 +32,7 @@ pub trait TypeOp<'tcx>: Sized + fmt::Debug { fn fully_perform( self, infcx: &InferCtxt<'_, 'tcx>, - ) -> Fallible<(Self::Output, Option>>>)>; + ) -> Fallible<(Self::Output, Option>>>)>; } /// "Query type ops" are type ops that are implemented using a @@ -85,7 +85,7 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Sized + TypeFoldable<'tcx> + 'tcx { fn fully_perform_into( query_key: ParamEnvAnd<'tcx, Self>, infcx: &InferCtxt<'_, 'tcx>, - output_query_region_constraints: &mut Vec>, + output_query_region_constraints: &mut Vec>, ) -> Fallible { if let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &query_key) { return Ok(result); @@ -140,7 +140,7 @@ where fn fully_perform( self, infcx: &InferCtxt<'_, 'tcx>, - ) -> Fallible<(Self::Output, Option>>>)> { + ) -> Fallible<(Self::Output, Option>>>)> { let mut qrc = vec![]; let r = Q::fully_perform_into(self, infcx, &mut qrc)?; diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index a4fa1d98255ba..4a539f4b449b7 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -8,7 +8,7 @@ use crate::borrow_check::Upvar; use crate::borrow_check::nll::type_check::free_region_relations::UniversalRegionRelations; use crate::borrow_check::nll::type_check::Locations; use rustc::hir::def_id::DefId; -use rustc::infer::canonical::QueryRegionConstraint; +use rustc::infer::canonical::QueryOutlivesConstraint; use rustc::infer::region_constraints::{GenericKind, VarInfos, VerifyBound}; use rustc::infer::{InferCtxt, NLLRegionVariableOrigin, RegionVariableOrigin}; use rustc::mir::{ @@ -1372,7 +1372,7 @@ pub trait ClosureRegionRequirementsExt<'tcx> { tcx: TyCtxt<'tcx>, closure_def_id: DefId, closure_substs: SubstsRef<'tcx>, - ) -> Vec>; + ) -> Vec>; fn subst_closure_mapping( &self, @@ -1402,7 +1402,7 @@ impl<'tcx> ClosureRegionRequirementsExt<'tcx> for ClosureRegionRequirements<'tcx tcx: TyCtxt<'tcx>, closure_def_id: DefId, closure_substs: SubstsRef<'tcx>, - ) -> Vec> { + ) -> Vec> { debug!( "apply_requirements(closure_def_id={:?}, closure_substs={:?})", closure_def_id, closure_substs diff --git a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs index 77a4d2699fff7..8c7562739a762 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs @@ -3,7 +3,7 @@ use crate::borrow_check::nll::region_infer::TypeTest; use crate::borrow_check::nll::type_check::{Locations, MirTypeckRegionConstraints}; use crate::borrow_check::nll::universal_regions::UniversalRegions; use crate::borrow_check::nll::ToRegionVid; -use rustc::infer::canonical::QueryRegionConstraint; +use rustc::infer::canonical::QueryOutlivesConstraint; use rustc::infer::outlives::env::RegionBoundPairs; use rustc::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate}; use rustc::infer::region_constraints::{GenericKind, VerifyBound}; @@ -49,13 +49,13 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { } } - pub(super) fn convert_all(&mut self, query_constraints: &[QueryRegionConstraint<'tcx>]) { + pub(super) fn convert_all(&mut self, query_constraints: &[QueryOutlivesConstraint<'tcx>]) { for query_constraint in query_constraints { self.convert(query_constraint); } } - pub(super) fn convert(&mut self, query_constraint: &QueryRegionConstraint<'tcx>) { + pub(super) fn convert(&mut self, query_constraint: &QueryOutlivesConstraint<'tcx>) { debug!("generate: constraints at: {:#?}", self.locations); // Extract out various useful fields we'll need below. diff --git a/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs b/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs index 1bb3acc28f0c8..09ea8dfd61c25 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs @@ -2,7 +2,7 @@ use crate::borrow_check::nll::type_check::constraint_conversion; use crate::borrow_check::nll::type_check::{Locations, MirTypeckRegionConstraints}; use crate::borrow_check::nll::universal_regions::UniversalRegions; use crate::borrow_check::nll::ToRegionVid; -use rustc::infer::canonical::QueryRegionConstraint; +use rustc::infer::canonical::QueryOutlivesConstraint; use rustc::infer::outlives::free_region_map::FreeRegionRelations; use rustc::infer::region_constraints::GenericKind; use rustc::infer::InferCtxt; @@ -311,7 +311,7 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> { /// either the return type of the MIR or one of its arguments. At /// the same time, compute and add any implied bounds that come /// from this local. - fn add_implied_bounds(&mut self, ty: Ty<'tcx>) -> Option>>> { + fn add_implied_bounds(&mut self, ty: Ty<'tcx>) -> Option>>> { debug!("add_implied_bounds(ty={:?})", ty); let (bounds, constraints) = self.param_env diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs index f1d568f0cf24c..70441cd258e70 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs @@ -6,7 +6,7 @@ use crate::borrow_check::nll::type_check::TypeChecker; use crate::dataflow::indexes::MovePathIndex; use crate::dataflow::move_paths::MoveData; use crate::dataflow::{FlowAtLocation, FlowsAtLocation, MaybeInitializedPlaces}; -use rustc::infer::canonical::QueryRegionConstraint; +use rustc::infer::canonical::QueryOutlivesConstraint; use rustc::mir::{BasicBlock, ConstraintCategory, Local, Location, Body}; use rustc::traits::query::dropck_outlives::DropckOutlivesResult; use rustc::traits::query::type_op::outlives::DropckOutlives; @@ -88,7 +88,7 @@ struct LivenessContext<'me, 'typeck, 'flow, 'tcx> { struct DropData<'tcx> { dropck_result: DropckOutlivesResult<'tcx>, - region_constraint_data: Option>>>, + region_constraint_data: Option>>>, } struct LivenessResults<'me, 'typeck, 'flow, 'tcx> { diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 9409fefb6bde7..134b51c4b7912 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -23,7 +23,7 @@ use crate::dataflow::MaybeInitializedPlaces; use either::Either; use rustc::hir; use rustc::hir::def_id::DefId; -use rustc::infer::canonical::QueryRegionConstraint; +use rustc::infer::canonical::QueryOutlivesConstraint; use rustc::infer::outlives::env::RegionBoundPairs; use rustc::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime, NLLRegionVariableOrigin}; use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -1093,7 +1093,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { &mut self, locations: Locations, category: ConstraintCategory, - data: &[QueryRegionConstraint<'tcx>], + data: &[QueryOutlivesConstraint<'tcx>], ) { debug!( "push_region_constraints: constraints generated at {:?} are {:#?}", From 7e66a96d586f2f0e25088ef19b455ea56c8aca17 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 3 Jun 2019 17:39:52 -0400 Subject: [PATCH 11/68] introduce `QueryRegionConstraints` struct (no-op) --- src/librustc/infer/canonical/mod.rs | 20 ++++++++++++++++++- .../infer/canonical/query_response.rs | 14 +++++++------ src/librustc_traits/chalk_context/mod.rs | 5 +++-- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/librustc/infer/canonical/mod.rs b/src/librustc/infer/canonical/mod.rs index 274dbd1efd5d8..9c616edba9f97 100644 --- a/src/librustc/infer/canonical/mod.rs +++ b/src/librustc/infer/canonical/mod.rs @@ -189,11 +189,16 @@ pub enum CanonicalTyVarKind { #[derive(Clone, Debug, HashStable)] pub struct QueryResponse<'tcx, R> { pub var_values: CanonicalVarValues<'tcx>, - pub region_constraints: Vec>, + pub region_constraints: QueryRegionConstraints<'tcx>, pub certainty: Certainty, pub value: R, } +#[derive(Clone, Debug, Default, HashStable)] +pub struct QueryRegionConstraints<'tcx> { + outlives: Vec>, +} + pub type Canonicalized<'tcx, V> = Canonical<'tcx, V>; pub type CanonicalizedQueryResponse<'tcx, T> = @@ -540,6 +545,19 @@ BraceStructLiftImpl! { } where R: Lift<'tcx> } +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for QueryRegionConstraints<'tcx> { + outlives + } +} + +BraceStructLiftImpl! { + impl<'a, 'tcx> Lift<'tcx> for QueryRegionConstraints<'a> { + type Lifted = QueryRegionConstraints<'tcx>; + outlives + } +} + impl<'tcx> Index for CanonicalVarValues<'tcx> { type Output = Kind<'tcx>; diff --git a/src/librustc/infer/canonical/query_response.rs b/src/librustc/infer/canonical/query_response.rs index c72af20a86301..acaed18939774 100644 --- a/src/librustc/infer/canonical/query_response.rs +++ b/src/librustc/infer/canonical/query_response.rs @@ -11,7 +11,7 @@ use crate::arena::ArenaAllocatable; use crate::infer::canonical::substitute::substitute_value; use crate::infer::canonical::{ Canonical, CanonicalVarValues, CanonicalizedQueryResponse, Certainty, - OriginalQueryValues, QueryOutlivesConstraint, QueryResponse, + OriginalQueryValues, QueryRegionConstraints, QueryOutlivesConstraint, QueryResponse, }; use crate::infer::region_constraints::{Constraint, RegionConstraintData}; use crate::infer::InferCtxtBuilder; @@ -132,7 +132,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { { self.canonicalize_response(&QueryResponse { var_values: inference_vars, - region_constraints: vec![], + region_constraints: QueryRegionConstraints::default(), certainty: Certainty::Proven, // Ambiguities are OK! value: answer, }) @@ -173,7 +173,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { debug!("ambig_errors = {:#?}", ambig_errors); let region_obligations = self.take_registered_region_obligations(); - let region_constraints = self.with_region_constraints(|region_constraints| { + let outlives_constraints = self.with_region_constraints(|region_constraints| { make_query_outlives( tcx, region_obligations @@ -191,7 +191,9 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { Ok(QueryResponse { var_values: inference_vars, - region_constraints, + region_constraints: QueryRegionConstraints { + outlives: outlives_constraints, + }, certainty, value: answer, }) @@ -225,7 +227,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { obligations.extend(self.query_outlives_constraints_into_obligations( cause, param_env, - &query_response.value.region_constraints, + &query_response.value.region_constraints.outlives, &result_subst, )); @@ -334,7 +336,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { // ...also include the other query region constraints from the query. output_query_outlives_constraints.extend( - query_response.value.region_constraints.iter().filter_map(|r_c| { + query_response.value.region_constraints.outlives.iter().filter_map(|r_c| { let r_c = substitute_value(self.tcx, &result_subst, r_c); // Screen out `'a: 'a` cases -- we skip the binder here but diff --git a/src/librustc_traits/chalk_context/mod.rs b/src/librustc_traits/chalk_context/mod.rs index bbb0825ee082a..5c23ad4a4edfb 100644 --- a/src/librustc_traits/chalk_context/mod.rs +++ b/src/librustc_traits/chalk_context/mod.rs @@ -17,6 +17,7 @@ use rustc::infer::canonical::{ CanonicalVarValues, OriginalQueryValues, QueryResponse, + QueryRegionConstraints, Certainty, }; use rustc::traits::{ @@ -151,14 +152,14 @@ impl context::AggregateOps> for ChalkContext<'tcx> { let solution = constrained_subst.unchecked_map(|cs| match ambiguous { true => QueryResponse { var_values: cs.subst.make_identity(self.tcx), - region_constraints: Vec::new(), + region_constraints: QueryRegionConstraints::default(), certainty: Certainty::Ambiguous, value: (), }, false => QueryResponse { var_values: cs.subst, - region_constraints: Vec::new(), + region_constraints: QueryRegionConstraints::default(), // FIXME: restore this later once we get better at handling regions // region_constraints: cs.constraints From 09bba9b89d05d3441d8c1acf092e7037e6e11295 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 3 Jun 2019 18:25:21 -0400 Subject: [PATCH 12/68] introduce `QueryRegionConstraints` struct --- src/librustc/infer/canonical/mod.rs | 10 +++++++++- src/librustc/infer/canonical/query_response.rs | 14 ++++++-------- src/librustc/traits/query/type_op/custom.rs | 12 ++++++------ src/librustc/traits/query/type_op/mod.rs | 14 +++++++------- .../nll/type_check/free_region_relations.rs | 7 ++++--- .../borrow_check/nll/type_check/liveness/trace.rs | 4 ++-- .../borrow_check/nll/type_check/mod.rs | 14 +++++++++----- 7 files changed, 43 insertions(+), 32 deletions(-) diff --git a/src/librustc/infer/canonical/mod.rs b/src/librustc/infer/canonical/mod.rs index 9c616edba9f97..f18eeca361028 100644 --- a/src/librustc/infer/canonical/mod.rs +++ b/src/librustc/infer/canonical/mod.rs @@ -196,7 +196,15 @@ pub struct QueryResponse<'tcx, R> { #[derive(Clone, Debug, Default, HashStable)] pub struct QueryRegionConstraints<'tcx> { - outlives: Vec>, + pub outlives: Vec>, +} + +impl QueryRegionConstraints<'_> { + /// Represents an empty (trivially true) set of region + /// constraints. + pub fn is_empty(&self) -> bool { + self.outlives.is_empty() + } } pub type Canonicalized<'tcx, V> = Canonical<'tcx, V>; diff --git a/src/librustc/infer/canonical/query_response.rs b/src/librustc/infer/canonical/query_response.rs index acaed18939774..b4c46e71b16d2 100644 --- a/src/librustc/infer/canonical/query_response.rs +++ b/src/librustc/infer/canonical/query_response.rs @@ -173,8 +173,8 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { debug!("ambig_errors = {:#?}", ambig_errors); let region_obligations = self.take_registered_region_obligations(); - let outlives_constraints = self.with_region_constraints(|region_constraints| { - make_query_outlives( + let region_constraints = self.with_region_constraints(|region_constraints| { + make_query_region_constraints( tcx, region_obligations .iter() @@ -191,9 +191,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { Ok(QueryResponse { var_values: inference_vars, - region_constraints: QueryRegionConstraints { - outlives: outlives_constraints, - }, + region_constraints, certainty, value: answer, }) @@ -647,11 +645,11 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// Given the region obligations and constraints scraped from the infcx, /// creates query region constraints. -pub fn make_query_outlives<'tcx>( +pub fn make_query_region_constraints<'tcx>( tcx: TyCtxt<'tcx>, outlives_obligations: impl Iterator, ty::Region<'tcx>)>, region_constraints: &RegionConstraintData<'tcx>, -) -> Vec> { +) -> QueryRegionConstraints<'tcx> { let RegionConstraintData { constraints, verifys, @@ -690,5 +688,5 @@ pub fn make_query_outlives<'tcx>( ) .collect(); - outlives + QueryRegionConstraints { outlives } } diff --git a/src/librustc/traits/query/type_op/custom.rs b/src/librustc/traits/query/type_op/custom.rs index 42d0608d358ac..a2a5f3f950c7a 100644 --- a/src/librustc/traits/query/type_op/custom.rs +++ b/src/librustc/traits/query/type_op/custom.rs @@ -3,7 +3,7 @@ use std::fmt; use crate::traits::query::Fallible; use crate::infer::canonical::query_response; -use crate::infer::canonical::QueryOutlivesConstraint; +use crate::infer::canonical::QueryRegionConstraints; use std::rc::Rc; use syntax::source_map::DUMMY_SP; use crate::traits::{ObligationCause, TraitEngine, TraitEngineExt}; @@ -39,7 +39,7 @@ where fn fully_perform( self, infcx: &InferCtxt<'_, 'tcx>, - ) -> Fallible<(Self::Output, Option>>>)> { + ) -> Fallible<(Self::Output, Option>>)> { if cfg!(debug_assertions) { info!("fully_perform({:?})", self); } @@ -62,7 +62,7 @@ where fn scrape_region_constraints<'tcx, R>( infcx: &InferCtxt<'_, 'tcx>, op: impl FnOnce() -> Fallible>, -) -> Fallible<(R, Option>>>)> { +) -> Fallible<(R, Option>>)> { let mut fulfill_cx = TraitEngine::new(infcx.tcx); let dummy_body_id = ObligationCause::dummy().body_id; @@ -92,7 +92,7 @@ fn scrape_region_constraints<'tcx, R>( let region_constraint_data = infcx.take_and_reset_region_constraints(); - let outlives = query_response::make_query_outlives( + let region_constraints = query_response::make_query_region_constraints( infcx.tcx, region_obligations .iter() @@ -101,9 +101,9 @@ fn scrape_region_constraints<'tcx, R>( ®ion_constraint_data, ); - if outlives.is_empty() { + if region_constraints.is_empty() { Ok((value, None)) } else { - Ok((value, Some(Rc::new(outlives)))) + Ok((value, Some(Rc::new(region_constraints)))) } } diff --git a/src/librustc/traits/query/type_op/mod.rs b/src/librustc/traits/query/type_op/mod.rs index 0c415876247da..bf8cace3a1bb4 100644 --- a/src/librustc/traits/query/type_op/mod.rs +++ b/src/librustc/traits/query/type_op/mod.rs @@ -1,6 +1,6 @@ use crate::infer::canonical::{ Canonical, Canonicalized, CanonicalizedQueryResponse, OriginalQueryValues, - QueryOutlivesConstraint, QueryResponse, + QueryRegionConstraints, QueryOutlivesConstraint, QueryResponse, }; use crate::infer::{InferCtxt, InferOk}; use std::fmt; @@ -32,7 +32,7 @@ pub trait TypeOp<'tcx>: Sized + fmt::Debug { fn fully_perform( self, infcx: &InferCtxt<'_, 'tcx>, - ) -> Fallible<(Self::Output, Option>>>)>; + ) -> Fallible<(Self::Output, Option>>)>; } /// "Query type ops" are type ops that are implemented using a @@ -140,16 +140,16 @@ where fn fully_perform( self, infcx: &InferCtxt<'_, 'tcx>, - ) -> Fallible<(Self::Output, Option>>>)> { - let mut qrc = vec![]; - let r = Q::fully_perform_into(self, infcx, &mut qrc)?; + ) -> Fallible<(Self::Output, Option>>)> { + let mut outlives = vec![]; + let r = Q::fully_perform_into(self, infcx, &mut outlives)?; // Promote the final query-region-constraints into a // (optional) ref-counted vector: - let opt_qrc = if qrc.is_empty() { + let opt_qrc = if outlives.is_empty() { None } else { - Some(Rc::new(qrc)) + Some(Rc::new(QueryRegionConstraints { outlives })) }; Ok((r, opt_qrc)) diff --git a/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs b/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs index 09ea8dfd61c25..2a21da064fa13 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs @@ -2,7 +2,7 @@ use crate::borrow_check::nll::type_check::constraint_conversion; use crate::borrow_check::nll::type_check::{Locations, MirTypeckRegionConstraints}; use crate::borrow_check::nll::universal_regions::UniversalRegions; use crate::borrow_check::nll::ToRegionVid; -use rustc::infer::canonical::QueryOutlivesConstraint; +use rustc::infer::canonical::QueryRegionConstraints; use rustc::infer::outlives::free_region_map::FreeRegionRelations; use rustc::infer::region_constraints::GenericKind; use rustc::infer::InferCtxt; @@ -288,6 +288,7 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> { } for data in constraint_sets { + let QueryRegionConstraints { outlives } = &*data; constraint_conversion::ConstraintConversion::new( self.infcx, &self.universal_regions, @@ -297,7 +298,7 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> { Locations::All(DUMMY_SP), ConstraintCategory::Internal, &mut self.constraints, - ).convert_all(&data); + ).convert_all(outlives); } CreateResult { @@ -311,7 +312,7 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> { /// either the return type of the MIR or one of its arguments. At /// the same time, compute and add any implied bounds that come /// from this local. - fn add_implied_bounds(&mut self, ty: Ty<'tcx>) -> Option>>> { + fn add_implied_bounds(&mut self, ty: Ty<'tcx>) -> Option>> { debug!("add_implied_bounds(ty={:?})", ty); let (bounds, constraints) = self.param_env diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs index 70441cd258e70..f160f658f5576 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs @@ -6,7 +6,7 @@ use crate::borrow_check::nll::type_check::TypeChecker; use crate::dataflow::indexes::MovePathIndex; use crate::dataflow::move_paths::MoveData; use crate::dataflow::{FlowAtLocation, FlowsAtLocation, MaybeInitializedPlaces}; -use rustc::infer::canonical::QueryOutlivesConstraint; +use rustc::infer::canonical::QueryRegionConstraints; use rustc::mir::{BasicBlock, ConstraintCategory, Local, Location, Body}; use rustc::traits::query::dropck_outlives::DropckOutlivesResult; use rustc::traits::query::type_op::outlives::DropckOutlives; @@ -88,7 +88,7 @@ struct LivenessContext<'me, 'typeck, 'flow, 'tcx> { struct DropData<'tcx> { dropck_result: DropckOutlivesResult<'tcx>, - region_constraint_data: Option>>>, + region_constraint_data: Option>>, } struct LivenessResults<'me, 'typeck, 'flow, 'tcx> { diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 134b51c4b7912..758c8df1d186a 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -23,7 +23,7 @@ use crate::dataflow::MaybeInitializedPlaces; use either::Either; use rustc::hir; use rustc::hir::def_id::DefId; -use rustc::infer::canonical::QueryOutlivesConstraint; +use rustc::infer::canonical::QueryRegionConstraints; use rustc::infer::outlives::env::RegionBoundPairs; use rustc::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime, NLLRegionVariableOrigin}; use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -1093,13 +1093,15 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { &mut self, locations: Locations, category: ConstraintCategory, - data: &[QueryOutlivesConstraint<'tcx>], + data: &QueryRegionConstraints<'tcx>, ) { debug!( "push_region_constraints: constraints generated at {:?} are {:#?}", locations, data ); + let QueryRegionConstraints { outlives } = data; + constraint_conversion::ConstraintConversion::new( self.infcx, self.borrowck_context.universal_regions, @@ -1109,7 +1111,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { locations, category, &mut self.borrowck_context.constraints, - ).convert_all(&data); + ).convert_all(outlives); } /// Convenient wrapper around `relate_tys::relate_types` -- see @@ -2508,10 +2510,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { location: Location, ) -> ty::InstantiatedPredicates<'tcx> { if let Some(closure_region_requirements) = tcx.mir_borrowck(def_id).closure_requirements { - let closure_constraints = - closure_region_requirements.apply_requirements(tcx, def_id, substs); + let closure_constraints = QueryRegionConstraints { + outlives: closure_region_requirements.apply_requirements(tcx, def_id, substs) + }; let bounds_mapping = closure_constraints + .outlives .iter() .enumerate() .filter_map(|(idx, constraint)| { From f673b24ba25a3ead2da990de9d308f5e15b7f710 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 4 Jun 2019 17:39:12 -0400 Subject: [PATCH 13/68] rename `ConstraintIndex` to `OutlivesConstraintIndex` --- .../borrow_check/nll/constraints/graph.rs | 12 ++++++------ .../borrow_check/nll/constraints/mod.rs | 16 ++++++++++------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/constraints/graph.rs b/src/librustc_mir/borrow_check/nll/constraints/graph.rs index c4b2a5daef89a..78d49309d0214 100644 --- a/src/librustc_mir/borrow_check/nll/constraints/graph.rs +++ b/src/librustc_mir/borrow_check/nll/constraints/graph.rs @@ -1,5 +1,5 @@ use crate::borrow_check::nll::type_check::Locations; -use crate::borrow_check::nll::constraints::ConstraintIndex; +use crate::borrow_check::nll::constraints::OutlivesConstraintIndex; use crate::borrow_check::nll::constraints::{ConstraintSet, OutlivesConstraint}; use rustc::mir::ConstraintCategory; use rustc::ty::RegionVid; @@ -12,8 +12,8 @@ use syntax_pos::DUMMY_SP; /// -> R2` or `R2 -> R1` depending on the direction type `D`. crate struct ConstraintGraph { _direction: D, - first_constraints: IndexVec>, - next_constraints: IndexVec>, + first_constraints: IndexVec>, + next_constraints: IndexVec>, } crate type NormalConstraintGraph = ConstraintGraph; @@ -81,9 +81,9 @@ impl ConstraintGraph { num_region_vars: usize, ) -> Self { let mut first_constraints = IndexVec::from_elem_n(None, num_region_vars); - let mut next_constraints = IndexVec::from_elem(None, &set.constraints); + let mut next_constraints = IndexVec::from_elem(None, &set.outlives); - for (idx, constraint) in set.constraints.iter_enumerated().rev() { + for (idx, constraint) in set.outlives.iter_enumerated().rev() { let head = &mut first_constraints[D::start_region(constraint)]; let next = &mut next_constraints[idx]; debug_assert!(next.is_none()); @@ -143,7 +143,7 @@ impl ConstraintGraph { crate struct Edges<'s, D: ConstraintGraphDirecton> { graph: &'s ConstraintGraph, constraints: &'s ConstraintSet, - pointer: Option, + pointer: Option, next_static_idx: Option, static_region: RegionVid, } diff --git a/src/librustc_mir/borrow_check/nll/constraints/mod.rs b/src/librustc_mir/borrow_check/nll/constraints/mod.rs index b1091eb5ac81f..a4c66d5ef6e0f 100644 --- a/src/librustc_mir/borrow_check/nll/constraints/mod.rs +++ b/src/librustc_mir/borrow_check/nll/constraints/mod.rs @@ -9,9 +9,13 @@ use std::ops::Deref; crate mod graph; +/// A set of NLL region constraints. These include "outlives" +/// constraints of the form `R1: R2`. Each constraint is identified by +/// a unique `OutlivesConstraintIndex` and you can index into the set +/// (`constraint_set[i]`) to access the constraint details. #[derive(Clone, Default)] crate struct ConstraintSet { - constraints: IndexVec, + outlives: IndexVec, } impl ConstraintSet { @@ -24,7 +28,7 @@ impl ConstraintSet { // 'a: 'a is pretty uninteresting return; } - self.constraints.push(constraint); + self.outlives.push(constraint); } /// Constructs a "normal" graph from the constraint set; the graph makes it @@ -57,10 +61,10 @@ impl ConstraintSet { } impl Deref for ConstraintSet { - type Target = IndexVec; + type Target = IndexVec; fn deref(&self) -> &Self::Target { - &self.constraints + &self.outlives } } @@ -94,8 +98,8 @@ impl fmt::Debug for OutlivesConstraint { } newtype_index! { - pub struct ConstraintIndex { - DEBUG_FORMAT = "ConstraintIndex({})" + pub struct OutlivesConstraintIndex { + DEBUG_FORMAT = "OutlivesConstraintIndex({})" } } From ec560e2c6d0e7ab17763208a8313d919602d6dd4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 4 Jun 2019 18:05:23 -0400 Subject: [PATCH 14/68] remove deref impl and add an index impl The constraint set is going to be more than just a set of outlives constraints. --- .../borrow_check/nll/constraints/mod.rs | 17 ++++++++++------- .../borrow_check/nll/region_infer/dump_mir.rs | 2 +- .../borrow_check/nll/region_infer/graphviz.rs | 2 +- .../borrow_check/nll/region_infer/mod.rs | 2 +- .../borrow_check/nll/type_check/mod.rs | 4 ++-- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/constraints/mod.rs b/src/librustc_mir/borrow_check/nll/constraints/mod.rs index a4c66d5ef6e0f..727e573f1a1f0 100644 --- a/src/librustc_mir/borrow_check/nll/constraints/mod.rs +++ b/src/librustc_mir/borrow_check/nll/constraints/mod.rs @@ -1,11 +1,10 @@ +use crate::borrow_check::nll::type_check::Locations; use rustc::mir::ConstraintCategory; use rustc::ty::RegionVid; use rustc_data_structures::graph::scc::Sccs; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; -use crate::borrow_check::nll::type_check::Locations; - use std::fmt; -use std::ops::Deref; +use std::ops::Index; crate mod graph; @@ -58,13 +57,17 @@ impl ConstraintSet { let region_graph = &constraint_graph.region_graph(self, static_region); Sccs::new(region_graph) } + + crate fn outlives(&self) -> &IndexVec { + &self.outlives + } } -impl Deref for ConstraintSet { - type Target = IndexVec; +impl Index for ConstraintSet { + type Output = OutlivesConstraint; - fn deref(&self) -> &Self::Target { - &self.outlives + fn index(&self, i: OutlivesConstraintIndex) -> &Self::Output { + &self.outlives[i] } } diff --git a/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs b/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs index 4931005a4547a..d4f6ce8801e63 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/dump_mir.rs @@ -71,7 +71,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } - let mut constraints: Vec<_> = self.constraints.iter().collect(); + let mut constraints: Vec<_> = self.constraints.outlives().iter().collect(); constraints.sort(); for constraint in &constraints { let OutlivesConstraint { diff --git a/src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs b/src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs index 0cf8a0d16f622..fdf2af9f44ebc 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/graphviz.rs @@ -63,7 +63,7 @@ impl<'a, 'this, 'tcx> dot::GraphWalk<'this> for RawConstraints<'a, 'tcx> { vids.into() } fn edges(&'this self) -> dot::Edges<'this, OutlivesConstraint> { - (&self.regioncx.constraints.raw[..]).into() + (&self.regioncx.constraints.outlives().raw[..]).into() } // Render `a: b` as `a -> b`, indicating the flow diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 4a539f4b449b7..5a56e8ba18683 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -472,7 +472,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!("propagate_constraints()"); debug!("propagate_constraints: constraints={:#?}", { - let mut constraints: Vec<_> = self.constraints.iter().collect(); + let mut constraints: Vec<_> = self.constraints.outlives().iter().collect(); constraints.sort(); constraints .into_iter() diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 758c8df1d186a..08553e6e81fca 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -215,7 +215,7 @@ fn translate_outlives_facts(cx: &mut BorrowCheckContext<'_, '_>) { let location_table = cx.location_table; facts .outlives - .extend(cx.constraints.outlives_constraints.iter().flat_map( + .extend(cx.constraints.outlives_constraints.outlives().iter().flat_map( |constraint: &OutlivesConstraint| { if let Some(from_location) = constraint.locations.from_location() { Either::Left(iter::once(( @@ -582,7 +582,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { ); let locations = location.to_locations(); - for constraint in constraints.iter() { + for constraint in constraints.outlives().iter() { let mut constraint = *constraint; constraint.locations = locations; if let ConstraintCategory::Return From f933e0971bbe85d8dbe938e46b73fc9b0375317f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 5 Jun 2019 05:52:58 -0400 Subject: [PATCH 15/68] pass a `&mut QueryRegionConstraints` not just outlives constraints --- src/librustc/infer/canonical/query_response.rs | 10 ++++++---- src/librustc/traits/query/type_op/mod.rs | 12 ++++++------ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/librustc/infer/canonical/query_response.rs b/src/librustc/infer/canonical/query_response.rs index b4c46e71b16d2..6ce644f844680 100644 --- a/src/librustc/infer/canonical/query_response.rs +++ b/src/librustc/infer/canonical/query_response.rs @@ -279,7 +279,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { param_env: ty::ParamEnv<'tcx>, original_values: &OriginalQueryValues<'tcx>, query_response: &Canonical<'tcx, QueryResponse<'tcx, R>>, - output_query_outlives_constraints: &mut Vec>, + output_query_region_constraints: &mut QueryRegionConstraints<'tcx>, ) -> InferResult<'tcx, R> where R: Debug + TypeFoldable<'tcx>, @@ -305,9 +305,11 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { (UnpackedKind::Lifetime(v_o), UnpackedKind::Lifetime(v_r)) => { // To make `v_o = v_r`, we emit `v_o: v_r` and `v_r: v_o`. if v_o != v_r { - output_query_outlives_constraints + output_query_region_constraints + .outlives .push(ty::Binder::dummy(ty::OutlivesPredicate(v_o.into(), v_r))); - output_query_outlives_constraints + output_query_region_constraints + .outlives .push(ty::Binder::dummy(ty::OutlivesPredicate(v_r.into(), v_o))); } } @@ -333,7 +335,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { } // ...also include the other query region constraints from the query. - output_query_outlives_constraints.extend( + output_query_region_constraints.outlives.extend( query_response.value.region_constraints.outlives.iter().filter_map(|r_c| { let r_c = substitute_value(self.tcx, &result_subst, r_c); diff --git a/src/librustc/traits/query/type_op/mod.rs b/src/librustc/traits/query/type_op/mod.rs index bf8cace3a1bb4..e2a5cd9670e0c 100644 --- a/src/librustc/traits/query/type_op/mod.rs +++ b/src/librustc/traits/query/type_op/mod.rs @@ -1,6 +1,6 @@ use crate::infer::canonical::{ Canonical, Canonicalized, CanonicalizedQueryResponse, OriginalQueryValues, - QueryRegionConstraints, QueryOutlivesConstraint, QueryResponse, + QueryRegionConstraints, QueryResponse, }; use crate::infer::{InferCtxt, InferOk}; use std::fmt; @@ -85,7 +85,7 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Sized + TypeFoldable<'tcx> + 'tcx { fn fully_perform_into( query_key: ParamEnvAnd<'tcx, Self>, infcx: &InferCtxt<'_, 'tcx>, - output_query_region_constraints: &mut Vec>, + output_query_region_constraints: &mut QueryRegionConstraints<'tcx>, ) -> Fallible { if let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &query_key) { return Ok(result); @@ -141,15 +141,15 @@ where self, infcx: &InferCtxt<'_, 'tcx>, ) -> Fallible<(Self::Output, Option>>)> { - let mut outlives = vec![]; - let r = Q::fully_perform_into(self, infcx, &mut outlives)?; + let mut region_constraints = QueryRegionConstraints::default(); + let r = Q::fully_perform_into(self, infcx, &mut region_constraints)?; // Promote the final query-region-constraints into a // (optional) ref-counted vector: - let opt_qrc = if outlives.is_empty() { + let opt_qrc = if region_constraints.is_empty() { None } else { - Some(Rc::new(QueryRegionConstraints { outlives })) + Some(Rc::new(region_constraints)) }; Ok((r, opt_qrc)) From ddc63ce19ffa87825b000e47294e9ec05e223126 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 5 Jun 2019 05:58:08 -0400 Subject: [PATCH 16/68] propagate the pick-constraints through queries --- src/librustc/infer/canonical/mod.rs | 8 +++++--- src/librustc/infer/canonical/query_response.rs | 14 +++++++++----- src/librustc/infer/region_constraints/mod.rs | 15 ++++++++++++++- src/librustc/ty/structural_impls.rs | 7 +++++++ .../nll/type_check/free_region_relations.rs | 2 +- .../borrow_check/nll/type_check/mod.rs | 5 +++-- 6 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/librustc/infer/canonical/mod.rs b/src/librustc/infer/canonical/mod.rs index f18eeca361028..4693cf7181e94 100644 --- a/src/librustc/infer/canonical/mod.rs +++ b/src/librustc/infer/canonical/mod.rs @@ -23,6 +23,7 @@ use crate::infer::{InferCtxt, RegionVariableOrigin, TypeVariableOrigin, TypeVariableOriginKind}; use crate::infer::{ConstVariableOrigin, ConstVariableOriginKind}; +use crate::infer::region_constraints::PickConstraint; use crate::mir::interpret::ConstValue; use rustc_data_structures::indexed_vec::IndexVec; use rustc_macros::HashStable; @@ -197,13 +198,14 @@ pub struct QueryResponse<'tcx, R> { #[derive(Clone, Debug, Default, HashStable)] pub struct QueryRegionConstraints<'tcx> { pub outlives: Vec>, + pub pick_constraints: Vec>, } impl QueryRegionConstraints<'_> { /// Represents an empty (trivially true) set of region /// constraints. pub fn is_empty(&self) -> bool { - self.outlives.is_empty() + self.outlives.is_empty() && self.pick_constraints.is_empty() } } @@ -555,14 +557,14 @@ BraceStructLiftImpl! { BraceStructTypeFoldableImpl! { impl<'tcx> TypeFoldable<'tcx> for QueryRegionConstraints<'tcx> { - outlives + outlives, pick_constraints } } BraceStructLiftImpl! { impl<'a, 'tcx> Lift<'tcx> for QueryRegionConstraints<'a> { type Lifted = QueryRegionConstraints<'tcx>; - outlives + outlives, pick_constraints } } diff --git a/src/librustc/infer/canonical/query_response.rs b/src/librustc/infer/canonical/query_response.rs index 6ce644f844680..02a5005be7604 100644 --- a/src/librustc/infer/canonical/query_response.rs +++ b/src/librustc/infer/canonical/query_response.rs @@ -340,7 +340,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { let r_c = substitute_value(self.tcx, &result_subst, r_c); // Screen out `'a: 'a` cases -- we skip the binder here but - // only care the inner values to one another, so they are still at + // only compare the inner values to one another, so they are still at // consistent binding levels. let &ty::OutlivesPredicate(k1, r2) = r_c.skip_binder(); if k1 != r2.into() { @@ -351,6 +351,13 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { }) ); + // ...also include the query pick constraints. + output_query_region_constraints.pick_constraints.extend( + query_response.value.region_constraints.pick_constraints.iter().map(|p_c| { + substitute_value(self.tcx, &result_subst, p_c) + }) + ); + let user_result: R = query_response.substitute_projected(self.tcx, &result_subst, |q_r| &q_r.value); @@ -662,9 +669,6 @@ pub fn make_query_region_constraints<'tcx>( assert!(verifys.is_empty()); assert!(givens.is_empty()); - // FIXME(ndm) -- we have to think about what to do here, perhaps - assert!(pick_constraints.is_empty()); - let outlives: Vec<_> = constraints .into_iter() .map(|(k, _)| match *k { @@ -690,5 +694,5 @@ pub fn make_query_region_constraints<'tcx>( ) .collect(); - QueryRegionConstraints { outlives } + QueryRegionConstraints { outlives, pick_constraints: pick_constraints.clone() } } diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index 1340fb807ee2a..a56c71b2b1f78 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -150,7 +150,7 @@ impl Constraint<'_> { /// ``` /// pick R0 from [O1..On] /// ``` -#[derive(Debug, Clone)] +#[derive(Debug, Clone, HashStable)] pub struct PickConstraint<'tcx> { /// the def-id of the opaque type causing this constraint: used for error reporting pub opaque_type_def_id: DefId, @@ -165,6 +165,19 @@ pub struct PickConstraint<'tcx> { pub option_regions: Rc>>, } +BraceStructTypeFoldableImpl! { + impl<'tcx> TypeFoldable<'tcx> for PickConstraint<'tcx> { + opaque_type_def_id, hidden_ty, pick_region, option_regions + } +} + +BraceStructLiftImpl! { + impl<'a, 'tcx> Lift<'tcx> for PickConstraint<'a> { + type Lifted = PickConstraint<'tcx>; + opaque_type_def_id, hidden_ty, pick_region, option_regions + } +} + /// `VerifyGenericBound(T, _, R, RS)`: the parameter type `T` (or /// associated type) must outlive the region `R`. `T` is known to /// outlive `RS`. Therefore, verify that `R <= RS[i]` for some diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 27cd745c20fcb..74970ebe08129 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -378,6 +378,13 @@ impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Box { } } +impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Rc { + type Lifted = Rc; + fn lift_to_tcx<'a, 'gcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option { + tcx.lift(&**self).map(Rc::new) + } +} + impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for [T] { type Lifted = Vec; fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { diff --git a/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs b/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs index 2a21da064fa13..e92a10f6d48ef 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs @@ -288,7 +288,7 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> { } for data in constraint_sets { - let QueryRegionConstraints { outlives } = &*data; + let QueryRegionConstraints { outlives, pick_constraints: _ } = &*data; // TODO constraint_conversion::ConstraintConversion::new( self.infcx, &self.universal_regions, diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 08553e6e81fca..def4132295d51 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -1100,7 +1100,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { locations, data ); - let QueryRegionConstraints { outlives } = data; + let QueryRegionConstraints { outlives, pick_constraints: _ } = data; // TODO constraint_conversion::ConstraintConversion::new( self.infcx, @@ -2511,7 +2511,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ) -> ty::InstantiatedPredicates<'tcx> { if let Some(closure_region_requirements) = tcx.mir_borrowck(def_id).closure_requirements { let closure_constraints = QueryRegionConstraints { - outlives: closure_region_requirements.apply_requirements(tcx, def_id, substs) + outlives: closure_region_requirements.apply_requirements(tcx, def_id, substs), + pick_constraints: vec![], // TODO }; let bounds_mapping = closure_constraints From 6ead1c869944b3d1cafd0ea73aa26939e95be117 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 5 Jun 2019 10:17:30 -0400 Subject: [PATCH 17/68] rename `ConstraintSet` to `OutlivesConstraintSet` --- .../borrow_check/nll/constraints/graph.rs | 14 +++++++------- .../borrow_check/nll/constraints/mod.rs | 10 +++++----- .../borrow_check/nll/region_infer/mod.rs | 6 +++--- .../borrow_check/nll/type_check/liveness/mod.rs | 4 ++-- .../borrow_check/nll/type_check/mod.rs | 6 +++--- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/constraints/graph.rs b/src/librustc_mir/borrow_check/nll/constraints/graph.rs index 78d49309d0214..1d9e6064c416b 100644 --- a/src/librustc_mir/borrow_check/nll/constraints/graph.rs +++ b/src/librustc_mir/borrow_check/nll/constraints/graph.rs @@ -1,6 +1,6 @@ use crate::borrow_check::nll::type_check::Locations; use crate::borrow_check::nll::constraints::OutlivesConstraintIndex; -use crate::borrow_check::nll::constraints::{ConstraintSet, OutlivesConstraint}; +use crate::borrow_check::nll::constraints::{OutlivesConstraintSet, OutlivesConstraint}; use rustc::mir::ConstraintCategory; use rustc::ty::RegionVid; use rustc_data_structures::graph; @@ -77,7 +77,7 @@ impl ConstraintGraph { /// reporting. crate fn new( direction: D, - set: &ConstraintSet, + set: &OutlivesConstraintSet, num_region_vars: usize, ) -> Self { let mut first_constraints = IndexVec::from_elem_n(None, num_region_vars); @@ -103,7 +103,7 @@ impl ConstraintGraph { /// and not constraints. crate fn region_graph<'rg>( &'rg self, - set: &'rg ConstraintSet, + set: &'rg OutlivesConstraintSet, static_region: RegionVid, ) -> RegionGraph<'rg, D> { RegionGraph::new(set, self, static_region) @@ -113,7 +113,7 @@ impl ConstraintGraph { crate fn outgoing_edges<'a>( &'a self, region_sup: RegionVid, - constraints: &'a ConstraintSet, + constraints: &'a OutlivesConstraintSet, static_region: RegionVid, ) -> Edges<'a, D> { //if this is the `'static` region and the graph's direction is normal, @@ -142,7 +142,7 @@ impl ConstraintGraph { crate struct Edges<'s, D: ConstraintGraphDirecton> { graph: &'s ConstraintGraph, - constraints: &'s ConstraintSet, + constraints: &'s OutlivesConstraintSet, pointer: Option, next_static_idx: Option, static_region: RegionVid, @@ -180,7 +180,7 @@ impl<'s, D: ConstraintGraphDirecton> Iterator for Edges<'s, D> { /// reverse) constraint graph. It implements the graph traits and is /// usd for doing the SCC computation. crate struct RegionGraph<'s, D: ConstraintGraphDirecton> { - set: &'s ConstraintSet, + set: &'s OutlivesConstraintSet, constraint_graph: &'s ConstraintGraph, static_region: RegionVid, } @@ -191,7 +191,7 @@ impl<'s, D: ConstraintGraphDirecton> RegionGraph<'s, D> { /// construct SCCs for region inference but also for error /// reporting. crate fn new( - set: &'s ConstraintSet, + set: &'s OutlivesConstraintSet, constraint_graph: &'s ConstraintGraph, static_region: RegionVid, ) -> Self { diff --git a/src/librustc_mir/borrow_check/nll/constraints/mod.rs b/src/librustc_mir/borrow_check/nll/constraints/mod.rs index 727e573f1a1f0..6121ed0cf0d1c 100644 --- a/src/librustc_mir/borrow_check/nll/constraints/mod.rs +++ b/src/librustc_mir/borrow_check/nll/constraints/mod.rs @@ -13,14 +13,14 @@ crate mod graph; /// a unique `OutlivesConstraintIndex` and you can index into the set /// (`constraint_set[i]`) to access the constraint details. #[derive(Clone, Default)] -crate struct ConstraintSet { +crate struct OutlivesConstraintSet { outlives: IndexVec, } -impl ConstraintSet { +impl OutlivesConstraintSet { crate fn push(&mut self, constraint: OutlivesConstraint) { debug!( - "ConstraintSet::push({:?}: {:?} @ {:?}", + "OutlivesConstraintSet::push({:?}: {:?} @ {:?}", constraint.sup, constraint.sub, constraint.locations ); if constraint.sup == constraint.sub { @@ -34,7 +34,7 @@ impl ConstraintSet { /// easy to find the constraints affecting a particular region. /// /// N.B., this graph contains a "frozen" view of the current - /// constraints. Any new constraints added to the `ConstraintSet` + /// constraints. Any new constraints added to the `OutlivesConstraintSet` /// after the graph is built will not be present in the graph. crate fn graph(&self, num_region_vars: usize) -> graph::NormalConstraintGraph { graph::ConstraintGraph::new(graph::Normal, self, num_region_vars) @@ -63,7 +63,7 @@ impl ConstraintSet { } } -impl Index for ConstraintSet { +impl Index for OutlivesConstraintSet { type Output = OutlivesConstraint; fn index(&self, i: OutlivesConstraintIndex) -> &Self::Output { diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 5a56e8ba18683..4a44c41eb819e 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -1,6 +1,6 @@ use super::universal_regions::UniversalRegions; use crate::borrow_check::nll::constraints::graph::NormalConstraintGraph; -use crate::borrow_check::nll::constraints::{ConstraintSccIndex, ConstraintSet, OutlivesConstraint}; +use crate::borrow_check::nll::constraints::{ConstraintSccIndex, OutlivesConstraintSet, OutlivesConstraint}; use crate::borrow_check::nll::region_infer::values::{ PlaceholderIndices, RegionElement, ToElementIndex }; @@ -49,7 +49,7 @@ pub struct RegionInferenceContext<'tcx> { liveness_constraints: LivenessValues, /// The outlives constraints computed by the type-check. - constraints: Rc, + constraints: Rc, /// The constraint-set, but in graph form, making it easy to traverse /// the constraints adjacent to a particular region. Used to construct @@ -186,7 +186,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { placeholder_indices: Rc, universal_region_relations: Rc>, _body: &Body<'tcx>, - outlives_constraints: ConstraintSet, + outlives_constraints: OutlivesConstraintSet, closure_bounds_mapping: FxHashMap< Location, FxHashMap<(RegionVid, RegionVid), (ConstraintCategory, Span)>, diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs index 3b138bc126257..4af78fa5e0f42 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs @@ -1,5 +1,5 @@ use crate::borrow_check::location::LocationTable; -use crate::borrow_check::nll::constraints::ConstraintSet; +use crate::borrow_check::nll::constraints::OutlivesConstraintSet; use crate::borrow_check::nll::facts::{AllFacts, AllFactsExt}; use crate::borrow_check::nll::region_infer::values::RegionValueElements; use crate::borrow_check::nll::universal_regions::UniversalRegions; @@ -107,7 +107,7 @@ fn compute_live_locals( fn regions_that_outlive_free_regions( num_region_vars: usize, universal_regions: &UniversalRegions<'tcx>, - constraint_set: &ConstraintSet, + constraint_set: &OutlivesConstraintSet, ) -> FxHashSet { // Build a graph of the outlives constraints thus far. This is // a reverse graph, so for each constraint `R1: R2` we have an diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index def4132295d51..221eb871dbf85 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -4,7 +4,7 @@ use crate::borrow_check::borrow_set::BorrowSet; use crate::borrow_check::location::LocationTable; -use crate::borrow_check::nll::constraints::{ConstraintSet, OutlivesConstraint}; +use crate::borrow_check::nll::constraints::{OutlivesConstraintSet, OutlivesConstraint}; use crate::borrow_check::nll::facts::AllFacts; use crate::borrow_check::nll::region_infer::values::LivenessValues; use crate::borrow_check::nll::region_infer::values::PlaceholderIndex; @@ -127,7 +127,7 @@ pub(crate) fn type_check<'tcx>( placeholder_indices: PlaceholderIndices::default(), placeholder_index_to_region: IndexVec::default(), liveness_constraints: LivenessValues::new(elements.clone()), - outlives_constraints: ConstraintSet::default(), + outlives_constraints: OutlivesConstraintSet::default(), closure_bounds_mapping: Default::default(), type_tests: Vec::default(), }; @@ -884,7 +884,7 @@ crate struct MirTypeckRegionConstraints<'tcx> { /// hence it must report on their liveness constraints. crate liveness_constraints: LivenessValues, - crate outlives_constraints: ConstraintSet, + crate outlives_constraints: OutlivesConstraintSet, crate closure_bounds_mapping: FxHashMap>, From 3aad20d8f87c8678ba0b2c37248c7153b8f7d73b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 24 Jun 2019 16:06:17 -0400 Subject: [PATCH 18/68] [WIP] fix `Lift` impl for `Rc` --- src/librustc/ty/structural_impls.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 74970ebe08129..0028d125cec78 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -380,7 +380,7 @@ impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Box { impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Rc { type Lifted = Rc; - fn lift_to_tcx<'a, 'gcx>(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option { + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { tcx.lift(&**self).map(Rc::new) } } From 330cb7668cd944e0cd4c9ba0c810bb2f0f862b50 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 5 Jun 2019 10:21:31 -0400 Subject: [PATCH 19/68] pass more than outlives constraints to constraint conversion --- .../borrow_check/nll/type_check/constraint_conversion.rs | 5 +++-- .../borrow_check/nll/type_check/free_region_relations.rs | 5 ++--- src/librustc_mir/borrow_check/nll/type_check/mod.rs | 4 +--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs index 8c7562739a762..0e7f678382383 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs @@ -3,6 +3,7 @@ use crate::borrow_check::nll::region_infer::TypeTest; use crate::borrow_check::nll::type_check::{Locations, MirTypeckRegionConstraints}; use crate::borrow_check::nll::universal_regions::UniversalRegions; use crate::borrow_check::nll::ToRegionVid; +use rustc::infer::canonical::QueryRegionConstraints; use rustc::infer::canonical::QueryOutlivesConstraint; use rustc::infer::outlives::env::RegionBoundPairs; use rustc::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate}; @@ -49,8 +50,8 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { } } - pub(super) fn convert_all(&mut self, query_constraints: &[QueryOutlivesConstraint<'tcx>]) { - for query_constraint in query_constraints { + pub(super) fn convert_all(&mut self, query_constraints: &QueryRegionConstraints<'tcx>) { + for query_constraint in &query_constraints.outlives { self.convert(query_constraint); } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs b/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs index e92a10f6d48ef..d18a8e87453a5 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/free_region_relations.rs @@ -287,8 +287,7 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> { self.relations.relate_universal_regions(fr, fr_fn_body); } - for data in constraint_sets { - let QueryRegionConstraints { outlives, pick_constraints: _ } = &*data; // TODO + for data in &constraint_sets { constraint_conversion::ConstraintConversion::new( self.infcx, &self.universal_regions, @@ -298,7 +297,7 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> { Locations::All(DUMMY_SP), ConstraintCategory::Internal, &mut self.constraints, - ).convert_all(outlives); + ).convert_all(data); } CreateResult { diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 221eb871dbf85..4b9c61068bdcb 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -1100,8 +1100,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { locations, data ); - let QueryRegionConstraints { outlives, pick_constraints: _ } = data; // TODO - constraint_conversion::ConstraintConversion::new( self.infcx, self.borrowck_context.universal_regions, @@ -1111,7 +1109,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { locations, category, &mut self.borrowck_context.constraints, - ).convert_all(outlives); + ).convert_all(data); } /// Convenient wrapper around `relate_tys::relate_types` -- see From d9596692a54205059a29677490fce7fd32311df6 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 5 Jun 2019 19:07:15 -0400 Subject: [PATCH 20/68] implement PickConstraintSet type --- src/librustc_mir/borrow_check/nll/mod.rs | 1 + .../borrow_check/nll/pick_constraints.rs | 210 ++++++++++++++++++ 2 files changed, 211 insertions(+) create mode 100644 src/librustc_mir/borrow_check/nll/pick_constraints.rs diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 5dd7b7452733c..364d12aa395fb 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -37,6 +37,7 @@ crate mod type_check; mod universal_regions; mod constraints; +mod pick_constraints; use self::facts::AllFacts; use self::region_infer::RegionInferenceContext; diff --git a/src/librustc_mir/borrow_check/nll/pick_constraints.rs b/src/librustc_mir/borrow_check/nll/pick_constraints.rs new file mode 100644 index 0000000000000..6bbd2e8052783 --- /dev/null +++ b/src/librustc_mir/borrow_check/nll/pick_constraints.rs @@ -0,0 +1,210 @@ +#![allow(dead_code)] // TODO + +use crate::rustc::ty::{self, Ty}; +use rustc::hir::def_id::DefId; +use rustc::infer::region_constraints::PickConstraint; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use std::hash::Hash; +use std::ops::Index; + +/// Compactly stores a set of `pick R0 in [R1...Rn]` constraints, +/// indexed by the region R0. +crate struct PickConstraintSet<'tcx, R> +where + R: Copy + Hash + Eq, +{ + /// Stores the first "pick" constraint for a given R0. This is an + /// index into the `constraints` vector below. + first_constraints: FxHashMap, + + /// Stores the data about each `pick R0 from [R1..Rn]` constraint. + /// These are organized into a linked list, so each constraint + /// contains the index of the next constraint with the same R0. + constraints: IndexVec>, + + /// Stores the `R1..Rn` regions for *all* sets. For any given + /// constraint, we keep two indices so that we can pull out a + /// slice. + option_regions: Vec, +} + +/// Represents a `pick R0 in [R1..Rn]` constraint +crate struct NllPickConstraint<'tcx> { + next_constraint: Option, + + /// The opaque type whose hidden type is being inferred. (Used in error reporting.) + crate opaque_type_def_id: DefId, + + /// The hidden type in which R0 appears. (Used in error reporting.) + crate hidden_ty: Ty<'tcx>, + + /// The region R0. + crate pick_region_vid: ty::RegionVid, + + /// Index of `R1` in `option_regions` vector from `PickConstraintSet`. + start_index: usize, + + /// Index of `Rn` in `option_regions` vector from `PickConstraintSet`. + end_index: usize, +} + +newtype_index! { + crate struct NllPickConstraintIndex { + DEBUG_FORMAT = "PickConstraintIndex({})" + } +} + +impl<'tcx> PickConstraintSet<'tcx, ty::RegionVid> { + crate fn new() -> Self { + Self { + first_constraints: Default::default(), + constraints: Default::default(), + option_regions: Default::default(), + } + } + + crate fn push_constraint( + &mut self, + p_c: PickConstraint<'tcx>, + mut to_region_vid: impl FnMut(ty::Region<'tcx>) -> ty::RegionVid, + ) { + let pick_region_vid: ty::RegionVid = to_region_vid(p_c.pick_region); + let next_constraint = self.first_constraints.get(&pick_region_vid).cloned(); + let start_index = self.option_regions.len(); + let end_index = start_index + p_c.option_regions.len(); + let constraint_index = self.constraints.push(NllPickConstraint { + next_constraint, + pick_region_vid, + opaque_type_def_id: p_c.opaque_type_def_id, + hidden_ty: p_c.hidden_ty, + start_index, + end_index, + }); + self.first_constraints.insert(pick_region_vid, constraint_index); + self.option_regions.extend(p_c.option_regions.iter().map(|&r| to_region_vid(r))); + } +} + +impl<'tcx, R1> PickConstraintSet<'tcx, R1> +where + R1: Copy + Hash + Eq, +{ + /// Remap the "pick region" key using `map_fn`, producing a new + /// pick-constraint set. This is used in the NLL code to map from + /// the original `RegionVid` to an scc index. In some cases, we + /// may have multiple R1 values mapping to the same R2 key -- that + /// is ok, the two sets will be merged. + crate fn into_mapped(self, mut map_fn: impl FnMut(R1) -> R2) -> PickConstraintSet<'tcx, R2> + where + R2: Copy + Hash + Eq, + { + // We can re-use most of the original data, just tweaking the + // linked list links a bit. + // + // For example if we had two keys Ra and Rb that both now wind + // up mapped to the same key S, we would append the linked + // list for Ra onto the end of the linked list for Rb (or vice + // versa) -- this basically just requires rewriting the final + // link from one list to point at the othe other (see + // `append_list`). + + let PickConstraintSet { first_constraints, mut constraints, option_regions } = self; + + let mut first_constraints2 = FxHashMap::default(); + first_constraints2.reserve(first_constraints.len()); + + for (r1, start1) in first_constraints { + let r2 = map_fn(r1); + if let Some(&start2) = first_constraints2.get(&r2) { + append_list(&mut constraints, start1, start2); + } + first_constraints2.insert(r2, start1); + } + + PickConstraintSet { + first_constraints: first_constraints2, + constraints, + option_regions, + } + } +} + +impl<'tcx, R> PickConstraintSet<'tcx, R> +where + R: Copy + Hash + Eq, +{ + /// Iterate down the constraint indices associated with a given + /// peek-region. You can then use `option_regions` and other + /// methods to access data. + crate fn indices( + &self, + pick_region_vid: R, + ) -> impl Iterator + '_ { + let mut next = self.first_constraints.get(&pick_region_vid).cloned(); + std::iter::from_fn(move || -> Option { + if let Some(current) = next { + next = self.constraints[current].next_constraint; + Some(current) + } else { + None + } + }) + } + + /// Returns the "option regions" for a given pick constraint. This is the R1..Rn from + /// a constraint like: + /// + /// ``` + /// pick R0 in [R1..Rn] + /// ``` + crate fn option_regions(&self, pci: NllPickConstraintIndex) -> &[ty::RegionVid] { + let NllPickConstraint { start_index, end_index, .. } = &self.constraints[pci]; + &self.option_regions[*start_index..*end_index] + } +} + +impl<'tcx, R> Index for PickConstraintSet<'tcx, R> +where + R: Copy + Hash + Eq, +{ + type Output = NllPickConstraint<'tcx>; + + fn index(&self, i: NllPickConstraintIndex) -> &NllPickConstraint<'tcx> { + &self.constraints[i] + } +} + +/// Given a linked list starting at `source_list` and another linked +/// list starting at `target_list`, modify `target_list` so that it is +/// followed by `source_list`. +/// +/// Before: +/// +/// ``` +/// target_list: A -> B -> C -> (None) +/// source_list: D -> E -> F -> (None) +/// ``` +/// +/// After: +/// +/// ``` +/// target_list: A -> B -> C -> D -> E -> F -> (None) +/// ``` +fn append_list( + constraints: &mut IndexVec>, + target_list: NllPickConstraintIndex, + source_list: NllPickConstraintIndex, +) { + let mut p = target_list; + loop { + let mut r = &mut constraints[p]; + match r.next_constraint { + Some(q) => p = q, + None => { + r.next_constraint = Some(source_list); + return; + } + } + } +} From 3b5a7276d2ca4d3c5081db6b12114a93aeb5c822 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 5 Jun 2019 19:33:29 -0400 Subject: [PATCH 21/68] construct pick-constraints and give them to region inference --- src/librustc_mir/borrow_check/nll/mod.rs | 2 ++ .../borrow_check/nll/pick_constraints.rs | 8 +++++--- .../borrow_check/nll/region_infer/mod.rs | 6 ++++++ .../nll/type_check/constraint_conversion.rs | 17 ++++++++++++++++- .../borrow_check/nll/type_check/mod.rs | 4 ++++ 5 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 364d12aa395fb..380bfb52e3ba3 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -130,6 +130,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( placeholder_index_to_region: _, mut liveness_constraints, outlives_constraints, + pick_constraints, closure_bounds_mapping, type_tests, } = constraints; @@ -151,6 +152,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( universal_region_relations, body, outlives_constraints, + pick_constraints, closure_bounds_mapping, type_tests, liveness_constraints, diff --git a/src/librustc_mir/borrow_check/nll/pick_constraints.rs b/src/librustc_mir/borrow_check/nll/pick_constraints.rs index 6bbd2e8052783..b594a15b6d886 100644 --- a/src/librustc_mir/borrow_check/nll/pick_constraints.rs +++ b/src/librustc_mir/borrow_check/nll/pick_constraints.rs @@ -55,18 +55,20 @@ newtype_index! { } } -impl<'tcx> PickConstraintSet<'tcx, ty::RegionVid> { - crate fn new() -> Self { +impl Default for PickConstraintSet<'tcx, ty::RegionVid> { + fn default() -> Self { Self { first_constraints: Default::default(), constraints: Default::default(), option_regions: Default::default(), } } +} +impl<'tcx> PickConstraintSet<'tcx, ty::RegionVid> { crate fn push_constraint( &mut self, - p_c: PickConstraint<'tcx>, + p_c: &PickConstraint<'tcx>, mut to_region_vid: impl FnMut(ty::Region<'tcx>) -> ty::RegionVid, ) { let pick_region_vid: ty::RegionVid = to_region_vid(p_c.pick_region); diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 4a44c41eb819e..adee5bda425fe 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -1,6 +1,7 @@ use super::universal_regions::UniversalRegions; use crate::borrow_check::nll::constraints::graph::NormalConstraintGraph; use crate::borrow_check::nll::constraints::{ConstraintSccIndex, OutlivesConstraintSet, OutlivesConstraint}; +use crate::borrow_check::nll::pick_constraints::PickConstraintSet; use crate::borrow_check::nll::region_infer::values::{ PlaceholderIndices, RegionElement, ToElementIndex }; @@ -187,6 +188,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { universal_region_relations: Rc>, _body: &Body<'tcx>, outlives_constraints: OutlivesConstraintSet, + pick_constraints: PickConstraintSet<'tcx, RegionVid>, closure_bounds_mapping: FxHashMap< Location, FxHashMap<(RegionVid, RegionVid), (ConstraintCategory, Span)>, @@ -218,6 +220,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { let scc_representatives = Self::compute_scc_representatives(&constraint_sccs, &definitions); + let _pick_constraints_scc = pick_constraints.into_mapped( // TODO + |r| constraint_sccs.scc(r), + ); + let mut result = Self { definitions, liveness_constraints, diff --git a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs index 0e7f678382383..c3bd4f6edf4dc 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs @@ -51,7 +51,22 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { } pub(super) fn convert_all(&mut self, query_constraints: &QueryRegionConstraints<'tcx>) { - for query_constraint in &query_constraints.outlives { + let QueryRegionConstraints { outlives, pick_constraints } = query_constraints; + + // Annoying: to invoke `self.to_region_vid`, we need access to + // `self.constraints`, but we also want to be mutating + // `self.pick_constraints`. For now, just swap out the value + // we want and replace at the end. + let mut tmp = std::mem::replace(&mut self.constraints.pick_constraints, Default::default()); + for pick_constraint in pick_constraints { + tmp.push_constraint( + pick_constraint, + |r| self.to_region_vid(r), + ); + } + self.constraints.pick_constraints = tmp; + + for query_constraint in outlives { self.convert(query_constraint); } } diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 4b9c61068bdcb..ea7db3096446c 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -5,6 +5,7 @@ use crate::borrow_check::borrow_set::BorrowSet; use crate::borrow_check::location::LocationTable; use crate::borrow_check::nll::constraints::{OutlivesConstraintSet, OutlivesConstraint}; +use crate::borrow_check::nll::pick_constraints::PickConstraintSet; use crate::borrow_check::nll::facts::AllFacts; use crate::borrow_check::nll::region_infer::values::LivenessValues; use crate::borrow_check::nll::region_infer::values::PlaceholderIndex; @@ -128,6 +129,7 @@ pub(crate) fn type_check<'tcx>( placeholder_index_to_region: IndexVec::default(), liveness_constraints: LivenessValues::new(elements.clone()), outlives_constraints: OutlivesConstraintSet::default(), + pick_constraints: PickConstraintSet::default(), closure_bounds_mapping: Default::default(), type_tests: Vec::default(), }; @@ -886,6 +888,8 @@ crate struct MirTypeckRegionConstraints<'tcx> { crate outlives_constraints: OutlivesConstraintSet, + crate pick_constraints: PickConstraintSet<'tcx, RegionVid>, + crate closure_bounds_mapping: FxHashMap>, From ec48b4ebe24c24d9d43a3dbf57c28c249343d776 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 7 Jun 2019 15:57:09 -0400 Subject: [PATCH 22/68] preliminary integration of "pick constraints" into nll solver --- src/librustc/infer/error_reporting/mod.rs | 4 +- src/librustc/infer/opaque_types/mod.rs | 11 +- .../borrow_check/nll/pick_constraints.rs | 8 + .../borrow_check/nll/region_infer/mod.rs | 333 +++++++++++------- .../nll/type_check/constraint_conversion.rs | 2 + 5 files changed, 231 insertions(+), 127 deletions(-) diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index ead75dcd7980e..5a89fd89941d4 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -385,13 +385,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { option_regions: _, } => { let hidden_ty = self.resolve_vars_if_possible(&hidden_ty); - opaque_types::report_unexpected_hidden_region( + opaque_types::unexpected_hidden_region_diagnostic( self.tcx, Some(region_scope_tree), opaque_type_def_id, hidden_ty, pick_region, - ); + ).emit(); } } } diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs index 656b9271115d2..1bc5ce473f5ee 100644 --- a/src/librustc/infer/opaque_types/mod.rs +++ b/src/librustc/infer/opaque_types/mod.rs @@ -9,6 +9,7 @@ use crate::ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder, TypeVisitor}; use crate::ty::subst::{InternalSubsts, Kind, SubstsRef, UnpackedKind}; use crate::ty::{self, GenericParamDefKind, Ty, TyCtxt}; use crate::util::nodemap::DefIdMap; +use errors::DiagnosticBuilder; use rustc_data_structures::fx::FxHashMap; use std::rc::Rc; @@ -499,13 +500,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } -pub fn report_unexpected_hidden_region( +pub fn unexpected_hidden_region_diagnostic( tcx: TyCtxt<'tcx>, region_scope_tree: Option<®ion::ScopeTree>, opaque_type_def_id: DefId, hidden_ty: Ty<'tcx>, hidden_region: ty::Region<'tcx>, -) { +) -> DiagnosticBuilder<'tcx> { let span = tcx.def_span(opaque_type_def_id); let mut err = struct_span_err!( tcx.sess, @@ -573,7 +574,7 @@ pub fn report_unexpected_hidden_region( } } - err.emit(); + err } // Visitor that requires that (almost) all regions in the type visited outlive @@ -724,13 +725,13 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> { None => { if !self.map_missing_regions_to_empty && !self.tainted_by_errors { if let Some(hidden_ty) = self.hidden_ty.take() { - report_unexpected_hidden_region( + unexpected_hidden_region_diagnostic( self.tcx, None, self.opaque_type_def_id, hidden_ty, r, - ); + ).emit(); } } self.tcx.lifetimes.re_empty diff --git a/src/librustc_mir/borrow_check/nll/pick_constraints.rs b/src/librustc_mir/borrow_check/nll/pick_constraints.rs index b594a15b6d886..dff0801f2ebc9 100644 --- a/src/librustc_mir/borrow_check/nll/pick_constraints.rs +++ b/src/librustc_mir/borrow_check/nll/pick_constraints.rs @@ -71,10 +71,12 @@ impl<'tcx> PickConstraintSet<'tcx, ty::RegionVid> { p_c: &PickConstraint<'tcx>, mut to_region_vid: impl FnMut(ty::Region<'tcx>) -> ty::RegionVid, ) { + debug!("push_constraint(p_c={:?})", p_c); let pick_region_vid: ty::RegionVid = to_region_vid(p_c.pick_region); let next_constraint = self.first_constraints.get(&pick_region_vid).cloned(); let start_index = self.option_regions.len(); let end_index = start_index + p_c.option_regions.len(); + debug!("push_constraint: pick_region_vid={:?}", pick_region_vid); let constraint_index = self.constraints.push(NllPickConstraint { next_constraint, pick_region_vid, @@ -136,6 +138,12 @@ impl<'tcx, R> PickConstraintSet<'tcx, R> where R: Copy + Hash + Eq, { + crate fn all_indices( + &self, + ) -> impl Iterator { + self.constraints.indices() + } + /// Iterate down the constraint indices associated with a given /// peek-region. You can then use `option_regions` and other /// methods to access data. diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index adee5bda425fe..ad58e4a47eb31 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -1,20 +1,23 @@ use super::universal_regions::UniversalRegions; use crate::borrow_check::nll::constraints::graph::NormalConstraintGraph; -use crate::borrow_check::nll::constraints::{ConstraintSccIndex, OutlivesConstraintSet, OutlivesConstraint}; +use crate::borrow_check::nll::constraints::{ + ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet, +}; use crate::borrow_check::nll::pick_constraints::PickConstraintSet; use crate::borrow_check::nll::region_infer::values::{ - PlaceholderIndices, RegionElement, ToElementIndex + PlaceholderIndices, RegionElement, ToElementIndex, }; -use crate::borrow_check::Upvar; use crate::borrow_check::nll::type_check::free_region_relations::UniversalRegionRelations; use crate::borrow_check::nll::type_check::Locations; +use crate::borrow_check::Upvar; use rustc::hir::def_id::DefId; use rustc::infer::canonical::QueryOutlivesConstraint; +use rustc::infer::opaque_types; use rustc::infer::region_constraints::{GenericKind, VarInfos, VerifyBound}; use rustc::infer::{InferCtxt, NLLRegionVariableOrigin, RegionVariableOrigin}; use rustc::mir::{ - ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, - ConstraintCategory, Local, Location, Body, + Body, ClosureOutlivesRequirement, ClosureOutlivesSubject, ClosureRegionRequirements, + ConstraintCategory, Local, Location, }; use rustc::ty::{self, subst::SubstsRef, RegionVid, Ty, TyCtxt, TypeFoldable}; use rustc::util::common::{self, ErrorReported}; @@ -61,6 +64,9 @@ pub struct RegionInferenceContext<'tcx> { /// compute the values of each region. constraint_sccs: Rc>, + /// The "pick R0 from [R1..Rn]" constraints, indexed by SCC. + pick_constraints: Rc>, + /// Map closure bounds to a `Span` that should be used for error reporting. closure_bounds_mapping: FxHashMap>, @@ -188,7 +194,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { universal_region_relations: Rc>, _body: &Body<'tcx>, outlives_constraints: OutlivesConstraintSet, - pick_constraints: PickConstraintSet<'tcx, RegionVid>, + pick_constraints_in: PickConstraintSet<'tcx, RegionVid>, closure_bounds_mapping: FxHashMap< Location, FxHashMap<(RegionVid, RegionVid), (ConstraintCategory, Span)>, @@ -220,9 +226,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let scc_representatives = Self::compute_scc_representatives(&constraint_sccs, &definitions); - let _pick_constraints_scc = pick_constraints.into_mapped( // TODO - |r| constraint_sccs.scc(r), - ); + let pick_constraints = Rc::new(pick_constraints_in.into_mapped(|r| constraint_sccs.scc(r))); let mut result = Self { definitions, @@ -230,6 +234,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { constraints, constraint_graph, constraint_sccs, + pick_constraints, closure_bounds_mapping, scc_universes, scc_representatives, @@ -347,9 +352,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!( "init_free_and_bound_regions: placeholder {:?} is \ not compatible with universe {:?} of its SCC {:?}", - placeholder, - scc_universe, - scc, + placeholder, scc_universe, scc, ); self.add_incompatible_universe(scc); } @@ -434,11 +437,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { // to store those. Otherwise, we'll pass in `None` to the // functions below, which will trigger them to report errors // eagerly. - let mut outlives_requirements = if infcx.tcx.is_closure(mir_def_id) { - Some(vec![]) - } else { - None - }; + let mut outlives_requirements = + if infcx.tcx.is_closure(mir_def_id) { Some(vec![]) } else { None }; self.check_type_tests( infcx, @@ -457,16 +457,15 @@ impl<'tcx> RegionInferenceContext<'tcx> { errors_buffer, ); + self.check_pick_constraints(infcx, mir_def_id, errors_buffer); + let outlives_requirements = outlives_requirements.unwrap_or(vec![]); if outlives_requirements.is_empty() { None } else { let num_external_vids = self.universal_regions.num_global_and_external_regions(); - Some(ClosureRegionRequirements { - num_external_vids, - outlives_requirements, - }) + Some(ClosureRegionRequirements { num_external_vids, outlives_requirements }) } } @@ -496,6 +495,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } + /// Computes the value of the SCC `scc_a` if it has not already + /// been computed. The `visited` parameter is a bitset #[inline] fn propagate_constraint_sccs_if_new( &mut self, @@ -507,6 +508,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } + /// Computes the value of the SCC `scc_a`, which has not yet been + /// computed. This works by first computing all successors of the + /// SCC (if they haven't been computed already) and then unioning + /// together their elements. fn propagate_constraint_sccs_new( &mut self, scc_a: ConstraintSccIndex, @@ -516,10 +521,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Walk each SCC `B` such that `A: B`... for &scc_b in constraint_sccs.successors(scc_a) { - debug!( - "propagate_constraint_sccs: scc_a = {:?} scc_b = {:?}", - scc_a, scc_b - ); + debug!("propagate_constraint_sccs: scc_a = {:?} scc_b = {:?}", scc_a, scc_b); // ...compute the value of `B`... self.propagate_constraint_sccs_if_new(scc_b, visited); @@ -537,6 +539,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } + // Now take pick constraints into account + let pick_constraints = self.pick_constraints.clone(); + for p_c_i in pick_constraints.indices(scc_a) { + self.apply_pick_constraint(scc_a, pick_constraints.option_regions(p_c_i)); + } + debug!( "propagate_constraint_sccs: scc_a = {:?} has value {:?}", scc_a, @@ -544,6 +552,114 @@ impl<'tcx> RegionInferenceContext<'tcx> { ); } + /// Invoked for each `pick R0 from [R1..Rn]` constraint. + /// + /// `scc` is the SCC containing R0, and `option_regions` are the + /// `R1..Rn` regions -- they are always known to be universal + /// regions (and if that's not true, we just don't attempt to + /// enforce the constraint). + /// + /// The current value of `scc` at the time the method is invoked + /// is considered a *lower bound*. If possible, we will modify + /// the constraint to set it equal to one of the option regions. + /// If we make any changes, returns true, else false. + fn apply_pick_constraint( + &mut self, + scc: ConstraintSccIndex, + option_regions: &[ty::RegionVid], + ) -> bool { + debug!("apply_pick_constraint(scc={:?}, option_regions={:#?})", scc, option_regions,); + + if let Some(uh_oh) = + option_regions.iter().find(|&&r| !self.universal_regions.is_universal_region(r)) + { + debug!("apply_pick_constraint: option region `{:?}` is not a universal region", uh_oh); + return false; + } + + // Create a mutable vector of the options. We'll try to winnow + // them down. + let mut option_regions: Vec = option_regions.to_vec(); + + // The 'pick-region' in a pick-constraint is part of the + // hidden type, which must be in the root universe. Therefore, + // it cannot have any placeholders in its value. + assert!(self.scc_universes[scc] == ty::UniverseIndex::ROOT); + debug_assert!( + self.scc_values.placeholders_contained_in(scc).next().is_none(), + "scc {:?} in a pick-constraint has placeholder value: {:?}", + scc, + self.scc_values.region_value_str(scc), + ); + + // The existing value for `scc` is a lower-bound. This will + // consist of some set {P} + {LB} of points {P} and + // lower-bound free regions {LB}. As each option region O is a + // free region, it will outlive the points. But we can only + // consider the option O if O: LB. + option_regions.retain(|&o_r| { + self.scc_values + .universal_regions_outlived_by(scc) + .all(|lb| self.universal_region_relations.outlives(o_r, lb)) + }); + + // Now find all the *upper bounds* -- that is, each UB is a free + // region that must outlive pick region R0 (`UB: R0`). Therefore, + // we need only keep an option O if `UB: O`. + // + // TODO -- need to implement the reverse graph construction for this + // + // let mut upper_bounds = ...; + // option_regions.retain(|&o_r| { + // upper_bounds + // .all(|ub| self.universal_region_relations.outlives( + // ub, + // o_r, + // }) + // }); + + // If we ruled everything out, we're done. + if option_regions.is_empty() { + return false; + } + + // Otherwise, we need to find the minimum option, if any, and take that. + debug!("apply_pick_constraint: option_regions remaining are {:#?}", option_regions); + let min = |r1: ty::RegionVid, r2: ty::RegionVid| -> Option { + let r1_outlives_r2 = self.universal_region_relations.outlives(r1, r2); + let r2_outlives_r1 = self.universal_region_relations.outlives(r2, r1); + if r1_outlives_r2 && r2_outlives_r1 { + Some(r1.min(r2)) + } else if r1_outlives_r2 { + Some(r2) + } else if r2_outlives_r1 { + Some(r1) + } else { + None + } + }; + let mut best_option = option_regions[0]; + for &other_option in &option_regions[1..] { + debug!( + "apply_pick_constraint: best_option={:?} other_option={:?}", + best_option, other_option, + ); + match min(best_option, other_option) { + Some(m) => best_option = m, + None => { + debug!( + "apply_pick_constraint: {:?} and {:?} are incomparable --> no best choice", + best_option, other_option, + ); + return false; + } + } + } + + debug!("apply_pick_constraint: best_choice={:?}", best_option); + self.scc_values.add_element(scc, best_option) + } + /// Returns `true` if all the elements in the value of `scc_b` are nameable /// in `scc_a`. Used during constraint propagation, and only once /// the value of `scc_b` has been computed. @@ -560,9 +676,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Otherwise, we have to iterate over the universe elements in // B's value, and check whether all of them are nameable // from universe_a - self.scc_values - .placeholders_contained_in(scc_b) - .all(|p| universe_a.can_name(p.universe)) + self.scc_values.placeholders_contained_in(scc_b).all(|p| universe_a.can_name(p.universe)) } /// Extend `scc` so that it can outlive some placeholder region @@ -737,12 +851,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { ) -> bool { let tcx = infcx.tcx; - let TypeTest { - generic_kind, - lower_bound, - locations, - verify_bound: _, - } = type_test; + let TypeTest { generic_kind, lower_bound, locations, verify_bound: _ } = type_test; let generic_ty = generic_kind.to_ty(tcx); let subject = match self.try_promote_type_test_subject(infcx, generic_ty) { @@ -892,11 +1001,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// except that it converts further takes the non-local upper /// bound of `'y`, so that the final result is non-local. fn non_local_universal_upper_bound(&self, r: RegionVid) -> RegionVid { - debug!( - "non_local_universal_upper_bound(r={:?}={})", - r, - self.region_value_str(r) - ); + debug!("non_local_universal_upper_bound(r={:?}={})", r, self.region_value_str(r)); let lub = self.universal_upper_bound(r); @@ -904,10 +1009,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // creator. let non_local_lub = self.universal_region_relations.non_local_upper_bound(lub); - debug!( - "non_local_universal_upper_bound: non_local_lub={:?}", - non_local_lub - ); + debug!("non_local_universal_upper_bound: non_local_lub={:?}", non_local_lub); non_local_lub } @@ -927,11 +1029,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// - For each `end('x)` element in `'r`, compute the mutual LUB, yielding /// a result `'y`. fn universal_upper_bound(&self, r: RegionVid) -> RegionVid { - debug!( - "universal_upper_bound(r={:?}={})", - r, - self.region_value_str(r) - ); + debug!("universal_upper_bound(r={:?}={})", r, self.region_value_str(r)); // Find the smallest universal region that contains all other // universal regions within `region`. @@ -956,10 +1054,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { lower_bound: RegionVid, verify_bound: &VerifyBound<'tcx>, ) -> bool { - debug!( - "eval_verify_bound(lower_bound={:?}, verify_bound={:?})", - lower_bound, verify_bound - ); + debug!("eval_verify_bound(lower_bound={:?}, verify_bound={:?})", lower_bound, verify_bound); match verify_bound { VerifyBound::IfEq(test_ty, verify_bound1) => { @@ -968,7 +1063,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { VerifyBound::OutlivedBy(r) => { let r_vid = self.to_region_vid(r); - self.eval_outlives(body, r_vid, lower_bound) + self.eval_outlives(r_vid, lower_bound) } VerifyBound::AnyBound(verify_bounds) => verify_bounds.iter().any(|verify_bound| { @@ -1041,23 +1136,17 @@ impl<'tcx> RegionInferenceContext<'tcx> { }) } - // Evaluate whether `sup_region: sub_region @ point`. - fn eval_outlives( - &self, - _body: &Body<'tcx>, - sup_region: RegionVid, - sub_region: RegionVid, - ) -> bool { + // Evaluate whether `sup_region == sub_region`. + fn eval_equal(&self, r1: RegionVid, r2: RegionVid) -> bool { + self.eval_outlives(r1, r2) && self.eval_outlives(r2, r1) + } + + // Evaluate whether `sup_region: sub_region`. + fn eval_outlives(&self, sup_region: RegionVid, sub_region: RegionVid) -> bool { debug!("eval_outlives({:?}: {:?})", sup_region, sub_region); - debug!( - "eval_outlives: sup_region's value = {:?}", - self.region_value_str(sup_region), - ); - debug!( - "eval_outlives: sub_region's value = {:?}", - self.region_value_str(sub_region), - ); + debug!("eval_outlives: sup_region's value = {:?}", self.region_value_str(sup_region),); + debug!("eval_outlives: sub_region's value = {:?}", self.region_value_str(sub_region),); let sub_region_scc = self.constraint_sccs.scc(sub_region); let sup_region_scc = self.constraint_sccs.scc(sup_region); @@ -1068,9 +1157,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { // now). Therefore, the sup-region outlives the sub-region if, // for each universal region R1 in the sub-region, there // exists some region R2 in the sup-region that outlives R1. - let universal_outlives = self.scc_values - .universal_regions_outlived_by(sub_region_scc) - .all(|r1| { + let universal_outlives = + self.scc_values.universal_regions_outlived_by(sub_region_scc).all(|r1| { self.scc_values .universal_regions_outlived_by(sup_region_scc) .any(|r2| self.universal_region_relations.outlives(r2, r1)) @@ -1088,8 +1176,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { return true; } - self.scc_values - .contains_points(sup_region_scc, sub_region_scc) + self.scc_values.contains_points(sup_region_scc, sub_region_scc) } /// Once regions have been propagated, this method is used to see @@ -1171,12 +1258,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Because this free region must be in the ROOT universe, we // know it cannot contain any bound universes. assert!(self.scc_universes[longer_fr_scc] == ty::UniverseIndex::ROOT); - debug_assert!( - self.scc_values - .placeholders_contained_in(longer_fr_scc) - .next() - .is_none() - ); + debug_assert!(self.scc_values.placeholders_contained_in(longer_fr_scc).next().is_none()); // Only check all of the relations for the main representative of each // SCC, otherwise just check that we outlive said representative. This @@ -1230,9 +1312,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { errors_buffer: &mut Vec, ) -> Option { // If it is known that `fr: o`, carry on. - if self.universal_region_relations - .outlives(longer_fr, shorter_fr) - { + if self.universal_region_relations.outlives(longer_fr, shorter_fr) { return None; } @@ -1246,9 +1326,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // We'll call it `fr-` -- it's ever so slightly smaller than // `longer_fr`. - if let Some(fr_minus) = self - .universal_region_relations - .non_local_lower_bound(longer_fr) + if let Some(fr_minus) = self.universal_region_relations.non_local_lower_bound(longer_fr) { debug!("check_universal_region: fr_minus={:?}", fr_minus); @@ -1258,12 +1336,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Grow `shorter_fr` until we find some non-local regions. (We // always will.) We'll call them `shorter_fr+` -- they're ever // so slightly larger than `shorter_fr`. - let shorter_fr_plus = self.universal_region_relations - .non_local_upper_bounds(&shorter_fr); - debug!( - "check_universal_region: shorter_fr_plus={:?}", - shorter_fr_plus - ); + let shorter_fr_plus = + self.universal_region_relations.non_local_upper_bounds(&shorter_fr); + debug!("check_universal_region: shorter_fr_plus={:?}", shorter_fr_plus); for &&fr in &shorter_fr_plus { // Push the constraint `fr-: shorter_fr+` propagated_outlives_requirements.push(ClosureOutlivesRequirement { @@ -1295,28 +1370,20 @@ impl<'tcx> RegionInferenceContext<'tcx> { longer_fr: RegionVid, placeholder: ty::PlaceholderRegion, ) { - debug!( - "check_bound_universal_region(fr={:?}, placeholder={:?})", - longer_fr, placeholder, - ); + debug!("check_bound_universal_region(fr={:?}, placeholder={:?})", longer_fr, placeholder,); let longer_fr_scc = self.constraint_sccs.scc(longer_fr); - debug!( - "check_bound_universal_region: longer_fr_scc={:?}", - longer_fr_scc, - ); + debug!("check_bound_universal_region: longer_fr_scc={:?}", longer_fr_scc,); // If we have some bound universal region `'a`, then the only // elements it can contain is itself -- we don't know anything // else about it! let error_element = match { - self.scc_values - .elements_contained_in(longer_fr_scc) - .find(|element| match element { - RegionElement::Location(_) => true, - RegionElement::RootUniversalRegion(_) => true, - RegionElement::PlaceholderRegion(placeholder1) => placeholder != *placeholder1, - }) + self.scc_values.elements_contained_in(longer_fr_scc).find(|element| match element { + RegionElement::Location(_) => true, + RegionElement::RootUniversalRegion(_) => true, + RegionElement::PlaceholderRegion(placeholder1) => placeholder != *placeholder1, + }) } { Some(v) => v, None => return, @@ -1327,7 +1394,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { let error_region = match error_element { RegionElement::Location(l) => self.find_sub_region_live_at(longer_fr, l), RegionElement::RootUniversalRegion(r) => r, - RegionElement::PlaceholderRegion(error_placeholder) => self.definitions + RegionElement::PlaceholderRegion(error_placeholder) => self + .definitions .iter_enumerated() .filter_map(|(r, definition)| match definition.origin { NLLRegionVariableOrigin::Placeholder(p) if p == error_placeholder => Some(r), @@ -1345,12 +1413,44 @@ impl<'tcx> RegionInferenceContext<'tcx> { // the AST-based checker uses a more conservative check, // so to even see this error, one must pass in a special // flag. - let mut diag = infcx - .tcx - .sess - .struct_span_err(span, "higher-ranked subtype error"); + let mut diag = infcx.tcx.sess.struct_span_err(span, "higher-ranked subtype error"); diag.emit(); } + + fn check_pick_constraints( + &self, + infcx: &InferCtxt<'_, 'tcx>, + mir_def_id: DefId, + errors_buffer: &mut Vec, // TODO + ) { + let pick_constraints = self.pick_constraints.clone(); + for p_c_i in pick_constraints.all_indices() { + debug!("check_pick_constraint(p_c_i={:?})", p_c_i); + let p_c = &pick_constraints[p_c_i]; + let pick_region_vid = p_c.pick_region_vid; + debug!("check_pick_constraint: pick_region_vid={:?} with value {}", pick_region_vid, self.region_value_str(pick_region_vid)); + let option_regions = pick_constraints.option_regions(p_c_i); + debug!("check_pick_constraint: option_regions={:?}", option_regions); + + // did the pick-region wind up equal to any of the option regions? + if let Some(o) = option_regions.iter().find(|&&o_r| self.eval_equal(o_r, p_c.pick_region_vid)) { + debug!("check_pick_constraint: evaluated as equal to {:?}", o); + continue; + } + + // if not, report an error + let region_scope_tree = &infcx.tcx.region_scope_tree(mir_def_id); + let pick_region = infcx.tcx.mk_region(ty::ReVar(pick_region_vid)); // XXX + opaque_types::unexpected_hidden_region_diagnostic( + infcx.tcx, + Some(region_scope_tree), + p_c.opaque_type_def_id, + p_c.hidden_ty, + pick_region, + ) + .buffer(errors_buffer); + } + } } impl<'tcx> RegionDefinition<'tcx> { @@ -1364,11 +1464,7 @@ impl<'tcx> RegionDefinition<'tcx> { _ => NLLRegionVariableOrigin::Existential, }; - Self { - origin, - universe, - external_name: None, - } + Self { origin, universe, external_name: None } } } @@ -1471,10 +1567,7 @@ impl<'tcx> ClosureRegionRequirementsExt<'tcx> for ClosureRegionRequirements<'tcx if let ty::ReClosureBound(vid) = r { closure_mapping[*vid] } else { - bug!( - "subst_closure_mapping: encountered non-closure bound free region {:?}", - r - ) + bug!("subst_closure_mapping: encountered non-closure bound free region {:?}", r) } }) } diff --git a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs index c3bd4f6edf4dc..e7ae9de3c2e6c 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs @@ -51,6 +51,8 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { } pub(super) fn convert_all(&mut self, query_constraints: &QueryRegionConstraints<'tcx>) { + debug!("convert_all(query_constraints={:#?})", query_constraints); + let QueryRegionConstraints { outlives, pick_constraints } = query_constraints; // Annoying: to invoke `self.to_region_vid`, we need access to From cc581bfa0e448f23060a2a6987a19f59461e0534 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 10 Jun 2019 17:17:56 -0400 Subject: [PATCH 23/68] remove old error and add an explanation --- .../impl-trait/needs_least_region_or_bound.rs | 14 +++++- .../needs_least_region_or_bound.stderr | 48 ------------------- 2 files changed, 13 insertions(+), 49 deletions(-) delete mode 100644 src/test/ui/impl-trait/needs_least_region_or_bound.stderr diff --git a/src/test/ui/impl-trait/needs_least_region_or_bound.rs b/src/test/ui/impl-trait/needs_least_region_or_bound.rs index 2a5b365559e77..8475122cfae32 100644 --- a/src/test/ui/impl-trait/needs_least_region_or_bound.rs +++ b/src/test/ui/impl-trait/needs_least_region_or_bound.rs @@ -1,10 +1,22 @@ +// run-pass + use std::fmt::Debug; trait MultiRegionTrait<'a, 'b> {} impl<'a, 'b> MultiRegionTrait<'a, 'b> for (&'a u32, &'b u32) {} fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> { -//~^ ERROR ambiguous lifetime bound + // Here we have a constraint that: + // + // (x, y) has type (&'0 u32, &'1 u32) + // + // where + // + // 'a: '0 + // + // then we require that `('0 u32, &'1 u32): MultiRegionTrait<'a, + // 'b>`, which winds up imposing a requirement that `'0 = 'a` and + // `'1 = 'b`. (x, y) } diff --git a/src/test/ui/impl-trait/needs_least_region_or_bound.stderr b/src/test/ui/impl-trait/needs_least_region_or_bound.stderr deleted file mode 100644 index 9b9196a213da8..0000000000000 --- a/src/test/ui/impl-trait/needs_least_region_or_bound.stderr +++ /dev/null @@ -1,48 +0,0 @@ -error: impl Trait captures unexpected lifetime - --> $DIR/needs_least_region_or_bound.rs:6:55 - | -LL | fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only lifetimes that appear in the impl Trait bounds may be captured - | -note: hidden type captures the lifetime 'a as defined on the function body at 6:20 - --> $DIR/needs_least_region_or_bound.rs:6:20 - | -LL | fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> { - | ^^ -note: hidden type would be allowed to capture the lifetime 'a as defined on the function body at 6:20 - --> $DIR/needs_least_region_or_bound.rs:6:20 - | -LL | fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> { - | ^^ -note: hidden type would be allowed to capture the lifetime 'b as defined on the function body at 6:24 - --> $DIR/needs_least_region_or_bound.rs:6:24 - | -LL | fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> { - | ^^ - = note: hidden type would be allowed to capture the static lifetime - -error: impl Trait captures unexpected lifetime - --> $DIR/needs_least_region_or_bound.rs:6:55 - | -LL | fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ only lifetimes that appear in the impl Trait bounds may be captured - | -note: hidden type captures the lifetime 'b as defined on the function body at 6:24 - --> $DIR/needs_least_region_or_bound.rs:6:24 - | -LL | fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> { - | ^^ -note: hidden type would be allowed to capture the lifetime 'a as defined on the function body at 6:20 - --> $DIR/needs_least_region_or_bound.rs:6:20 - | -LL | fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> { - | ^^ -note: hidden type would be allowed to capture the lifetime 'b as defined on the function body at 6:24 - --> $DIR/needs_least_region_or_bound.rs:6:24 - | -LL | fn no_least_region<'a, 'b>(x: &'a u32, y: &'b u32) -> impl MultiRegionTrait<'a, 'b> { - | ^^ - = note: hidden type would be allowed to capture the static lifetime - -error: aborting due to 2 previous errors - From 07ee532031b4c9306e5db565c88bec3a0bc7308e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 10 Jun 2019 17:22:02 -0400 Subject: [PATCH 24/68] improve tests with migration mode, mir mode --- .../inverse-bounds.migrate.stderr | 19 +++++++ .../multiple-lifetimes/inverse-bounds.rs | 3 ++ .../multiple-lifetimes/inverse-bounds.stderr | 32 +++++------ ...dinary-bounds-pick-original.migrate.stderr | 22 ++++++++ .../ordinary-bounds-pick-original.rs | 5 +- .../ordinary-bounds-pick-original.stderr | 54 +++++-------------- .../ordinary-bounds-pick-other.migrate.stderr | 11 ++++ .../ordinary-bounds-pick-other.rs | 5 +- .../ordinary-bounds-pick-other.stderr | 28 +++------- .../ordinary-bounds-unrelated.rs | 2 - .../ordinary-bounds-unrelated.stderr | 6 +-- .../ordinary-bounds-unsuited.rs | 2 - .../ordinary-bounds-unsuited.stderr | 6 +-- 13 files changed, 101 insertions(+), 94 deletions(-) create mode 100644 src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.migrate.stderr create mode 100644 src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.migrate.stderr create mode 100644 src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.migrate.stderr diff --git a/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.migrate.stderr b/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.migrate.stderr new file mode 100644 index 0000000000000..a4eafbd3850e4 --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.migrate.stderr @@ -0,0 +1,19 @@ +warning[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds + --> $DIR/inverse-bounds.rs:18:70 + | +LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Invert<'a>, b: Invert<'b>) -> impl Trait<'d, 'e> + | ^^^^^^^^^^^^^^^^^^ + | + = note: hidden type `Invert<'_>` captures lifetime '_#8r + = warning: this error has been downgraded to a warning for backwards compatibility with previous releases + = warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future + = note: for more information, try `rustc --explain E0729` + +warning: the feature `pin` has been stable since 1.33.0 and no longer requires an attribute to enable + --> $DIR/inverse-bounds.rs:6:60 + | +LL | #![feature(arbitrary_self_types, async_await, await_macro, pin)] + | ^^^ + | + = note: #[warn(stable_features)] on by default + diff --git a/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs b/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs index 60275ac3ab0d2..ac240e8ccde30 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs @@ -1,4 +1,7 @@ // edition:2018 +// run-pass +// revisions: migrate mir +//[mir]compile-flags: -Z borrowck=mir #![feature(arbitrary_self_types, async_await, await_macro, pin)] diff --git a/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.stderr b/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.stderr index 13fb6f5d73e02..4de872e8441ef 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.stderr +++ b/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.stderr @@ -1,25 +1,19 @@ -error: impl Trait captures unexpected lifetime - --> $DIR/inverse-bounds.rs:15:70 +warning[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds + --> $DIR/inverse-bounds.rs:16:70 | LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Invert<'a>, b: Invert<'b>) -> impl Trait<'d, 'e> - | ^^^^^^^^^^^^^^^^^^ only lifetimes that appear in the impl Trait bounds may be captured + | ^^^^^^^^^^^^^^^^^^ | -note: hidden type captures the lifetime 'd as defined on the function body at 15:29 - --> $DIR/inverse-bounds.rs:15:29 - | -LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Invert<'a>, b: Invert<'b>) -> impl Trait<'d, 'e> - | ^^ -note: hidden type would be allowed to capture the lifetime 'd as defined on the function body at 15:29 - --> $DIR/inverse-bounds.rs:15:29 + = note: hidden type `Invert<'_>` captures lifetime '_#8r + = warning: this error has been downgraded to a warning for backwards compatibility with previous releases + = warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future + = note: for more information, try `rustc --explain E0729` + +warning: the feature `pin` has been stable since 1.33.0 and no longer requires an attribute to enable + --> $DIR/inverse-bounds.rs:4:60 | -LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Invert<'a>, b: Invert<'b>) -> impl Trait<'d, 'e> - | ^^ -note: hidden type would be allowed to capture the lifetime 'e as defined on the function body at 15:33 - --> $DIR/inverse-bounds.rs:15:33 +LL | #![feature(arbitrary_self_types, async_await, await_macro, pin)] + | ^^^ | -LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Invert<'a>, b: Invert<'b>) -> impl Trait<'d, 'e> - | ^^ - = note: hidden type would be allowed to capture the static lifetime - -error: aborting due to previous error + = note: #[warn(stable_features)] on by default diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.migrate.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.migrate.stderr new file mode 100644 index 0000000000000..3c6fe0b8de6b8 --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.migrate.stderr @@ -0,0 +1,22 @@ +warning[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds + --> $DIR/ordinary-bounds-pick-original.rs:12:50 + | +LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { + | ^^^^^^^^^^^^^^^^^^ + | + = note: hidden type `(&u8, &u8)` captures lifetime '_#6r + = warning: this error has been downgraded to a warning for backwards compatibility with previous releases + = warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future + = note: for more information, try `rustc --explain E0729` + +warning[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds + --> $DIR/ordinary-bounds-pick-original.rs:12:50 + | +LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { + | ^^^^^^^^^^^^^^^^^^ + | + = note: hidden type `(&u8, &u8)` captures lifetime '_#7r + = warning: this error has been downgraded to a warning for backwards compatibility with previous releases + = warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future + = note: for more information, try `rustc --explain E0729` + diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs index dde3e6d72c9ae..0d04980d37a8f 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs @@ -1,6 +1,7 @@ // edition:2018 - -#![feature(arbitrary_self_types, async_await, await_macro, pin)] +// run-pass +// revisions: migrate mir +//[mir]compile-flags: -Z borrowck=mir trait Trait<'a, 'b> { } impl Trait<'_, '_> for T { } diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.stderr index 43e92ac68272b..f55bba987ec3f 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.stderr +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.stderr @@ -1,48 +1,22 @@ -error: impl Trait captures unexpected lifetime - --> $DIR/ordinary-bounds-pick-original.rs:11:50 +warning[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds + --> $DIR/ordinary-bounds-pick-original.rs:10:50 | LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { - | ^^^^^^^^^^^^^^^^^^ only lifetimes that appear in the impl Trait bounds may be captured + | ^^^^^^^^^^^^^^^^^^ | -note: hidden type captures the lifetime 'a as defined on the function body at 11:17 - --> $DIR/ordinary-bounds-pick-original.rs:11:17 - | -LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { - | ^^ -note: hidden type would be allowed to capture the lifetime 'a as defined on the function body at 11:17 - --> $DIR/ordinary-bounds-pick-original.rs:11:17 - | -LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { - | ^^ -note: hidden type would be allowed to capture the lifetime 'b as defined on the function body at 11:21 - --> $DIR/ordinary-bounds-pick-original.rs:11:21 - | -LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { - | ^^ - = note: hidden type would be allowed to capture the static lifetime + = note: hidden type `(&u8, &u8)` captures lifetime '_#6r + = warning: this error has been downgraded to a warning for backwards compatibility with previous releases + = warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future + = note: for more information, try `rustc --explain E0729` -error: impl Trait captures unexpected lifetime - --> $DIR/ordinary-bounds-pick-original.rs:11:50 +warning[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds + --> $DIR/ordinary-bounds-pick-original.rs:10:50 | LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { - | ^^^^^^^^^^^^^^^^^^ only lifetimes that appear in the impl Trait bounds may be captured - | -note: hidden type captures the lifetime 'b as defined on the function body at 11:21 - --> $DIR/ordinary-bounds-pick-original.rs:11:21 + | ^^^^^^^^^^^^^^^^^^ | -LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { - | ^^ -note: hidden type would be allowed to capture the lifetime 'a as defined on the function body at 11:17 - --> $DIR/ordinary-bounds-pick-original.rs:11:17 - | -LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { - | ^^ -note: hidden type would be allowed to capture the lifetime 'b as defined on the function body at 11:21 - --> $DIR/ordinary-bounds-pick-original.rs:11:21 - | -LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { - | ^^ - = note: hidden type would be allowed to capture the static lifetime - -error: aborting due to 2 previous errors + = note: hidden type `(&u8, &u8)` captures lifetime '_#7r + = warning: this error has been downgraded to a warning for backwards compatibility with previous releases + = warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future + = note: for more information, try `rustc --explain E0729` diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.migrate.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.migrate.stderr new file mode 100644 index 0000000000000..f1c2fc8695acd --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.migrate.stderr @@ -0,0 +1,11 @@ +warning[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds + --> $DIR/ordinary-bounds-pick-other.rs:19:74 + | +LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> + | ^^^^^^^^^^^^^^^^^^ + | + = note: hidden type `Ordinary<'_>` captures lifetime '_#8r + = warning: this error has been downgraded to a warning for backwards compatibility with previous releases + = warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future + = note: for more information, try `rustc --explain E0729` + diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.rs index b53ef1dde42b2..6555bacba6716 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.rs @@ -1,6 +1,7 @@ // edition:2018 - -#![feature(arbitrary_self_types, async_await, await_macro, pin)] +// run-pass +// revisions: migrate mir +//[mir]compile-flags: -Z borrowck=mir trait Trait<'a, 'b> {} impl Trait<'_, '_> for T {} diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.stderr index ff09f9471079f..a9b56210b0fea 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.stderr +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.stderr @@ -1,25 +1,11 @@ -error: impl Trait captures unexpected lifetime - --> $DIR/ordinary-bounds-pick-other.rs:18:74 +warning[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds + --> $DIR/ordinary-bounds-pick-other.rs:17:74 | LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> - | ^^^^^^^^^^^^^^^^^^ only lifetimes that appear in the impl Trait bounds may be captured + | ^^^^^^^^^^^^^^^^^^ | -note: hidden type captures the lifetime 'e as defined on the function body at 18:33 - --> $DIR/ordinary-bounds-pick-other.rs:18:33 - | -LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> - | ^^ -note: hidden type would be allowed to capture the lifetime 'd as defined on the function body at 18:29 - --> $DIR/ordinary-bounds-pick-other.rs:18:29 - | -LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> - | ^^ -note: hidden type would be allowed to capture the lifetime 'e as defined on the function body at 18:33 - --> $DIR/ordinary-bounds-pick-other.rs:18:33 - | -LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> - | ^^ - = note: hidden type would be allowed to capture the static lifetime - -error: aborting due to previous error + = note: hidden type `Ordinary<'_>` captures lifetime '_#8r + = warning: this error has been downgraded to a warning for backwards compatibility with previous releases + = warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future + = note: for more information, try `rustc --explain E0729` diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.rs index c1322a391402e..3a97624647efd 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.rs @@ -1,7 +1,5 @@ // edition:2018 -#![feature(arbitrary_self_types, async_await, await_macro, pin)] - trait Trait<'a, 'b> {} impl Trait<'_, '_> for T {} diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr index cd2d46ac18218..c807048ce5468 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr @@ -1,11 +1,11 @@ error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds - --> $DIR/ordinary-bounds-unrelated.rs:18:74 + --> $DIR/ordinary-bounds-unrelated.rs:16:74 | LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> | ^^^^^^^^^^^^^^^^^^ | -note: hidden type `Ordinary<'_>` captures the scope of call-site for function at 23:1 - --> $DIR/ordinary-bounds-unrelated.rs:23:1 +note: hidden type `Ordinary<'_>` captures the scope of call-site for function at 21:1 + --> $DIR/ordinary-bounds-unrelated.rs:21:1 | LL | / { LL | | // Hidden type `Ordinary<'0>` with constraints: diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.rs index a28bbd5df7828..85c478d8d313a 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.rs @@ -1,7 +1,5 @@ // edition:2018 -#![feature(arbitrary_self_types, async_await, await_macro, pin)] - trait Trait<'a, 'b> {} impl Trait<'_, '_> for T {} diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr index 59ce93fa78b6b..6687c40f957a6 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr @@ -1,11 +1,11 @@ error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds - --> $DIR/ordinary-bounds-unsuited.rs:20:62 + --> $DIR/ordinary-bounds-unsuited.rs:18:62 | LL | fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> | ^^^^^^^^^^^^^^^^^^ | -note: hidden type `Ordinary<'_>` captures the scope of call-site for function at 22:1 - --> $DIR/ordinary-bounds-unsuited.rs:22:1 +note: hidden type `Ordinary<'_>` captures the scope of call-site for function at 20:1 + --> $DIR/ordinary-bounds-unsuited.rs:20:1 | LL | / { LL | | // We return a value: From 4e85665e08059a3cd96600b7972f7dcd1f62b871 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 11 Jun 2019 08:48:40 -0400 Subject: [PATCH 25/68] implement the graph traits for SCC --- src/librustc_data_structures/graph/scc/mod.rs | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/librustc_data_structures/graph/scc/mod.rs b/src/librustc_data_structures/graph/scc/mod.rs index 24c5448639e7d..7d29bd63707ad 100644 --- a/src/librustc_data_structures/graph/scc/mod.rs +++ b/src/librustc_data_structures/graph/scc/mod.rs @@ -4,7 +4,7 @@ //! O(n) time. use crate::fx::FxHashSet; -use crate::graph::{DirectedGraph, WithNumNodes, WithSuccessors}; +use crate::graph::{DirectedGraph, WithNumNodes, WithSuccessors, GraphSuccessors}; use crate::indexed_vec::{Idx, IndexVec}; use std::ops::Range; @@ -60,6 +60,31 @@ impl Sccs { } } +impl DirectedGraph for Sccs { + type Node = S; +} + +impl WithNumNodes for Sccs { + fn num_nodes(&self) -> usize { + self.num_sccs() + } +} + +impl GraphSuccessors<'graph> for Sccs { + type Item = S; + + type Iter = std::iter::Cloned>; +} + +impl WithSuccessors for Sccs { + fn successors<'graph>( + &'graph self, + node: S + ) -> >::Iter { + self.successors(node).iter().cloned() + } +} + impl SccData { /// Number of SCCs, fn len(&self) -> usize { From 4c91bb9571ffbc7ddad52cc98552f5b19b0d44d7 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 11 Jun 2019 13:40:24 -0400 Subject: [PATCH 26/68] introduce a `VecGraph` abstraction that cheaply stores graphs This is perhaps better than the linked list approach I was using before. Lower memory overhead, Theta(N+E) storage. Does require a sort. =) --- src/librustc_data_structures/graph/mod.rs | 5 + src/librustc_data_structures/graph/scc/mod.rs | 21 +++- .../graph/vec_graph/mod.rs | 113 ++++++++++++++++++ .../graph/vec_graph/test.rs | 46 +++++++ src/librustc_data_structures/indexed_vec.rs | 15 ++- 5 files changed, 197 insertions(+), 3 deletions(-) create mode 100644 src/librustc_data_structures/graph/vec_graph/mod.rs create mode 100644 src/librustc_data_structures/graph/vec_graph/test.rs diff --git a/src/librustc_data_structures/graph/mod.rs b/src/librustc_data_structures/graph/mod.rs index 3d47b7d49fb96..45e7e5db38f04 100644 --- a/src/librustc_data_structures/graph/mod.rs +++ b/src/librustc_data_structures/graph/mod.rs @@ -5,6 +5,7 @@ pub mod implementation; pub mod iterate; mod reference; pub mod scc; +pub mod vec_graph; #[cfg(test)] mod test; @@ -17,6 +18,10 @@ pub trait WithNumNodes: DirectedGraph { fn num_nodes(&self) -> usize; } +pub trait WithNumEdges: DirectedGraph { + fn num_edges(&self) -> usize; +} + pub trait WithSuccessors: DirectedGraph where Self: for<'graph> GraphSuccessors<'graph, Item = ::Node>, diff --git a/src/librustc_data_structures/graph/scc/mod.rs b/src/librustc_data_structures/graph/scc/mod.rs index 7d29bd63707ad..78554cda77b44 100644 --- a/src/librustc_data_structures/graph/scc/mod.rs +++ b/src/librustc_data_structures/graph/scc/mod.rs @@ -4,7 +4,8 @@ //! O(n) time. use crate::fx::FxHashSet; -use crate::graph::{DirectedGraph, WithNumNodes, WithSuccessors, GraphSuccessors}; +use crate::graph::{DirectedGraph, WithNumNodes, WithNumEdges, WithSuccessors, GraphSuccessors}; +use crate::graph::vec_graph::VecGraph; use crate::indexed_vec::{Idx, IndexVec}; use std::ops::Range; @@ -58,6 +59,18 @@ impl Sccs { pub fn successors(&self, scc: S) -> &[S] { self.scc_data.successors(scc) } + + /// Construct the reverse graph of the SCC graph. + pub fn reverse(&self) -> VecGraph { + VecGraph::new( + self.num_sccs(), + self.all_sccs() + .flat_map(|source| self.successors(source).iter().map(move |&target| { + (target, source) + })) + .collect(), + ) + } } impl DirectedGraph for Sccs { @@ -70,6 +83,12 @@ impl WithNumNodes for Sccs { } } +impl WithNumEdges for Sccs { + fn num_edges(&self) -> usize { + self.scc_data.all_successors.len() + } +} + impl GraphSuccessors<'graph> for Sccs { type Item = S; diff --git a/src/librustc_data_structures/graph/vec_graph/mod.rs b/src/librustc_data_structures/graph/vec_graph/mod.rs new file mode 100644 index 0000000000000..6b3349e3e1549 --- /dev/null +++ b/src/librustc_data_structures/graph/vec_graph/mod.rs @@ -0,0 +1,113 @@ +use crate::indexed_vec::{Idx, IndexVec}; +use crate::graph::{DirectedGraph, WithNumNodes, WithNumEdges, WithSuccessors, GraphSuccessors}; + +mod test; + +pub struct VecGraph { + /// Maps from a given node to an index where the set of successors + /// for that node starts. The index indexes into the `edges` + /// vector. To find the range for a given node, we look up the + /// start for that node and then the start for the next node + /// (i.e., with an index 1 higher) and get the range between the + /// two. This vector always has an extra entry so that this works + /// even for the max element. + node_starts: IndexVec, + + edge_targets: Vec, +} + +impl VecGraph { + pub fn new( + num_nodes: usize, + mut edge_pairs: Vec<(N, N)>, + ) -> Self { + // Sort the edges by the source -- this is important. + edge_pairs.sort(); + + let num_edges = edge_pairs.len(); + + // Store the *target* of each edge into `edge_targets` + let edge_targets: Vec = edge_pairs.iter().map(|&(_, target)| target).collect(); + + // Create the *edge starts* array. We are iterating over over + // the (sorted) edge pairs. We maintain the invariant that the + // length of the `node_starts` arary is enough to store the + // current source node -- so when we see that the source node + // for an edge is greater than the current length, we grow the + // edge-starts array by just enough. + let mut node_starts = IndexVec::with_capacity(num_edges); + for (index, &(source, _)) in edge_pairs.iter().enumerate() { + // If we have a list like `[(0, x), (2, y)]`: + // + // - Start out with `node_starts` of `[]` + // - Iterate to `(0, x)` at index 0: + // - Push one entry because `node_starts.len()` (0) is <= the source (0) + // - Leaving us with `node_starts` of `[0]` + // - Iterate to `(2, y)` at index 1: + // - Push one entry because `node_starts.len()` (1) is <= the source (2) + // - Push one entry because `node_starts.len()` (2) is <= the source (2) + // - Leaving us with `node_starts` of `[0, 1, 1]` + // - Loop terminates + while node_starts.len() <= source.index() { + node_starts.push(index); + } + } + + // Pad out the `node_starts` array so that it has `num_nodes + + // 1` entries. Continuing our example above, if `num_nodes` is + // be `3`, we would push one more index: `[0, 1, 1, 2]`. + // + // Interpretation of that vector: + // + // [0, 1, 1, 2] + // ---- range for N=2 + // ---- range for N=1 + // ---- range for N=0 + while node_starts.len() <= num_nodes { + node_starts.push(edge_targets.len()); + } + + assert_eq!(node_starts.len(), num_nodes + 1); + + Self { node_starts, edge_targets } + } + + /// Gets the successors for `source` as a slice. + pub fn successors(&self, source: N) -> &[N] { + let start_index = self.node_starts[source]; + let end_index = self.node_starts[source.plus(1)]; + &self.edge_targets[start_index..end_index] + } +} + +impl DirectedGraph for VecGraph { + type Node = N; +} + +impl WithNumNodes for VecGraph { + fn num_nodes(&self) -> usize { + self.node_starts.len() - 1 + } +} + +impl WithNumEdges for VecGraph { + fn num_edges(&self) -> usize { + self.edge_targets.len() + } +} + +impl GraphSuccessors<'graph> for VecGraph { + type Item = N; + + type Iter = std::iter::Cloned>; +} + +impl WithSuccessors for VecGraph { + fn successors<'graph>( + &'graph self, + node: N + ) -> >::Iter { + self.successors(node).iter().cloned() + } +} + diff --git a/src/librustc_data_structures/graph/vec_graph/test.rs b/src/librustc_data_structures/graph/vec_graph/test.rs new file mode 100644 index 0000000000000..4a168ee1d44fd --- /dev/null +++ b/src/librustc_data_structures/graph/vec_graph/test.rs @@ -0,0 +1,46 @@ +#![cfg(test)] + +use super::*; + +fn create_graph() -> VecGraph { + // Create a simple graph + // + // 5 + // | + // V + // 0 --> 1 --> 2 + // | + // v + // 3 --> 4 + // + // 6 + + VecGraph::new( + 7, + vec![ + (0, 1), + (1, 2), + (1, 3), + (3, 4), + (5, 1), + ], + ) +} + +#[test] +fn num_nodes() { + let graph = create_graph(); + assert_eq!(graph.num_nodes(), 7); +} + +#[test] +fn succesors() { + let graph = create_graph(); + assert_eq!(graph.successors(0), &[1]); + assert_eq!(graph.successors(1), &[2, 3]); + assert_eq!(graph.successors(2), &[]); + assert_eq!(graph.successors(3), &[4]); + assert_eq!(graph.successors(4), &[]); + assert_eq!(graph.successors(5), &[1]); + assert_eq!(graph.successors(6), &[]); +} diff --git a/src/librustc_data_structures/indexed_vec.rs b/src/librustc_data_structures/indexed_vec.rs index 635edbb927e5c..b3a810a622d03 100644 --- a/src/librustc_data_structures/indexed_vec.rs +++ b/src/librustc_data_structures/indexed_vec.rs @@ -19,8 +19,11 @@ pub trait Idx: Copy + 'static + Ord + Debug + Hash { fn index(self) -> usize; fn increment_by(&mut self, amount: usize) { - let v = self.index() + amount; - *self = Self::new(v); + *self = self.plus(amount); + } + + fn plus(self, amount: usize) -> Self { + Self::new(self.index() + amount) } } @@ -167,6 +170,14 @@ macro_rules! newtype_index { } } + impl std::ops::Add for $type { + type Output = Self; + + fn add(self, other: usize) -> Self { + Self::new(self.index() + other) + } + } + impl Idx for $type { #[inline] fn new(value: usize) -> Self { From 7fd0db7dd319cfb73664c8a068474dc8759ebabf Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 11 Jun 2019 16:29:27 -0400 Subject: [PATCH 27/68] add a `depth_first_search` helper function --- .../graph/iterate/mod.rs | 36 ++++++++++++++++++- src/librustc_data_structures/graph/mod.rs | 7 ++++ .../graph/vec_graph/test.rs | 7 ++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/librustc_data_structures/graph/iterate/mod.rs b/src/librustc_data_structures/graph/iterate/mod.rs index c09364b0a5395..5612778ce07ed 100644 --- a/src/librustc_data_structures/graph/iterate/mod.rs +++ b/src/librustc_data_structures/graph/iterate/mod.rs @@ -1,5 +1,6 @@ use super::super::indexed_vec::IndexVec; -use super::{DirectedGraph, WithSuccessors, WithNumNodes}; +use super::{DirectedGraph, WithNumNodes, WithSuccessors}; +use crate::bit_set::BitSet; #[cfg(test)] mod test; @@ -51,3 +52,36 @@ pub fn reverse_post_order( vec.reverse(); vec } + +/// A "depth-first search" iterator for a directed graph. +pub struct DepthFirstSearch<'graph, G> +where + G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors, +{ + graph: &'graph G, + stack: Vec, + visited: BitSet, +} + +impl DepthFirstSearch<'graph, G> +where + G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors, +{ + pub fn new(graph: &'graph G, start_node: G::Node) -> Self { + Self { graph, stack: vec![start_node], visited: BitSet::new_empty(graph.num_nodes()) } + } +} + +impl Iterator for DepthFirstSearch<'_, G> +where + G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors, +{ + type Item = G::Node; + + fn next(&mut self) -> Option { + let DepthFirstSearch { stack, visited, graph } = self; + let n = stack.pop()?; + stack.extend(graph.successors(n).filter(|&m| visited.insert(m))); + Some(n) + } +} diff --git a/src/librustc_data_structures/graph/mod.rs b/src/librustc_data_structures/graph/mod.rs index 45e7e5db38f04..2787fa3c6b1e4 100644 --- a/src/librustc_data_structures/graph/mod.rs +++ b/src/librustc_data_structures/graph/mod.rs @@ -30,6 +30,13 @@ where &'graph self, node: Self::Node, ) -> >::Iter; + + fn depth_first_search(&self, from: Self::Node) -> iterate::DepthFirstSearch<'_, Self> + where + Self: WithNumNodes, + { + iterate::DepthFirstSearch::new(self, from) + } } pub trait GraphSuccessors<'graph> { diff --git a/src/librustc_data_structures/graph/vec_graph/test.rs b/src/librustc_data_structures/graph/vec_graph/test.rs index 4a168ee1d44fd..c54a72264f66c 100644 --- a/src/librustc_data_structures/graph/vec_graph/test.rs +++ b/src/librustc_data_structures/graph/vec_graph/test.rs @@ -44,3 +44,10 @@ fn succesors() { assert_eq!(graph.successors(5), &[1]); assert_eq!(graph.successors(6), &[]); } + +#[test] +fn dfs() { + let graph = create_graph(); + let dfs: Vec<_> = graph.depth_first_search(0).collect(); + assert_eq!(dfs, vec![0, 1, 3, 4, 2]); +} From 8d39bdd5f9e3a3f712d9a8e8aaea5c426fafd86c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 12 Jun 2019 10:22:07 -0400 Subject: [PATCH 28/68] integrate reverse graph and upper-bound computation --- .../borrow_check/nll/region_infer/mod.rs | 86 +++++++++++++++---- 1 file changed, 69 insertions(+), 17 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index ad58e4a47eb31..39818de231088 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -23,7 +23,9 @@ use rustc::ty::{self, subst::SubstsRef, RegionVid, Ty, TyCtxt, TypeFoldable}; use rustc::util::common::{self, ErrorReported}; use rustc_data_structures::bit_set::BitSet; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use crate::rustc_data_structures::graph::WithSuccessors; use rustc_data_structures::graph::scc::Sccs; +use rustc_data_structures::graph::vec_graph::VecGraph; use rustc_data_structures::indexed_vec::IndexVec; use rustc_errors::{Diagnostic, DiagnosticBuilder}; use syntax_pos::Span; @@ -60,10 +62,15 @@ pub struct RegionInferenceContext<'tcx> { /// the SCC (see `constraint_sccs`) and for error reporting. constraint_graph: Rc, - /// The SCC computed from `constraints` and the constraint graph. Used to + /// The SCC computed from `constraints` and the constraint + /// graph. We have an edge from SCC A to SCC B if `A: B`. Used to /// compute the values of each region. constraint_sccs: Rc>, + /// Reverse of the SCC constraint graph -- i.e., an edge `A -> B` + /// exists if `B: A`. Computed lazilly. + rev_constraint_graph: Option>>, + /// The "pick R0 from [R1..Rn]" constraints, indexed by SCC. pick_constraints: Rc>, @@ -234,6 +241,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { constraints, constraint_graph, constraint_sccs, + rev_constraint_graph: None, pick_constraints, closure_bounds_mapping, scc_universes, @@ -602,21 +610,19 @@ impl<'tcx> RegionInferenceContext<'tcx> { .universal_regions_outlived_by(scc) .all(|lb| self.universal_region_relations.outlives(o_r, lb)) }); + debug!("apply_pick_constraint: after lb, option_regions={:?}", option_regions); // Now find all the *upper bounds* -- that is, each UB is a free // region that must outlive pick region R0 (`UB: R0`). Therefore, - // we need only keep an option O if `UB: O`. - // - // TODO -- need to implement the reverse graph construction for this - // - // let mut upper_bounds = ...; - // option_regions.retain(|&o_r| { - // upper_bounds - // .all(|ub| self.universal_region_relations.outlives( - // ub, - // o_r, - // }) - // }); + // we need only keep an option O if `UB: O` for all UB. + if option_regions.len() > 1 { + let universal_region_relations = self.universal_region_relations.clone(); + for ub in self.upper_bounds(scc) { + debug!("apply_pick_constraint: ub={:?}", ub); + option_regions.retain(|&o_r| universal_region_relations.outlives(ub, o_r)); + } + debug!("apply_pick_constraint: after ub, option_regions={:?}", option_regions); + } // If we ruled everything out, we're done. if option_regions.is_empty() { @@ -656,8 +662,46 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } - debug!("apply_pick_constraint: best_choice={:?}", best_option); - self.scc_values.add_element(scc, best_option) + let best_option_scc = self.constraint_sccs.scc(best_option); + debug!( + "apply_pick_constraint: best_choice={:?} best_option_scc={:?}", + best_option, + best_option_scc, + ); + self.scc_values.add_region(scc, best_option_scc) + } + + /// Compute and return the reverse SCC-based constraint graph (lazilly). + fn upper_bounds( + &mut self, + scc0: ConstraintSccIndex, + ) -> Vec { + // I wanted to return an `impl Iterator` here, but it's + // annoying because the `rev_constraint_graph` is in a local + // variable. We'd need a "once-cell" or some such thing to let + // us borrow it for the right amount of time. + let rev_constraint_graph = self.rev_constraint_graph(); + let scc_values = &self.scc_values; + let mut duplicates = FxHashSet::default(); + rev_constraint_graph + .depth_first_search(scc0) + .skip(1) + .flat_map(|scc1| scc_values.universal_regions_outlived_by(scc1)) + .filter(|&r| duplicates.insert(r)) + .collect() + } + + /// Compute and return the reverse SCC-based constraint graph (lazilly). + fn rev_constraint_graph( + &mut self, + ) -> Rc> { + if let Some(g) = &self.rev_constraint_graph { + return g.clone(); + } + + let rev_graph = Rc::new(self.constraint_sccs.reverse()); + self.rev_constraint_graph = Some(rev_graph.clone()); + rev_graph } /// Returns `true` if all the elements in the value of `scc_b` are nameable @@ -1145,8 +1189,16 @@ impl<'tcx> RegionInferenceContext<'tcx> { fn eval_outlives(&self, sup_region: RegionVid, sub_region: RegionVid) -> bool { debug!("eval_outlives({:?}: {:?})", sup_region, sub_region); - debug!("eval_outlives: sup_region's value = {:?}", self.region_value_str(sup_region),); - debug!("eval_outlives: sub_region's value = {:?}", self.region_value_str(sub_region),); + debug!( + "eval_outlives: sup_region's value = {:?} universal={:?}", + self.region_value_str(sup_region), + self.universal_regions.is_universal_region(sup_region), + ); + debug!( + "eval_outlives: sub_region's value = {:?} universal={:?}", + self.region_value_str(sub_region), + self.universal_regions.is_universal_region(sub_region), + ); let sub_region_scc = self.constraint_sccs.scc(sub_region); let sup_region_scc = self.constraint_sccs.scc(sup_region); From b5fb9067668383067373a4f8a07a4731209788d1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 12 Jun 2019 10:22:27 -0400 Subject: [PATCH 29/68] fix tests and remove outdated stderr files --- .../inverse-bounds.migrate.stderr | 19 ---------------- .../multiple-lifetimes/inverse-bounds.rs | 2 -- ...dinary-bounds-pick-original.migrate.stderr | 22 ------------------- .../ordinary-bounds-pick-other.migrate.stderr | 11 ---------- 4 files changed, 54 deletions(-) delete mode 100644 src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.migrate.stderr delete mode 100644 src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.migrate.stderr delete mode 100644 src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.migrate.stderr diff --git a/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.migrate.stderr b/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.migrate.stderr deleted file mode 100644 index a4eafbd3850e4..0000000000000 --- a/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.migrate.stderr +++ /dev/null @@ -1,19 +0,0 @@ -warning[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds - --> $DIR/inverse-bounds.rs:18:70 - | -LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Invert<'a>, b: Invert<'b>) -> impl Trait<'d, 'e> - | ^^^^^^^^^^^^^^^^^^ - | - = note: hidden type `Invert<'_>` captures lifetime '_#8r - = warning: this error has been downgraded to a warning for backwards compatibility with previous releases - = warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future - = note: for more information, try `rustc --explain E0729` - -warning: the feature `pin` has been stable since 1.33.0 and no longer requires an attribute to enable - --> $DIR/inverse-bounds.rs:6:60 - | -LL | #![feature(arbitrary_self_types, async_await, await_macro, pin)] - | ^^^ - | - = note: #[warn(stable_features)] on by default - diff --git a/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs b/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs index ac240e8ccde30..53d2bc190895b 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs @@ -3,8 +3,6 @@ // revisions: migrate mir //[mir]compile-flags: -Z borrowck=mir -#![feature(arbitrary_self_types, async_await, await_macro, pin)] - trait Trait<'a, 'b> {} impl Trait<'_, '_> for T {} diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.migrate.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.migrate.stderr deleted file mode 100644 index 3c6fe0b8de6b8..0000000000000 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.migrate.stderr +++ /dev/null @@ -1,22 +0,0 @@ -warning[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds - --> $DIR/ordinary-bounds-pick-original.rs:12:50 - | -LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { - | ^^^^^^^^^^^^^^^^^^ - | - = note: hidden type `(&u8, &u8)` captures lifetime '_#6r - = warning: this error has been downgraded to a warning for backwards compatibility with previous releases - = warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future - = note: for more information, try `rustc --explain E0729` - -warning[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds - --> $DIR/ordinary-bounds-pick-original.rs:12:50 - | -LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { - | ^^^^^^^^^^^^^^^^^^ - | - = note: hidden type `(&u8, &u8)` captures lifetime '_#7r - = warning: this error has been downgraded to a warning for backwards compatibility with previous releases - = warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future - = note: for more information, try `rustc --explain E0729` - diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.migrate.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.migrate.stderr deleted file mode 100644 index f1c2fc8695acd..0000000000000 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.migrate.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds - --> $DIR/ordinary-bounds-pick-other.rs:19:74 - | -LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> - | ^^^^^^^^^^^^^^^^^^ - | - = note: hidden type `Ordinary<'_>` captures lifetime '_#8r - = warning: this error has been downgraded to a warning for backwards compatibility with previous releases - = warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future - = note: for more information, try `rustc --explain E0729` - From 2057136326bfac61b29b7fac87289728f105ffe3 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 12 Jun 2019 10:47:04 -0400 Subject: [PATCH 30/68] update the async-fn-multiple-lifetimes test --- .../async-fn-multiple-lifetimes.rs | 7 ++++--- .../async-fn-multiple-lifetimes.stderr | 20 ------------------- 2 files changed, 4 insertions(+), 23 deletions(-) delete mode 100644 src/test/ui/async-await/async-fn-multiple-lifetimes.stderr diff --git a/src/test/ui/async-await/async-fn-multiple-lifetimes.rs b/src/test/ui/async-await/async-fn-multiple-lifetimes.rs index e3ac817b15ca5..c1202bfe21992 100644 --- a/src/test/ui/async-await/async-fn-multiple-lifetimes.rs +++ b/src/test/ui/async-await/async-fn-multiple-lifetimes.rs @@ -1,11 +1,13 @@ // edition:2018 +// run-pass -#![feature(arbitrary_self_types, async_await, await_macro, pin)] +// Test that we can use async fns with multiple arbitrary lifetimes. + +#![feature(arbitrary_self_types, async_await, await_macro)] use std::ops::Add; async fn multiple_named_lifetimes<'a, 'b>(_: &'a u8, _: &'b u8) {} -//~^ ERROR ambiguous lifetime bound in `async fn` async fn multiple_hrtb_and_single_named_lifetime_ok<'c>( _: impl for<'a> Add<&'a u8>, @@ -14,6 +16,5 @@ async fn multiple_hrtb_and_single_named_lifetime_ok<'c>( ) {} async fn multiple_elided_lifetimes(_: &u8, _: &u8) {} -//~^ ambiguous lifetime bound in `async fn` fn main() {} diff --git a/src/test/ui/async-await/async-fn-multiple-lifetimes.stderr b/src/test/ui/async-await/async-fn-multiple-lifetimes.stderr deleted file mode 100644 index 8c3ee2bed830b..0000000000000 --- a/src/test/ui/async-await/async-fn-multiple-lifetimes.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error: ambiguous lifetime bound in `async fn` - --> $DIR/async-fn-multiple-lifetimes.rs:7:65 - | -LL | async fn multiple_named_lifetimes<'a, 'b>(_: &'a u8, _: &'b u8) {} - | ^ neither `'a` nor `'b` outlives the other - | - = note: multiple unrelated lifetimes are not allowed in `async fn`. - = note: if you're using argument-position elided lifetimes, consider switching to a single named lifetime. - -error: ambiguous lifetime bound in `async fn` - --> $DIR/async-fn-multiple-lifetimes.rs:16:52 - | -LL | async fn multiple_elided_lifetimes(_: &u8, _: &u8) {} - | ^ the elided lifetimes here do not outlive one another - | - = note: multiple unrelated lifetimes are not allowed in `async fn`. - = note: if you're using argument-position elided lifetimes, consider switching to a single named lifetime. - -error: aborting due to 2 previous errors - From 4de99600b0ca8dafd5310f900def87bbcbf0aae6 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 12 Jun 2019 11:16:59 -0400 Subject: [PATCH 31/68] add a FIXME related to the non-free-region case I don't think it would actually be harmful to just ignore such cases but I'm inclined not to take chances. --- .../borrow_check/nll/region_infer/mod.rs | 19 ++++++++++--- .../ordinary-bounds-pick-original-elided.rs | 27 +++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-elided.rs diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 39818de231088..822c7b76b8fd1 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -550,7 +550,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Now take pick constraints into account let pick_constraints = self.pick_constraints.clone(); for p_c_i in pick_constraints.indices(scc_a) { - self.apply_pick_constraint(scc_a, pick_constraints.option_regions(p_c_i)); + self.apply_pick_constraint( + scc_a, + pick_constraints[p_c_i].opaque_type_def_id, + pick_constraints.option_regions(p_c_i), + ); } debug!( @@ -574,6 +578,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { fn apply_pick_constraint( &mut self, scc: ConstraintSccIndex, + opaque_type_def_id: DefId, option_regions: &[ty::RegionVid], ) -> bool { debug!("apply_pick_constraint(scc={:?}, option_regions={:#?})", scc, option_regions,); @@ -581,8 +586,16 @@ impl<'tcx> RegionInferenceContext<'tcx> { if let Some(uh_oh) = option_regions.iter().find(|&&r| !self.universal_regions.is_universal_region(r)) { - debug!("apply_pick_constraint: option region `{:?}` is not a universal region", uh_oh); - return false; + // FIXME(#61773): This case can only occur with + // `impl_trait_in_bindings`, I believe, and we are just + // opting not to handle it for now. See #61773 for + // details. + bug!( + "pick constraint for `{:?}` has an option region `{:?}` \ + that is not a universal region", + opaque_type_def_id, + uh_oh, + ); } // Create a mutable vector of the options. We'll try to winnow diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-elided.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-elided.rs new file mode 100644 index 0000000000000..23981d92562d2 --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-elided.rs @@ -0,0 +1,27 @@ +// edition:2018 +// run-pass +// revisions: migrate mir +//[mir]compile-flags: -Z borrowck=mir + +trait Trait<'a, 'b> { } +impl Trait<'_, '_> for T { } + +// Test case where we have elision in the impl trait and we have to +// pick the right region. + +// Ultimately `Trait<'x, 'static>`. +fn upper_bounds1(a: &u8) -> impl Trait<'_, 'static> { + (a, a) +} + +// Ultimately `Trait<'x, 'x>`, so not really multiple bounds. +fn upper_bounds2(a: &u8) -> impl Trait<'_, '_> { + (a, a) +} + +// Kind of a weird annoying case. +fn upper_bounds3<'b>(a: &u8) -> impl Trait<'_, 'b> { + (a, a) +} + +fn main() { } From 2ea6094938a5ba4b0fa0f263bf458f81eaea264b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 12 Jun 2019 11:47:48 -0400 Subject: [PATCH 32/68] explain why pick-constraints can be an empty vector --- src/librustc_mir/borrow_check/nll/type_check/mod.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index ea7db3096446c..9b7a62ba7648b 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -2514,7 +2514,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { if let Some(closure_region_requirements) = tcx.mir_borrowck(def_id).closure_requirements { let closure_constraints = QueryRegionConstraints { outlives: closure_region_requirements.apply_requirements(tcx, def_id, substs), - pick_constraints: vec![], // TODO + + // Presently, closures never propagate pick + // constraints to their parents -- they are enforced + // locally. This is largely a non-issue as pick + // constraints only come from `-> impl Trait` and + // friends which don't appear (thus far...) in + // closures. + pick_constraints: vec![], }; let bounds_mapping = closure_constraints From 4831146afab6d7a2b66f094dabb4f6664587b89b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 12 Jun 2019 11:48:08 -0400 Subject: [PATCH 33/68] remove outdated TODO markers These were left behind to remind me to cleanup the code -- but the relevant cleanups were already done. --- src/librustc_mir/borrow_check/nll/pick_constraints.rs | 2 -- src/librustc_mir/borrow_check/nll/region_infer/mod.rs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/pick_constraints.rs b/src/librustc_mir/borrow_check/nll/pick_constraints.rs index dff0801f2ebc9..b130d958db7cf 100644 --- a/src/librustc_mir/borrow_check/nll/pick_constraints.rs +++ b/src/librustc_mir/borrow_check/nll/pick_constraints.rs @@ -1,5 +1,3 @@ -#![allow(dead_code)] // TODO - use crate::rustc::ty::{self, Ty}; use rustc::hir::def_id::DefId; use rustc::infer::region_constraints::PickConstraint; diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 822c7b76b8fd1..1e377f94fae3a 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -1486,7 +1486,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self, infcx: &InferCtxt<'_, 'tcx>, mir_def_id: DefId, - errors_buffer: &mut Vec, // TODO + errors_buffer: &mut Vec, ) { let pick_constraints = self.pick_constraints.clone(); for p_c_i in pick_constraints.all_indices() { From a18c779fa21d0259da8e38ef6022a14310d3ae15 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 12 Jun 2019 11:48:29 -0400 Subject: [PATCH 34/68] pacify the mercilous tidy long lines, trailing newlines --- src/librustc/infer/canonical/mod.rs | 3 ++- src/librustc/infer/region_constraints/mod.rs | 5 ++++- src/librustc_data_structures/graph/vec_graph/mod.rs | 1 - .../borrow_check/nll/region_infer/mod.rs | 12 +++++++++--- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/librustc/infer/canonical/mod.rs b/src/librustc/infer/canonical/mod.rs index 4693cf7181e94..308abb5e13325 100644 --- a/src/librustc/infer/canonical/mod.rs +++ b/src/librustc/infer/canonical/mod.rs @@ -307,7 +307,8 @@ impl<'tcx, V> Canonical<'tcx, V> { } } -pub type QueryOutlivesConstraint<'tcx> = ty::Binder, Region<'tcx>>>; +pub type QueryOutlivesConstraint<'tcx> = + ty::Binder, Region<'tcx>>>; impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// Creates a substitution S for the canonical value with fresh diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index a56c71b2b1f78..6ec093ee02605 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -973,6 +973,9 @@ impl<'tcx> RegionConstraintData<'tcx> { verifys, givens, } = self; - constraints.is_empty() && pick_constraints.is_empty() && verifys.is_empty() && givens.is_empty() + constraints.is_empty() && + pick_constraints.is_empty() && + verifys.is_empty() && + givens.is_empty() } } diff --git a/src/librustc_data_structures/graph/vec_graph/mod.rs b/src/librustc_data_structures/graph/vec_graph/mod.rs index 6b3349e3e1549..7078a5a9c9b4a 100644 --- a/src/librustc_data_structures/graph/vec_graph/mod.rs +++ b/src/librustc_data_structures/graph/vec_graph/mod.rs @@ -110,4 +110,3 @@ impl WithSuccessors for VecGraph { self.successors(node).iter().cloned() } } - diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 1e377f94fae3a..c86ce9952389b 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -1493,19 +1493,25 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!("check_pick_constraint(p_c_i={:?})", p_c_i); let p_c = &pick_constraints[p_c_i]; let pick_region_vid = p_c.pick_region_vid; - debug!("check_pick_constraint: pick_region_vid={:?} with value {}", pick_region_vid, self.region_value_str(pick_region_vid)); + debug!( + "check_pick_constraint: pick_region_vid={:?} with value {}", + pick_region_vid, + self.region_value_str(pick_region_vid), + ); let option_regions = pick_constraints.option_regions(p_c_i); debug!("check_pick_constraint: option_regions={:?}", option_regions); // did the pick-region wind up equal to any of the option regions? - if let Some(o) = option_regions.iter().find(|&&o_r| self.eval_equal(o_r, p_c.pick_region_vid)) { + if let Some(o) = option_regions.iter().find(|&&o_r| { + self.eval_equal(o_r, p_c.pick_region_vid) + }) { debug!("check_pick_constraint: evaluated as equal to {:?}", o); continue; } // if not, report an error let region_scope_tree = &infcx.tcx.region_scope_tree(mir_def_id); - let pick_region = infcx.tcx.mk_region(ty::ReVar(pick_region_vid)); // XXX + let pick_region = infcx.tcx.mk_region(ty::ReVar(pick_region_vid)); opaque_types::unexpected_hidden_region_diagnostic( infcx.tcx, Some(region_scope_tree), From b170c0f1c4510cbabe0c767f6fcbeb58c8a05c0d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 12 Jun 2019 11:50:28 -0400 Subject: [PATCH 35/68] add a preliminary existential test; not really enough --- ...dinary-bounds-pick-original-existential.rs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-existential.rs diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-existential.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-existential.rs new file mode 100644 index 0000000000000..86760dfcbdac8 --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-existential.rs @@ -0,0 +1,29 @@ +// edition:2018 +// run-pass +// revisions: migrate mir +//[mir]compile-flags: -Z borrowck=mir + +trait Trait<'a, 'b> { } +impl Trait<'_, '_> for T { } + +// Here we wind up selecting `'a` and `'b` in the hidden type because +// those are the types that appear inth e original values. + +existential type Foo<'a, 'b>: Trait<'a, 'b>; + +fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> Foo<'a, 'b> { + // In this simple case, you have a hidden type `(&'0 u8, &'1 u8)` and constraints like + // + // ``` + // 'a: '0 + // 'b: '1 + // '0 in ['a, 'b] + // '1 in ['a, 'b] + // ``` + // + // We use the fact that `'a: 0'` must hold (combined with the in + // constraint) to determine that `'0 = 'a` must be the answer. + (a, b) +} + +fn main() { } From e9de08a5ea8e507b2e6f341ccfe34d4a5213048f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 12 Jun 2019 18:02:39 -0400 Subject: [PATCH 36/68] test with explicit existential type --- .../ordinary-bounds-pick-original-existential.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-existential.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-existential.rs index 86760dfcbdac8..be249eeb9eadb 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-existential.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-existential.rs @@ -3,6 +3,8 @@ // revisions: migrate mir //[mir]compile-flags: -Z borrowck=mir +#![feature(existential_type)] + trait Trait<'a, 'b> { } impl Trait<'_, '_> for T { } From 89a205bf4486115baeb3710ba8a4652c16666511 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 17 Jun 2019 07:48:53 -0400 Subject: [PATCH 37/68] add a `VecMap` data structure --- src/librustc_data_structures/lib.rs | 1 + src/librustc_data_structures/vec_map/mod.rs | 85 ++++++++++++++++++++ src/librustc_data_structures/vec_map/test.rs | 28 +++++++ 3 files changed, 114 insertions(+) create mode 100644 src/librustc_data_structures/vec_map/mod.rs create mode 100644 src/librustc_data_structures/vec_map/test.rs diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index a1d7ab8856daa..1829da2508738 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -95,6 +95,7 @@ pub mod tiny_list; pub mod thin_vec; pub mod transitive_relation; pub use ena::unify; +pub mod vec_map; pub mod vec_linked_list; pub mod work_queue; pub mod fingerprint; diff --git a/src/librustc_data_structures/vec_map/mod.rs b/src/librustc_data_structures/vec_map/mod.rs new file mode 100644 index 0000000000000..9d9e54bfc7b12 --- /dev/null +++ b/src/librustc_data_structures/vec_map/mod.rs @@ -0,0 +1,85 @@ +#[cfg(test)] +mod test; + +/// A (multi-)map based on a sorted vector. This uses binary search to +/// find the starting index for a given element and can be a fairly +/// efficient data structure, particularly for small-ish sets of data. +/// +/// To use, you supply the starting vector along with a "key fn" that +/// extracts the key from each element. +pub struct VecMap { + data: Vec, + key_fn: KeyFn, +} + +impl VecMap +where + KeyFn: Fn(&E) -> K, + K: Ord + std::fmt::Debug, +{ + pub fn new( + mut data: Vec, + key_fn: KeyFn, + ) -> Self { + data.sort_by_key(&key_fn); + Self { data, key_fn } + } + + /// Extract the first index for the given key using binary search. + /// Returns `None` if there is no such index. + fn get_range(&self, key: &K) -> Option<(usize, usize)> { + match self.data.binary_search_by_key(key, &self.key_fn) { + Ok(mid) => { + // We get back *some* element with the given key -- so + // search backwards to find the *first* one. + // + // (It'd be more efficient to use a "galloping" search + // here, but it's not really worth it for small-ish + // amounts of data.) + let mut start = mid; + while start > 0 { + if (self.key_fn)(&self.data[start - 1]) == *key { + start -= 1; + } else { + break; + } + } + + // Now search forward to find the *last* one. + // + // (It'd be more efficient to use a "galloping" search + // here, but it's not really worth it for small-ish + // amounts of data.) + let mut end = mid + 1; + let max = self.data.len(); + while end < max { + if (self.key_fn)(&self.data[end]) == *key { + end += 1; + } else { + break; + } + } + + Some((start, end)) + } + Err(_) => None, + } + } + + /// Gets the (first) value associated with a given key. + pub fn get_first(&self, key: &K) -> Option<&E> { + let (start, _) = self.get_range(key)?; + Some(&self.data[start]) + } + + /// Gets a slice of values associated with the given key. + pub fn get_all(&self, key: &K) -> &[E] { + let (start, end) = self.get_range(key).unwrap_or((0, 0)); + &self.data[start..end] + } + + /// Gets a slice of values associated with the given key. + pub fn get_iter<'k>(&'k self, key: &'k K) -> impl Iterator { + self.get_all(key).iter() + } +} diff --git a/src/librustc_data_structures/vec_map/test.rs b/src/librustc_data_structures/vec_map/test.rs new file mode 100644 index 0000000000000..9e4f581a8cf4a --- /dev/null +++ b/src/librustc_data_structures/vec_map/test.rs @@ -0,0 +1,28 @@ +use super::*; + +type Element = (usize, &'static str); + +fn test_map() -> VecMap usize> { + let data = vec![(3, "three-a"), (0, "zero"), (3, "three-b"), (22, "twenty-two")]; + VecMap::new(data, |&(key, _)| key) +} + +#[test] +fn get_first() { + let map = test_map(); + assert_eq!(map.get_first(&0), Some(&(0, "zero"))); + assert_eq!(map.get_first(&1), None); + assert_eq!(map.get_first(&3), Some(&(3, "three-a"))); + assert_eq!(map.get_first(&22), Some(&(22, "twenty-two"))); + assert_eq!(map.get_first(&23), None); +} + +#[test] +fn get_all() { + let map = test_map(); + assert_eq!(map.get_all(&0), &[(0, "zero")]); + assert_eq!(map.get_all(&1), &[]); + assert_eq!(map.get_all(&3), &[(3, "three-a"), (3, "three-b")]); + assert_eq!(map.get_all(&22), &[(22, "twenty-two")]); + assert_eq!(map.get_all(&23), &[]); +} From 3e01c7416a091dc29dd0d349fafb6cc5d3c4d97c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 17 Jun 2019 07:55:54 -0400 Subject: [PATCH 38/68] just create a binary search slice helper fn --- .../binary_search_util/mod.rs | 49 +++++++++++ .../binary_search_util/test.rs | 22 +++++ src/librustc_data_structures/lib.rs | 2 +- src/librustc_data_structures/vec_map/mod.rs | 85 ------------------- src/librustc_data_structures/vec_map/test.rs | 28 ------ 5 files changed, 72 insertions(+), 114 deletions(-) create mode 100644 src/librustc_data_structures/binary_search_util/mod.rs create mode 100644 src/librustc_data_structures/binary_search_util/test.rs delete mode 100644 src/librustc_data_structures/vec_map/mod.rs delete mode 100644 src/librustc_data_structures/vec_map/test.rs diff --git a/src/librustc_data_structures/binary_search_util/mod.rs b/src/librustc_data_structures/binary_search_util/mod.rs new file mode 100644 index 0000000000000..32aa1cb6b1d36 --- /dev/null +++ b/src/librustc_data_structures/binary_search_util/mod.rs @@ -0,0 +1,49 @@ +#[cfg(test)] +mod test; + +/// Uses a sorted slice `data: &[E]` as a kind of "multi-map". The +/// `key_fn` extracts a key of type `K` from the data, and this +/// function finds the range of elements that match the key. `data` +/// must have been sorted as if by a call to `sort_by_key` for this to +/// work. +pub fn binary_search_slice(data: &'d [E], key_fn: impl Fn(&E) -> K, key: &K) -> &'d [E] +where + K: Ord, +{ + let mid = match data.binary_search_by_key(key, &key_fn) { + Ok(mid) => mid, + Err(_) => return &[], + }; + + // We get back *some* element with the given key -- so + // search backwards to find the *first* one. + // + // (It'd be more efficient to use a "galloping" search + // here, but it's not really worth it for small-ish + // amounts of data.) + let mut start = mid; + while start > 0 { + if key_fn(&data[start - 1]) == *key { + start -= 1; + } else { + break; + } + } + + // Now search forward to find the *last* one. + // + // (It'd be more efficient to use a "galloping" search + // here, but it's not really worth it for small-ish + // amounts of data.) + let mut end = mid + 1; + let max = data.len(); + while end < max { + if key_fn(&data[end]) == *key { + end += 1; + } else { + break; + } + } + + &data[start..end] +} diff --git a/src/librustc_data_structures/binary_search_util/test.rs b/src/librustc_data_structures/binary_search_util/test.rs new file mode 100644 index 0000000000000..a6361d912656a --- /dev/null +++ b/src/librustc_data_structures/binary_search_util/test.rs @@ -0,0 +1,22 @@ +use super::*; + +type Element = (usize, &'static str); + +fn test_map() -> Vec { + let mut data = vec![(3, "three-a"), (0, "zero"), (3, "three-b"), (22, "twenty-two")]; + data.sort_by_key(get_key) +} + +fn get_key(data: &Element) -> usize { + data.0 +} + +#[test] +fn binary_search_slice() { + let map = test_map(); + assert_eq!(binary_search_slice(&map, get_key, &0), &[(0, "zero")]); + assert_eq!(binary_search_slice(&map, get_key, &1), &[]); + assert_eq!(binary_search_slice(&map, get_key, &3), &[(3, "three-a"), (3, "three-b")]); + assert_eq!(binary_search_slice(&map, get_key, &22), &[(22, "twenty-two")]); + assert_eq!(binary_search_slice(&map, get_key, &23), &[]); +} diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index 1829da2508738..98c809f7e2595 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -72,6 +72,7 @@ macro_rules! unlikely { pub mod macros; pub mod svh; pub mod base_n; +pub mod binary_search_util; pub mod bit_set; pub mod box_region; pub mod const_cstr; @@ -95,7 +96,6 @@ pub mod tiny_list; pub mod thin_vec; pub mod transitive_relation; pub use ena::unify; -pub mod vec_map; pub mod vec_linked_list; pub mod work_queue; pub mod fingerprint; diff --git a/src/librustc_data_structures/vec_map/mod.rs b/src/librustc_data_structures/vec_map/mod.rs deleted file mode 100644 index 9d9e54bfc7b12..0000000000000 --- a/src/librustc_data_structures/vec_map/mod.rs +++ /dev/null @@ -1,85 +0,0 @@ -#[cfg(test)] -mod test; - -/// A (multi-)map based on a sorted vector. This uses binary search to -/// find the starting index for a given element and can be a fairly -/// efficient data structure, particularly for small-ish sets of data. -/// -/// To use, you supply the starting vector along with a "key fn" that -/// extracts the key from each element. -pub struct VecMap { - data: Vec, - key_fn: KeyFn, -} - -impl VecMap -where - KeyFn: Fn(&E) -> K, - K: Ord + std::fmt::Debug, -{ - pub fn new( - mut data: Vec, - key_fn: KeyFn, - ) -> Self { - data.sort_by_key(&key_fn); - Self { data, key_fn } - } - - /// Extract the first index for the given key using binary search. - /// Returns `None` if there is no such index. - fn get_range(&self, key: &K) -> Option<(usize, usize)> { - match self.data.binary_search_by_key(key, &self.key_fn) { - Ok(mid) => { - // We get back *some* element with the given key -- so - // search backwards to find the *first* one. - // - // (It'd be more efficient to use a "galloping" search - // here, but it's not really worth it for small-ish - // amounts of data.) - let mut start = mid; - while start > 0 { - if (self.key_fn)(&self.data[start - 1]) == *key { - start -= 1; - } else { - break; - } - } - - // Now search forward to find the *last* one. - // - // (It'd be more efficient to use a "galloping" search - // here, but it's not really worth it for small-ish - // amounts of data.) - let mut end = mid + 1; - let max = self.data.len(); - while end < max { - if (self.key_fn)(&self.data[end]) == *key { - end += 1; - } else { - break; - } - } - - Some((start, end)) - } - Err(_) => None, - } - } - - /// Gets the (first) value associated with a given key. - pub fn get_first(&self, key: &K) -> Option<&E> { - let (start, _) = self.get_range(key)?; - Some(&self.data[start]) - } - - /// Gets a slice of values associated with the given key. - pub fn get_all(&self, key: &K) -> &[E] { - let (start, end) = self.get_range(key).unwrap_or((0, 0)); - &self.data[start..end] - } - - /// Gets a slice of values associated with the given key. - pub fn get_iter<'k>(&'k self, key: &'k K) -> impl Iterator { - self.get_all(key).iter() - } -} diff --git a/src/librustc_data_structures/vec_map/test.rs b/src/librustc_data_structures/vec_map/test.rs deleted file mode 100644 index 9e4f581a8cf4a..0000000000000 --- a/src/librustc_data_structures/vec_map/test.rs +++ /dev/null @@ -1,28 +0,0 @@ -use super::*; - -type Element = (usize, &'static str); - -fn test_map() -> VecMap usize> { - let data = vec![(3, "three-a"), (0, "zero"), (3, "three-b"), (22, "twenty-two")]; - VecMap::new(data, |&(key, _)| key) -} - -#[test] -fn get_first() { - let map = test_map(); - assert_eq!(map.get_first(&0), Some(&(0, "zero"))); - assert_eq!(map.get_first(&1), None); - assert_eq!(map.get_first(&3), Some(&(3, "three-a"))); - assert_eq!(map.get_first(&22), Some(&(22, "twenty-two"))); - assert_eq!(map.get_first(&23), None); -} - -#[test] -fn get_all() { - let map = test_map(); - assert_eq!(map.get_all(&0), &[(0, "zero")]); - assert_eq!(map.get_all(&1), &[]); - assert_eq!(map.get_all(&3), &[(3, "three-a"), (3, "three-b")]); - assert_eq!(map.get_all(&22), &[(22, "twenty-two")]); - assert_eq!(map.get_all(&23), &[]); -} From 0b15a66a806a8f46af6ae24f640814f3a69eddfb Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 17 Jun 2019 08:40:50 -0400 Subject: [PATCH 39/68] account for the pick-constraint edges when reporting errors Also, thread through better span info to improve the error message to something tolerable. --- src/librustc/infer/mod.rs | 3 +- src/librustc/infer/opaque_types/mod.rs | 28 ++++++++ src/librustc/infer/region_constraints/mod.rs | 10 ++- .../borrow_check/nll/pick_constraints.rs | 5 ++ .../nll/region_infer/error_reporting/mod.rs | 41 +++++++++-- .../borrow_check/nll/region_infer/mod.rs | 72 +++++++++++++++++-- .../borrow_check/nll/type_check/mod.rs | 4 ++ src/librustc_typeck/check/mod.rs | 14 +++- .../multiple-lifetimes/error-handling.rs | 21 ++++++ .../multiple-lifetimes/error-handling.stderr | 15 ++++ 10 files changed, 195 insertions(+), 18 deletions(-) create mode 100644 src/test/ui/impl-trait/multiple-lifetimes/error-handling.rs create mode 100644 src/test/ui/impl-trait/multiple-lifetimes/error-handling.stderr diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index f6c6b5e93a791..6f13cd8162965 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -910,13 +910,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn pick_constraint( &self, opaque_type_def_id: DefId, + definition_span: Span, hidden_ty: Ty<'tcx>, region: ty::Region<'tcx>, in_regions: &Rc>>, ) { debug!("sub_regions({:?} <: {:?})", region, in_regions); self.borrow_region_constraints() - .pick_constraint(opaque_type_def_id, hidden_ty, region, in_regions); + .pick_constraint(opaque_type_def_id, definition_span, hidden_ty, region, in_regions); } pub fn subtype_predicate( diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs index 1bc5ce473f5ee..9a339db047d33 100644 --- a/src/librustc/infer/opaque_types/mod.rs +++ b/src/librustc/infer/opaque_types/mod.rs @@ -12,6 +12,7 @@ use crate::util::nodemap::DefIdMap; use errors::DiagnosticBuilder; use rustc_data_structures::fx::FxHashMap; use std::rc::Rc; +use syntax_pos::Span; pub type OpaqueTypeMap<'tcx> = DefIdMap>; @@ -33,6 +34,20 @@ pub struct OpaqueTypeDecl<'tcx> { /// then `substs` would be `['a, T]`. pub substs: SubstsRef<'tcx>, + /// The span of this particular definition of the opaque type. So + /// for example: + /// + /// ``` + /// existential type Foo; + /// fn bar() -> Foo { + /// ^^^ this is the span we are looking for! + /// ``` + /// + /// In cases where the fn returns `(impl Trait, impl Trait)` or + /// other such combinations, the result is currently + /// over-approximated, but better than nothing. + pub definition_span: Span, + /// The type variable that represents the value of the abstract type /// that we require. In other words, after we compile this function, /// we will be created a constraint like: @@ -99,12 +114,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// - `param_env` -- the in-scope parameter environment to be used for /// obligations /// - `value` -- the value within which we are instantiating opaque types + /// - `value_span` -- the span where the value came from, used in error reporting pub fn instantiate_opaque_types>( &self, parent_def_id: DefId, body_id: hir::HirId, param_env: ty::ParamEnv<'tcx>, value: &T, + value_span: Span, ) -> InferOk<'tcx, (T, OpaqueTypeMap<'tcx>)> { debug!( "instantiate_opaque_types(value={:?}, parent_def_id={:?}, body_id={:?}, \ @@ -116,6 +133,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { parent_def_id, body_id, param_env, + value_span, opaque_types: Default::default(), obligations: vec![], }; @@ -427,6 +445,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { tcx: self.tcx, op: |r| self.pick_constraint( opaque_type_def_id, + opaque_defn.definition_span, concrete_ty, r, &option_regions, @@ -807,6 +826,7 @@ struct Instantiator<'a, 'tcx> { parent_def_id: DefId, body_id: hir::HirId, param_env: ty::ParamEnv<'tcx>, + value_span: Span, opaque_types: OpaqueTypeMap<'tcx>, obligations: Vec>, } @@ -954,10 +974,18 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { debug!("instantiate_opaque_types: param_env={:#?}", self.param_env,); debug!("instantiate_opaque_types: generics={:#?}", tcx.generics_of(def_id),); + // Ideally, we'd get the span where *this specific `ty` came + // from*, but right now we just use the span from the overall + // value being folded. In simple cases like `-> impl Foo`, + // these are the same span, but not in cases like `-> (impl + // Foo, impl Bar)`. + let definition_span = self.value_span; + self.opaque_types.insert( def_id, OpaqueTypeDecl { substs, + definition_span, concrete_ty: ty_var, has_required_region_bounds: !required_region_bounds.is_empty(), origin, diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index 6ec093ee02605..1558022fd3172 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -14,6 +14,7 @@ use crate::ty::ReStatic; use crate::ty::{self, Ty, TyCtxt}; use crate::ty::{ReLateBound, ReVar}; use crate::ty::{Region, RegionVid}; +use syntax_pos::Span; use std::collections::BTreeMap; use std::{cmp, fmt, mem}; @@ -155,6 +156,9 @@ pub struct PickConstraint<'tcx> { /// the def-id of the opaque type causing this constraint: used for error reporting pub opaque_type_def_id: DefId, + /// the span where the hidden type was instantiated + pub definition_span: Span, + /// the hidden type in which `pick_region` appears: used for error reporting pub hidden_ty: Ty<'tcx>, @@ -167,14 +171,14 @@ pub struct PickConstraint<'tcx> { BraceStructTypeFoldableImpl! { impl<'tcx> TypeFoldable<'tcx> for PickConstraint<'tcx> { - opaque_type_def_id, hidden_ty, pick_region, option_regions + opaque_type_def_id, definition_span, hidden_ty, pick_region, option_regions } } BraceStructLiftImpl! { impl<'a, 'tcx> Lift<'tcx> for PickConstraint<'a> { type Lifted = PickConstraint<'tcx>; - opaque_type_def_id, hidden_ty, pick_region, option_regions + opaque_type_def_id, definition_span, hidden_ty, pick_region, option_regions } } @@ -687,6 +691,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { pub fn pick_constraint( &mut self, opaque_type_def_id: DefId, + definition_span: Span, hidden_ty: Ty<'tcx>, pick_region: ty::Region<'tcx>, option_regions: &Rc>>, @@ -699,6 +704,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { self.data.pick_constraints.push(PickConstraint { opaque_type_def_id, + definition_span, hidden_ty, pick_region, option_regions: option_regions.clone() diff --git a/src/librustc_mir/borrow_check/nll/pick_constraints.rs b/src/librustc_mir/borrow_check/nll/pick_constraints.rs index b130d958db7cf..aa0f7852273ab 100644 --- a/src/librustc_mir/borrow_check/nll/pick_constraints.rs +++ b/src/librustc_mir/borrow_check/nll/pick_constraints.rs @@ -5,6 +5,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use std::hash::Hash; use std::ops::Index; +use syntax_pos::Span; /// Compactly stores a set of `pick R0 in [R1...Rn]` constraints, /// indexed by the region R0. @@ -34,6 +35,9 @@ crate struct NllPickConstraint<'tcx> { /// The opaque type whose hidden type is being inferred. (Used in error reporting.) crate opaque_type_def_id: DefId, + /// The span where the hidden type was instantiated. + crate definition_span: Span, + /// The hidden type in which R0 appears. (Used in error reporting.) crate hidden_ty: Ty<'tcx>, @@ -79,6 +83,7 @@ impl<'tcx> PickConstraintSet<'tcx, ty::RegionVid> { next_constraint, pick_region_vid, opaque_type_def_id: p_c.opaque_type_def_id, + definition_span: p_c.definition_span, hidden_ty: p_c.hidden_ty, start_index, end_index, diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs index 04ff54e9a5e45..34fd3427d326c 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs @@ -1,4 +1,5 @@ use crate::borrow_check::nll::constraints::OutlivesConstraint; +use crate::borrow_check::nll::region_infer::AppliedPickConstraint; use crate::borrow_check::nll::region_infer::RegionInferenceContext; use crate::borrow_check::nll::type_check::Locations; use crate::borrow_check::nll::universal_regions::DefiningTy; @@ -195,6 +196,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { Trace::NotVisited => { bug!("found unvisited region {:?} on path to {:?}", p, r) } + Trace::FromOutlivesConstraint(c) => { result.push(c); p = c.sup; @@ -211,10 +213,30 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Otherwise, walk over the outgoing constraints and // enqueue any regions we find, keeping track of how we // reached them. + + // A constraint like `'r: 'x` can come from our constraint + // graph. let fr_static = self.universal_regions.fr_static; - for constraint in self.constraint_graph - .outgoing_edges(r, &self.constraints, fr_static) - { + let outgoing_edges_from_graph = self.constraint_graph + .outgoing_edges(r, &self.constraints, fr_static); + + + // But pick-constraints can also give rise to `'r: 'x` + // edges that were not part of the graph initially, so + // watch out for those. + let outgoing_edges_from_picks = self.applied_pick_constraints(r) + .iter() + .map(|&AppliedPickConstraint { best_option, pick_constraint_index, .. }| { + let p_c = &self.pick_constraints[pick_constraint_index]; + OutlivesConstraint { + sup: r, + sub: best_option, + locations: Locations::All(p_c.definition_span), + category: ConstraintCategory::OpaqueType, + } + }); + + for constraint in outgoing_edges_from_graph.chain(outgoing_edges_from_picks) { debug_assert_eq!(constraint.sup, r); let sub_region = constraint.sub; if let Trace::NotVisited = context[sub_region] { @@ -687,7 +709,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { // Finds some region R such that `fr1: R` and `R` is live at // `elem`. - crate fn find_sub_region_live_at(&self, fr1: RegionVid, elem: Location) -> RegionVid { + crate fn find_sub_region_live_at( + &self, + fr1: RegionVid, + elem: Location, + ) -> RegionVid { debug!("find_sub_region_live_at(fr1={:?}, elem={:?})", fr1, elem); self.find_constraint_paths_between_regions(fr1, |r| { // First look for some `r` such that `fr1: r` and `r` is live at `elem` @@ -729,8 +755,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { fr1: RegionVid, fr2: RegionVid, ) -> (ConstraintCategory, Span) { - let (category, _, span) = - self.best_blame_constraint(body, fr1, |r| self.provides_universal_region(r, fr1, fr2)); + let (category, _, span) = self.best_blame_constraint( + body, + fr1, + |r| self.provides_universal_region(r, fr1, fr2), + ); (category, span) } diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index c86ce9952389b..db519fad0d9e1 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -3,7 +3,7 @@ use crate::borrow_check::nll::constraints::graph::NormalConstraintGraph; use crate::borrow_check::nll::constraints::{ ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet, }; -use crate::borrow_check::nll::pick_constraints::PickConstraintSet; +use crate::borrow_check::nll::pick_constraints::{PickConstraintSet, NllPickConstraintIndex}; use crate::borrow_check::nll::region_infer::values::{ PlaceholderIndices, RegionElement, ToElementIndex, }; @@ -21,9 +21,10 @@ use rustc::mir::{ }; use rustc::ty::{self, subst::SubstsRef, RegionVid, Ty, TyCtxt, TypeFoldable}; use rustc::util::common::{self, ErrorReported}; +use rustc_data_structures::binary_search_util; use rustc_data_structures::bit_set::BitSet; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use crate::rustc_data_structures::graph::WithSuccessors; +use rustc_data_structures::graph::WithSuccessors; use rustc_data_structures::graph::scc::Sccs; use rustc_data_structures::graph::vec_graph::VecGraph; use rustc_data_structures::indexed_vec::IndexVec; @@ -74,6 +75,12 @@ pub struct RegionInferenceContext<'tcx> { /// The "pick R0 from [R1..Rn]" constraints, indexed by SCC. pick_constraints: Rc>, + /// Records the pick-constraints that we applied to each scc. + /// This is useful for error reporting. Once constraint + /// propagation is done, this vector is sorted according to + /// `pick_region_scc`. + pick_constraints_applied: Vec, + /// Map closure bounds to a `Span` that should be used for error reporting. closure_bounds_mapping: FxHashMap>, @@ -109,6 +116,32 @@ pub struct RegionInferenceContext<'tcx> { universal_region_relations: Rc>, } +/// Each time that `apply_pick_constraint` is successful, it appends +/// one of these structs to the `pick_constraints_applied` field. +/// This is used in error reporting to trace out what happened. +/// +/// The way that `apply_pick_constraint` works is that it effectively +/// adds a new lower bound to the SCC it is analyzing: so you wind up +/// with `'R: 'O` where `'R` is the pick-region and `'O` is the +/// minimal viable option. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] +struct AppliedPickConstraint { + /// The SCC that was affected. (The "pick region".) + /// + /// The vector if `AppliedPickConstraint` elements is kept sorted + /// by this field. + pick_region_scc: ConstraintSccIndex, + + /// The "best option" that `apply_pick_constraint` found -- this was + /// added as an "ad-hoc" lower-bound to `pick_region_scc`. + best_option: ty::RegionVid, + + /// The "pick constraint index" -- we can find out details about + /// the constraint from + /// `set.pick_constraints[pick_constraint_index]`. + pick_constraint_index: NllPickConstraintIndex, +} + struct RegionDefinition<'tcx> { /// What kind of variable is this -- a free region? existential /// variable? etc. (See the `NLLRegionVariableOrigin` for more @@ -243,6 +276,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { constraint_sccs, rev_constraint_graph: None, pick_constraints, + pick_constraints_applied: Vec::new(), closure_bounds_mapping, scc_universes, scc_representatives, @@ -411,6 +445,18 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.scc_universes[scc] } + /// Once region solving has completed, this function will return + /// the pick-constraints that were applied to the value of a given + /// region `r`. See `AppliedPickConstraint`. + fn applied_pick_constraints(&self, r: impl ToRegionVid) -> &[AppliedPickConstraint] { + let scc = self.constraint_sccs.scc(r.to_region_vid()); + binary_search_util::binary_search_slice( + &self.pick_constraints_applied, + |applied| applied.pick_region_scc, + &scc, + ) + } + /// Performs region inference and report errors if we see any /// unsatisfiable constraints. If this is a closure, returns the /// region requirements to propagate to our creator, if any. @@ -501,6 +547,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { for scc_index in self.constraint_sccs.all_sccs() { self.propagate_constraint_sccs_if_new(scc_index, visited); } + + // Sort the applied pick constraints so we can binary search + // through them later. + self.pick_constraints_applied.sort_by_key(|applied| applied.pick_region_scc); } /// Computes the value of the SCC `scc_a` if it has not already @@ -552,7 +602,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { for p_c_i in pick_constraints.indices(scc_a) { self.apply_pick_constraint( scc_a, - pick_constraints[p_c_i].opaque_type_def_id, + p_c_i, pick_constraints.option_regions(p_c_i), ); } @@ -578,7 +628,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { fn apply_pick_constraint( &mut self, scc: ConstraintSccIndex, - opaque_type_def_id: DefId, + pick_constraint_index: NllPickConstraintIndex, option_regions: &[ty::RegionVid], ) -> bool { debug!("apply_pick_constraint(scc={:?}, option_regions={:#?})", scc, option_regions,); @@ -593,7 +643,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { bug!( "pick constraint for `{:?}` has an option region `{:?}` \ that is not a universal region", - opaque_type_def_id, + self.pick_constraints[pick_constraint_index].opaque_type_def_id, uh_oh, ); } @@ -681,7 +731,17 @@ impl<'tcx> RegionInferenceContext<'tcx> { best_option, best_option_scc, ); - self.scc_values.add_region(scc, best_option_scc) + if self.scc_values.add_region(scc, best_option_scc) { + self.pick_constraints_applied.push(AppliedPickConstraint { + pick_region_scc: scc, + best_option, + pick_constraint_index, + }); + + true + } else { + false + } } /// Compute and return the reverse SCC-based constraint graph (lazilly). diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 9b7a62ba7648b..09f9b5510d0db 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -836,6 +836,7 @@ struct TypeChecker<'a, 'tcx> { infcx: &'a InferCtxt<'a, 'tcx>, param_env: ty::ParamEnv<'tcx>, last_span: Span, + body: &'a Body<'tcx>, /// User type annotations are shared between the main MIR and the MIR of /// all of the promoted items. user_type_annotations: &'a CanonicalUserTypeAnnotations<'tcx>, @@ -996,6 +997,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { infcx, last_span: DUMMY_SP, mir_def_id, + body, user_type_annotations: &body.user_type_annotations, param_env, region_bound_pairs, @@ -1233,6 +1235,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let infcx = self.infcx; let tcx = infcx.tcx; let param_env = self.param_env; + let body = self.body; debug!("eq_opaque_type_and_type: mir_def_id={:?}", self.mir_def_id); let opaque_type_map = self.fully_perform_op( locations, @@ -1248,6 +1251,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { dummy_body_id, param_env, &anon_ty, + locations.span(body), )); debug!( "eq_opaque_type_and_type: \ diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 37866bab9009d..b0be37772af39 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -856,7 +856,8 @@ fn typeck_tables_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx ty::TypeckT let revealed_ty = if tcx.features().impl_trait_in_bindings { fcx.instantiate_opaque_types_from_value( id, - &expected_type + &expected_type, + body.value.span, ) } else { expected_type @@ -962,7 +963,8 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { let revealed_ty = if self.fcx.tcx.features().impl_trait_in_bindings { self.fcx.instantiate_opaque_types_from_value( self.parent_id, - &o_ty + &o_ty, + ty.span, ) } else { o_ty @@ -1058,7 +1060,11 @@ fn check_fn<'a, 'tcx>( let declared_ret_ty = fn_sig.output(); fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); - let revealed_ret_ty = fcx.instantiate_opaque_types_from_value(fn_id, &declared_ret_ty); + let revealed_ret_ty = fcx.instantiate_opaque_types_from_value( + fn_id, + &declared_ret_ty, + decl.output.span(), + ); fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(revealed_ret_ty))); fn_sig = fcx.tcx.mk_fn_sig( fn_sig.inputs().iter().cloned(), @@ -2445,6 +2451,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, parent_id: hir::HirId, value: &T, + value_span: Span, ) -> T { let parent_def_id = self.tcx.hir().local_def_id_from_hir_id(parent_id); debug!("instantiate_opaque_types_from_value(parent_def_id={:?}, value={:?})", @@ -2457,6 +2464,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.body_id, self.param_env, value, + value_span, ) ); diff --git a/src/test/ui/impl-trait/multiple-lifetimes/error-handling.rs b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.rs new file mode 100644 index 0000000000000..b4cbf0ba8ed44 --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.rs @@ -0,0 +1,21 @@ +// compile-flags:-Zborrowck=mir + +#![feature(existential_type)] + +#[derive(Clone)] +struct CopyIfEq(T, U); + +impl Copy for CopyIfEq {} + +existential type E<'a, 'b>: Sized; +//~^ ERROR lifetime may not live long enough + +fn foo<'a, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> { + let v = CopyIfEq::<*mut _, *mut _>(&mut {x}, &mut y); + let u = v; + let _: *mut &'a i32 = u.1; + unsafe { let _: &'b i32 = *u.0; } + u.0 +} + +fn main() {} diff --git a/src/test/ui/impl-trait/multiple-lifetimes/error-handling.stderr b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.stderr new file mode 100644 index 0000000000000..f42ec5b62f11e --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.stderr @@ -0,0 +1,15 @@ +error: lifetime may not live long enough + --> $DIR/error-handling.rs:10:1 + | +LL | existential type E<'a, 'b>: Sized; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ opaque type requires that `'a` must outlive `'static` +... +LL | fn foo<'a, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> { + | -- lifetime `'a` defined here +help: to allow this `impl Trait` to capture borrowed data with lifetime `'a`, add `'a` as a constraint + | +LL | existential type E<'a, 'b>: Sized; + 'a + | + +error: aborting due to previous error + From adba6a8f902d967d762855b565677ee1d16608f6 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 17 Jun 2019 09:26:48 -0400 Subject: [PATCH 40/68] address nits by mattewjasper --- .../infer/lexical_region_resolve/mod.rs | 32 +++++-------------- src/librustc/infer/mod.rs | 2 +- src/librustc/infer/opaque_types/mod.rs | 7 ++-- .../graph/vec_graph/mod.rs | 1 + .../graph/vec_graph/test.rs | 2 -- .../ordinary-bounds-pick-original.stderr | 22 ------------- .../ordinary-bounds-pick-other.stderr | 11 ------- 7 files changed, 13 insertions(+), 64 deletions(-) delete mode 100644 src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.stderr delete mode 100644 src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.stderr diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index ac8db3c43365a..2779845a17aeb 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -197,26 +197,8 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } } - /// Enforce constraints of the form: - /// - /// ``` - /// 'r0 in ['o1...'oN] - /// ``` - /// - /// such a constraint simply means that `'r0` must be equal to one - /// of the regions `'o1...'oN`. This is an annoying constraint to - /// integrate into our inference, which generally works by - /// iteratively growing regions until we find a match -- that's - /// not an option here. - /// - /// What we currently do: - /// - /// - Search forward in the graph from `'r0` to find each region `'b` - /// where `'r0 <= 'b` must hold. - /// - Try to rule out some of the `'o1..'oN` options: - /// - if `'o[i] <= 'b` is false, then `'o[i]` is not an option - /// - /// Hopefully this narrows it down to just one option. + /// Enforce all pick constraints and return true if anything + /// changed. See `enforce_pick_constraint` for more details. fn enforce_pick_constraints( &self, graph: &RegionGraph<'tcx>, @@ -237,15 +219,17 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { /// pick 'r from ['o...] /// ``` /// - /// We look to see if there is a unique option `'o` from the list of options - /// that: + /// We look for all option regions from the list `'o...` that: /// - /// (a) is greater than the current value of `'r` (which is a lower bound) + /// (a) are greater than the current value of `'r` (which is a lower bound) /// /// and /// - /// (b) is compatible with the upper bounds of `'r` that we can + /// (b) are compatible with the upper bounds of `'r` that we can /// find by traversing the graph. + /// + /// From that list, we look for a *minimal* option `'o_min`. If we + /// find one, then we can enforce that `'r: 'o_min`. fn enforce_pick_constraint( &self, graph: &RegionGraph<'tcx>, diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 6f13cd8162965..95f224f546b37 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -915,7 +915,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { region: ty::Region<'tcx>, in_regions: &Rc>>, ) { - debug!("sub_regions({:?} <: {:?})", region, in_regions); + debug!("pick_constraint({:?} <: {:?})", region, in_regions); self.borrow_region_constraints() .pick_constraint(opaque_type_def_id, definition_span, hidden_ty, region, in_regions); } diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs index 9a339db047d33..8b5a0420f84b6 100644 --- a/src/librustc/infer/opaque_types/mod.rs +++ b/src/librustc/infer/opaque_types/mod.rs @@ -271,8 +271,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// is an inference variable). If we generated a constraint that /// `Tc: 'a`, then this would incorrectly require that `T: 'a` -- /// but this is not necessary, because the existential type we - /// create will be allowed to reference `T`. So instead we just - /// generate a constraint that `'0: 'a`. + /// create will be allowed to reference `T`. So we only generate a + /// constraint that `'0: 'a`. /// /// # The `free_region_relations` parameter /// @@ -563,8 +563,7 @@ pub fn unexpected_hidden_region_diagnostic( // ``` // // Here the captured lifetime is the intersection of `'a` and - // `'b`, which we can't quite express. This prticulararticular - // is kind of an unfortunate error anyway. + // `'b`, which we can't quite express. if let Some(region_scope_tree) = region_scope_tree { // If the `region_scope_tree` is available, this is being diff --git a/src/librustc_data_structures/graph/vec_graph/mod.rs b/src/librustc_data_structures/graph/vec_graph/mod.rs index 7078a5a9c9b4a..c425502f21948 100644 --- a/src/librustc_data_structures/graph/vec_graph/mod.rs +++ b/src/librustc_data_structures/graph/vec_graph/mod.rs @@ -1,6 +1,7 @@ use crate::indexed_vec::{Idx, IndexVec}; use crate::graph::{DirectedGraph, WithNumNodes, WithNumEdges, WithSuccessors, GraphSuccessors}; +#[cfg(test)] mod test; pub struct VecGraph { diff --git a/src/librustc_data_structures/graph/vec_graph/test.rs b/src/librustc_data_structures/graph/vec_graph/test.rs index c54a72264f66c..97a9bd2ad0b08 100644 --- a/src/librustc_data_structures/graph/vec_graph/test.rs +++ b/src/librustc_data_structures/graph/vec_graph/test.rs @@ -1,5 +1,3 @@ -#![cfg(test)] - use super::*; fn create_graph() -> VecGraph { diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.stderr deleted file mode 100644 index f55bba987ec3f..0000000000000 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.stderr +++ /dev/null @@ -1,22 +0,0 @@ -warning[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds - --> $DIR/ordinary-bounds-pick-original.rs:10:50 - | -LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { - | ^^^^^^^^^^^^^^^^^^ - | - = note: hidden type `(&u8, &u8)` captures lifetime '_#6r - = warning: this error has been downgraded to a warning for backwards compatibility with previous releases - = warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future - = note: for more information, try `rustc --explain E0729` - -warning[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds - --> $DIR/ordinary-bounds-pick-original.rs:10:50 - | -LL | fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { - | ^^^^^^^^^^^^^^^^^^ - | - = note: hidden type `(&u8, &u8)` captures lifetime '_#7r - = warning: this error has been downgraded to a warning for backwards compatibility with previous releases - = warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future - = note: for more information, try `rustc --explain E0729` - diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.stderr deleted file mode 100644 index a9b56210b0fea..0000000000000 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds - --> $DIR/ordinary-bounds-pick-other.rs:17:74 - | -LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> - | ^^^^^^^^^^^^^^^^^^ - | - = note: hidden type `Ordinary<'_>` captures lifetime '_#8r - = warning: this error has been downgraded to a warning for backwards compatibility with previous releases - = warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future - = note: for more information, try `rustc --explain E0729` - From 5d44bebb32b12687638a93cde551ee8919c305c1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 17 Jun 2019 11:38:38 -0400 Subject: [PATCH 41/68] bless test output --- .../ui/impl-trait/multiple-lifetimes/error-handling.rs | 2 +- .../ui/impl-trait/multiple-lifetimes/error-handling.stderr | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/test/ui/impl-trait/multiple-lifetimes/error-handling.rs b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.rs index b4cbf0ba8ed44..061221846345b 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/error-handling.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.rs @@ -8,9 +8,9 @@ struct CopyIfEq(T, U); impl Copy for CopyIfEq {} existential type E<'a, 'b>: Sized; -//~^ ERROR lifetime may not live long enough fn foo<'a, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> { + //~^ ERROR lifetime may not live long enough let v = CopyIfEq::<*mut _, *mut _>(&mut {x}, &mut y); let u = v; let _: *mut &'a i32 = u.1; diff --git a/src/test/ui/impl-trait/multiple-lifetimes/error-handling.stderr b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.stderr index f42ec5b62f11e..8355399506d79 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/error-handling.stderr +++ b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.stderr @@ -1,11 +1,8 @@ error: lifetime may not live long enough - --> $DIR/error-handling.rs:10:1 + --> $DIR/error-handling.rs:12:56 | -LL | existential type E<'a, 'b>: Sized; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ opaque type requires that `'a` must outlive `'static` -... LL | fn foo<'a, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> { - | -- lifetime `'a` defined here + | -- lifetime `'a` defined here ^^^^^^^^^ opaque type requires that `'a` must outlive `'static` help: to allow this `impl Trait` to capture borrowed data with lifetime `'a`, add `'a` as a constraint | LL | existential type E<'a, 'b>: Sized; + 'a From fb2c997d4eb34b616bb391442c2ab3b5a2f20e36 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 17 Jun 2019 16:23:07 -0400 Subject: [PATCH 42/68] fix silly bugs in binary_search_util test --- src/librustc_data_structures/binary_search_util/test.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/librustc_data_structures/binary_search_util/test.rs b/src/librustc_data_structures/binary_search_util/test.rs index a6361d912656a..d74febb5c0fc4 100644 --- a/src/librustc_data_structures/binary_search_util/test.rs +++ b/src/librustc_data_structures/binary_search_util/test.rs @@ -4,7 +4,8 @@ type Element = (usize, &'static str); fn test_map() -> Vec { let mut data = vec![(3, "three-a"), (0, "zero"), (3, "three-b"), (22, "twenty-two")]; - data.sort_by_key(get_key) + data.sort_by_key(get_key); + data } fn get_key(data: &Element) -> usize { @@ -12,7 +13,7 @@ fn get_key(data: &Element) -> usize { } #[test] -fn binary_search_slice() { +fn binary_search_slice_test() { let map = test_map(); assert_eq!(binary_search_slice(&map, get_key, &0), &[(0, "zero")]); assert_eq!(binary_search_slice(&map, get_key, &1), &[]); From e6b6873525795ecc36fafcce86db3ce08bc1cb6c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 17 Jun 2019 23:56:35 -0400 Subject: [PATCH 43/68] switch to Lrc --- src/librustc/infer/lexical_region_resolve/mod.rs | 8 ++++---- src/librustc/infer/mod.rs | 4 ++-- src/librustc/infer/opaque_types/mod.rs | 4 ++-- src/librustc/infer/region_constraints/mod.rs | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index 2779845a17aeb..4492560816401 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -247,7 +247,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // The current value of `vid` is a lower bound LB -- i.e., we // know that `LB <= vid` must be true. - let pick_lower_bound = match var_values.value(pick_vid) { + let pick_lower_bound: ty::Region<'tcx> = match var_values.value(pick_vid) { VarValue::ErrorValue => return false, VarValue::Value(r) => r, }; @@ -270,12 +270,12 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // if there >1 option, we only make a choice if there is a // single *least* choice -- i.e., some available region that // is `<=` all the others. - let mut least_choice = match options.next() { - Some(r) => r, + let mut least_choice: ty::Region<'tcx> = match options.next() { + Some(&r) => r, None => return false, }; debug!("enforce_pick_constraint: least_choice={:?}", least_choice); - for option in options { + for &option in options { debug!("enforce_pick_constraint: option={:?}", option); if !self.sub_concrete_regions(least_choice, option) { if self.sub_concrete_regions(option, least_choice) { diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 95f224f546b37..d887dc13f59d2 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -26,11 +26,11 @@ use crate::ty::{FloatVid, IntVid, TyVid, ConstVid}; use crate::util::nodemap::FxHashMap; use errors::DiagnosticBuilder; +use rustc_data_structures::sync::Lrc; use rustc_data_structures::unify as ut; use std::cell::{Cell, Ref, RefCell, RefMut}; use std::collections::BTreeMap; use std::fmt; -use std::rc::Rc; use syntax::ast; use syntax_pos::symbol::InternedString; use syntax_pos::Span; @@ -913,7 +913,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { definition_span: Span, hidden_ty: Ty<'tcx>, region: ty::Region<'tcx>, - in_regions: &Rc>>, + in_regions: &Lrc>>, ) { debug!("pick_constraint({:?} <: {:?})", region, in_regions); self.borrow_region_constraints() diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs index 8b5a0420f84b6..0571de8680f8b 100644 --- a/src/librustc/infer/opaque_types/mod.rs +++ b/src/librustc/infer/opaque_types/mod.rs @@ -11,7 +11,7 @@ use crate::ty::{self, GenericParamDefKind, Ty, TyCtxt}; use crate::util::nodemap::DefIdMap; use errors::DiagnosticBuilder; use rustc_data_structures::fx::FxHashMap; -use std::rc::Rc; +use rustc_data_structures::sync::Lrc; use syntax_pos::Span; pub type OpaqueTypeMap<'tcx> = DefIdMap>; @@ -428,7 +428,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // Create the set of option regions: each region in the hidden // type can be equal to any of the region parameters of the // opaque type definition. - let option_regions: Rc>> = Rc::new( + let option_regions: Lrc>> = Lrc::new( abstract_type_generics .params .iter() diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index 1558022fd3172..a8a2ae343a592 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -8,6 +8,7 @@ use super::{MiscVariable, RegionVariableOrigin, SubregionOrigin}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::indexed_vec::IndexVec; +use rustc_data_structures::sync::Lrc; use rustc_data_structures::unify as ut; use crate::hir::def_id::DefId; use crate::ty::ReStatic; @@ -19,7 +20,6 @@ use syntax_pos::Span; use std::collections::BTreeMap; use std::{cmp, fmt, mem}; use std::ops::Range; -use std::rc::Rc; mod leak_check; @@ -166,7 +166,7 @@ pub struct PickConstraint<'tcx> { pub pick_region: Region<'tcx>, /// the options O1..On - pub option_regions: Rc>>, + pub option_regions: Lrc>>, } BraceStructTypeFoldableImpl! { @@ -694,7 +694,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { definition_span: Span, hidden_ty: Ty<'tcx>, pick_region: ty::Region<'tcx>, - option_regions: &Rc>>, + option_regions: &Lrc>>, ) { debug!("pick_constraint({:?} in {:#?})", pick_region, option_regions); From e39f66a859160668504b43d6664a01df86147b42 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 18 Jun 2019 08:15:27 -0400 Subject: [PATCH 44/68] implement Lift for Arc --- src/librustc/ty/structural_impls.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 0028d125cec78..b37e676570d33 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -15,6 +15,7 @@ use crate::mir::interpret; use std::fmt; use std::rc::Rc; +use std::sync::Arc; impl fmt::Debug for ty::GenericParamDef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -385,6 +386,13 @@ impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Rc { } } +impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Arc { + type Lifted = Arc; + fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(&**self).map(Arc::new) + } +} + impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for [T] { type Lifted = Vec; fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { From eca55b58d6bd493e33f627a9d566695e4feab5e0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 18 Jun 2019 08:52:23 -0400 Subject: [PATCH 45/68] rename to "member constraints" --- src/librustc/infer/canonical/mod.rs | 10 +- .../infer/canonical/query_response.rs | 10 +- src/librustc/infer/error_reporting/mod.rs | 12 +- .../infer/lexical_region_resolve/mod.rs | 89 ++++---- src/librustc/infer/mod.rs | 6 +- src/librustc/infer/opaque_types/mod.rs | 12 +- src/librustc/infer/region_constraints/mod.rs | 46 ++--- src/librustc/ty/structural_impls.rs | 2 +- src/librustc_mir/borrow_check/nll/mod.rs | 6 +- .../borrow_check/nll/pick_constraints.rs | 119 +++++------ .../nll/region_infer/error_reporting/mod.rs | 10 +- .../borrow_check/nll/region_infer/mod.rs | 191 +++++++++--------- .../nll/type_check/constraint_conversion.rs | 15 +- .../borrow_check/nll/type_check/mod.rs | 8 +- 14 files changed, 275 insertions(+), 261 deletions(-) diff --git a/src/librustc/infer/canonical/mod.rs b/src/librustc/infer/canonical/mod.rs index 308abb5e13325..b508f91e01ebb 100644 --- a/src/librustc/infer/canonical/mod.rs +++ b/src/librustc/infer/canonical/mod.rs @@ -23,7 +23,7 @@ use crate::infer::{InferCtxt, RegionVariableOrigin, TypeVariableOrigin, TypeVariableOriginKind}; use crate::infer::{ConstVariableOrigin, ConstVariableOriginKind}; -use crate::infer::region_constraints::PickConstraint; +use crate::infer::region_constraints::MemberConstraint; use crate::mir::interpret::ConstValue; use rustc_data_structures::indexed_vec::IndexVec; use rustc_macros::HashStable; @@ -198,14 +198,14 @@ pub struct QueryResponse<'tcx, R> { #[derive(Clone, Debug, Default, HashStable)] pub struct QueryRegionConstraints<'tcx> { pub outlives: Vec>, - pub pick_constraints: Vec>, + pub member_constraints: Vec>, } impl QueryRegionConstraints<'_> { /// Represents an empty (trivially true) set of region /// constraints. pub fn is_empty(&self) -> bool { - self.outlives.is_empty() && self.pick_constraints.is_empty() + self.outlives.is_empty() && self.member_constraints.is_empty() } } @@ -558,14 +558,14 @@ BraceStructLiftImpl! { BraceStructTypeFoldableImpl! { impl<'tcx> TypeFoldable<'tcx> for QueryRegionConstraints<'tcx> { - outlives, pick_constraints + outlives, member_constraints } } BraceStructLiftImpl! { impl<'a, 'tcx> Lift<'tcx> for QueryRegionConstraints<'a> { type Lifted = QueryRegionConstraints<'tcx>; - outlives, pick_constraints + outlives, member_constraints } } diff --git a/src/librustc/infer/canonical/query_response.rs b/src/librustc/infer/canonical/query_response.rs index 02a5005be7604..79c5538626be1 100644 --- a/src/librustc/infer/canonical/query_response.rs +++ b/src/librustc/infer/canonical/query_response.rs @@ -351,9 +351,9 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { }) ); - // ...also include the query pick constraints. - output_query_region_constraints.pick_constraints.extend( - query_response.value.region_constraints.pick_constraints.iter().map(|p_c| { + // ...also include the query member constraints. + output_query_region_constraints.member_constraints.extend( + query_response.value.region_constraints.member_constraints.iter().map(|p_c| { substitute_value(self.tcx, &result_subst, p_c) }) ); @@ -663,7 +663,7 @@ pub fn make_query_region_constraints<'tcx>( constraints, verifys, givens, - pick_constraints, + member_constraints, } = region_constraints; assert!(verifys.is_empty()); @@ -694,5 +694,5 @@ pub fn make_query_region_constraints<'tcx>( ) .collect(); - QueryRegionConstraints { outlives, pick_constraints: pick_constraints.clone() } + QueryRegionConstraints { outlives, member_constraints: member_constraints.clone() } } diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 5a89fd89941d4..cbfb048c064a2 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -377,12 +377,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } - RegionResolutionError::PickConstraintFailure { + RegionResolutionError::MemberConstraintFailure { opaque_type_def_id, hidden_ty, - pick_region, + member_region, span: _, - option_regions: _, + choice_regions: _, } => { let hidden_ty = self.resolve_vars_if_possible(&hidden_ty); opaque_types::unexpected_hidden_region_diagnostic( @@ -390,7 +390,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { Some(region_scope_tree), opaque_type_def_id, hidden_ty, - pick_region, + member_region, ).emit(); } } @@ -430,7 +430,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { RegionResolutionError::GenericBoundFailure(..) => true, RegionResolutionError::ConcreteFailure(..) | RegionResolutionError::SubSupConflict(..) - | RegionResolutionError::PickConstraintFailure { .. } => false, + | RegionResolutionError::MemberConstraintFailure { .. } => false, }; let mut errors = if errors.iter().all(|e| is_bound_failure(e)) { @@ -448,7 +448,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(), RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(), RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _) => rvo.span(), - RegionResolutionError::PickConstraintFailure { span, .. } => span, + RegionResolutionError::MemberConstraintFailure { span, .. } => span, }); errors } diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index 4492560816401..c718badf6e063 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -3,7 +3,7 @@ use crate::hir::def_id::DefId; use crate::infer::region_constraints::Constraint; use crate::infer::region_constraints::GenericKind; -use crate::infer::region_constraints::PickConstraint; +use crate::infer::region_constraints::MemberConstraint; use crate::infer::region_constraints::RegionConstraintData; use crate::infer::region_constraints::VarInfos; use crate::infer::region_constraints::VerifyBound; @@ -84,15 +84,15 @@ pub enum RegionResolutionError<'tcx> { Region<'tcx>, ), - /// Indicates a failure of a `PickConstraint`. These arise during + /// Indicates a failure of a `MemberConstraint`. These arise during /// impl trait processing explicitly -- basically, the impl trait's hidden type /// included some region that it was not supposed to. - PickConstraintFailure { + MemberConstraintFailure { span: Span, opaque_type_def_id: DefId, hidden_ty: Ty<'tcx>, - pick_region: Region<'tcx>, - option_regions: Vec>, + member_region: Region<'tcx>, + choice_regions: Vec>, }, } @@ -133,7 +133,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { self.expand_givens(&graph); loop { self.expansion(&mut var_data); - if !self.enforce_pick_constraints(&graph, &mut var_data) { + if !self.enforce_member_constraints(&graph, &mut var_data) { break; } } @@ -197,16 +197,16 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } } - /// Enforce all pick constraints and return true if anything - /// changed. See `enforce_pick_constraint` for more details. - fn enforce_pick_constraints( + /// Enforce all member constraints and return true if anything + /// changed. See `enforce_member_constraint` for more details. + fn enforce_member_constraints( &self, graph: &RegionGraph<'tcx>, var_values: &mut LexicalRegionResolutions<'tcx>, ) -> bool { let mut any_changed = false; - for pick_constraint in &self.data.pick_constraints { - if self.enforce_pick_constraint(graph, pick_constraint, var_values) { + for member_constraint in &self.data.member_constraints { + if self.enforce_member_constraint(graph, member_constraint, var_values) { any_changed = true; } } @@ -230,39 +230,44 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { /// /// From that list, we look for a *minimal* option `'o_min`. If we /// find one, then we can enforce that `'r: 'o_min`. - fn enforce_pick_constraint( + fn enforce_member_constraint( &self, graph: &RegionGraph<'tcx>, - pick_constraint: &PickConstraint<'tcx>, + member_constraint: &MemberConstraint<'tcx>, var_values: &mut LexicalRegionResolutions<'tcx>, ) -> bool { - debug!("enforce_pick_constraint(pick_constraint={:#?})", pick_constraint); + debug!("enforce_member_constraint(member_constraint={:#?})", member_constraint); // the constraint is some inference variable (`vid`) which // must be equal to one of the options - let pick_vid = match pick_constraint.pick_region { + let member_vid = match member_constraint.member_region { ty::ReVar(vid) => *vid, _ => return false, }; // The current value of `vid` is a lower bound LB -- i.e., we // know that `LB <= vid` must be true. - let pick_lower_bound: ty::Region<'tcx> = match var_values.value(pick_vid) { + let member_lower_bound: ty::Region<'tcx> = match var_values.value(member_vid) { VarValue::ErrorValue => return false, VarValue::Value(r) => r, }; // find all the "upper bounds" -- that is, each region `b` such that // `r0 <= b` must hold. - let (pick_upper_bounds, _) = self.collect_concrete_regions(graph, pick_vid, OUTGOING, None); + let (member_upper_bounds, _) = self.collect_concrete_regions( + graph, + member_vid, + OUTGOING, + None, + ); - // get an iterator over the *available options* -- that is, - // each constraint regions `o` where `lb <= o` and `o <= ub` for all the + // get an iterator over the *available choice* -- that is, + // each choice region `c` where `lb <= c` and `c <= ub` for all the // upper bounds `ub`. - debug!("enforce_pick_constraint: upper_bounds={:#?}", pick_upper_bounds); - let mut options = pick_constraint.option_regions.iter().filter(|option| { - self.sub_concrete_regions(pick_lower_bound, option) - && pick_upper_bounds + debug!("enforce_member_constraint: upper_bounds={:#?}", member_upper_bounds); + let mut options = member_constraint.choice_regions.iter().filter(|option| { + self.sub_concrete_regions(member_lower_bound, option) + && member_upper_bounds .iter() .all(|upper_bound| self.sub_concrete_regions(option, upper_bound.region)) }); @@ -274,23 +279,23 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { Some(&r) => r, None => return false, }; - debug!("enforce_pick_constraint: least_choice={:?}", least_choice); + debug!("enforce_member_constraint: least_choice={:?}", least_choice); for &option in options { - debug!("enforce_pick_constraint: option={:?}", option); + debug!("enforce_member_constraint: option={:?}", option); if !self.sub_concrete_regions(least_choice, option) { if self.sub_concrete_regions(option, least_choice) { - debug!("enforce_pick_constraint: new least choice"); + debug!("enforce_member_constraint: new least choice"); least_choice = option; } else { - debug!("enforce_pick_constraint: no least choice"); + debug!("enforce_member_constraint: no least choice"); return false; } } } - debug!("enforce_pick_constraint: final least choice = {:?}", least_choice); - if least_choice != pick_lower_bound { - *var_values.value_mut(pick_vid) = VarValue::Value(least_choice); + debug!("enforce_member_constraint: final least choice = {:?}", least_choice); + if least_choice != member_lower_bound { + *var_values.value_mut(member_vid) = VarValue::Value(least_choice); true } else { false @@ -547,20 +552,20 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } } - for pick_constraint in &self.data.pick_constraints { - let pick_region = var_data.normalize(self.tcx(), pick_constraint.pick_region); - let option_regions = pick_constraint - .option_regions + for member_constraint in &self.data.member_constraints { + let member_region = var_data.normalize(self.tcx(), member_constraint.member_region); + let choice_regions = member_constraint + .choice_regions .iter() - .map(|&option_region| var_data.normalize(self.tcx(), option_region)); - if !option_regions.clone().any(|option_region| pick_region == option_region) { - let span = self.tcx().def_span(pick_constraint.opaque_type_def_id); - errors.push(RegionResolutionError::PickConstraintFailure { + .map(|&choice_region| var_data.normalize(self.tcx(), choice_region)); + if !choice_regions.clone().any(|choice_region| member_region == choice_region) { + let span = self.tcx().def_span(member_constraint.opaque_type_def_id); + errors.push(RegionResolutionError::MemberConstraintFailure { span, - opaque_type_def_id: pick_constraint.opaque_type_def_id, - hidden_ty: pick_constraint.hidden_ty, - pick_region, - option_regions: option_regions.collect(), + opaque_type_def_id: member_constraint.opaque_type_def_id, + hidden_ty: member_constraint.hidden_ty, + member_region, + choice_regions: choice_regions.collect(), }); } } diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index d887dc13f59d2..663acd67dcd83 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -907,7 +907,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// Require that the region `r` be equal to one of the regions in /// the set `regions`. - pub fn pick_constraint( + pub fn member_constraint( &self, opaque_type_def_id: DefId, definition_span: Span, @@ -915,9 +915,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { region: ty::Region<'tcx>, in_regions: &Lrc>>, ) { - debug!("pick_constraint({:?} <: {:?})", region, in_regions); + debug!("member_constraint({:?} <: {:?})", region, in_regions); self.borrow_region_constraints() - .pick_constraint(opaque_type_def_id, definition_span, hidden_ty, region, in_regions); + .member_constraint(opaque_type_def_id, definition_span, hidden_ty, region, in_regions); } pub fn subtype_predicate( diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs index 0571de8680f8b..2e35aefa88ae6 100644 --- a/src/librustc/infer/opaque_types/mod.rs +++ b/src/librustc/infer/opaque_types/mod.rs @@ -393,7 +393,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // we will create a "in bound" like `'r in // ['a, 'b, 'c]`, where `'a..'c` are the // regions that appear in the impl trait. - return self.generate_pick_constraint( + return self.generate_member_constraint( concrete_ty, abstract_type_generics, opaque_defn, @@ -418,17 +418,17 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// related, we would generate a constraint `'r in ['a, 'b, /// 'static]` for each region `'r` that appears in the hidden type /// (i.e., it must be equal to `'a`, `'b`, or `'static`). - fn generate_pick_constraint( + fn generate_member_constraint( &self, concrete_ty: Ty<'tcx>, abstract_type_generics: &ty::Generics, opaque_defn: &OpaqueTypeDecl<'tcx>, opaque_type_def_id: DefId, ) { - // Create the set of option regions: each region in the hidden + // Create the set of choice regions: each region in the hidden // type can be equal to any of the region parameters of the // opaque type definition. - let option_regions: Lrc>> = Lrc::new( + let choice_regions: Lrc>> = Lrc::new( abstract_type_generics .params .iter() @@ -443,12 +443,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { tcx: self.tcx, - op: |r| self.pick_constraint( + op: |r| self.member_constraint( opaque_type_def_id, opaque_defn.definition_span, concrete_ty, r, - &option_regions, + &choice_regions, ), }); } diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index a8a2ae343a592..0636589060763 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -81,10 +81,10 @@ pub struct RegionConstraintData<'tcx> { /// be a region variable (or neither, as it happens). pub constraints: BTreeMap, SubregionOrigin<'tcx>>, - /// Constraints of the form `pick R0 from [R1, ..., Rn]`, meaning that + /// Constraints of the form `R0 member of [R1, ..., Rn]`, meaning that /// `R0` must be equal to one of the regions `R1..Rn`. These occur /// with `impl Trait` quite frequently. - pub pick_constraints: Vec>, + pub member_constraints: Vec>, /// A "verify" is something that we need to verify after inference /// is done, but which does not directly affect inference in any @@ -145,40 +145,40 @@ impl Constraint<'_> { } } -/// Requires that `region` must be equal to one of the regions in `option_regions`. +/// Requires that `region` must be equal to one of the regions in `choice_regions`. /// We often denote this using the syntax: /// /// ``` -/// pick R0 from [O1..On] +/// R0 member of [O1..On] /// ``` #[derive(Debug, Clone, HashStable)] -pub struct PickConstraint<'tcx> { +pub struct MemberConstraint<'tcx> { /// the def-id of the opaque type causing this constraint: used for error reporting pub opaque_type_def_id: DefId, /// the span where the hidden type was instantiated pub definition_span: Span, - /// the hidden type in which `pick_region` appears: used for error reporting + /// the hidden type in which `member_region` appears: used for error reporting pub hidden_ty: Ty<'tcx>, /// the region R0 - pub pick_region: Region<'tcx>, + pub member_region: Region<'tcx>, /// the options O1..On - pub option_regions: Lrc>>, + pub choice_regions: Lrc>>, } BraceStructTypeFoldableImpl! { - impl<'tcx> TypeFoldable<'tcx> for PickConstraint<'tcx> { - opaque_type_def_id, definition_span, hidden_ty, pick_region, option_regions + impl<'tcx> TypeFoldable<'tcx> for MemberConstraint<'tcx> { + opaque_type_def_id, definition_span, hidden_ty, member_region, choice_regions } } BraceStructLiftImpl! { - impl<'a, 'tcx> Lift<'tcx> for PickConstraint<'a> { - type Lifted = PickConstraint<'tcx>; - opaque_type_def_id, definition_span, hidden_ty, pick_region, option_regions + impl<'a, 'tcx> Lift<'tcx> for MemberConstraint<'a> { + type Lifted = MemberConstraint<'tcx>; + opaque_type_def_id, definition_span, hidden_ty, member_region, choice_regions } } @@ -688,26 +688,26 @@ impl<'tcx> RegionConstraintCollector<'tcx> { } } - pub fn pick_constraint( + pub fn member_constraint( &mut self, opaque_type_def_id: DefId, definition_span: Span, hidden_ty: Ty<'tcx>, - pick_region: ty::Region<'tcx>, - option_regions: &Lrc>>, + member_region: ty::Region<'tcx>, + choice_regions: &Lrc>>, ) { - debug!("pick_constraint({:?} in {:#?})", pick_region, option_regions); + debug!("member_constraint({:?} in {:#?})", member_region, choice_regions); - if option_regions.iter().any(|&r| r == pick_region) { + if choice_regions.iter().any(|&r| r == member_region) { return; } - self.data.pick_constraints.push(PickConstraint { + self.data.member_constraints.push(MemberConstraint { opaque_type_def_id, definition_span, hidden_ty, - pick_region, - option_regions: option_regions.clone() + member_region, + choice_regions: choice_regions.clone() }); } @@ -975,12 +975,12 @@ impl<'tcx> RegionConstraintData<'tcx> { pub fn is_empty(&self) -> bool { let RegionConstraintData { constraints, - pick_constraints, + member_constraints, verifys, givens, } = self; constraints.is_empty() && - pick_constraints.is_empty() && + member_constraints.is_empty() && verifys.is_empty() && givens.is_empty() } diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index b37e676570d33..f1749d39d6e41 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -349,7 +349,7 @@ impl<'tcx, A: Lift<'tcx>, B: Lift<'tcx>, C: Lift<'tcx>> Lift<'tcx> for (A, B, C) tcx.lift(&self.0).and_then(|a| { tcx.lift(&self.1).and_then(|b| tcx.lift(&self.2).map(|c| (a, b, c))) }) - } + } } impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Option { diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 380bfb52e3ba3..eb63e0de195e5 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -37,7 +37,7 @@ crate mod type_check; mod universal_regions; mod constraints; -mod pick_constraints; +mod member_constraints; use self::facts::AllFacts; use self::region_infer::RegionInferenceContext; @@ -130,7 +130,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( placeholder_index_to_region: _, mut liveness_constraints, outlives_constraints, - pick_constraints, + member_constraints, closure_bounds_mapping, type_tests, } = constraints; @@ -152,7 +152,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( universal_region_relations, body, outlives_constraints, - pick_constraints, + member_constraints, closure_bounds_mapping, type_tests, liveness_constraints, diff --git a/src/librustc_mir/borrow_check/nll/pick_constraints.rs b/src/librustc_mir/borrow_check/nll/pick_constraints.rs index aa0f7852273ab..43212ffddf3a5 100644 --- a/src/librustc_mir/borrow_check/nll/pick_constraints.rs +++ b/src/librustc_mir/borrow_check/nll/pick_constraints.rs @@ -1,36 +1,36 @@ use crate::rustc::ty::{self, Ty}; use rustc::hir::def_id::DefId; -use rustc::infer::region_constraints::PickConstraint; +use rustc::infer::region_constraints::MemberConstraint; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use std::hash::Hash; use std::ops::Index; use syntax_pos::Span; -/// Compactly stores a set of `pick R0 in [R1...Rn]` constraints, +/// Compactly stores a set of `R0 member of [R1...Rn]` constraints, /// indexed by the region R0. -crate struct PickConstraintSet<'tcx, R> +crate struct MemberConstraintSet<'tcx, R> where R: Copy + Hash + Eq, { - /// Stores the first "pick" constraint for a given R0. This is an + /// Stores the first "member" constraint for a given R0. This is an /// index into the `constraints` vector below. - first_constraints: FxHashMap, + first_constraints: FxHashMap, - /// Stores the data about each `pick R0 from [R1..Rn]` constraint. + /// Stores the data about each `R0 member of [R1..Rn]` constraint. /// These are organized into a linked list, so each constraint /// contains the index of the next constraint with the same R0. - constraints: IndexVec>, + constraints: IndexVec>, /// Stores the `R1..Rn` regions for *all* sets. For any given /// constraint, we keep two indices so that we can pull out a /// slice. - option_regions: Vec, + choice_regions: Vec, } -/// Represents a `pick R0 in [R1..Rn]` constraint -crate struct NllPickConstraint<'tcx> { - next_constraint: Option, +/// Represents a `R0 member of [R1..Rn]` constraint +crate struct NllMemberConstraint<'tcx> { + next_constraint: Option, /// The opaque type whose hidden type is being inferred. (Used in error reporting.) crate opaque_type_def_id: DefId, @@ -42,67 +42,70 @@ crate struct NllPickConstraint<'tcx> { crate hidden_ty: Ty<'tcx>, /// The region R0. - crate pick_region_vid: ty::RegionVid, + crate member_region_vid: ty::RegionVid, - /// Index of `R1` in `option_regions` vector from `PickConstraintSet`. + /// Index of `R1` in `choice_regions` vector from `MemberConstraintSet`. start_index: usize, - /// Index of `Rn` in `option_regions` vector from `PickConstraintSet`. + /// Index of `Rn` in `choice_regions` vector from `MemberConstraintSet`. end_index: usize, } newtype_index! { - crate struct NllPickConstraintIndex { - DEBUG_FORMAT = "PickConstraintIndex({})" + crate struct NllMemberConstraintIndex { + DEBUG_FORMAT = "MemberConstraintIndex({})" } } -impl Default for PickConstraintSet<'tcx, ty::RegionVid> { +impl Default for MemberConstraintSet<'tcx, ty::RegionVid> { fn default() -> Self { Self { first_constraints: Default::default(), constraints: Default::default(), - option_regions: Default::default(), + choice_regions: Default::default(), } } } -impl<'tcx> PickConstraintSet<'tcx, ty::RegionVid> { +impl<'tcx> MemberConstraintSet<'tcx, ty::RegionVid> { crate fn push_constraint( &mut self, - p_c: &PickConstraint<'tcx>, + m_c: &MemberConstraint<'tcx>, mut to_region_vid: impl FnMut(ty::Region<'tcx>) -> ty::RegionVid, ) { - debug!("push_constraint(p_c={:?})", p_c); - let pick_region_vid: ty::RegionVid = to_region_vid(p_c.pick_region); - let next_constraint = self.first_constraints.get(&pick_region_vid).cloned(); - let start_index = self.option_regions.len(); - let end_index = start_index + p_c.option_regions.len(); - debug!("push_constraint: pick_region_vid={:?}", pick_region_vid); - let constraint_index = self.constraints.push(NllPickConstraint { + debug!("push_constraint(m_c={:?})", m_c); + let member_region_vid: ty::RegionVid = to_region_vid(m_c.member_region); + let next_constraint = self.first_constraints.get(&member_region_vid).cloned(); + let start_index = self.choice_regions.len(); + let end_index = start_index + m_c.choice_regions.len(); + debug!("push_constraint: member_region_vid={:?}", member_region_vid); + let constraint_index = self.constraints.push(NllMemberConstraint { next_constraint, - pick_region_vid, - opaque_type_def_id: p_c.opaque_type_def_id, - definition_span: p_c.definition_span, - hidden_ty: p_c.hidden_ty, + member_region_vid, + opaque_type_def_id: m_c.opaque_type_def_id, + definition_span: m_c.definition_span, + hidden_ty: m_c.hidden_ty, start_index, end_index, }); - self.first_constraints.insert(pick_region_vid, constraint_index); - self.option_regions.extend(p_c.option_regions.iter().map(|&r| to_region_vid(r))); + self.first_constraints.insert(member_region_vid, constraint_index); + self.choice_regions.extend(m_c.choice_regions.iter().map(|&r| to_region_vid(r))); } } -impl<'tcx, R1> PickConstraintSet<'tcx, R1> +impl<'tcx, R1> MemberConstraintSet<'tcx, R1> where R1: Copy + Hash + Eq, { - /// Remap the "pick region" key using `map_fn`, producing a new + /// Remap the "member region" key using `map_fn`, producing a new /// pick-constraint set. This is used in the NLL code to map from /// the original `RegionVid` to an scc index. In some cases, we /// may have multiple R1 values mapping to the same R2 key -- that /// is ok, the two sets will be merged. - crate fn into_mapped(self, mut map_fn: impl FnMut(R1) -> R2) -> PickConstraintSet<'tcx, R2> + crate fn into_mapped( + self, + mut map_fn: impl FnMut(R1) -> R2, + ) -> MemberConstraintSet<'tcx, R2> where R2: Copy + Hash + Eq, { @@ -116,7 +119,7 @@ where // link from one list to point at the othe other (see // `append_list`). - let PickConstraintSet { first_constraints, mut constraints, option_regions } = self; + let MemberConstraintSet { first_constraints, mut constraints, choice_regions } = self; let mut first_constraints2 = FxHashMap::default(); first_constraints2.reserve(first_constraints.len()); @@ -129,33 +132,33 @@ where first_constraints2.insert(r2, start1); } - PickConstraintSet { + MemberConstraintSet { first_constraints: first_constraints2, constraints, - option_regions, + choice_regions, } } } -impl<'tcx, R> PickConstraintSet<'tcx, R> +impl<'tcx, R> MemberConstraintSet<'tcx, R> where R: Copy + Hash + Eq, { crate fn all_indices( &self, - ) -> impl Iterator { + ) -> impl Iterator { self.constraints.indices() } /// Iterate down the constraint indices associated with a given - /// peek-region. You can then use `option_regions` and other + /// peek-region. You can then use `choice_regions` and other /// methods to access data. crate fn indices( &self, - pick_region_vid: R, - ) -> impl Iterator + '_ { - let mut next = self.first_constraints.get(&pick_region_vid).cloned(); - std::iter::from_fn(move || -> Option { + member_region_vid: R, + ) -> impl Iterator + '_ { + let mut next = self.first_constraints.get(&member_region_vid).cloned(); + std::iter::from_fn(move || -> Option { if let Some(current) = next { next = self.constraints[current].next_constraint; Some(current) @@ -165,25 +168,25 @@ where }) } - /// Returns the "option regions" for a given pick constraint. This is the R1..Rn from - /// a constraint like: + /// Returns the "choice regions" for a given member + /// constraint. This is the R1..Rn from a constraint like: /// /// ``` - /// pick R0 in [R1..Rn] + /// R0 member of [R1..Rn] /// ``` - crate fn option_regions(&self, pci: NllPickConstraintIndex) -> &[ty::RegionVid] { - let NllPickConstraint { start_index, end_index, .. } = &self.constraints[pci]; - &self.option_regions[*start_index..*end_index] + crate fn choice_regions(&self, pci: NllMemberConstraintIndex) -> &[ty::RegionVid] { + let NllMemberConstraint { start_index, end_index, .. } = &self.constraints[pci]; + &self.choice_regions[*start_index..*end_index] } } -impl<'tcx, R> Index for PickConstraintSet<'tcx, R> +impl<'tcx, R> Index for MemberConstraintSet<'tcx, R> where R: Copy + Hash + Eq, { - type Output = NllPickConstraint<'tcx>; + type Output = NllMemberConstraint<'tcx>; - fn index(&self, i: NllPickConstraintIndex) -> &NllPickConstraint<'tcx> { + fn index(&self, i: NllMemberConstraintIndex) -> &NllMemberConstraint<'tcx> { &self.constraints[i] } } @@ -205,9 +208,9 @@ where /// target_list: A -> B -> C -> D -> E -> F -> (None) /// ``` fn append_list( - constraints: &mut IndexVec>, - target_list: NllPickConstraintIndex, - source_list: NllPickConstraintIndex, + constraints: &mut IndexVec>, + target_list: NllMemberConstraintIndex, + source_list: NllMemberConstraintIndex, ) { let mut p = target_list; loop { diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs index 34fd3427d326c..b3649b2179fff 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs @@ -1,5 +1,5 @@ use crate::borrow_check::nll::constraints::OutlivesConstraint; -use crate::borrow_check::nll::region_infer::AppliedPickConstraint; +use crate::borrow_check::nll::region_infer::AppliedMemberConstraint; use crate::borrow_check::nll::region_infer::RegionInferenceContext; use crate::borrow_check::nll::type_check::Locations; use crate::borrow_check::nll::universal_regions::DefiningTy; @@ -224,13 +224,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { // But pick-constraints can also give rise to `'r: 'x` // edges that were not part of the graph initially, so // watch out for those. - let outgoing_edges_from_picks = self.applied_pick_constraints(r) + let outgoing_edges_from_picks = self.applied_member_constraints(r) .iter() - .map(|&AppliedPickConstraint { best_option, pick_constraint_index, .. }| { - let p_c = &self.pick_constraints[pick_constraint_index]; + .map(|&AppliedMemberConstraint { min_choice, member_constraint_index, .. }| { + let p_c = &self.member_constraints[member_constraint_index]; OutlivesConstraint { sup: r, - sub: best_option, + sub: min_choice, locations: Locations::All(p_c.definition_span), category: ConstraintCategory::OpaqueType, } diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index db519fad0d9e1..78f6e731ce244 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -3,7 +3,7 @@ use crate::borrow_check::nll::constraints::graph::NormalConstraintGraph; use crate::borrow_check::nll::constraints::{ ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet, }; -use crate::borrow_check::nll::pick_constraints::{PickConstraintSet, NllPickConstraintIndex}; +use crate::borrow_check::nll::member_constraints::{MemberConstraintSet, NllMemberConstraintIndex}; use crate::borrow_check::nll::region_infer::values::{ PlaceholderIndices, RegionElement, ToElementIndex, }; @@ -72,14 +72,14 @@ pub struct RegionInferenceContext<'tcx> { /// exists if `B: A`. Computed lazilly. rev_constraint_graph: Option>>, - /// The "pick R0 from [R1..Rn]" constraints, indexed by SCC. - pick_constraints: Rc>, + /// The "R0 member of [R1..Rn]" constraints, indexed by SCC. + member_constraints: Rc>, /// Records the pick-constraints that we applied to each scc. /// This is useful for error reporting. Once constraint /// propagation is done, this vector is sorted according to - /// `pick_region_scc`. - pick_constraints_applied: Vec, + /// `member_region_scc`. + member_constraints_applied: Vec, /// Map closure bounds to a `Span` that should be used for error reporting. closure_bounds_mapping: @@ -116,30 +116,30 @@ pub struct RegionInferenceContext<'tcx> { universal_region_relations: Rc>, } -/// Each time that `apply_pick_constraint` is successful, it appends -/// one of these structs to the `pick_constraints_applied` field. +/// Each time that `apply_member_constraint` is successful, it appends +/// one of these structs to the `member_constraints_applied` field. /// This is used in error reporting to trace out what happened. /// -/// The way that `apply_pick_constraint` works is that it effectively +/// The way that `apply_member_constraint` works is that it effectively /// adds a new lower bound to the SCC it is analyzing: so you wind up /// with `'R: 'O` where `'R` is the pick-region and `'O` is the /// minimal viable option. #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] -struct AppliedPickConstraint { - /// The SCC that was affected. (The "pick region".) +struct AppliedMemberConstraint { + /// The SCC that was affected. (The "member region".) /// - /// The vector if `AppliedPickConstraint` elements is kept sorted + /// The vector if `AppliedMemberConstraint` elements is kept sorted /// by this field. - pick_region_scc: ConstraintSccIndex, + member_region_scc: ConstraintSccIndex, - /// The "best option" that `apply_pick_constraint` found -- this was - /// added as an "ad-hoc" lower-bound to `pick_region_scc`. - best_option: ty::RegionVid, + /// The "best option" that `apply_member_constraint` found -- this was + /// added as an "ad-hoc" lower-bound to `member_region_scc`. + min_choice: ty::RegionVid, - /// The "pick constraint index" -- we can find out details about + /// The "member constraint index" -- we can find out details about /// the constraint from - /// `set.pick_constraints[pick_constraint_index]`. - pick_constraint_index: NllPickConstraintIndex, + /// `set.member_constraints[member_constraint_index]`. + member_constraint_index: NllMemberConstraintIndex, } struct RegionDefinition<'tcx> { @@ -234,7 +234,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { universal_region_relations: Rc>, _body: &Body<'tcx>, outlives_constraints: OutlivesConstraintSet, - pick_constraints_in: PickConstraintSet<'tcx, RegionVid>, + member_constraints_in: MemberConstraintSet<'tcx, RegionVid>, closure_bounds_mapping: FxHashMap< Location, FxHashMap<(RegionVid, RegionVid), (ConstraintCategory, Span)>, @@ -266,7 +266,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { let scc_representatives = Self::compute_scc_representatives(&constraint_sccs, &definitions); - let pick_constraints = Rc::new(pick_constraints_in.into_mapped(|r| constraint_sccs.scc(r))); + let member_constraints = + Rc::new(member_constraints_in.into_mapped(|r| constraint_sccs.scc(r))); let mut result = Self { definitions, @@ -275,8 +276,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { constraint_graph, constraint_sccs, rev_constraint_graph: None, - pick_constraints, - pick_constraints_applied: Vec::new(), + member_constraints, + member_constraints_applied: Vec::new(), closure_bounds_mapping, scc_universes, scc_representatives, @@ -447,12 +448,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// Once region solving has completed, this function will return /// the pick-constraints that were applied to the value of a given - /// region `r`. See `AppliedPickConstraint`. - fn applied_pick_constraints(&self, r: impl ToRegionVid) -> &[AppliedPickConstraint] { + /// region `r`. See `AppliedMemberConstraint`. + fn applied_member_constraints(&self, r: impl ToRegionVid) -> &[AppliedMemberConstraint] { let scc = self.constraint_sccs.scc(r.to_region_vid()); binary_search_util::binary_search_slice( - &self.pick_constraints_applied, - |applied| applied.pick_region_scc, + &self.member_constraints_applied, + |applied| applied.member_region_scc, &scc, ) } @@ -511,7 +512,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { errors_buffer, ); - self.check_pick_constraints(infcx, mir_def_id, errors_buffer); + self.check_member_constraints(infcx, mir_def_id, errors_buffer); let outlives_requirements = outlives_requirements.unwrap_or(vec![]); @@ -548,9 +549,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.propagate_constraint_sccs_if_new(scc_index, visited); } - // Sort the applied pick constraints so we can binary search + // Sort the applied member constraints so we can binary search // through them later. - self.pick_constraints_applied.sort_by_key(|applied| applied.pick_region_scc); + self.member_constraints_applied.sort_by_key(|applied| applied.member_region_scc); } /// Computes the value of the SCC `scc_a` if it has not already @@ -597,13 +598,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } - // Now take pick constraints into account - let pick_constraints = self.pick_constraints.clone(); - for p_c_i in pick_constraints.indices(scc_a) { - self.apply_pick_constraint( + // Now take member constraints into account + let member_constraints = self.member_constraints.clone(); + for m_c_i in member_constraints.indices(scc_a) { + self.apply_member_constraint( scc_a, - p_c_i, - pick_constraints.option_regions(p_c_i), + m_c_i, + member_constraints.choice_regions(m_c_i), ); } @@ -614,9 +615,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { ); } - /// Invoked for each `pick R0 from [R1..Rn]` constraint. + /// Invoked for each `member R0 of [R1..Rn]` constraint. /// - /// `scc` is the SCC containing R0, and `option_regions` are the + /// `scc` is the SCC containing R0, and `choice_regions` are the /// `R1..Rn` regions -- they are always known to be universal /// regions (and if that's not true, we just don't attempt to /// enforce the constraint). @@ -625,34 +626,34 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// is considered a *lower bound*. If possible, we will modify /// the constraint to set it equal to one of the option regions. /// If we make any changes, returns true, else false. - fn apply_pick_constraint( + fn apply_member_constraint( &mut self, scc: ConstraintSccIndex, - pick_constraint_index: NllPickConstraintIndex, - option_regions: &[ty::RegionVid], + member_constraint_index: NllMemberConstraintIndex, + choice_regions: &[ty::RegionVid], ) -> bool { - debug!("apply_pick_constraint(scc={:?}, option_regions={:#?})", scc, option_regions,); + debug!("apply_member_constraint(scc={:?}, choice_regions={:#?})", scc, choice_regions,); if let Some(uh_oh) = - option_regions.iter().find(|&&r| !self.universal_regions.is_universal_region(r)) + choice_regions.iter().find(|&&r| !self.universal_regions.is_universal_region(r)) { // FIXME(#61773): This case can only occur with // `impl_trait_in_bindings`, I believe, and we are just // opting not to handle it for now. See #61773 for // details. bug!( - "pick constraint for `{:?}` has an option region `{:?}` \ + "member constraint for `{:?}` has an option region `{:?}` \ that is not a universal region", - self.pick_constraints[pick_constraint_index].opaque_type_def_id, + self.member_constraints[member_constraint_index].opaque_type_def_id, uh_oh, ); } // Create a mutable vector of the options. We'll try to winnow // them down. - let mut option_regions: Vec = option_regions.to_vec(); + let mut choice_regions: Vec = choice_regions.to_vec(); - // The 'pick-region' in a pick-constraint is part of the + // The 'member region' in a member constraint is part of the // hidden type, which must be in the root universe. Therefore, // it cannot have any placeholders in its value. assert!(self.scc_universes[scc] == ty::UniverseIndex::ROOT); @@ -665,35 +666,37 @@ impl<'tcx> RegionInferenceContext<'tcx> { // The existing value for `scc` is a lower-bound. This will // consist of some set {P} + {LB} of points {P} and - // lower-bound free regions {LB}. As each option region O is a + // lower-bound free regions {LB}. As each choice region O is a // free region, it will outlive the points. But we can only // consider the option O if O: LB. - option_regions.retain(|&o_r| { + choice_regions.retain(|&o_r| { self.scc_values .universal_regions_outlived_by(scc) .all(|lb| self.universal_region_relations.outlives(o_r, lb)) }); - debug!("apply_pick_constraint: after lb, option_regions={:?}", option_regions); + debug!("apply_member_constraint: after lb, choice_regions={:?}", choice_regions); - // Now find all the *upper bounds* -- that is, each UB is a free - // region that must outlive pick region R0 (`UB: R0`). Therefore, - // we need only keep an option O if `UB: O` for all UB. - if option_regions.len() > 1 { + // Now find all the *upper bounds* -- that is, each UB is a + // free region that must outlive the member region R0 (`UB: + // R0`). Therefore, we need only keep an option O if `UB: O` + // for all UB. + if choice_regions.len() > 1 { let universal_region_relations = self.universal_region_relations.clone(); for ub in self.upper_bounds(scc) { - debug!("apply_pick_constraint: ub={:?}", ub); - option_regions.retain(|&o_r| universal_region_relations.outlives(ub, o_r)); + debug!("apply_member_constraint: ub={:?}", ub); + choice_regions.retain(|&o_r| universal_region_relations.outlives(ub, o_r)); } - debug!("apply_pick_constraint: after ub, option_regions={:?}", option_regions); + debug!("apply_member_constraint: after ub, choice_regions={:?}", choice_regions); } // If we ruled everything out, we're done. - if option_regions.is_empty() { + if choice_regions.is_empty() { return false; } - // Otherwise, we need to find the minimum option, if any, and take that. - debug!("apply_pick_constraint: option_regions remaining are {:#?}", option_regions); + // Otherwise, we need to find the minimum remaining choice, if + // any, and take that. + debug!("apply_member_constraint: choice_regions remaining are {:#?}", choice_regions); let min = |r1: ty::RegionVid, r2: ty::RegionVid| -> Option { let r1_outlives_r2 = self.universal_region_relations.outlives(r1, r2); let r2_outlives_r1 = self.universal_region_relations.outlives(r2, r1); @@ -707,35 +710,35 @@ impl<'tcx> RegionInferenceContext<'tcx> { None } }; - let mut best_option = option_regions[0]; - for &other_option in &option_regions[1..] { + let mut min_choice = choice_regions[0]; + for &other_option in &choice_regions[1..] { debug!( - "apply_pick_constraint: best_option={:?} other_option={:?}", - best_option, other_option, + "apply_member_constraint: min_choice={:?} other_option={:?}", + min_choice, other_option, ); - match min(best_option, other_option) { - Some(m) => best_option = m, + match min(min_choice, other_option) { + Some(m) => min_choice = m, None => { debug!( - "apply_pick_constraint: {:?} and {:?} are incomparable --> no best choice", - best_option, other_option, + "apply_member_constraint: {:?} and {:?} are incomparable; no min choice", + min_choice, other_option, ); return false; } } } - let best_option_scc = self.constraint_sccs.scc(best_option); + let min_choice_scc = self.constraint_sccs.scc(min_choice); debug!( - "apply_pick_constraint: best_choice={:?} best_option_scc={:?}", - best_option, - best_option_scc, + "apply_member_constraint: min_choice={:?} best_choice_scc={:?}", + min_choice, + min_choice_scc, ); - if self.scc_values.add_region(scc, best_option_scc) { - self.pick_constraints_applied.push(AppliedPickConstraint { - pick_region_scc: scc, - best_option, - pick_constraint_index, + if self.scc_values.add_region(scc, min_choice_scc) { + self.member_constraints_applied.push(AppliedMemberConstraint { + member_region_scc: scc, + min_choice, + member_constraint_index, }); true @@ -1542,42 +1545,42 @@ impl<'tcx> RegionInferenceContext<'tcx> { diag.emit(); } - fn check_pick_constraints( + fn check_member_constraints( &self, infcx: &InferCtxt<'_, 'tcx>, mir_def_id: DefId, errors_buffer: &mut Vec, ) { - let pick_constraints = self.pick_constraints.clone(); - for p_c_i in pick_constraints.all_indices() { - debug!("check_pick_constraint(p_c_i={:?})", p_c_i); - let p_c = &pick_constraints[p_c_i]; - let pick_region_vid = p_c.pick_region_vid; + let member_constraints = self.member_constraints.clone(); + for m_c_i in member_constraints.all_indices() { + debug!("check_member_constraint(m_c_i={:?})", m_c_i); + let m_c = &member_constraints[m_c_i]; + let member_region_vid = m_c.member_region_vid; debug!( - "check_pick_constraint: pick_region_vid={:?} with value {}", - pick_region_vid, - self.region_value_str(pick_region_vid), + "check_member_constraint: member_region_vid={:?} with value {}", + member_region_vid, + self.region_value_str(member_region_vid), ); - let option_regions = pick_constraints.option_regions(p_c_i); - debug!("check_pick_constraint: option_regions={:?}", option_regions); + let choice_regions = member_constraints.choice_regions(m_c_i); + debug!("check_member_constraint: choice_regions={:?}", choice_regions); // did the pick-region wind up equal to any of the option regions? - if let Some(o) = option_regions.iter().find(|&&o_r| { - self.eval_equal(o_r, p_c.pick_region_vid) + if let Some(o) = choice_regions.iter().find(|&&o_r| { + self.eval_equal(o_r, m_c.member_region_vid) }) { - debug!("check_pick_constraint: evaluated as equal to {:?}", o); + debug!("check_member_constraint: evaluated as equal to {:?}", o); continue; } // if not, report an error let region_scope_tree = &infcx.tcx.region_scope_tree(mir_def_id); - let pick_region = infcx.tcx.mk_region(ty::ReVar(pick_region_vid)); + let member_region = infcx.tcx.mk_region(ty::ReVar(member_region_vid)); opaque_types::unexpected_hidden_region_diagnostic( infcx.tcx, Some(region_scope_tree), - p_c.opaque_type_def_id, - p_c.hidden_ty, - pick_region, + m_c.opaque_type_def_id, + m_c.hidden_ty, + member_region, ) .buffer(errors_buffer); } diff --git a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs index e7ae9de3c2e6c..8de014522dea7 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/constraint_conversion.rs @@ -53,20 +53,23 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { pub(super) fn convert_all(&mut self, query_constraints: &QueryRegionConstraints<'tcx>) { debug!("convert_all(query_constraints={:#?})", query_constraints); - let QueryRegionConstraints { outlives, pick_constraints } = query_constraints; + let QueryRegionConstraints { outlives, member_constraints } = query_constraints; // Annoying: to invoke `self.to_region_vid`, we need access to // `self.constraints`, but we also want to be mutating - // `self.pick_constraints`. For now, just swap out the value + // `self.member_constraints`. For now, just swap out the value // we want and replace at the end. - let mut tmp = std::mem::replace(&mut self.constraints.pick_constraints, Default::default()); - for pick_constraint in pick_constraints { + let mut tmp = std::mem::replace( + &mut self.constraints.member_constraints, + Default::default(), + ); + for member_constraint in member_constraints { tmp.push_constraint( - pick_constraint, + member_constraint, |r| self.to_region_vid(r), ); } - self.constraints.pick_constraints = tmp; + self.constraints.member_constraints = tmp; for query_constraint in outlives { self.convert(query_constraint); diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 09f9b5510d0db..2cdd1363cdf96 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -5,7 +5,7 @@ use crate::borrow_check::borrow_set::BorrowSet; use crate::borrow_check::location::LocationTable; use crate::borrow_check::nll::constraints::{OutlivesConstraintSet, OutlivesConstraint}; -use crate::borrow_check::nll::pick_constraints::PickConstraintSet; +use crate::borrow_check::nll::member_constraints::MemberConstraintSet; use crate::borrow_check::nll::facts::AllFacts; use crate::borrow_check::nll::region_infer::values::LivenessValues; use crate::borrow_check::nll::region_infer::values::PlaceholderIndex; @@ -129,7 +129,7 @@ pub(crate) fn type_check<'tcx>( placeholder_index_to_region: IndexVec::default(), liveness_constraints: LivenessValues::new(elements.clone()), outlives_constraints: OutlivesConstraintSet::default(), - pick_constraints: PickConstraintSet::default(), + member_constraints: MemberConstraintSet::default(), closure_bounds_mapping: Default::default(), type_tests: Vec::default(), }; @@ -889,7 +889,7 @@ crate struct MirTypeckRegionConstraints<'tcx> { crate outlives_constraints: OutlivesConstraintSet, - crate pick_constraints: PickConstraintSet<'tcx, RegionVid>, + crate member_constraints: MemberConstraintSet<'tcx, RegionVid>, crate closure_bounds_mapping: FxHashMap>, @@ -2525,7 +2525,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // constraints only come from `-> impl Trait` and // friends which don't appear (thus far...) in // closures. - pick_constraints: vec![], + member_constraints: vec![], }; let bounds_mapping = closure_constraints From 134fc4a929d65978752d958bdc7a92ea7ec95b0a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 18 Jun 2019 08:54:52 -0400 Subject: [PATCH 46/68] rename `pick_constraints.rs` to `member_constraints.rs` --- .../nll/{pick_constraints.rs => member_constraints.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/librustc_mir/borrow_check/nll/{pick_constraints.rs => member_constraints.rs} (100%) diff --git a/src/librustc_mir/borrow_check/nll/pick_constraints.rs b/src/librustc_mir/borrow_check/nll/member_constraints.rs similarity index 100% rename from src/librustc_mir/borrow_check/nll/pick_constraints.rs rename to src/librustc_mir/borrow_check/nll/member_constraints.rs From cbc75c699c1be9bf6c735351b7b07ab7d0569cdf Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 18 Jun 2019 19:15:31 -0400 Subject: [PATCH 47/68] implement `TypeFoldable` for `Arc` --- src/librustc/ty/structural_impls.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index f1749d39d6e41..28b52dcea80f1 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -853,6 +853,16 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Rc { } } +impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Arc { + fn super_fold_with>(&self, folder: &mut F) -> Self { + Arc::new((**self).fold_with(folder)) + } + + fn super_visit_with>(&self, visitor: &mut V) -> bool { + (**self).visit_with(visitor) + } +} + impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Box { fn super_fold_with>(&self, folder: &mut F) -> Self { let content: T = (**self).fold_with(folder); From 74a6efbf00e2fff08afe623a0a01155b4bebd0a3 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 20 Jun 2019 10:44:20 -0400 Subject: [PATCH 48/68] feature-gate member constraints outside of async-await Minimizes risk. --- .../language-features/member-constraints.md | 29 ++++++++ src/librustc/infer/opaque_types/mod.rs | 72 +++++++++++++++++++ src/libsyntax/feature_gate.rs | 3 + src/libsyntax_pos/symbol.rs | 1 + .../feature-gate-member-constraints.rs | 9 +++ .../feature-gate-member-constraints.stderr | 10 +++ .../multiple-lifetimes/error-handling.rs | 1 + .../multiple-lifetimes/error-handling.stderr | 2 +- .../multiple-lifetimes/inverse-bounds.rs | 2 + .../ordinary-bounds-pick-original-elided.rs | 2 + ...dinary-bounds-pick-original-existential.rs | 1 + .../ordinary-bounds-pick-original.rs | 2 + .../ordinary-bounds-pick-other.rs | 2 + .../ordinary-bounds-unrelated.rs | 2 + .../ordinary-bounds-unrelated.stderr | 6 +- .../ordinary-bounds-unsuited.rs | 2 + .../ordinary-bounds-unsuited.stderr | 6 +- .../impl-trait/needs_least_region_or_bound.rs | 2 + 18 files changed, 147 insertions(+), 7 deletions(-) create mode 100644 src/doc/unstable-book/src/language-features/member-constraints.md create mode 100644 src/test/ui/feature-gates/feature-gate-member-constraints.rs create mode 100644 src/test/ui/feature-gates/feature-gate-member-constraints.stderr diff --git a/src/doc/unstable-book/src/language-features/member-constraints.md b/src/doc/unstable-book/src/language-features/member-constraints.md new file mode 100644 index 0000000000000..71f2a10d092e6 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/member-constraints.md @@ -0,0 +1,29 @@ +# `member_constraints` + +The tracking issue for this feature is: [#61977] + +[#61977]: https://github.com/rust-lang/rust/issues/61977 + +------------------------ + +The `member_constraints` feature gate lets you use `impl Trait` syntax with +multiple unrelated lifetime parameters. + +A simple example is: + +```rust +#![feature(member_constraints)] + +trait Trait { } +impl Trait<'_, '_> for T {} + +fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Trait<'a, 'b> { + (x, y) +} + +fn main() { } +``` + +Without the `member_constraints` feature gate, the above example is an +error because both `'a` and `'b` appear in the impl Trait bounds, but +neither outlives the other. diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs index 2e35aefa88ae6..9c2cc5815de2e 100644 --- a/src/librustc/infer/opaque_types/mod.rs +++ b/src/librustc/infer/opaque_types/mod.rs @@ -10,6 +10,7 @@ use crate::ty::subst::{InternalSubsts, Kind, SubstsRef, UnpackedKind}; use crate::ty::{self, GenericParamDefKind, Ty, TyCtxt}; use crate::util::nodemap::DefIdMap; use errors::DiagnosticBuilder; +use rustc::session::config::nightly_options; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; use syntax_pos::Span; @@ -398,6 +399,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { abstract_type_generics, opaque_defn, def_id, + lr, + subst_arg, ); } } @@ -418,13 +421,28 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// related, we would generate a constraint `'r in ['a, 'b, /// 'static]` for each region `'r` that appears in the hidden type /// (i.e., it must be equal to `'a`, `'b`, or `'static`). + /// + /// `conflict1` and `conflict2` are the two region bounds that we + /// detected which were unrelated. They are used for diagnostics. fn generate_member_constraint( &self, concrete_ty: Ty<'tcx>, abstract_type_generics: &ty::Generics, opaque_defn: &OpaqueTypeDecl<'tcx>, opaque_type_def_id: DefId, + conflict1: ty::Region<'tcx>, + conflict2: ty::Region<'tcx>, ) { + // For now, enforce a feature gate outside of async functions. + if self.member_constraint_feature_gate( + opaque_defn, + opaque_type_def_id, + conflict1, + conflict2, + ) { + return; + } + // Create the set of choice regions: each region in the hidden // type can be equal to any of the region parameters of the // opaque type definition. @@ -453,6 +471,60 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }); } + /// Member constraints are presently feature-gated except for + /// async-await. We expect to lift this once we've had a bit more + /// time. + fn member_constraint_feature_gate( + &self, + opaque_defn: &OpaqueTypeDecl<'tcx>, + opaque_type_def_id: DefId, + conflict1: ty::Region<'tcx>, + conflict2: ty::Region<'tcx>, + ) -> bool { + // If we have `#![feature(member_constraints)]`, no problems. + if self.tcx.features().member_constraints { + return false; + } + + let span = self.tcx.def_span(opaque_type_def_id); + + // Otherwise, we allow for async-await but not otherwise. + let context_name = match opaque_defn.origin { + hir::ExistTyOrigin::ExistentialType => "existential type", + hir::ExistTyOrigin::ReturnImplTrait => "impl Trait", + hir::ExistTyOrigin::AsyncFn => { + // we permit + return false; + } + }; + let msg = format!("ambiguous lifetime bound in `{}`", context_name); + let mut err = self.tcx.sess.struct_span_err(span, &msg); + + let conflict1_name = conflict1.to_string(); + let conflict2_name = conflict2.to_string(); + let label_owned; + let label = match (&*conflict1_name, &*conflict2_name) { + ("'_", "'_") => "the elided lifetimes here do not outlive one another", + _ => { + label_owned = format!( + "neither `{}` nor `{}` outlives the other", + conflict1_name, conflict2_name, + ); + &label_owned + } + }; + err.span_label(span, label); + + if nightly_options::is_nightly_build() { + help!(err, + "add #![feature(member_constraints)] to the crate attributes \ + to enable"); + } + + err.emit(); + true + } + /// Given the fully resolved, instantiated type for an opaque /// type, i.e., the value of an inference variable like C1 or C2 /// (*), computes the "definition type" for an abstract type diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index f97e9d43854fc..1223c069d6569 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -570,6 +570,9 @@ declare_features! ( // Allows explicit discriminants on non-unit enum variants. (active, arbitrary_enum_discriminant, "1.37.0", Some(60553), None), + // Allows impl trait with multiple unrelated lifetimes + (active, member_constraints, "1.37.0", Some(61977), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 266bd2a04a222..aee988d5148ae 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -389,6 +389,7 @@ symbols! { match_beginning_vert, match_default_bindings, may_dangle, + member_constraints, message, meta, min_const_fn, diff --git a/src/test/ui/feature-gates/feature-gate-member-constraints.rs b/src/test/ui/feature-gates/feature-gate-member-constraints.rs new file mode 100644 index 0000000000000..293a93352e641 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-member-constraints.rs @@ -0,0 +1,9 @@ +trait Trait<'a, 'b> { } +impl Trait<'_, '_> for T {} + +fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Trait<'a, 'b> { + //~^ ERROR ambiguous lifetime bound + (x, y) +} + +fn main() { } diff --git a/src/test/ui/feature-gates/feature-gate-member-constraints.stderr b/src/test/ui/feature-gates/feature-gate-member-constraints.stderr new file mode 100644 index 0000000000000..3745d5e1c59d6 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-member-constraints.stderr @@ -0,0 +1,10 @@ +error: ambiguous lifetime bound in `impl Trait` + --> $DIR/feature-gate-member-constraints.rs:4:43 + | +LL | fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Trait<'a, 'b> { + | ^^^^^^^^^^^^^^^^^^ neither `'a` nor `'b` outlives the other + | + = help: add #![feature(member_constraints)] to the crate attributes to enable + +error: aborting due to previous error + diff --git a/src/test/ui/impl-trait/multiple-lifetimes/error-handling.rs b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.rs index 061221846345b..61e858ee02d44 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/error-handling.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.rs @@ -1,5 +1,6 @@ // compile-flags:-Zborrowck=mir +#![feature(member_constraints)] #![feature(existential_type)] #[derive(Clone)] diff --git a/src/test/ui/impl-trait/multiple-lifetimes/error-handling.stderr b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.stderr index 8355399506d79..b59dfbe9f2ada 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/error-handling.stderr +++ b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.stderr @@ -1,5 +1,5 @@ error: lifetime may not live long enough - --> $DIR/error-handling.rs:12:56 + --> $DIR/error-handling.rs:13:56 | LL | fn foo<'a, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> { | -- lifetime `'a` defined here ^^^^^^^^^ opaque type requires that `'a` must outlive `'static` diff --git a/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs b/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs index 53d2bc190895b..2da3886bb552b 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs @@ -3,6 +3,8 @@ // revisions: migrate mir //[mir]compile-flags: -Z borrowck=mir +#![feature(member_constraints)] + trait Trait<'a, 'b> {} impl Trait<'_, '_> for T {} diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-elided.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-elided.rs index 23981d92562d2..e2cb574fac084 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-elided.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-elided.rs @@ -3,6 +3,8 @@ // revisions: migrate mir //[mir]compile-flags: -Z borrowck=mir +#![feature(member_constraints)] + trait Trait<'a, 'b> { } impl Trait<'_, '_> for T { } diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-existential.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-existential.rs index be249eeb9eadb..a1ec89e8fbd05 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-existential.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-existential.rs @@ -3,6 +3,7 @@ // revisions: migrate mir //[mir]compile-flags: -Z borrowck=mir +#![feature(member_constraints)] #![feature(existential_type)] trait Trait<'a, 'b> { } diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs index 0d04980d37a8f..21979b001797e 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs @@ -3,6 +3,8 @@ // revisions: migrate mir //[mir]compile-flags: -Z borrowck=mir +#![feature(member_constraints)] + trait Trait<'a, 'b> { } impl Trait<'_, '_> for T { } diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.rs index 6555bacba6716..0dfc118d78c29 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.rs @@ -3,6 +3,8 @@ // revisions: migrate mir //[mir]compile-flags: -Z borrowck=mir +#![feature(member_constraints)] + trait Trait<'a, 'b> {} impl Trait<'_, '_> for T {} diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.rs index 3a97624647efd..db1641b0140b9 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.rs @@ -1,5 +1,7 @@ // edition:2018 +#![feature(member_constraints)] + trait Trait<'a, 'b> {} impl Trait<'_, '_> for T {} diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr index c807048ce5468..cd2d46ac18218 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr @@ -1,11 +1,11 @@ error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds - --> $DIR/ordinary-bounds-unrelated.rs:16:74 + --> $DIR/ordinary-bounds-unrelated.rs:18:74 | LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> | ^^^^^^^^^^^^^^^^^^ | -note: hidden type `Ordinary<'_>` captures the scope of call-site for function at 21:1 - --> $DIR/ordinary-bounds-unrelated.rs:21:1 +note: hidden type `Ordinary<'_>` captures the scope of call-site for function at 23:1 + --> $DIR/ordinary-bounds-unrelated.rs:23:1 | LL | / { LL | | // Hidden type `Ordinary<'0>` with constraints: diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.rs index 85c478d8d313a..8f85b444d0886 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.rs @@ -1,5 +1,7 @@ // edition:2018 +#![feature(member_constraints)] + trait Trait<'a, 'b> {} impl Trait<'_, '_> for T {} diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr index 6687c40f957a6..59ce93fa78b6b 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr @@ -1,11 +1,11 @@ error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds - --> $DIR/ordinary-bounds-unsuited.rs:18:62 + --> $DIR/ordinary-bounds-unsuited.rs:20:62 | LL | fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> | ^^^^^^^^^^^^^^^^^^ | -note: hidden type `Ordinary<'_>` captures the scope of call-site for function at 20:1 - --> $DIR/ordinary-bounds-unsuited.rs:20:1 +note: hidden type `Ordinary<'_>` captures the scope of call-site for function at 22:1 + --> $DIR/ordinary-bounds-unsuited.rs:22:1 | LL | / { LL | | // We return a value: diff --git a/src/test/ui/impl-trait/needs_least_region_or_bound.rs b/src/test/ui/impl-trait/needs_least_region_or_bound.rs index 8475122cfae32..52475f65a8353 100644 --- a/src/test/ui/impl-trait/needs_least_region_or_bound.rs +++ b/src/test/ui/impl-trait/needs_least_region_or_bound.rs @@ -1,5 +1,7 @@ // run-pass +#![feature(member_constraints)] + use std::fmt::Debug; trait MultiRegionTrait<'a, 'b> {} From cf721c5c73104bfe99bba02d7912ba38f159be88 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 24 Jun 2019 14:51:29 -0400 Subject: [PATCH 49/68] Update src/doc/unstable-book/src/language-features/member-constraints.md Co-Authored-By: Mazdak Farrokhzad --- .../unstable-book/src/language-features/member-constraints.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/unstable-book/src/language-features/member-constraints.md b/src/doc/unstable-book/src/language-features/member-constraints.md index 71f2a10d092e6..0d11c31aca6e9 100644 --- a/src/doc/unstable-book/src/language-features/member-constraints.md +++ b/src/doc/unstable-book/src/language-features/member-constraints.md @@ -14,7 +14,7 @@ A simple example is: ```rust #![feature(member_constraints)] -trait Trait { } +trait Trait<'a, 'b> { } impl Trait<'_, '_> for T {} fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Trait<'a, 'b> { From 56ae0bfc7965b34e9170f2c954c6fbe6cbd86839 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 24 Jun 2019 15:03:19 -0400 Subject: [PATCH 50/68] Update src/librustc/infer/lexical_region_resolve/mod.rs Co-Authored-By: Mazdak Farrokhzad --- src/librustc/infer/lexical_region_resolve/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index c718badf6e063..17f2321f8dbab 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -238,7 +238,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { ) -> bool { debug!("enforce_member_constraint(member_constraint={:#?})", member_constraint); - // the constraint is some inference variable (`vid`) which + // The constraint is some inference variable (`vid`) which // must be equal to one of the options let member_vid = match member_constraint.member_region { ty::ReVar(vid) => *vid, From d2772e89899071bfb830d949ab53022b758cdf06 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 24 Jun 2019 15:00:09 -0400 Subject: [PATCH 51/68] explain why the code is the way it is --- src/librustc/infer/lexical_region_resolve/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index 17f2321f8dbab..aa37ed6e9e060 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -204,6 +204,8 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { graph: &RegionGraph<'tcx>, var_values: &mut LexicalRegionResolutions<'tcx>, ) -> bool { + // Note: we don't use the `any` combinator because we don't + // want to stop at the first constraint that makes a change. let mut any_changed = false; for member_constraint in &self.data.member_constraints { if self.enforce_member_constraint(graph, member_constraint, var_values) { From b4a3753cfb9472da78539ecbd97913118f132f8b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 24 Jun 2019 15:06:58 -0400 Subject: [PATCH 52/68] various centril nits --- .../infer/lexical_region_resolve/mod.rs | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index aa37ed6e9e060..7517f9c6de9d2 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -218,10 +218,10 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { /// Enforce a constraint like /// /// ``` - /// pick 'r from ['o...] + /// 'r member of ['c...] /// ``` /// - /// We look for all option regions from the list `'o...` that: + /// We look for all choice regions from the list `'c...` that: /// /// (a) are greater than the current value of `'r` (which is a lower bound) /// @@ -230,8 +230,8 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { /// (b) are compatible with the upper bounds of `'r` that we can /// find by traversing the graph. /// - /// From that list, we look for a *minimal* option `'o_min`. If we - /// find one, then we can enforce that `'r: 'o_min`. + /// From that list, we look for a *minimal* option `'c_min`. If we + /// find one, then we can enforce that `'r: 'c_min`. fn enforce_member_constraint( &self, graph: &RegionGraph<'tcx>, @@ -241,7 +241,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { debug!("enforce_member_constraint(member_constraint={:#?})", member_constraint); // The constraint is some inference variable (`vid`) which - // must be equal to one of the options + // must be equal to one of the options. let member_vid = match member_constraint.member_region { ty::ReVar(vid) => *vid, _ => return false, @@ -254,7 +254,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { VarValue::Value(r) => r, }; - // find all the "upper bounds" -- that is, each region `b` such that + // Find all the "upper bounds" -- that is, each region `b` such that // `r0 <= b` must hold. let (member_upper_bounds, _) = self.collect_concrete_regions( graph, @@ -263,7 +263,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { None, ); - // get an iterator over the *available choice* -- that is, + // Get an iterator over the *available choice* -- that is, // each choice region `c` where `lb <= c` and `c <= ub` for all the // upper bounds `ub`. debug!("enforce_member_constraint: upper_bounds={:#?}", member_upper_bounds); @@ -274,9 +274,9 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { .all(|upper_bound| self.sub_concrete_regions(option, upper_bound.region)) }); - // if there >1 option, we only make a choice if there is a - // single *least* choice -- i.e., some available region that - // is `<=` all the others. + // If there is more than one option, we only make a choice if + // there is a single *least* choice -- i.e., some available + // region that is `<=` all the others. let mut least_choice: ty::Region<'tcx> = match options.next() { Some(&r) => r, None => return false, @@ -554,6 +554,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { } } + // Check that all member constraints are satisfied. for member_constraint in &self.data.member_constraints { let member_region = var_data.normalize(self.tcx(), member_constraint.member_region); let choice_regions = member_constraint From 9588f7fe7434fa7201799d10a16ef810bf1ae2a1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 24 Jun 2019 15:12:47 -0400 Subject: [PATCH 53/68] rewrite `dup_vec` to use `IndexVec` instead of `u32` --- src/librustc/infer/lexical_region_resolve/mod.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index 7517f9c6de9d2..3808a0014a12c 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -22,7 +22,6 @@ use rustc_data_structures::graph::implementation::{ use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use smallvec::SmallVec; use std::fmt; -use std::u32; use syntax_pos::Span; mod graphviz; @@ -623,7 +622,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // idea is to report errors that derive from independent // regions of the graph, but not those that derive from // overlapping locations. - let mut dup_vec = vec![u32::MAX; self.num_vars()]; + let mut dup_vec = IndexVec::from_elem_n(None, self.num_vars()); for (node_vid, value) in var_data.values.iter_enumerated() { match *value { @@ -702,7 +701,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { fn collect_error_for_expanding_node( &self, graph: &RegionGraph<'tcx>, - dup_vec: &mut [u32], + dup_vec: &mut IndexVec>, node_idx: RegionVid, errors: &mut Vec>, ) { @@ -781,7 +780,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { graph: &RegionGraph<'tcx>, orig_node_idx: RegionVid, dir: Direction, - mut dup_vec: Option<&mut [u32]>, + mut dup_vec: Option<&mut IndexVec>>, ) -> (Vec>, bool) { struct WalkState<'tcx> { set: FxHashSet, @@ -806,9 +805,9 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // check whether we've visited this node on some previous walk if let Some(dup_vec) = &mut dup_vec { - if dup_vec[node_idx.index() as usize] == u32::MAX { - dup_vec[node_idx.index() as usize] = orig_node_idx.index() as u32; - } else if dup_vec[node_idx.index() as usize] != orig_node_idx.index() as u32 { + if dup_vec[node_idx].is_none() { + dup_vec[node_idx] = Some(orig_node_idx); + } else if dup_vec[node_idx] != Some(orig_node_idx) { state.dup_found = true; } From 6cab003fd1d83c55068b7193d91290aef56424df Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 24 Jun 2019 15:14:09 -0400 Subject: [PATCH 54/68] cleanup formatting of comment and add attribution --- .../infer/lexical_region_resolve/mod.rs | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/librustc/infer/lexical_region_resolve/mod.rs b/src/librustc/infer/lexical_region_resolve/mod.rs index 3808a0014a12c..d06c4434b3aaf 100644 --- a/src/librustc/infer/lexical_region_resolve/mod.rs +++ b/src/librustc/infer/lexical_region_resolve/mod.rs @@ -628,28 +628,29 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { match *value { VarValue::Value(_) => { /* Inference successful */ } VarValue::ErrorValue => { - /* Inference impossible: this value contains - inconsistent constraints. - - I think that in this case we should report an - error now -- unlike the case above, we can't - wait to see whether the user needs the result - of this variable. The reason is that the mere - existence of this variable implies that the - region graph is inconsistent, whether or not it - is used. - - For example, we may have created a region - variable that is the GLB of two other regions - which do not have a GLB. Even if that variable - is not used, it implies that those two regions - *should* have a GLB. - - At least I think this is true. It may be that - the mere existence of a conflict in a region variable - that is not used is not a problem, so if this rule - starts to create problems we'll have to revisit - this portion of the code and think hard about it. =) */ + // Inference impossible: this value contains + // inconsistent constraints. + // + // I think that in this case we should report an + // error now -- unlike the case above, we can't + // wait to see whether the user needs the result + // of this variable. The reason is that the mere + // existence of this variable implies that the + // region graph is inconsistent, whether or not it + // is used. + // + // For example, we may have created a region + // variable that is the GLB of two other regions + // which do not have a GLB. Even if that variable + // is not used, it implies that those two regions + // *should* have a GLB. + // + // At least I think this is true. It may be that + // the mere existence of a conflict in a region + // variable that is not used is not a problem, so + // if this rule starts to create problems we'll + // have to revisit this portion of the code and + // think hard about it. =) -- nikomatsakis self.collect_error_for_expanding_node(graph, &mut dup_vec, node_vid, errors); } } From 6234aed6c71741911726d5e8731f23957bc524e6 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 24 Jun 2019 15:16:17 -0400 Subject: [PATCH 55/68] opaque_types: various nits --- src/librustc/infer/opaque_types/mod.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs index 9c2cc5815de2e..949e3eaf48fe9 100644 --- a/src/librustc/infer/opaque_types/mod.rs +++ b/src/librustc/infer/opaque_types/mod.rs @@ -41,7 +41,7 @@ pub struct OpaqueTypeDecl<'tcx> { /// ``` /// existential type Foo; /// fn bar() -> Foo { - /// ^^^ this is the span we are looking for! + /// ^^^ This is the span we are looking for! /// ``` /// /// In cases where the fn returns `(impl Trait, impl Trait)` or @@ -234,13 +234,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// /// # The Solution /// - /// We generally prefer to make us our `<=` constraints, since - /// they integrate best into the region solve. To do that, we find - /// the "minimum" of all the arguments that appear in the substs: - /// that is, some region which is less than all the others. In the - /// case of `Foo1<'a>`, that would be `'a` (it's the only choice, - /// after all). Then we apply that as a least bound to the - /// variables (e.g., `'a <= '0`). + /// We generally prefer to make `<=` constraints, since they + /// integrate best into the region solver. To do that, we find the + /// "minimum" of all the arguments that appear in the substs: that + /// is, some region which is less than all the others. In the case + /// of `Foo1<'a>`, that would be `'a` (it's the only choice, after + /// all). Then we apply that as a least bound to the variables + /// (e.g., `'a <= '0`). /// /// In some cases, there is no minimum. Consider this example: /// @@ -316,7 +316,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } - /// See `constrain_opaque_types` for docs + /// See `constrain_opaque_types` for documentation. pub fn constrain_opaque_type>( &self, def_id: DefId, @@ -417,7 +417,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } /// As a fallback, we sometimes generate an "in constraint". For - /// case like `impl Foo<'a, 'b>`, where `'a` and `'b` cannot be + /// a case like `impl Foo<'a, 'b>`, where `'a` and `'b` cannot be /// related, we would generate a constraint `'r in ['a, 'b, /// 'static]` for each region `'r` that appears in the hidden type /// (i.e., it must be equal to `'a`, `'b`, or `'static`). From 0c8ebea227306704b98aaee362c89ae951cab89b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 24 Jun 2019 15:17:48 -0400 Subject: [PATCH 56/68] s/abstract_type_generics/opaque_type_generics/ --- src/librustc/infer/opaque_types/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs index 949e3eaf48fe9..b1ede2ae2cd46 100644 --- a/src/librustc/infer/opaque_types/mod.rs +++ b/src/librustc/infer/opaque_types/mod.rs @@ -333,7 +333,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { debug!("constrain_opaque_type: concrete_ty={:?}", concrete_ty); - let abstract_type_generics = tcx.generics_of(def_id); + let opaque_type_generics = tcx.generics_of(def_id); let span = tcx.def_span(def_id); @@ -365,7 +365,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // `['a]` for the first impl trait and `'b` for the // second. let mut least_region = None; - for param in &abstract_type_generics.params { + for param in &opaque_type_generics.params { match param.kind { GenericParamDefKind::Lifetime => {} _ => continue, @@ -396,7 +396,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // regions that appear in the impl trait. return self.generate_member_constraint( concrete_ty, - abstract_type_generics, + opaque_type_generics, opaque_defn, def_id, lr, @@ -427,7 +427,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { fn generate_member_constraint( &self, concrete_ty: Ty<'tcx>, - abstract_type_generics: &ty::Generics, + opaque_type_generics: &ty::Generics, opaque_defn: &OpaqueTypeDecl<'tcx>, opaque_type_def_id: DefId, conflict1: ty::Region<'tcx>, @@ -447,7 +447,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // type can be equal to any of the region parameters of the // opaque type definition. let choice_regions: Lrc>> = Lrc::new( - abstract_type_generics + opaque_type_generics .params .iter() .filter(|param| match param.kind { From 3ba1e19a87ca1be091df894ca288e679b8159d2f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 24 Jun 2019 15:21:25 -0400 Subject: [PATCH 57/68] opaque_types: more nits --- src/librustc/infer/opaque_types/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs index b1ede2ae2cd46..55eb29638751c 100644 --- a/src/librustc/infer/opaque_types/mod.rs +++ b/src/librustc/infer/opaque_types/mod.rs @@ -488,14 +488,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let span = self.tcx.def_span(opaque_type_def_id); - // Otherwise, we allow for async-await but not otherwise. + // Without a feature-gate, we only generate member-constraints for async-await. let context_name = match opaque_defn.origin { + // No feature-gate required for `async fn`. + hir::ExistTyOrigin::AsyncFn => return false, + + // Otherwise, generate the label we'll use in the error message. hir::ExistTyOrigin::ExistentialType => "existential type", hir::ExistTyOrigin::ReturnImplTrait => "impl Trait", - hir::ExistTyOrigin::AsyncFn => { - // we permit - return false; - } }; let msg = format!("ambiguous lifetime bound in `{}`", context_name); let mut err = self.tcx.sess.struct_span_err(span, &msg); @@ -624,7 +624,7 @@ pub fn unexpected_hidden_region_diagnostic( ); } else { // Ugh. This is a painful case: the hidden region is not one - // that we can easily summarize or explain. This can happens + // that we can easily summarize or explain. This can happen // in a case like // `src/test/ui/multiple-lifetimes/ordinary-bounds-unsuited.rs`: // From d9f4d2ad1a6af13390f99a8e8bf153e77f40bc12 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 24 Jun 2019 15:21:36 -0400 Subject: [PATCH 58/68] region_constraints: nits --- src/librustc/infer/region_constraints/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/librustc/infer/region_constraints/mod.rs b/src/librustc/infer/region_constraints/mod.rs index 0636589060763..fcb116fce5c9b 100644 --- a/src/librustc/infer/region_constraints/mod.rs +++ b/src/librustc/infer/region_constraints/mod.rs @@ -153,19 +153,19 @@ impl Constraint<'_> { /// ``` #[derive(Debug, Clone, HashStable)] pub struct MemberConstraint<'tcx> { - /// the def-id of the opaque type causing this constraint: used for error reporting + /// The `DefId` of the opaque type causing this constraint: used for error reporting. pub opaque_type_def_id: DefId, - /// the span where the hidden type was instantiated + /// The span where the hidden type was instantiated. pub definition_span: Span, - /// the hidden type in which `member_region` appears: used for error reporting + /// The hidden type in which `member_region` appears: used for error reporting. pub hidden_ty: Ty<'tcx>, - /// the region R0 + /// The region `R0`. pub member_region: Region<'tcx>, - /// the options O1..On + /// The options `O1..On`. pub choice_regions: Lrc>>, } From 0dd074e8548ce386d38e647a47d41ec75c93f307 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 24 Jun 2019 15:34:37 -0400 Subject: [PATCH 59/68] more centril nits --- .../graph/vec_graph/mod.rs | 2 +- .../borrow_check/nll/member_constraints.rs | 41 +++++++++++-------- .../nll/region_infer/error_reporting/mod.rs | 2 +- .../borrow_check/nll/region_infer/mod.rs | 24 +++++------ 4 files changed, 39 insertions(+), 30 deletions(-) diff --git a/src/librustc_data_structures/graph/vec_graph/mod.rs b/src/librustc_data_structures/graph/vec_graph/mod.rs index c425502f21948..6fb1bb42d2cfd 100644 --- a/src/librustc_data_structures/graph/vec_graph/mod.rs +++ b/src/librustc_data_structures/graph/vec_graph/mod.rs @@ -27,7 +27,7 @@ impl VecGraph { let num_edges = edge_pairs.len(); - // Store the *target* of each edge into `edge_targets` + // Store the *target* of each edge into `edge_targets`. let edge_targets: Vec = edge_pairs.iter().map(|&(_, target)| target).collect(); // Create the *edge starts* array. We are iterating over over diff --git a/src/librustc_mir/borrow_check/nll/member_constraints.rs b/src/librustc_mir/borrow_check/nll/member_constraints.rs index 43212ffddf3a5..b5e2e111f38e5 100644 --- a/src/librustc_mir/borrow_check/nll/member_constraints.rs +++ b/src/librustc_mir/borrow_check/nll/member_constraints.rs @@ -8,18 +8,18 @@ use std::ops::Index; use syntax_pos::Span; /// Compactly stores a set of `R0 member of [R1...Rn]` constraints, -/// indexed by the region R0. +/// indexed by the region `R0`. crate struct MemberConstraintSet<'tcx, R> where R: Copy + Hash + Eq, { - /// Stores the first "member" constraint for a given R0. This is an + /// Stores the first "member" constraint for a given `R0`. This is an /// index into the `constraints` vector below. first_constraints: FxHashMap, /// Stores the data about each `R0 member of [R1..Rn]` constraint. /// These are organized into a linked list, so each constraint - /// contains the index of the next constraint with the same R0. + /// contains the index of the next constraint with the same `R0`. constraints: IndexVec>, /// Stores the `R1..Rn` regions for *all* sets. For any given @@ -38,10 +38,10 @@ crate struct NllMemberConstraint<'tcx> { /// The span where the hidden type was instantiated. crate definition_span: Span, - /// The hidden type in which R0 appears. (Used in error reporting.) + /// The hidden type in which `R0` appears. (Used in error reporting.) crate hidden_ty: Ty<'tcx>, - /// The region R0. + /// The region `R0`. crate member_region_vid: ty::RegionVid, /// Index of `R1` in `choice_regions` vector from `MemberConstraintSet`. @@ -68,6 +68,15 @@ impl Default for MemberConstraintSet<'tcx, ty::RegionVid> { } impl<'tcx> MemberConstraintSet<'tcx, ty::RegionVid> { + /// Pushes a member constraint into the set. + /// + /// The input member constraint `m_c` is in the form produced by + /// the the `rustc::infer` code. + /// + /// The `to_region_vid` callback fn is used to convert the regions + /// within into `RegionVid` format -- it typically consults the + /// `UniversalRegions` data structure that is known to the caller + /// (but which this code is unaware of). crate fn push_constraint( &mut self, m_c: &MemberConstraint<'tcx>, @@ -93,14 +102,14 @@ impl<'tcx> MemberConstraintSet<'tcx, ty::RegionVid> { } } -impl<'tcx, R1> MemberConstraintSet<'tcx, R1> +impl MemberConstraintSet<'tcx, R1> where R1: Copy + Hash + Eq, { /// Remap the "member region" key using `map_fn`, producing a new - /// pick-constraint set. This is used in the NLL code to map from + /// member constraint set. This is used in the NLL code to map from /// the original `RegionVid` to an scc index. In some cases, we - /// may have multiple R1 values mapping to the same R2 key -- that + /// may have multiple `R1` values mapping to the same `R2` key -- that /// is ok, the two sets will be merged. crate fn into_mapped( self, @@ -112,12 +121,12 @@ where // We can re-use most of the original data, just tweaking the // linked list links a bit. // - // For example if we had two keys Ra and Rb that both now wind - // up mapped to the same key S, we would append the linked - // list for Ra onto the end of the linked list for Rb (or vice - // versa) -- this basically just requires rewriting the final - // link from one list to point at the othe other (see - // `append_list`). + // For example if we had two keys `Ra` and `Rb` that both now + // wind up mapped to the same key `S`, we would append the + // linked list for `Ra` onto the end of the linked list for + // `Rb` (or vice versa) -- this basically just requires + // rewriting the final link from one list to point at the othe + // other (see `append_list`). let MemberConstraintSet { first_constraints, mut constraints, choice_regions } = self; @@ -140,7 +149,7 @@ where } } -impl<'tcx, R> MemberConstraintSet<'tcx, R> +impl MemberConstraintSet<'tcx, R> where R: Copy + Hash + Eq, { @@ -169,7 +178,7 @@ where } /// Returns the "choice regions" for a given member - /// constraint. This is the R1..Rn from a constraint like: + /// constraint. This is the `R1..Rn` from a constraint like: /// /// ``` /// R0 member of [R1..Rn] diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs index b3649b2179fff..9e08961f440f2 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs @@ -221,7 +221,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { .outgoing_edges(r, &self.constraints, fr_static); - // But pick-constraints can also give rise to `'r: 'x` + // But member constraints can also give rise to `'r: 'x` // edges that were not part of the graph initially, so // watch out for those. let outgoing_edges_from_picks = self.applied_member_constraints(r) diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 78f6e731ce244..7e83ea2aa4ed9 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -75,7 +75,7 @@ pub struct RegionInferenceContext<'tcx> { /// The "R0 member of [R1..Rn]" constraints, indexed by SCC. member_constraints: Rc>, - /// Records the pick-constraints that we applied to each scc. + /// Records the member constraints that we applied to each scc. /// This is useful for error reporting. Once constraint /// propagation is done, this vector is sorted according to /// `member_region_scc`. @@ -447,7 +447,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } /// Once region solving has completed, this function will return - /// the pick-constraints that were applied to the value of a given + /// the member constraints that were applied to the value of a given /// region `r`. See `AppliedMemberConstraint`. fn applied_member_constraints(&self, r: impl ToRegionVid) -> &[AppliedMemberConstraint] { let scc = self.constraint_sccs.scc(r.to_region_vid()); @@ -598,7 +598,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } - // Now take member constraints into account + // Now take member constraints into account. let member_constraints = self.member_constraints.clone(); for m_c_i in member_constraints.indices(scc_a) { self.apply_member_constraint( @@ -615,7 +615,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { ); } - /// Invoked for each `member R0 of [R1..Rn]` constraint. + /// Invoked for each `R0 member of [R1..Rn]` constraint. /// /// `scc` is the SCC containing R0, and `choice_regions` are the /// `R1..Rn` regions -- they are always known to be universal @@ -659,16 +659,16 @@ impl<'tcx> RegionInferenceContext<'tcx> { assert!(self.scc_universes[scc] == ty::UniverseIndex::ROOT); debug_assert!( self.scc_values.placeholders_contained_in(scc).next().is_none(), - "scc {:?} in a pick-constraint has placeholder value: {:?}", + "scc {:?} in a member constraint has placeholder value: {:?}", scc, self.scc_values.region_value_str(scc), ); // The existing value for `scc` is a lower-bound. This will - // consist of some set {P} + {LB} of points {P} and - // lower-bound free regions {LB}. As each choice region O is a - // free region, it will outlive the points. But we can only - // consider the option O if O: LB. + // consist of some set `{P} + {LB}` of points `{P}` and + // lower-bound free regions `{LB}`. As each choice region `O` + // is a free region, it will outlive the points. But we can + // only consider the option `O` if `O: LB`. choice_regions.retain(|&o_r| { self.scc_values .universal_regions_outlived_by(scc) @@ -677,8 +677,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { debug!("apply_member_constraint: after lb, choice_regions={:?}", choice_regions); // Now find all the *upper bounds* -- that is, each UB is a - // free region that must outlive the member region R0 (`UB: - // R0`). Therefore, we need only keep an option O if `UB: O` + // free region that must outlive the member region `R0` (`UB: + // R0`). Therefore, we need only keep an option `O` if `UB: O` // for all UB. if choice_regions.len() > 1 { let universal_region_relations = self.universal_region_relations.clone(); @@ -755,7 +755,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // I wanted to return an `impl Iterator` here, but it's // annoying because the `rev_constraint_graph` is in a local // variable. We'd need a "once-cell" or some such thing to let - // us borrow it for the right amount of time. + // us borrow it for the right amount of time. -- nikomatsakis let rev_constraint_graph = self.rev_constraint_graph(); let scc_values = &self.scc_values; let mut duplicates = FxHashSet::default(); From 9217909518cecc3b8636e970bfcb6c0be231e3f2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 24 Jun 2019 15:52:46 -0400 Subject: [PATCH 60/68] pass a parameter to enable impl Trait instead of a vec --- .../borrow_check/nll/region_infer/mod.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 7e83ea2aa4ed9..89d0be3a10cef 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -682,7 +682,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { // for all UB. if choice_regions.len() > 1 { let universal_region_relations = self.universal_region_relations.clone(); - for ub in self.upper_bounds(scc) { + let rev_constraint_graph = self.rev_constraint_graph(); + for ub in self.upper_bounds(scc, &rev_constraint_graph) { debug!("apply_member_constraint: ub={:?}", ub); choice_regions.retain(|&o_r| universal_region_relations.outlives(ub, o_r)); } @@ -749,22 +750,17 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// Compute and return the reverse SCC-based constraint graph (lazilly). fn upper_bounds( - &mut self, + &'a mut self, scc0: ConstraintSccIndex, - ) -> Vec { - // I wanted to return an `impl Iterator` here, but it's - // annoying because the `rev_constraint_graph` is in a local - // variable. We'd need a "once-cell" or some such thing to let - // us borrow it for the right amount of time. -- nikomatsakis - let rev_constraint_graph = self.rev_constraint_graph(); + rev_constraint_graph: &'a VecGraph, + ) -> impl Iterator + 'a { let scc_values = &self.scc_values; let mut duplicates = FxHashSet::default(); rev_constraint_graph .depth_first_search(scc0) .skip(1) - .flat_map(|scc1| scc_values.universal_regions_outlived_by(scc1)) - .filter(|&r| duplicates.insert(r)) - .collect() + .flat_map(move |scc1| scc_values.universal_regions_outlived_by(scc1)) + .filter(move |&r| duplicates.insert(r)) } /// Compute and return the reverse SCC-based constraint graph (lazilly). From 076b0d0f5cc33f108fda043c2e6e12229d734cf4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 24 Jun 2019 15:55:39 -0400 Subject: [PATCH 61/68] more nits + typos --- src/librustc_mir/borrow_check/nll/region_infer/mod.rs | 6 +++--- src/librustc_mir/borrow_check/nll/type_check/mod.rs | 4 ++-- src/libsyntax/feature_gate.rs | 2 +- .../ordinary-bounds-pick-original-existential.rs | 2 +- .../multiple-lifetimes/ordinary-bounds-pick-original.rs | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 89d0be3a10cef..4e609460c1f70 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -598,7 +598,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } - // Now take member constraints into account. + // Now take member constraints into account. let member_constraints = self.member_constraints.clone(); for m_c_i in member_constraints.indices(scc_a) { self.apply_member_constraint( @@ -1560,7 +1560,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let choice_regions = member_constraints.choice_regions(m_c_i); debug!("check_member_constraint: choice_regions={:?}", choice_regions); - // did the pick-region wind up equal to any of the option regions? + // Did the member region wind up equal to any of the option regions? if let Some(o) = choice_regions.iter().find(|&&o_r| { self.eval_equal(o_r, m_c.member_region_vid) }) { @@ -1568,7 +1568,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { continue; } - // if not, report an error + // If not, report an error. let region_scope_tree = &infcx.tcx.region_scope_tree(mir_def_id); let member_region = infcx.tcx.mk_region(ty::ReVar(member_region_vid)); opaque_types::unexpected_hidden_region_diagnostic( diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 2cdd1363cdf96..cdbbe1d02bd92 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -2519,9 +2519,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let closure_constraints = QueryRegionConstraints { outlives: closure_region_requirements.apply_requirements(tcx, def_id, substs), - // Presently, closures never propagate pick + // Presently, closures never propagate member // constraints to their parents -- they are enforced - // locally. This is largely a non-issue as pick + // locally. This is largely a non-issue as member // constraints only come from `-> impl Trait` and // friends which don't appear (thus far...) in // closures. diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 1223c069d6569..e6a09e7f873ea 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -570,7 +570,7 @@ declare_features! ( // Allows explicit discriminants on non-unit enum variants. (active, arbitrary_enum_discriminant, "1.37.0", Some(60553), None), - // Allows impl trait with multiple unrelated lifetimes + // Allows `impl Trait` with multiple unrelated lifetimes. (active, member_constraints, "1.37.0", Some(61977), None), // ------------------------------------------------------------------------- diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-existential.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-existential.rs index a1ec89e8fbd05..a103e21e047e5 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-existential.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-existential.rs @@ -10,7 +10,7 @@ trait Trait<'a, 'b> { } impl Trait<'_, '_> for T { } // Here we wind up selecting `'a` and `'b` in the hidden type because -// those are the types that appear inth e original values. +// those are the types that appear in the original values. existential type Foo<'a, 'b>: Trait<'a, 'b>; diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs index 21979b001797e..43f8bd71e4108 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs @@ -9,7 +9,7 @@ trait Trait<'a, 'b> { } impl Trait<'_, '_> for T { } // Here we wind up selecting `'a` and `'b` in the hidden type because -// those are the types that appear inth e original values. +// those are the types that appear in the original values. fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { // In this simple case, you have a hidden type `(&'0 u8, &'1 u8)` and constraints like From 5bd423a7ca5920e175a5d3eb8180dc24d9aa7cc4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 25 Jun 2019 10:55:32 -0400 Subject: [PATCH 62/68] introduce more tests covering `async fn` surface --- .../async-await/multiple-lifetimes/elided.rs | 12 +++++ .../async-await/multiple-lifetimes/fn-ptr.rs | 14 ++++++ .../hrtb.rs} | 5 +- .../async-await/multiple-lifetimes/named.rs | 12 +++++ .../multiple-lifetimes/partial-relation.rs | 15 ++++++ .../multiple-lifetimes/ret-impl-trait-fg.rs | 18 ++++++++ .../ret-impl-trait-no-fg.rs | 18 ++++++++ .../ret-impl-trait-no-fg.stderr | 10 ++++ .../multiple-lifetimes/ret-impl-trait-one.rs | 27 +++++++++++ .../ret-impl-trait-one.stderr | 11 +++++ .../async-await/multiple-lifetimes/ret-ref.rs | 46 +++++++++++++++++++ .../multiple-lifetimes/ret-ref.stderr | 37 +++++++++++++++ .../multiple-lifetimes/variance.rs | 18 ++++++++ 13 files changed, 239 insertions(+), 4 deletions(-) create mode 100644 src/test/ui/async-await/multiple-lifetimes/elided.rs create mode 100644 src/test/ui/async-await/multiple-lifetimes/fn-ptr.rs rename src/test/ui/async-await/{async-fn-multiple-lifetimes.rs => multiple-lifetimes/hrtb.rs} (73%) create mode 100644 src/test/ui/async-await/multiple-lifetimes/named.rs create mode 100644 src/test/ui/async-await/multiple-lifetimes/partial-relation.rs create mode 100644 src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-fg.rs create mode 100644 src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.rs create mode 100644 src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.stderr create mode 100644 src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.rs create mode 100644 src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.stderr create mode 100644 src/test/ui/async-await/multiple-lifetimes/ret-ref.rs create mode 100644 src/test/ui/async-await/multiple-lifetimes/ret-ref.stderr create mode 100644 src/test/ui/async-await/multiple-lifetimes/variance.rs diff --git a/src/test/ui/async-await/multiple-lifetimes/elided.rs b/src/test/ui/async-await/multiple-lifetimes/elided.rs new file mode 100644 index 0000000000000..8355a450ae641 --- /dev/null +++ b/src/test/ui/async-await/multiple-lifetimes/elided.rs @@ -0,0 +1,12 @@ +// edition:2018 +// run-pass + +// Test that we can use async fns with multiple arbitrary lifetimes. + +#![feature(async_await, await_macro)] + +async fn multiple_elided_lifetimes(_: &u8, _: &u8) {} + +fn main() { + let _ = multiple_elided_lifetimes(&22, &44); +} diff --git a/src/test/ui/async-await/multiple-lifetimes/fn-ptr.rs b/src/test/ui/async-await/multiple-lifetimes/fn-ptr.rs new file mode 100644 index 0000000000000..5f3820b6c31cd --- /dev/null +++ b/src/test/ui/async-await/multiple-lifetimes/fn-ptr.rs @@ -0,0 +1,14 @@ +// edition:2018 +// run-pass + +// Test that we can use async fns with multiple arbitrary lifetimes. + +#![feature(arbitrary_self_types, async_await, await_macro)] + +async fn multiple_named_lifetimes<'a, 'b>(_: &'a u8, _: &'b u8, _: fn(&u8)) {} + +fn gimme(_: &u8) { } + +fn main() { + let _ = multiple_named_lifetimes(&22, &44, gimme); +} diff --git a/src/test/ui/async-await/async-fn-multiple-lifetimes.rs b/src/test/ui/async-await/multiple-lifetimes/hrtb.rs similarity index 73% rename from src/test/ui/async-await/async-fn-multiple-lifetimes.rs rename to src/test/ui/async-await/multiple-lifetimes/hrtb.rs index c1202bfe21992..620b0080966b9 100644 --- a/src/test/ui/async-await/async-fn-multiple-lifetimes.rs +++ b/src/test/ui/async-await/multiple-lifetimes/hrtb.rs @@ -4,17 +4,14 @@ // Test that we can use async fns with multiple arbitrary lifetimes. #![feature(arbitrary_self_types, async_await, await_macro)] +#![allow(dead_code)] use std::ops::Add; -async fn multiple_named_lifetimes<'a, 'b>(_: &'a u8, _: &'b u8) {} - async fn multiple_hrtb_and_single_named_lifetime_ok<'c>( _: impl for<'a> Add<&'a u8>, _: impl for<'b> Add<&'b u8>, _: &'c u8, ) {} -async fn multiple_elided_lifetimes(_: &u8, _: &u8) {} - fn main() {} diff --git a/src/test/ui/async-await/multiple-lifetimes/named.rs b/src/test/ui/async-await/multiple-lifetimes/named.rs new file mode 100644 index 0000000000000..7d13d48bc8bbd --- /dev/null +++ b/src/test/ui/async-await/multiple-lifetimes/named.rs @@ -0,0 +1,12 @@ +// edition:2018 +// run-pass + +// Test that we can use async fns with multiple arbitrary lifetimes. + +#![feature(arbitrary_self_types, async_await, await_macro)] + +async fn multiple_named_lifetimes<'a, 'b>(_: &'a u8, _: &'b u8) {} + +fn main() { + let _ = multiple_named_lifetimes(&22, &44); +} diff --git a/src/test/ui/async-await/multiple-lifetimes/partial-relation.rs b/src/test/ui/async-await/multiple-lifetimes/partial-relation.rs new file mode 100644 index 0000000000000..903c43950a5c4 --- /dev/null +++ b/src/test/ui/async-await/multiple-lifetimes/partial-relation.rs @@ -0,0 +1,15 @@ +// edition:2018 +// run-pass + +#![feature(async_await)] + +async fn lotsa_lifetimes<'a, 'b, 'c>(a: &'a u32, b: &'b u32, c: &'c u32) -> (&'a u32, &'b u32) + where 'b: 'a +{ + drop((a, c)); + (b, b) +} + +fn main() { + let _ = lotsa_lifetimes(&22, &44, &66); +} diff --git a/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-fg.rs b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-fg.rs new file mode 100644 index 0000000000000..08622311f7b1c --- /dev/null +++ b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-fg.rs @@ -0,0 +1,18 @@ +// edition:2018 +// run-pass + +// Test that a feature gate is needed to use `impl Trait` as the +// return type of an async. + +#![feature(async_await, member_constraints)] + +trait Trait<'a, 'b> { } +impl Trait<'_, '_> for T { } + +async fn async_ret_impl_trait<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { + (a, b) +} + +fn main() { + let _ = async_ret_impl_trait(&22, &44); +} diff --git a/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.rs b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.rs new file mode 100644 index 0000000000000..0359d5deb4d30 --- /dev/null +++ b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.rs @@ -0,0 +1,18 @@ +// edition:2018 + +// Test that a feature gate is needed to use `impl Trait` as the +// return type of an async. + +#![feature(arbitrary_self_types, async_await, await_macro)] + +trait Trait<'a, 'b> { } +impl Trait<'_, '_> for T { } + +async fn async_ret_impl_trait<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { + //~^ ERROR ambiguous lifetime bound + (a, b) +} + +fn main() { + let _ = async_ret_impl_trait(&22, &44); +} diff --git a/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.stderr b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.stderr new file mode 100644 index 0000000000000..de2c85d772a72 --- /dev/null +++ b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.stderr @@ -0,0 +1,10 @@ +error: ambiguous lifetime bound in `impl Trait` + --> $DIR/ret-impl-trait-no-fg.rs:11:64 + | +LL | async fn async_ret_impl_trait<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> { + | ^^^^^^^^^^^^^^^^^^ neither `'a` nor `'b` outlives the other + | + = help: add #![feature(member_constraints)] to the crate attributes to enable + +error: aborting due to previous error + diff --git a/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.rs b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.rs new file mode 100644 index 0000000000000..27f789e340d09 --- /dev/null +++ b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.rs @@ -0,0 +1,27 @@ +// edition:2018 + +// Test that a feature gate is needed to use `impl Trait` as the +// return type of an async. + +#![feature(async_await, member_constraints)] + +trait Trait<'a> { } +impl Trait<'_> for T { } + +// Only `'a` permitted in return type, not `'b`. +async fn async_ret_impl_trait1<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a> { + //~^ ERROR lifetime mismatch + (a, b) +} + +// As above, but `'b: 'a`, so return type ca be inferred to `(&'a u8, +// &'a u8)`. +async fn async_ret_impl_trait2<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a> +where + 'b: 'a, +{ + (a, b) +} + +fn main() { +} diff --git a/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.stderr b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.stderr new file mode 100644 index 0000000000000..f6d611517bc8f --- /dev/null +++ b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.stderr @@ -0,0 +1,11 @@ +error[E0623]: lifetime mismatch + --> $DIR/ret-impl-trait-one.rs:12:65 + | +LL | async fn async_ret_impl_trait1<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a> { + | ------ ^^^^^^^^^^^^^^ + | | | + | | ...but data from `b` is returned here + | this parameter and the return type are declared with different lifetimes... + +error: aborting due to previous error + diff --git a/src/test/ui/async-await/multiple-lifetimes/ret-ref.rs b/src/test/ui/async-await/multiple-lifetimes/ret-ref.rs new file mode 100644 index 0000000000000..98da90161e5fd --- /dev/null +++ b/src/test/ui/async-await/multiple-lifetimes/ret-ref.rs @@ -0,0 +1,46 @@ +// edition:2018 + +// Test that we get the expected borrow check errors when an async +// function (which takes multiple lifetimes) only returns data from +// one of them. + +#![feature(async_await)] + +async fn multiple_named_lifetimes<'a, 'b>(a: &'a u8, _: &'b u8) -> &'a u8 { + a +} + +// Both are borrowed whilst the future is live. +async fn future_live() { + let mut a = 22; + let mut b = 44; + let future = multiple_named_lifetimes(&a, &b); + a += 1; //~ ERROR cannot assign + b += 1; //~ ERROR cannot assign + let p = future.await; + drop(p); +} + +// Just the return value is live after future is awaited. +async fn just_return_live() { + let mut a = 22; + let mut b = 44; + let future = multiple_named_lifetimes(&a, &b); + let p = future.await; + a += 1; //~ ERROR cannot assign + b += 1; + drop(p); +} + +// Once `p` is dead, both `a` and `b` are unborrowed. +async fn after_both_dead() { + let mut a = 22; + let mut b = 44; + let future = multiple_named_lifetimes(&a, &b); + let p = future.await; + drop(p); + a += 1; + b += 1; +} + +fn main() { } diff --git a/src/test/ui/async-await/multiple-lifetimes/ret-ref.stderr b/src/test/ui/async-await/multiple-lifetimes/ret-ref.stderr new file mode 100644 index 0000000000000..fe70d35942c7c --- /dev/null +++ b/src/test/ui/async-await/multiple-lifetimes/ret-ref.stderr @@ -0,0 +1,37 @@ +error[E0506]: cannot assign to `a` because it is borrowed + --> $DIR/ret-ref.rs:18:5 + | +LL | let future = multiple_named_lifetimes(&a, &b); + | -- borrow of `a` occurs here +LL | a += 1; + | ^^^^^^ assignment to borrowed `a` occurs here +LL | b += 1; +LL | let p = future.await; + | ------ borrow later used here + +error[E0506]: cannot assign to `b` because it is borrowed + --> $DIR/ret-ref.rs:19:5 + | +LL | let future = multiple_named_lifetimes(&a, &b); + | -- borrow of `b` occurs here +LL | a += 1; +LL | b += 1; + | ^^^^^^ assignment to borrowed `b` occurs here +LL | let p = future.await; + | ------ borrow later used here + +error[E0506]: cannot assign to `a` because it is borrowed + --> $DIR/ret-ref.rs:30:5 + | +LL | let future = multiple_named_lifetimes(&a, &b); + | -- borrow of `a` occurs here +LL | let p = future.await; +LL | a += 1; + | ^^^^^^ assignment to borrowed `a` occurs here +LL | b += 1; +LL | drop(p); + | - borrow later used here + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0506`. diff --git a/src/test/ui/async-await/multiple-lifetimes/variance.rs b/src/test/ui/async-await/multiple-lifetimes/variance.rs new file mode 100644 index 0000000000000..4c1788712148b --- /dev/null +++ b/src/test/ui/async-await/multiple-lifetimes/variance.rs @@ -0,0 +1,18 @@ +// edition:2018 +// run-pass + +// Test for async fn where the parameters have distinct lifetime +// parameters that appear in all possible variances. + +#![feature(arbitrary_self_types, async_await, await_macro)] + +#[allow(dead_code)] +async fn lotsa_lifetimes<'a, 'b, 'c>(_: fn(&'a u8), _: fn(&'b u8) -> &'b u8, _: fn() -> &'c u8) { } + +fn take_any(_: &u8) { } +fn identify(x: &u8) -> &u8 { x } +fn give_back() -> &'static u8 { &22 } + +fn main() { + let _ = lotsa_lifetimes(take_any, identify, give_back); +} From 8f9a3aff8b2f13336353a7bc43d7e277a83c4a6b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 29 Jun 2019 06:52:35 -0400 Subject: [PATCH 63/68] Update src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.rs Co-Authored-By: matthewjasper --- .../ui/async-await/multiple-lifetimes/ret-impl-trait-one.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.rs b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.rs index 27f789e340d09..e1b714652737f 100644 --- a/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.rs +++ b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.rs @@ -14,7 +14,7 @@ async fn async_ret_impl_trait1<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a> { (a, b) } -// As above, but `'b: 'a`, so return type ca be inferred to `(&'a u8, +// As above, but `'b: 'a`, so return type can be inferred to `(&'a u8, // &'a u8)`. async fn async_ret_impl_trait2<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a> where From 1d93e3cef963db1fbb9bb018f75cfbca4481522c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 29 Jun 2019 06:52:47 -0400 Subject: [PATCH 64/68] Update src/test/ui/async-await/multiple-lifetimes/fn-ptr.rs Co-Authored-By: matthewjasper --- src/test/ui/async-await/multiple-lifetimes/fn-ptr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/ui/async-await/multiple-lifetimes/fn-ptr.rs b/src/test/ui/async-await/multiple-lifetimes/fn-ptr.rs index 5f3820b6c31cd..a7254cee75526 100644 --- a/src/test/ui/async-await/multiple-lifetimes/fn-ptr.rs +++ b/src/test/ui/async-await/multiple-lifetimes/fn-ptr.rs @@ -3,7 +3,7 @@ // Test that we can use async fns with multiple arbitrary lifetimes. -#![feature(arbitrary_self_types, async_await, await_macro)] +#![feature(async_await)] async fn multiple_named_lifetimes<'a, 'b>(_: &'a u8, _: &'b u8, _: fn(&u8)) {} From f130e9a64da945742b5ee2034d2bac540bf5808f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 1 Jul 2019 17:53:08 -0400 Subject: [PATCH 65/68] Update src/test/ui/async-await/multiple-lifetimes/elided.rs Co-Authored-By: matthewjasper --- src/test/ui/async-await/multiple-lifetimes/elided.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/ui/async-await/multiple-lifetimes/elided.rs b/src/test/ui/async-await/multiple-lifetimes/elided.rs index 8355a450ae641..45f3170d4c309 100644 --- a/src/test/ui/async-await/multiple-lifetimes/elided.rs +++ b/src/test/ui/async-await/multiple-lifetimes/elided.rs @@ -3,7 +3,7 @@ // Test that we can use async fns with multiple arbitrary lifetimes. -#![feature(async_await, await_macro)] +#![feature(async_await)] async fn multiple_elided_lifetimes(_: &u8, _: &u8) {} From 7f319a7a41d8cebbfc8d98517c4bb2862e8fa1ef Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 1 Jul 2019 17:53:21 -0400 Subject: [PATCH 66/68] Update src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.rs Co-Authored-By: matthewjasper --- .../ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.rs b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.rs index 0359d5deb4d30..08ecea4cc85fc 100644 --- a/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.rs +++ b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.rs @@ -3,7 +3,7 @@ // Test that a feature gate is needed to use `impl Trait` as the // return type of an async. -#![feature(arbitrary_self_types, async_await, await_macro)] +#![feature(async_await)] trait Trait<'a, 'b> { } impl Trait<'_, '_> for T { } From 0c2c2416bfc312f9d119b6ec13773d9e6a49ee90 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 1 Jul 2019 17:54:57 -0400 Subject: [PATCH 67/68] address nits --- src/test/ui/async-await/multiple-lifetimes/variance.rs | 2 +- .../multiple-lifetimes/ordinary-bounds-pick-original-elided.rs | 2 +- .../ordinary-bounds-pick-original-existential.rs | 2 +- .../multiple-lifetimes/ordinary-bounds-pick-original.rs | 2 +- .../impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.rs | 2 +- .../impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.rs | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/ui/async-await/multiple-lifetimes/variance.rs b/src/test/ui/async-await/multiple-lifetimes/variance.rs index 4c1788712148b..b52ad17d5631d 100644 --- a/src/test/ui/async-await/multiple-lifetimes/variance.rs +++ b/src/test/ui/async-await/multiple-lifetimes/variance.rs @@ -4,7 +4,7 @@ // Test for async fn where the parameters have distinct lifetime // parameters that appear in all possible variances. -#![feature(arbitrary_self_types, async_await, await_macro)] +#![feature(async_await)] #[allow(dead_code)] async fn lotsa_lifetimes<'a, 'b, 'c>(_: fn(&'a u8), _: fn(&'b u8) -> &'b u8, _: fn() -> &'c u8) { } diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-elided.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-elided.rs index e2cb574fac084..5f484773405cd 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-elided.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-elided.rs @@ -1,5 +1,5 @@ // edition:2018 -// run-pass +// compile-pass // revisions: migrate mir //[mir]compile-flags: -Z borrowck=mir diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-existential.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-existential.rs index a103e21e047e5..c17ae6f0519bf 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-existential.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-existential.rs @@ -1,5 +1,5 @@ // edition:2018 -// run-pass +// compile-pass // revisions: migrate mir //[mir]compile-flags: -Z borrowck=mir diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs index 43f8bd71e4108..31891ef15c754 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs @@ -1,5 +1,5 @@ // edition:2018 -// run-pass +// compile-pass // revisions: migrate mir //[mir]compile-flags: -Z borrowck=mir diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.rs index 0dfc118d78c29..29c997085d8f4 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.rs @@ -1,5 +1,5 @@ // edition:2018 -// run-pass +// compile-pass // revisions: migrate mir //[mir]compile-flags: -Z borrowck=mir diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.rs index 8f85b444d0886..7f9c92f15a2f9 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.rs @@ -25,7 +25,7 @@ fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> // ``` // 'a: '0 // 'b: '1 - // '0 in ['d, 'e] + // '0 in ['a, 'b] // ``` // // but we don't have it. From f7e00a55bba51de1d58f829b562f9aff05d543f3 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 2 Jul 2019 14:23:38 -0400 Subject: [PATCH 68/68] fix ICE with delay-span-bug --- src/librustc/infer/opaque_types/mod.rs | 9 ++++++--- .../ret-impl-trait-one.nll.stderr | 15 +++++++++++++++ .../ordinary-bounds-unrelated.nll.stderr | 9 +++++++++ .../ordinary-bounds-unsuited.nll.stderr | 9 +++++++++ 4 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.nll.stderr create mode 100644 src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.nll.stderr create mode 100644 src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.nll.stderr diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs index 55eb29638751c..f43e3fa0b7787 100644 --- a/src/librustc/infer/opaque_types/mod.rs +++ b/src/librustc/infer/opaque_types/mod.rs @@ -656,10 +656,13 @@ pub fn unexpected_hidden_region_diagnostic( // error reporting, which in turn should have prevented us // from getting trying to infer the hidden type // completely. - span_bug!( + tcx.sess.delay_span_bug( span, - "hidden type captures unexpected lifetime `{:?}` but no region inference failure", - hidden_region, + &format!( + "hidden type captures unexpected lifetime `{:?}` \ + but no region inference failure", + hidden_region, + ), ); } } diff --git a/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.nll.stderr b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.nll.stderr new file mode 100644 index 0000000000000..b4d5d3ec051e2 --- /dev/null +++ b/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.nll.stderr @@ -0,0 +1,15 @@ +error: lifetime may not live long enough + --> $DIR/ret-impl-trait-one.rs:12:80 + | +LL | async fn async_ret_impl_trait1<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a> { + | ________________________________--__--__________________________________________^ + | | | | + | | | lifetime `'b` defined here + | | lifetime `'a` defined here +LL | | +LL | | (a, b) +LL | | } + | |_^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` + +error: aborting due to previous error + diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.nll.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.nll.stderr new file mode 100644 index 0000000000000..a255c48ec6ef1 --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.nll.stderr @@ -0,0 +1,9 @@ +error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds + --> $DIR/ordinary-bounds-unrelated.rs:18:74 + | +LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e> + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0700`. diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.nll.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.nll.stderr new file mode 100644 index 0000000000000..af42ed1c5c15a --- /dev/null +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.nll.stderr @@ -0,0 +1,9 @@ +error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds + --> $DIR/ordinary-bounds-unsuited.rs:20:62 + | +LL | fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0700`.